summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLoic Poulain <loic.poulain@linaro.org>2020-01-10 09:58:04 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-01-14 14:07:51 +0100
commit76460fbd845b2b55d6fdb651b9dac4ef8122038b (patch)
treee143eec63e5cbe5e945d4f9dc18c85c6b2abcbf8 /drivers
parentdc56ecb81a0aa46a7e127e916df5c8fdb8364f0b (diff)
downloadlinux-stable-76460fbd845b2b55d6fdb651b9dac4ef8122038b.tar.gz
linux-stable-76460fbd845b2b55d6fdb651b9dac4ef8122038b.tar.bz2
linux-stable-76460fbd845b2b55d6fdb651b9dac4ef8122038b.zip
tty: serial: msm_serial: RX SW/FIFO mode fallback
During db410c stress test and when the system is low on memory, the UART/console becomes unresponsive and never recover back. This has been narrowed down to the msm_start_rx_dma which does not manage error cases correctly (e.g. dma mapping failure), indeed, when an error happens, dma transfer is simply discarded and so never completed, leading to unconfigured RX path. This patch fixes this issue by switching to SW/FIFO mode in case of DMA issue. This mainly consists in resetting the receiver to apply RX BAM/DMA disabling change and re-enabling the RX level and stale interrupts (previously disabled for DMA transfers). The DMA will be re-enabled once memory is available since the SW/FIFO read function (msm_handle_rx_dm) retries to start dma on completion. Signed-off-by: Loic Poulain <loic.poulain@linaro.org> Link: https://lore.kernel.org/r/1578646684-17379-1-git-send-email-loic.poulain@linaro.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/tty/serial/msm_serial.c18
1 files changed, 17 insertions, 1 deletions
diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c
index 19e897dad5fe..60a9c53fa7cb 100644
--- a/drivers/tty/serial/msm_serial.c
+++ b/drivers/tty/serial/msm_serial.c
@@ -606,7 +606,7 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
UARTDM_RX_SIZE, dma->dir);
ret = dma_mapping_error(uart->dev, dma->phys);
if (ret)
- return;
+ goto sw_mode;
dma->desc = dmaengine_prep_slave_single(dma->chan, dma->phys,
UARTDM_RX_SIZE, DMA_DEV_TO_MEM,
@@ -657,6 +657,22 @@ static void msm_start_rx_dma(struct msm_port *msm_port)
return;
unmap:
dma_unmap_single(uart->dev, dma->phys, UARTDM_RX_SIZE, dma->dir);
+
+sw_mode:
+ /*
+ * Switch from DMA to SW/FIFO mode. After clearing Rx BAM (UARTDM_DMEN),
+ * receiver must be reset.
+ */
+ msm_write(uart, UART_CR_CMD_RESET_RX, UART_CR);
+ msm_write(uart, UART_CR_RX_ENABLE, UART_CR);
+
+ msm_write(uart, UART_CR_CMD_RESET_STALE_INT, UART_CR);
+ msm_write(uart, 0xFFFFFF, UARTDM_DMRX);
+ msm_write(uart, UART_CR_CMD_STALE_EVENT_ENABLE, UART_CR);
+
+ /* Re-enable RX interrupts */
+ msm_port->imr |= (UART_IMR_RXLEV | UART_IMR_RXSTALE);
+ msm_write(uart, msm_port->imr, UART_IMR);
}
static void msm_stop_rx(struct uart_port *port)