serial: sh-sci: Pause DMA engine and get DMA status again
[firefly-linux-kernel-4.4.55.git] / drivers / tty / serial / sh-sci.c
index 36a11110acf4cab5eef643970ed9a4e619f0de65..b1d1ce1986e6c0647d70b7d02e0d1838f5bd6007 100644 (file)
@@ -1104,6 +1104,7 @@ static void sci_rx_dma_release(struct sci_port *s, bool enable_pio)
 static void sci_dma_rx_complete(void *arg)
 {
        struct sci_port *s = arg;
+       struct dma_chan *chan = s->chan_rx;
        struct uart_port *port = &s->port;
        struct dma_async_tx_descriptor *desc;
        unsigned long flags;
@@ -1137,6 +1138,8 @@ static void sci_dma_rx_complete(void *arg)
 
        s->active_rx = s->cookie_rx[!active];
 
+       dma_async_issue_pending(chan);
+
        dev_dbg(port->dev, "%s: cookie %d #%d, new active cookie %d\n",
                __func__, s->cookie_rx[active], active, s->active_rx);
        spin_unlock_irqrestore(&port->lock, flags);
@@ -1274,6 +1277,7 @@ static bool filter(struct dma_chan *chan, void *slave)
 static void rx_timer_fn(unsigned long arg)
 {
        struct sci_port *s = (struct sci_port *)arg;
+       struct dma_chan *chan = s->chan_rx;
        struct uart_port *port = &s->port;
        struct dma_tx_state state;
        enum dma_status status;
@@ -1285,12 +1289,6 @@ static void rx_timer_fn(unsigned long arg)
        spin_lock_irqsave(&port->lock, flags);
 
        dev_dbg(port->dev, "DMA Rx timed out\n");
-       scr = serial_port_in(port, SCSCR);
-       if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
-               scr &= ~SCSCR_RDRQE;
-               enable_irq(s->irqs[SCIx_RXI_IRQ]);
-       }
-       serial_port_out(port, SCSCR, scr | SCSCR_RIE);
 
        active = sci_dma_rx_find_active(s);
        if (active < 0) {
@@ -1299,9 +1297,29 @@ static void rx_timer_fn(unsigned long arg)
        }
 
        status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
-       if (status == DMA_COMPLETE)
+       if (status == DMA_COMPLETE) {
                dev_dbg(port->dev, "Cookie %d #%d has already completed\n",
                        s->active_rx, active);
+               spin_unlock_irqrestore(&port->lock, flags);
+
+               /* Let packet complete handler take care of the packet */
+               return;
+       }
+
+       dmaengine_pause(chan);
+
+       /*
+        * sometimes DMA transfer doesn't stop even if it is stopped and
+        * data keeps on coming until transaction is complete so check
+        * for DMA_COMPLETE again
+        * Let packet complete handler take care of the packet
+        */
+       status = dmaengine_tx_status(s->chan_rx, s->active_rx, &state);
+       if (status == DMA_COMPLETE) {
+               spin_unlock_irqrestore(&port->lock, flags);
+               dev_dbg(port->dev, "Transaction complete after DMA engine was stopped");
+               return;
+       }
 
        /* Handle incomplete DMA receive */
        dmaengine_terminate_all(s->chan_rx);
@@ -1315,10 +1333,18 @@ static void rx_timer_fn(unsigned long arg)
                        tty_flip_buffer_push(&port->state->port);
        }
 
-       spin_unlock_irqrestore(&port->lock, flags);
-
        if (port->type == PORT_SCIFA || port->type == PORT_SCIFB)
                sci_submit_rx(s);
+
+       /* Direct new serial port interrupts back to CPU */
+       scr = serial_port_in(port, SCSCR);
+       if (port->type == PORT_SCIFA || port->type == PORT_SCIFB) {
+               scr &= ~SCSCR_RDRQE;
+               enable_irq(s->irqs[SCIx_RXI_IRQ]);
+       }
+       serial_port_out(port, SCSCR, scr | SCSCR_RIE);
+
+       spin_unlock_irqrestore(&port->lock, flags);
 }
 
 static void sci_request_dma(struct uart_port *port)