serial: 8250: 8250_omap: Account for data in flight during DMA teardown
authorVignesh Raghavendra <vigneshr@ti.com>
Thu, 19 Mar 2020 11:03:40 +0000 (16:33 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Mar 2020 11:25:10 +0000 (12:25 +0100)
Take into account data stuck in DMA internal buffers before pushing data
to higher layer. dma_tx_state has "in_flight_bytes" member that provides
this information.

Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
Link: https://lore.kernel.org/r/20200319110344.21348-3-vigneshr@ti.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/8250/8250_omap.c

index c13b48ad7cdbc0ea3920991a6353775ca08a026a..bf1f2bba1d4175af2f5e7c4a819266ef107127dd 100644 (file)
@@ -741,6 +741,8 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
        struct omap8250_priv    *priv = p->port.private_data;
        struct uart_8250_dma    *dma = p->dma;
        struct tty_port         *tty_port = &p->port.state->port;
+       struct dma_chan         *rxchan = dma->rxchan;
+       dma_cookie_t            cookie;
        struct dma_tx_state     state;
        int                     count;
        unsigned long           flags;
@@ -751,12 +753,29 @@ static void __dma_rx_do_complete(struct uart_8250_port *p)
        if (!dma->rx_running)
                goto unlock;
 
+       cookie = dma->rx_cookie;
        dma->rx_running = 0;
-       dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
+       dmaengine_tx_status(rxchan, cookie, &state);
 
-       count = dma->rx_size - state.residue;
-       if (count < dma->rx_size)
-               dmaengine_terminate_async(dma->rxchan);
+       count = dma->rx_size - state.residue + state.in_flight_bytes;
+       if (count < dma->rx_size) {
+               dmaengine_terminate_async(rxchan);
+
+               /*
+                * Poll for teardown to complete which guarantees in
+                * flight data is drained.
+                */
+               if (state.in_flight_bytes) {
+                       int poll_count = 25;
+
+                       while (dmaengine_tx_status(rxchan, cookie, NULL) &&
+                              poll_count--)
+                               cpu_relax();
+
+                       if (!poll_count)
+                               dev_err(p->port.dev, "teardown incomplete\n");
+               }
+       }
        if (!count)
                goto unlock;
        ret = tty_insert_flip_string(tty_port, dma->rx_buf, count);