diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 44 |
1 files changed, 42 insertions, 2 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index 159a2de509f3..5c889c65d8ea 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -288,6 +288,7 @@ struct lpuart_port { wait_queue_head_t dma_wait; bool is_cs7; /* Set to true when character size is 7 */ /* and the parity is enabled */ + bool dma_idle_int; }; struct lpuart_soc_data { @@ -1246,7 +1247,8 @@ exit: spin_unlock_irqrestore(&sport->port.lock, flags); tty_flip_buffer_push(port); - mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); + if (!sport->dma_idle_int) + mod_timer(&sport->lpuart_timer, jiffies + sport->dma_rx_timeout); } static void lpuart_dma_rx_complete(void *arg) @@ -1256,6 +1258,28 @@ static void lpuart_dma_rx_complete(void *arg) lpuart_copy_rx_to_tty(sport); } +static void lpuart32_dma_idleint(struct lpuart_port *sport) +{ + enum dma_status dmastat; + struct dma_chan *chan = sport->dma_rx_chan; + struct circ_buf *ring = &sport->rx_ring; + struct dma_tx_state state; + int count = 0; + + 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) + lpuart_copy_rx_to_tty(sport); +} + static irqreturn_t lpuart32_int(int irq, void *dev_id) { struct lpuart_port *sport = dev_id; @@ -1271,6 +1295,9 @@ static irqreturn_t lpuart32_int(int irq, void *dev_id) if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use) lpuart32_txint(sport); + if ((sts & UARTSTAT_IDLE) && sport->lpuart_dma_rx_use && sport->dma_idle_int) + lpuart32_dma_idleint(sport); + lpuart32_write(&sport->port, sts, UARTSTAT); return IRQ_HANDLED; } @@ -1391,6 +1418,12 @@ static inline int lpuart_start_rx_dma(struct lpuart_port *sport) unsigned long temp = lpuart32_read(&sport->port, UARTBAUD); lpuart32_write(&sport->port, temp | UARTBAUD_RDMAE, UARTBAUD); + + if (sport->dma_idle_int) { + unsigned long ctrl = lpuart32_read(&sport->port, UARTCTRL); + + lpuart32_write(&sport->port, ctrl | UARTCTRL_ILIE, UARTCTRL); + } } else { writeb(readb(sport->port.membase + UARTCR5) | UARTCR5_RDMAS, sport->port.membase + UARTCR5); @@ -1406,7 +1439,9 @@ static void lpuart_dma_rx_free(struct uart_port *port) struct dma_chan *chan = sport->dma_rx_chan; dmaengine_terminate_sync(chan); - del_timer_sync(&sport->lpuart_timer); + if (!sport->dma_idle_int) + del_timer_sync(&sport->lpuart_timer); + dma_unmap_sg(chan->device->dev, &sport->rx_sgl, 1, DMA_FROM_DEVICE); kfree(sport->rx_ring.buf); sport->rx_ring.tail = 0; @@ -1668,6 +1703,9 @@ static void lpuart32_setup_watermark_enable(struct lpuart_port *sport) static void rx_dma_timer_init(struct lpuart_port *sport) { + if (sport->dma_idle_int) + return; + timer_setup(&sport->lpuart_timer, lpuart_timer_func, 0); sport->lpuart_timer.expires = jiffies + sport->dma_rx_timeout; add_timer(&sport->lpuart_timer); @@ -2821,6 +2859,8 @@ static int lpuart_probe(struct platform_device *pdev) sport->port.type = PORT_LPUART; sport->devtype = sdata->devtype; sport->rx_watermark = sdata->rx_watermark; + sport->dma_idle_int = is_imx7ulp_lpuart(sport) || is_imx8ulp_lpuart(sport) || + is_imx8qxp_lpuart(sport); ret = platform_get_irq(pdev, 0); if (ret < 0) return ret; |