amba-pl011/dma: Add check for the residue in DMA callback
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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
b26469a8b1
commit
6dc01aa653
@@ -827,7 +827,12 @@ static void pl011_dma_rx_callback(void *data)
|
|||||||
{
|
{
|
||||||
struct uart_amba_port *uap = data;
|
struct uart_amba_port *uap = data;
|
||||||
struct pl011_dmarx_data *dmarx = &uap->dmarx;
|
struct pl011_dmarx_data *dmarx = &uap->dmarx;
|
||||||
|
struct dma_chan *rxchan = dmarx->chan;
|
||||||
bool lastbuf = dmarx->use_buf_b;
|
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;
|
int ret;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -838,11 +843,21 @@ static void pl011_dma_rx_callback(void *data)
|
|||||||
* we immediately trigger the next DMA job.
|
* we immediately trigger the next DMA job.
|
||||||
*/
|
*/
|
||||||
spin_lock_irq(&uap->port.lock);
|
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;
|
uap->dmarx.running = false;
|
||||||
dmarx->use_buf_b = !lastbuf;
|
dmarx->use_buf_b = !lastbuf;
|
||||||
ret = pl011_dma_rx_trigger_dma(uap);
|
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);
|
spin_unlock_irq(&uap->port.lock);
|
||||||
/*
|
/*
|
||||||
* Do this check after we picked the DMA chars so we don't
|
* Do this check after we picked the DMA chars so we don't
|
||||||
|
Reference in New Issue
Block a user