diff options
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 51 |
1 files changed, 46 insertions, 5 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 1ed30e50d1f3..cd39eaeb6151 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -244,6 +244,7 @@ /* 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" @@ -291,6 +292,7 @@ struct lpuart_port { struct circ_buf rx_ring; int rx_dma_rng_buf_len; int rx_dma_periods; + 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 */ @@ -340,8 +342,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 = { @@ -1327,6 +1328,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); @@ -1347,11 +1350,37 @@ 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); + 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); + dmaengine_tx_status(chan, sport->dma_rx_cookie, &state); + 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 int lpuart_sched_rx_dma(struct lpuart_port *sport) @@ -1411,6 +1440,13 @@ static void lpuart_get_rx_dma_rng_len(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; } @@ -1427,6 +1463,10 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) else sport->rx_dma_rng_buf_len = PAGE_SIZE; + 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; @@ -1880,12 +1920,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; |