Merge tag 'tty-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[platform/kernel/linux-starfive.git] / drivers / tty / serial / fsl_lpuart.c
index 7fd30fc..4d80fae 100644 (file)
 
 /* Rx DMA timeout in ms, which is used to calculate Rx ring buffer size */
 #define DMA_RX_TIMEOUT         (10)
+#define DMA_RX_IDLE_CHARS      8
 #define UART_AUTOSUSPEND_TIMEOUT       3000
 
 #define DRIVER_NAME    "fsl-lpuart"
@@ -282,6 +283,7 @@ struct lpuart_port {
        struct scatterlist      rx_sgl, tx_sgl[2];
        struct circ_buf         rx_ring;
        int                     rx_dma_rng_buf_len;
+       int                     last_residue;
        unsigned int            dma_tx_nents;
        wait_queue_head_t       dma_wait;
        bool                    is_cs7; /* Set to true when character size is 7 */
@@ -331,7 +333,7 @@ static struct lpuart_soc_data imx8qxp_data = {
        .devtype = IMX8QXP_LPUART,
        .iotype = UPIO_MEM32,
        .reg_off = IMX_REG_OFF,
-       .rx_watermark = 31,
+       .rx_watermark = 7, /* A lower watermark is ideal for low baud rates. */
 };
 static struct lpuart_soc_data imxrt1050_data = {
        .devtype = IMXRT1050_LPUART,
@@ -1255,6 +1257,8 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
                sport->port.icount.rx += copied;
        }
 
+       sport->last_residue = state.residue;
+
 exit:
        dma_sync_sg_for_device(chan->device->dev, &sport->rx_sgl, 1,
                               DMA_FROM_DEVICE);
@@ -1272,11 +1276,43 @@ static void lpuart_dma_rx_complete(void *arg)
        lpuart_copy_rx_to_tty(sport);
 }
 
+/*
+ * Timer function to simulate the hardware EOP (End Of Package) event.
+ * The timer callback is to check for new RX data and copy to TTY buffer.
+ * If no new data are received since last interval, the EOP condition is
+ * met, complete the DMA transfer by copying the data. Otherwise, just
+ * restart timer.
+ */
 static void lpuart_timer_func(struct timer_list *t)
 {
        struct lpuart_port *sport = from_timer(sport, t, lpuart_timer);
+       enum dma_status dmastat;
+       struct dma_chan *chan = sport->dma_rx_chan;
+       struct circ_buf *ring = &sport->rx_ring;
+       struct dma_tx_state state;
+       unsigned long flags;
+       int count;
 
-       lpuart_copy_rx_to_tty(sport);
+       dmastat = dmaengine_tx_status(chan, sport->dma_rx_cookie, &state);
+       if (dmastat == DMA_ERROR) {
+               dev_err(sport->port.dev, "Rx DMA transfer failed!\n");
+               return;
+       }
+
+       ring->head = sport->rx_sgl.length - state.residue;
+       count = CIRC_CNT(ring->head, ring->tail, sport->rx_sgl.length);
+
+       /* Check if new data received before copying */
+       if ((count != 0) && (sport->last_residue == state.residue))
+               lpuart_copy_rx_to_tty(sport);
+       else
+               mod_timer(&sport->lpuart_timer,
+                         jiffies + sport->dma_rx_timeout);
+
+       if (spin_trylock_irqsave(&sport->port.lock, flags)) {
+               sport->last_residue = state.residue;
+               spin_unlock_irqrestore(&sport->port.lock, flags);
+       }
 }
 
 static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
@@ -1297,9 +1333,20 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport)
         */
        sport->rx_dma_rng_buf_len = (DMA_RX_TIMEOUT * baud /  bits / 1000) * 2;
        sport->rx_dma_rng_buf_len = (1 << fls(sport->rx_dma_rng_buf_len));
+       sport->rx_dma_rng_buf_len = max_t(int,
+                                         sport->rxfifo_size * 2,
+                                         sport->rx_dma_rng_buf_len);
+       /*
+        * Keep this condition check in case rxfifo_size is unavailable
+        * for some SoCs.
+        */
        if (sport->rx_dma_rng_buf_len < 16)
                sport->rx_dma_rng_buf_len = 16;
 
+       sport->last_residue = 0;
+       sport->dma_rx_timeout = max(nsecs_to_jiffies(
+               sport->port.frame_time * DMA_RX_IDLE_CHARS), 1UL);
+
        ring->buf = kzalloc(sport->rx_dma_rng_buf_len, GFP_ATOMIC);
        if (!ring->buf)
                return -ENOMEM;
@@ -1689,12 +1736,13 @@ static void lpuart_rx_dma_startup(struct lpuart_port *sport)
        if (!sport->dma_rx_chan)
                goto err;
 
+       /* set default Rx DMA timeout */
+       sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
+
        ret = lpuart_start_rx_dma(sport);
        if (ret)
                goto err;
 
-       /* set Rx DMA timeout */
-       sport->dma_rx_timeout = msecs_to_jiffies(DMA_RX_TIMEOUT);
        if (!sport->dma_rx_timeout)
                sport->dma_rx_timeout = 1;
 
@@ -2676,6 +2724,7 @@ OF_EARLYCON_DECLARE(lpuart, "fsl,vf610-lpuart", lpuart_early_console_setup);
 OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1021a-lpuart", lpuart32_early_console_setup);
 OF_EARLYCON_DECLARE(lpuart32, "fsl,ls1028a-lpuart", ls1028a_early_console_setup);
 OF_EARLYCON_DECLARE(lpuart32, "fsl,imx7ulp-lpuart", lpuart32_imx_early_console_setup);
+OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8ulp-lpuart", lpuart32_imx_early_console_setup);
 OF_EARLYCON_DECLARE(lpuart32, "fsl,imx8qxp-lpuart", lpuart32_imx_early_console_setup);
 OF_EARLYCON_DECLARE(lpuart32, "fsl,imxrt1050-lpuart", lpuart32_imx_early_console_setup);
 EARLYCON_DECLARE(lpuart, lpuart_early_console_setup);