MODULE_DEVICE_TABLE(of, stm32_match);
#endif
+static void stm32_usart_of_dma_rx_remove(struct stm32_port *stm32port,
+ struct platform_device *pdev)
+{
+ if (stm32port->rx_buf)
+ dma_free_coherent(&pdev->dev, RX_BUF_L, stm32port->rx_buf,
+ stm32port->rx_dma_buf);
+}
+
static int stm32_usart_of_dma_rx_probe(struct stm32_port *stm32port,
struct platform_device *pdev)
{
if (uart_console(port))
return -ENODEV;
- /* Request DMA RX channel */
- stm32port->rx_ch = dma_request_slave_channel(dev, "rx");
- if (!stm32port->rx_ch) {
- dev_info(dev, "rx dma alloc failed\n");
- return -ENODEV;
- }
stm32port->rx_buf = dma_alloc_coherent(&pdev->dev, RX_BUF_L,
&stm32port->rx_dma_buf,
GFP_KERNEL);
- if (!stm32port->rx_buf) {
- ret = -ENOMEM;
- goto alloc_err;
- }
+ if (!stm32port->rx_buf)
+ return -ENOMEM;
/* Configure DMA channel */
memset(&config, 0, sizeof(config));
ret = dmaengine_slave_config(stm32port->rx_ch, &config);
if (ret < 0) {
dev_err(dev, "rx dma channel config failed\n");
- ret = -ENODEV;
- goto config_err;
+ stm32_usart_of_dma_rx_remove(stm32port, pdev);
+ return ret;
}
/* Prepare a DMA cyclic transaction */
DMA_PREP_INTERRUPT);
if (!desc) {
dev_err(dev, "rx dma prep cyclic failed\n");
- ret = -ENODEV;
- goto config_err;
+ stm32_usart_of_dma_rx_remove(stm32port, pdev);
+ return -ENODEV;
}
/* No callback as dma buffer is drained on usart interrupt */
ret = dma_submit_error(dmaengine_submit(desc));
if (ret) {
dmaengine_terminate_sync(stm32port->rx_ch);
- goto config_err;
+ stm32_usart_of_dma_rx_remove(stm32port, pdev);
+ return ret;
}
/* Issue pending DMA requests */
dma_async_issue_pending(stm32port->rx_ch);
return 0;
+}
-config_err:
- dma_free_coherent(&pdev->dev,
- RX_BUF_L, stm32port->rx_buf,
- stm32port->rx_dma_buf);
-
-alloc_err:
- dma_release_channel(stm32port->rx_ch);
- stm32port->rx_ch = NULL;
-
- return ret;
+static void stm32_usart_of_dma_tx_remove(struct stm32_port *stm32port,
+ struct platform_device *pdev)
+{
+ if (stm32port->tx_buf)
+ dma_free_coherent(&pdev->dev, TX_BUF_L, stm32port->tx_buf,
+ stm32port->tx_dma_buf);
}
static int stm32_usart_of_dma_tx_probe(struct stm32_port *stm32port,
stm32port->tx_dma_busy = false;
- /* Request DMA TX channel */
- stm32port->tx_ch = dma_request_slave_channel(dev, "tx");
- if (!stm32port->tx_ch) {
- dev_info(dev, "tx dma alloc failed\n");
- return -ENODEV;
- }
stm32port->tx_buf = dma_alloc_coherent(&pdev->dev, TX_BUF_L,
&stm32port->tx_dma_buf,
GFP_KERNEL);
- if (!stm32port->tx_buf) {
- ret = -ENOMEM;
- goto alloc_err;
- }
+ if (!stm32port->tx_buf)
+ return -ENOMEM;
/* Configure DMA channel */
memset(&config, 0, sizeof(config));
ret = dmaengine_slave_config(stm32port->tx_ch, &config);
if (ret < 0) {
dev_err(dev, "tx dma channel config failed\n");
- ret = -ENODEV;
- goto config_err;
+ stm32_usart_of_dma_tx_remove(stm32port, pdev);
+ return ret;
}
return 0;
-
-config_err:
- dma_free_coherent(&pdev->dev,
- TX_BUF_L, stm32port->tx_buf,
- stm32port->tx_dma_buf);
-
-alloc_err:
- dma_release_channel(stm32port->tx_ch);
- stm32port->tx_ch = NULL;
-
- return ret;
}
static int stm32_usart_serial_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
ret = dev_pm_set_wake_irq(&pdev->dev, stm32port->port.irq);
if (ret)
- goto err_nowup;
+ goto err_deinit_port;
}
- ret = stm32_usart_of_dma_rx_probe(stm32port, pdev);
- if (ret)
- dev_info(&pdev->dev, "interrupt mode used for rx (no dma)\n");
+ stm32port->rx_ch = dma_request_chan(&pdev->dev, "rx");
+ if (PTR_ERR(stm32port->rx_ch) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_wakeirq;
+ }
+ /* Fall back in interrupt mode for any non-deferral error */
+ if (IS_ERR(stm32port->rx_ch))
+ stm32port->rx_ch = NULL;
+
+ stm32port->tx_ch = dma_request_chan(&pdev->dev, "tx");
+ if (PTR_ERR(stm32port->tx_ch) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ goto err_dma_rx;
+ }
+ /* Fall back in interrupt mode for any non-deferral error */
+ if (IS_ERR(stm32port->tx_ch))
+ stm32port->tx_ch = NULL;
- ret = stm32_usart_of_dma_tx_probe(stm32port, pdev);
- if (ret)
- dev_info(&pdev->dev, "interrupt mode used for tx (no dma)\n");
+ if (stm32port->rx_ch && stm32_usart_of_dma_rx_probe(stm32port, pdev)) {
+ /* Fall back in interrupt mode */
+ dma_release_channel(stm32port->rx_ch);
+ stm32port->rx_ch = NULL;
+ }
+
+ if (stm32port->tx_ch && stm32_usart_of_dma_tx_probe(stm32port, pdev)) {
+ /* Fall back in interrupt mode */
+ dma_release_channel(stm32port->tx_ch);
+ stm32port->tx_ch = NULL;
+ }
+
+ if (!stm32port->rx_ch)
+ dev_info(&pdev->dev, "interrupt mode for rx (no dma)\n");
+ if (!stm32port->tx_ch)
+ dev_info(&pdev->dev, "interrupt mode for tx (no dma)\n");
platform_set_drvdata(pdev, &stm32port->port);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_put_noidle(&pdev->dev);
- if (stm32port->rx_ch) {
- dmaengine_terminate_async(stm32port->rx_ch);
- dma_release_channel(stm32port->rx_ch);
- }
-
- if (stm32port->rx_dma_buf)
- dma_free_coherent(&pdev->dev,
- RX_BUF_L, stm32port->rx_buf,
- stm32port->rx_dma_buf);
-
if (stm32port->tx_ch) {
- dmaengine_terminate_async(stm32port->tx_ch);
+ stm32_usart_of_dma_tx_remove(stm32port, pdev);
dma_release_channel(stm32port->tx_ch);
}
- if (stm32port->tx_dma_buf)
- dma_free_coherent(&pdev->dev,
- TX_BUF_L, stm32port->tx_buf,
- stm32port->tx_dma_buf);
+ if (stm32port->rx_ch)
+ stm32_usart_of_dma_rx_remove(stm32port, pdev);
+err_dma_rx:
+ if (stm32port->rx_ch)
+ dma_release_channel(stm32port->rx_ch);
+
+err_wakeirq:
if (stm32port->wakeup_src)
dev_pm_clear_wake_irq(&pdev->dev);
-err_nowup:
+err_deinit_port:
if (stm32port->wakeup_src)
device_set_wakeup_capable(&pdev->dev, false);
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAR);
+ if (stm32_port->tx_ch) {
+ dmaengine_terminate_async(stm32_port->tx_ch);
+ stm32_usart_of_dma_tx_remove(stm32_port, pdev);
+ dma_release_channel(stm32_port->tx_ch);
+ }
+
if (stm32_port->rx_ch) {
dmaengine_terminate_async(stm32_port->rx_ch);
+ stm32_usart_of_dma_rx_remove(stm32_port, pdev);
dma_release_channel(stm32_port->rx_ch);
}
- if (stm32_port->rx_dma_buf)
- dma_free_coherent(&pdev->dev,
- RX_BUF_L, stm32_port->rx_buf,
- stm32_port->rx_dma_buf);
-
stm32_usart_clr_bits(port, ofs->cr3, USART_CR3_DMAT);
- if (stm32_port->tx_ch) {
- dmaengine_terminate_async(stm32_port->tx_ch);
- dma_release_channel(stm32_port->tx_ch);
- }
-
- if (stm32_port->tx_dma_buf)
- dma_free_coherent(&pdev->dev,
- TX_BUF_L, stm32_port->tx_buf,
- stm32_port->tx_dma_buf);
-
if (stm32_port->wakeup_src) {
dev_pm_clear_wake_irq(&pdev->dev);
device_init_wakeup(&pdev->dev, false);