serial: imx: Support sw flow control in DMA mode
[cascardo/linux.git] / drivers / tty / serial / imx.c
index 8f62a3c..72b800b 100644 (file)
@@ -74,6 +74,7 @@
 #define IMX21_UTS 0xb4 /* UART Test Register on all other i.mx*/
 
 /* UART Control Register Bit Fields.*/
+#define URXD_DUMMY_READ (1<<16)
 #define URXD_CHARRDY   (1<<15)
 #define URXD_ERR       (1<<14)
 #define URXD_OVRRUN    (1<<13)
@@ -302,7 +303,7 @@ static inline int is_imx6q_uart(struct imx_port *sport)
 /*
  * Save and restore functions for UCR1, UCR2 and UCR3 registers
  */
-#if defined(CONFIG_CONSOLE_POLL) || defined(CONFIG_SERIAL_IMX_CONSOLE)
+#if defined(CONFIG_SERIAL_IMX_CONSOLE)
 static void imx_port_ucrs_save(struct uart_port *port,
                               struct imx_port_ucrs *ucr)
 {
@@ -463,13 +464,17 @@ static void imx_enable_ms(struct uart_port *port)
        mod_timer(&sport->timer, jiffies);
 }
 
+static void imx_dma_tx(struct imx_port *sport);
 static inline void imx_transmit_buffer(struct imx_port *sport)
 {
        struct circ_buf *xmit = &sport->port.state->xmit;
+       unsigned long temp;
 
        if (sport->port.x_char) {
                /* Send next char */
                writel(sport->port.x_char, sport->port.membase + URTX0);
+               sport->port.icount.tx++;
+               sport->port.x_char = 0;
                return;
        }
 
@@ -478,6 +483,22 @@ static inline void imx_transmit_buffer(struct imx_port *sport)
                return;
        }
 
+       if (sport->dma_is_enabled) {
+               /*
+                * We've just sent a X-char Ensure the TX DMA is enabled
+                * and the TX IRQ is disabled.
+                **/
+               temp = readl(sport->port.membase + UCR1);
+               temp &= ~UCR1_TXMPTYEN;
+               if (sport->dma_is_txing) {
+                       temp |= UCR1_TDMAEN;
+                       writel(temp, sport->port.membase + UCR1);
+               } else {
+                       writel(temp, sport->port.membase + UCR1);
+                       imx_dma_tx(sport);
+               }
+       }
+
        while (!uart_circ_empty(xmit) &&
               !(readl(sport->port.membase + uts_reg(sport)) & UTS_TXFULL)) {
                /* send xmit->buf[xmit->tail]
@@ -500,26 +521,39 @@ static void dma_tx_callback(void *data)
        struct scatterlist *sgl = &sport->tx_sgl[0];
        struct circ_buf *xmit = &sport->port.state->xmit;
        unsigned long flags;
+       unsigned long temp;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
 
-       sport->dma_is_txing = 0;
+       temp = readl(sport->port.membase + UCR1);
+       temp &= ~UCR1_TDMAEN;
+       writel(temp, sport->port.membase + UCR1);
 
        /* update the stat */
-       spin_lock_irqsave(&sport->port.lock, flags);
        xmit->tail = (xmit->tail + sport->tx_bytes) & (UART_XMIT_SIZE - 1);
        sport->port.icount.tx += sport->tx_bytes;
-       spin_unlock_irqrestore(&sport->port.lock, flags);
 
        dev_dbg(sport->port.dev, "we finish the TX DMA.\n");
 
-       uart_write_wakeup(&sport->port);
+       sport->dma_is_txing = 0;
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
+               uart_write_wakeup(&sport->port);
 
        if (waitqueue_active(&sport->dma_wait)) {
                wake_up(&sport->dma_wait);
                dev_dbg(sport->port.dev, "exit in %s.\n", __func__);
                return;
        }
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+       if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port))
+               imx_dma_tx(sport);
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static void imx_dma_tx(struct imx_port *sport)
@@ -529,24 +563,23 @@ static void imx_dma_tx(struct imx_port *sport)
        struct dma_async_tx_descriptor *desc;
        struct dma_chan *chan = sport->dma_chan_tx;
        struct device *dev = sport->port.dev;
-       enum dma_status status;
+       unsigned long temp;
        int ret;
 
-       status = dmaengine_tx_status(chan, (dma_cookie_t)0, NULL);
-       if (DMA_IN_PROGRESS == status)
+       if (sport->dma_is_txing)
                return;
 
        sport->tx_bytes = uart_circ_chars_pending(xmit);
 
-       if (xmit->tail > xmit->head && xmit->head > 0) {
+       if (xmit->tail < xmit->head) {
+               sport->dma_tx_nents = 1;
+               sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
+       } else {
                sport->dma_tx_nents = 2;
                sg_init_table(sgl, 2);
                sg_set_buf(sgl, xmit->buf + xmit->tail,
                                UART_XMIT_SIZE - xmit->tail);
                sg_set_buf(sgl + 1, xmit->buf, xmit->head);
-       } else {
-               sport->dma_tx_nents = 1;
-               sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes);
        }
 
        ret = dma_map_sg(dev, sgl, sport->dma_tx_nents, DMA_TO_DEVICE);
@@ -557,6 +590,8 @@ static void imx_dma_tx(struct imx_port *sport)
        desc = dmaengine_prep_slave_sg(chan, sgl, sport->dma_tx_nents,
                                        DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
        if (!desc) {
+               dma_unmap_sg(dev, sgl, sport->dma_tx_nents,
+                            DMA_TO_DEVICE);
                dev_err(dev, "We cannot prepare for the TX slave dma!\n");
                return;
        }
@@ -565,6 +600,11 @@ static void imx_dma_tx(struct imx_port *sport)
 
        dev_dbg(dev, "TX: prepare to send %lu bytes by DMA.\n",
                        uart_circ_chars_pending(xmit));
+
+       temp = readl(sport->port.membase + UCR1);
+       temp |= UCR1_TDMAEN;
+       writel(temp, sport->port.membase + UCR1);
+
        /* fire it */
        sport->dma_is_txing = 1;
        dmaengine_submit(desc);
@@ -590,13 +630,6 @@ static void imx_start_tx(struct uart_port *port)
                temp &= ~(UCR1_RRDYEN);
                writel(temp, sport->port.membase + UCR1);
        }
-       /* Clear any pending ORE flag before enabling interrupt */
-       temp = readl(sport->port.membase + USR2);
-       writel(temp | USR2_ORE, sport->port.membase + USR2);
-
-       temp = readl(sport->port.membase + UCR4);
-       temp |= UCR4_OREN;
-       writel(temp, sport->port.membase + UCR4);
 
        if (!sport->dma_is_enabled) {
                temp = readl(sport->port.membase + UCR1);
@@ -614,15 +647,21 @@ static void imx_start_tx(struct uart_port *port)
        }
 
        if (sport->dma_is_enabled) {
-               /* FIXME: port->x_char must be transmitted if != 0 */
+               if (sport->port.x_char) {
+                       /* We have X-char to send, so enable TX IRQ and
+                        * disable TX DMA to let TX interrupt to send X-char */
+                       temp = readl(sport->port.membase + UCR1);
+                       temp &= ~UCR1_TDMAEN;
+                       temp |= UCR1_TXMPTYEN;
+                       writel(temp, sport->port.membase + UCR1);
+                       return;
+               }
+
                if (!uart_circ_empty(&port->state->xmit) &&
                    !uart_tx_stopped(port))
                        imx_dma_tx(sport);
                return;
        }
-
-       if (readl(sport->port.membase + uts_reg(sport)) & UTS_TXEMPTY)
-               imx_transmit_buffer(sport);
 }
 
 static irqreturn_t imx_rtsint(int irq, void *dev_id)
@@ -710,6 +749,9 @@ static irqreturn_t imx_rxint(int irq, void *dev_id)
 #endif
                }
 
+               if (sport->port.ignore_status_mask & URXD_DUMMY_READ)
+                       goto out;
+
                tty_insert_flip_char(port, rx, flg);
        }
 
@@ -727,6 +769,9 @@ static int start_rx_dma(struct imx_port *sport);
 static void imx_dma_rxint(struct imx_port *sport)
 {
        unsigned long temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        temp = readl(sport->port.membase + USR2);
        if ((temp & USR2_RDR) && !sport->dma_is_rxing) {
@@ -740,6 +785,8 @@ static void imx_dma_rxint(struct imx_port *sport)
                /* tell the DMA to receive the data. */
                start_rx_dma(sport);
        }
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 static irqreturn_t imx_int(int irq, void *dev_id)
@@ -869,6 +916,9 @@ static int imx_setup_ufcr(struct imx_port *sport, unsigned int mode)
 static void imx_rx_dma_done(struct imx_port *sport)
 {
        unsigned long temp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sport->port.lock, flags);
 
        /* Enable this interrupt when the RXFIFO is empty. */
        temp = readl(sport->port.membase + UCR1);
@@ -880,6 +930,8 @@ static void imx_rx_dma_done(struct imx_port *sport)
        /* Is the shutdown waiting for us? */
        if (waitqueue_active(&sport->dma_wait))
                wake_up(&sport->dma_wait);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
 /*
@@ -910,12 +962,26 @@ static void dma_rx_callback(void *data)
        dev_dbg(sport->port.dev, "We get %d bytes.\n", count);
 
        if (count) {
-               tty_insert_flip_string(port, sport->rx_buf, count);
+               if (!(sport->port.ignore_status_mask & URXD_DUMMY_READ))
+                       tty_insert_flip_string(port, sport->rx_buf, count);
                tty_flip_buffer_push(port);
 
                start_rx_dma(sport);
-       } else
+       } else if (readl(sport->port.membase + USR2) & USR2_RDR) {
+               /*
+                * start rx_dma directly once data in RXFIFO, more efficient
+                * than before:
+                *      1. call imx_rx_dma_done to stop dma if no data received
+                *      2. wait next  RDR interrupt to start dma transfer.
+                */
+               start_rx_dma(sport);
+       } else {
+               /*
+                * stop dma to prevent too many IDLE event trigged if no data
+                * in RXFIFO
+                */
                imx_rx_dma_done(sport);
+       }
 }
 
 static int start_rx_dma(struct imx_port *sport)
@@ -935,6 +1001,7 @@ static int start_rx_dma(struct imx_port *sport)
        desc = dmaengine_prep_slave_sg(chan, sgl, 1, DMA_DEV_TO_MEM,
                                        DMA_PREP_INTERRUPT);
        if (!desc) {
+               dma_unmap_sg(dev, sgl, 1, DMA_FROM_DEVICE);
                dev_err(dev, "We cannot prepare for the RX slave dma!\n");
                return -EINVAL;
        }
@@ -991,7 +1058,6 @@ static int imx_uart_dma_init(struct imx_port *sport)
 
        sport->rx_buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
        if (!sport->rx_buf) {
-               dev_err(dev, "cannot alloc DMA buffer.\n");
                ret = -ENOMEM;
                goto err;
        }
@@ -1076,11 +1142,11 @@ static int imx_startup(struct uart_port *port)
 
        retval = clk_prepare_enable(sport->clk_per);
        if (retval)
-               goto error_out1;
+               return retval;
        retval = clk_prepare_enable(sport->clk_ipg);
        if (retval) {
                clk_disable_unprepare(sport->clk_per);
-               goto error_out1;
+               return retval;
        }
 
        imx_setup_ufcr(sport, 0);
@@ -1109,36 +1175,10 @@ static int imx_startup(struct uart_port *port)
        while (!(readl(sport->port.membase + UCR2) & UCR2_SRST) && (--i > 0))
                udelay(1);
 
-       /*
-        * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
-        * chips only have one interrupt.
-        */
-       if (sport->txirq > 0) {
-               retval = request_irq(sport->rxirq, imx_rxint, 0,
-                                    dev_name(port->dev), sport);
-               if (retval)
-                       goto error_out1;
-
-               retval = request_irq(sport->txirq, imx_txint, 0,
-                                    dev_name(port->dev), sport);
-               if (retval)
-                       goto error_out2;
-
-               /* do not use RTS IRQ on IrDA */
-               if (!USE_IRDA(sport)) {
-                       retval = request_irq(sport->rtsirq, imx_rtsint, 0,
-                                            dev_name(port->dev), sport);
-                       if (retval)
-                               goto error_out3;
-               }
-       } else {
-               retval = request_irq(sport->port.irq, imx_int, 0,
-                                    dev_name(port->dev), sport);
-               if (retval) {
-                       free_irq(sport->port.irq, sport);
-                       goto error_out1;
-               }
-       }
+       /* Can we enable the DMA support? */
+       if (is_imx6q_uart(sport) && !uart_console(port) &&
+           !sport->dma_is_inited)
+               imx_uart_dma_init(sport);
 
        spin_lock_irqsave(&sport->port.lock, flags);
        /*
@@ -1146,6 +1186,9 @@ static int imx_startup(struct uart_port *port)
         */
        writel(USR1_RTSD, sport->port.membase + USR1);
 
+       if (sport->dma_is_inited && !sport->dma_is_enabled)
+               imx_enable_dma(sport);
+
        temp = readl(sport->port.membase + UCR1);
        temp |= UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN;
 
@@ -1156,6 +1199,14 @@ static int imx_startup(struct uart_port *port)
 
        writel(temp, sport->port.membase + UCR1);
 
+       /* Clear any pending ORE flag before enabling interrupt */
+       temp = readl(sport->port.membase + USR2);
+       writel(temp | USR2_ORE, sport->port.membase + USR2);
+
+       temp = readl(sport->port.membase + UCR4);
+       temp |= UCR4_OREN;
+       writel(temp, sport->port.membase + UCR4);
+
        temp = readl(sport->port.membase + UCR2);
        temp |= (UCR2_RXEN | UCR2_TXEN);
        if (!sport->have_rtscts)
@@ -1201,15 +1252,6 @@ static int imx_startup(struct uart_port *port)
        }
 
        return 0;
-
-error_out3:
-       if (sport->txirq)
-               free_irq(sport->txirq, sport);
-error_out2:
-       if (sport->rxirq)
-               free_irq(sport->rxirq, sport);
-error_out1:
-       return retval;
 }
 
 static void imx_shutdown(struct uart_port *port)
@@ -1230,9 +1272,11 @@ static void imx_shutdown(struct uart_port *port)
                        dmaengine_terminate_all(sport->dma_chan_tx);
                        dmaengine_terminate_all(sport->dma_chan_rx);
                }
+               spin_lock_irqsave(&sport->port.lock, flags);
                imx_stop_tx(port);
                imx_stop_rx(port);
                imx_disable_dma(sport);
+               spin_unlock_irqrestore(&sport->port.lock, flags);
                imx_uart_dma_exit(sport);
        }
 
@@ -1254,17 +1298,6 @@ static void imx_shutdown(struct uart_port *port)
         */
        del_timer_sync(&sport->timer);
 
-       /*
-        * Free the interrupts
-        */
-       if (sport->txirq > 0) {
-               if (!USE_IRDA(sport))
-                       free_irq(sport->rtsirq, sport);
-               free_irq(sport->txirq, sport);
-               free_irq(sport->rxirq, sport);
-       } else
-               free_irq(sport->port.irq, sport);
-
        /*
         * Disable all interrupts, port and break condition.
         */
@@ -1285,10 +1318,21 @@ static void imx_shutdown(struct uart_port *port)
 static void imx_flush_buffer(struct uart_port *port)
 {
        struct imx_port *sport = (struct imx_port *)port;
+       struct scatterlist *sgl = &sport->tx_sgl[0];
+       unsigned long temp;
 
-       if (sport->dma_is_enabled) {
-               sport->tx_bytes = 0;
-               dmaengine_terminate_all(sport->dma_chan_tx);
+       if (!sport->dma_chan_tx)
+               return;
+
+       sport->tx_bytes = 0;
+       dmaengine_terminate_all(sport->dma_chan_tx);
+       if (sport->dma_is_txing) {
+               dma_unmap_sg(sport->port.dev, sgl, sport->dma_tx_nents,
+                            DMA_TO_DEVICE);
+               temp = readl(sport->port.membase + UCR1);
+               temp &= ~UCR1_TDMAEN;
+               writel(temp, sport->port.membase + UCR1);
+               sport->dma_is_txing = false;
        }
 }
 
@@ -1332,11 +1376,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
                if (sport->have_rtscts) {
                        ucr2 &= ~UCR2_IRTS;
                        ucr2 |= UCR2_CTSC;
-
-                       /* Can we enable the DMA support? */
-                       if (is_imx6q_uart(sport) && !uart_console(port)
-                               && !sport->dma_is_inited)
-                               imx_uart_dma_init(sport);
                } else {
                        termios->c_cflag &= ~CRTSCTS;
                }
@@ -1382,6 +1421,9 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
                        sport->port.ignore_status_mask |= URXD_OVRRUN;
        }
 
+       if ((termios->c_cflag & CREAD) == 0)
+               sport->port.ignore_status_mask |= URXD_DUMMY_READ;
+
        /*
         * Update the per-port timeout.
         */
@@ -1455,8 +1497,6 @@ imx_set_termios(struct uart_port *port, struct ktermios *termios,
        if (UART_ENABLE_MS(&sport->port, termios->c_cflag))
                imx_enable_ms(&sport->port);
 
-       if (sport->dma_is_inited && !sport->dma_is_enabled)
-               imx_enable_dma(sport);
        spin_unlock_irqrestore(&sport->port.lock, flags);
 }
 
@@ -1507,44 +1547,65 @@ imx_verify_port(struct uart_port *port, struct serial_struct *ser)
 }
 
 #if defined(CONFIG_CONSOLE_POLL)
+
+static int imx_poll_init(struct uart_port *port)
+{
+       struct imx_port *sport = (struct imx_port *)port;
+       unsigned long flags;
+       unsigned long temp;
+       int retval;
+
+       retval = clk_prepare_enable(sport->clk_ipg);
+       if (retval)
+               return retval;
+       retval = clk_prepare_enable(sport->clk_per);
+       if (retval)
+               clk_disable_unprepare(sport->clk_ipg);
+
+       imx_setup_ufcr(sport, 0);
+
+       spin_lock_irqsave(&sport->port.lock, flags);
+
+       temp = readl(sport->port.membase + UCR1);
+       if (is_imx1_uart(sport))
+               temp |= IMX1_UCR1_UARTCLKEN;
+       temp |= UCR1_UARTEN | UCR1_RRDYEN;
+       temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN);
+       writel(temp, sport->port.membase + UCR1);
+
+       temp = readl(sport->port.membase + UCR2);
+       temp |= UCR2_RXEN;
+       writel(temp, sport->port.membase + UCR2);
+
+       spin_unlock_irqrestore(&sport->port.lock, flags);
+
+       return 0;
+}
+
 static int imx_poll_get_char(struct uart_port *port)
 {
-       if (!(readl(port->membase + USR2) & USR2_RDR))
+       if (!(readl_relaxed(port->membase + USR2) & USR2_RDR))
                return NO_POLL_CHAR;
 
-       return readl(port->membase + URXD0) & URXD_RX_DATA;
+       return readl_relaxed(port->membase + URXD0) & URXD_RX_DATA;
 }
 
 static void imx_poll_put_char(struct uart_port *port, unsigned char c)
 {
-       struct imx_port_ucrs old_ucr;
        unsigned int status;
 
-       /* save control registers */
-       imx_port_ucrs_save(port, &old_ucr);
-
-       /* disable interrupts */
-       writel(UCR1_UARTEN, port->membase + UCR1);
-       writel(old_ucr.ucr2 & ~(UCR2_ATEN | UCR2_RTSEN | UCR2_ESCI),
-              port->membase + UCR2);
-       writel(old_ucr.ucr3 & ~(UCR3_DCD | UCR3_RI | UCR3_DTREN),
-              port->membase + UCR3);
-
        /* drain */
        do {
-               status = readl(port->membase + USR1);
+               status = readl_relaxed(port->membase + USR1);
        } while (~status & USR1_TRDY);
 
        /* write */
-       writel(c, port->membase + URTX0);
+       writel_relaxed(c, port->membase + URTX0);
 
        /* flush */
        do {
-               status = readl(port->membase + USR2);
+               status = readl_relaxed(port->membase + USR2);
        } while (~status & USR2_TXDC);
-
-       /* restore control registers */
-       imx_port_ucrs_restore(port, &old_ucr);
 }
 #endif
 
@@ -1565,6 +1626,7 @@ static struct uart_ops imx_pops = {
        .config_port    = imx_config_port,
        .verify_port    = imx_verify_port,
 #if defined(CONFIG_CONSOLE_POLL)
+       .poll_init      = imx_poll_init,
        .poll_get_char  = imx_poll_get_char,
        .poll_put_char  = imx_poll_put_char,
 #endif
@@ -1929,6 +1991,36 @@ static int serial_imx_probe(struct platform_device *pdev)
 
        sport->port.uartclk = clk_get_rate(sport->clk_per);
 
+       /*
+        * Allocate the IRQ(s) i.MX1 has three interrupts whereas later
+        * chips only have one interrupt.
+        */
+       if (sport->txirq > 0) {
+               ret = devm_request_irq(&pdev->dev, sport->rxirq, imx_rxint, 0,
+                                      dev_name(&pdev->dev), sport);
+               if (ret)
+                       return ret;
+
+               ret = devm_request_irq(&pdev->dev, sport->txirq, imx_txint, 0,
+                                      dev_name(&pdev->dev), sport);
+               if (ret)
+                       return ret;
+
+               /* do not use RTS IRQ on IrDA */
+               if (!USE_IRDA(sport)) {
+                       ret = devm_request_irq(&pdev->dev, sport->rtsirq,
+                                              imx_rtsint, 0,
+                                              dev_name(&pdev->dev), sport);
+                       if (ret)
+                               return ret;
+               }
+       } else {
+               ret = devm_request_irq(&pdev->dev, sport->port.irq, imx_int, 0,
+                                      dev_name(&pdev->dev), sport);
+               if (ret)
+                       return ret;
+       }
+
        imx_ports[sport->port.line] = sport;
 
        platform_set_drvdata(pdev, sport);
@@ -1952,18 +2044,14 @@ static struct platform_driver serial_imx_driver = {
        .id_table       = imx_uart_devtype,
        .driver         = {
                .name   = "imx-uart",
-               .owner  = THIS_MODULE,
                .of_match_table = imx_uart_dt_ids,
        },
 };
 
 static int __init imx_serial_init(void)
 {
-       int ret;
-
-       pr_info("Serial: IMX driver\n");
+       int ret = uart_register_driver(&imx_reg);
 
-       ret = uart_register_driver(&imx_reg);
        if (ret)
                return ret;