serial: tegra: Correct error handling on DMA setup
authorJon Hunter <jonathanh@nvidia.com>
Tue, 5 May 2015 14:17:59 +0000 (15:17 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 10 May 2015 17:12:19 +0000 (19:12 +0200)
Function tegra_uart_dma_channel_allocate() does not check that
dma_map_single() mapped the DMA buffer correctly. Add a check for this
and appropriate error handling.

Furthermore, if dmaengine_slave_config() (called by
tegra_uart_dma_channel_allocate()) fails, then memory allocated/mapped
is not freed/unmapped. Therefore, call tegra_uart_dma_channel_free()
instead of just dma_release_channel() if  dmaengine_slave_config() fails.

Signed-off-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/serial-tegra.c

index 96378da9aefce841270f2ca2bcfdd07a3427abe0..3b63f103f0c9b07eca06c548b8c32ccb37d0460a 100644 (file)
@@ -949,6 +949,28 @@ static int tegra_uart_hw_init(struct tegra_uart_port *tup)
        return 0;
 }
 
+static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
+               bool dma_to_memory)
+{
+       if (dma_to_memory) {
+               dmaengine_terminate_all(tup->rx_dma_chan);
+               dma_release_channel(tup->rx_dma_chan);
+               dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE,
+                               tup->rx_dma_buf_virt, tup->rx_dma_buf_phys);
+               tup->rx_dma_chan = NULL;
+               tup->rx_dma_buf_phys = 0;
+               tup->rx_dma_buf_virt = NULL;
+       } else {
+               dmaengine_terminate_all(tup->tx_dma_chan);
+               dma_release_channel(tup->tx_dma_chan);
+               dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys,
+                       UART_XMIT_SIZE, DMA_TO_DEVICE);
+               tup->tx_dma_chan = NULL;
+               tup->tx_dma_buf_phys = 0;
+               tup->tx_dma_buf_virt = NULL;
+       }
+}
+
 static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
                        bool dma_to_memory)
 {
@@ -981,6 +1003,11 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
                dma_phys = dma_map_single(tup->uport.dev,
                        tup->uport.state->xmit.buf, UART_XMIT_SIZE,
                        DMA_TO_DEVICE);
+               if (dma_mapping_error(tup->uport.dev, dma_phys)) {
+                       dev_err(tup->uport.dev, "dma_map_single tx failed\n");
+                       dma_release_channel(dma_chan);
+                       return -ENOMEM;
+               }
                dma_buf = tup->uport.state->xmit.buf;
        }
 
@@ -1013,32 +1040,10 @@ static int tegra_uart_dma_channel_allocate(struct tegra_uart_port *tup,
        return 0;
 
 scrub:
-       dma_release_channel(dma_chan);
+       tegra_uart_dma_channel_free(tup, dma_to_memory);
        return ret;
 }
 
-static void tegra_uart_dma_channel_free(struct tegra_uart_port *tup,
-               bool dma_to_memory)
-{
-       if (dma_to_memory) {
-               dmaengine_terminate_all(tup->rx_dma_chan);
-               dma_release_channel(tup->rx_dma_chan);
-               dma_free_coherent(tup->uport.dev, TEGRA_UART_RX_DMA_BUFFER_SIZE,
-                               tup->rx_dma_buf_virt, tup->rx_dma_buf_phys);
-               tup->rx_dma_chan = NULL;
-               tup->rx_dma_buf_phys = 0;
-               tup->rx_dma_buf_virt = NULL;
-       } else {
-               dmaengine_terminate_all(tup->tx_dma_chan);
-               dma_release_channel(tup->tx_dma_chan);
-               dma_unmap_single(tup->uport.dev, tup->tx_dma_buf_phys,
-                       UART_XMIT_SIZE, DMA_TO_DEVICE);
-               tup->tx_dma_chan = NULL;
-               tup->tx_dma_buf_phys = 0;
-               tup->tx_dma_buf_virt = NULL;
-       }
-}
-
 static int tegra_uart_startup(struct uart_port *u)
 {
        struct tegra_uart_port *tup = to_tegra_uport(u);