diff options
Diffstat (limited to 'drivers/tty/serial/fsl_lpuart.c')
-rw-r--r-- | drivers/tty/serial/fsl_lpuart.c | 88 |
1 files changed, 63 insertions, 25 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c index c569a08b5b19..f72e1340b47d 100644 --- a/drivers/tty/serial/fsl_lpuart.c +++ b/drivers/tty/serial/fsl_lpuart.c @@ -18,9 +18,9 @@ #include <linux/irq.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_dma.h> #include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> #include <linux/pm_runtime.h> #include <linux/serial_core.h> #include <linux/slab.h> @@ -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 { @@ -1064,26 +1065,6 @@ static irqreturn_t lpuart_int(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t lpuart32_int(int irq, void *dev_id) -{ - struct lpuart_port *sport = dev_id; - unsigned long sts, rxcount; - - sts = lpuart32_read(&sport->port, UARTSTAT); - rxcount = lpuart32_read(&sport->port, UARTWATER); - rxcount = rxcount >> UARTWATER_RXCNT_OFF; - - if ((sts & UARTSTAT_RDRF || rxcount > 0) && !sport->lpuart_dma_rx_use) - lpuart32_rxint(sport); - - if ((sts & UARTSTAT_TDRE) && !sport->lpuart_dma_tx_use) - lpuart32_txint(sport); - - lpuart32_write(&sport->port, sts, UARTSTAT); - return IRQ_HANDLED; -} - - static inline void lpuart_handle_sysrq_chars(struct uart_port *port, unsigned char *p, int count) { @@ -1266,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) @@ -1276,6 +1258,50 @@ 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; + unsigned long sts, rxcount; + + sts = lpuart32_read(&sport->port, UARTSTAT); + rxcount = lpuart32_read(&sport->port, UARTWATER); + rxcount = rxcount >> UARTWATER_RXCNT_OFF; + + if ((sts & UARTSTAT_RDRF || rxcount > 0) && !sport->lpuart_dma_rx_use) + lpuart32_rxint(sport); + + 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; +} + /* * 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. @@ -1392,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); @@ -1407,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; @@ -1669,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); @@ -2811,8 +2848,7 @@ static int lpuart_probe(struct platform_device *pdev) if (!sport) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sport->port.membase = devm_ioremap_resource(&pdev->dev, res); + sport->port.membase = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(sport->port.membase)) return PTR_ERR(sport->port.membase); @@ -2822,6 +2858,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; |