amba-pl011​/dma: Add check for the residue in DMA callback
authorChanho Min <chanho0207@gmail.com>
Mon, 20 Feb 2012 01:24:40 +0000 (10:24 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 24 Feb 2012 21:50:47 +0000 (13:50 -0800)
In DMA-operated uart, I found that rx data can be taken by the UART
interrupts during the DMA irq handler. pl011_int is occurred just
before it goes inside spin_lock_irq. When it returns to the callback,
DMA buffer already has been flushed. Then, pl011_dma_rx_chars gets
invalid data. So I add check for the residue as the patch bellow.

Signed-off-by: Chanho Min <chanho.min@lge.com>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/serial/amba-pl011.c

index 6800f5f26241430789a92efea30d58920dca9453..cc3ea066c43a2293490f40011d57b6b33d043a72 100644 (file)
@@ -827,7 +827,12 @@ static void pl011_dma_rx_callback(void *data)
 {
        struct uart_amba_port *uap = data;
        struct pl011_dmarx_data *dmarx = &uap->dmarx;
+       struct dma_chan *rxchan = dmarx->chan;
        bool lastbuf = dmarx->use_buf_b;
+       struct pl011_sgbuf *sgbuf = dmarx->use_buf_b ?
+               &dmarx->sgbuf_b : &dmarx->sgbuf_a;
+       size_t pending;
+       struct dma_tx_state state;
        int ret;
 
        /*
@@ -838,11 +843,21 @@ static void pl011_dma_rx_callback(void *data)
         * we immediately trigger the next DMA job.
         */
        spin_lock_irq(&uap->port.lock);
+       /*
+        * Rx data can be taken by the UART interrupts during
+        * the DMA irq handler. So we check the residue here.
+        */
+       rxchan->device->device_tx_status(rxchan, dmarx->cookie, &state);
+       pending = sgbuf->sg.length - state.residue;
+       BUG_ON(pending > PL011_DMA_BUFFER_SIZE);
+       /* Then we terminate the transfer - we now know our residue */
+       dmaengine_terminate_all(rxchan);
+
        uap->dmarx.running = false;
        dmarx->use_buf_b = !lastbuf;
        ret = pl011_dma_rx_trigger_dma(uap);
 
-       pl011_dma_rx_chars(uap, PL011_DMA_BUFFER_SIZE, lastbuf, false);
+       pl011_dma_rx_chars(uap, pending, lastbuf, false);
        spin_unlock_irq(&uap->port.lock);
        /*
         * Do this check after we picked the DMA chars so we don't