[PATCH] spi: atmel: fix corruption caused by too early transfer completion

Ludovic Desroches ludovic.desroches at atmel.com
Tue Aug 12 23:16:39 PDT 2014


Hi,

On Wed, Aug 13, 2014 at 01:20:13AM +0000, Yang, Wenyou wrote:
> Hi,
> > -----Original Message-----
> > From: linux-arm-kernel [mailto:linux-arm-kernel-bounces at lists.infradead.org] On
> > Behalf Of Ronald Wahl
> > Sent: Wednesday, August 06, 2014 9:01 PM
> > To: linux-arm-kernel at lists.infradead.org
> > Subject: [PATCH] spi: atmel: fix corruption caused by too early transfer
> > completion
> > 
> > The PDC (peripheral DMA controller) on AT91 supports two transfer counters and
> > associated registers - one for current and one for the next transfer. If the current
> > transfer is done the next transfer is moved into the current transfer. Now there are
> > two interrupts: one is raised whenever a single transfer is done (ENDRX) and the
> > other one is raised when the current and the next transfer has finished (RXBUFF).
> > The issue is that the driver only enables the ENDRX interrupt which may lead to
> > queuing a new request while there is still a transfer running.
> > This can lead to overruns and/or corruption. By using the RXBUFF interrupt only
> > we queue new requests only when the hardware queue is empty avoiding this
> > problem.
> 
> The patch can work, but it maybe decrease the performance.

Can you give more details? I had also the same feeling at the beginning
but due to the way pdc is implemented in the spi driver (doesn't really
take advantage of the double buffer), I think it should not change performances.

My only concern is why ENDRX was chosen instead of RXBUFF? Simple bug or
to manage a specific case we are thinking about.

> 
> Could you share the scenario caused the overruns and/or corruption, or log message?
> 
> > 
> > Signed-off-by: Ronald Wahl <ronald.wahl at raritan.com>
> > ---
> >  drivers/spi/spi-atmel.c | 17 ++++++++---------
> >  1 file changed, 8 insertions(+), 9 deletions(-)
> > 
> > diff --git a/drivers/spi/spi-atmel.c b/drivers/spi/spi-atmel.c index 113c83f..3f7d138
> > 100644
> > --- a/drivers/spi/spi-atmel.c
> > +++ b/drivers/spi/spi-atmel.c
> > @@ -775,17 +775,17 @@ static void atmel_spi_pdc_next_xfer(struct spi_master
> > *master,
> >  			(unsigned long long)xfer->rx_dma);
> >  	}
> > 
> > -	/* REVISIT: We're waiting for ENDRX before we start the next
> > +	/* REVISIT: We're waiting for RXBUFF before we start the next
> >  	 * transfer because we need to handle some difficult timing
> > -	 * issues otherwise. If we wait for ENDTX in one transfer and
> > -	 * then starts waiting for ENDRX in the next, it's difficult
> > -	 * to tell the difference between the ENDRX interrupt we're
> > -	 * actually waiting for and the ENDRX interrupt of the
> > +	 * issues otherwise. If we wait for TXBUFE in one transfer and
> > +	 * then starts waiting for RXBUFF in the next, it's difficult
> > +	 * to tell the difference between the RXBUFF interrupt we're
> > +	 * actually waiting for and the RXBUFF interrupt of the
> >  	 * previous transfer.
> >  	 *
> >  	 * It should be doable, though. Just not now...
> >  	 */
> > -	spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES));
> > +	spi_writel(as, IER, SPI_BIT(RXBUFF) | SPI_BIT(OVRES));
> >  	spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN));  }
> > 
> > @@ -956,8 +956,7 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id)
> > 
> >  		ret = IRQ_HANDLED;
> > 
> > -		spi_writel(as, IDR, (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX)
> > -				     | SPI_BIT(OVRES)));
> > +		spi_writel(as, IDR, (SPI_BIT(RXBUFF) | SPI_BIT(OVRES)));
> > 
> >  		/* Clear any overrun happening while cleaning up */
> >  		spi_readl(as, SR);
> > @@ -966,7 +965,7 @@ atmel_spi_pdc_interrupt(int irq, void *dev_id)
> > 
> >  		complete(&as->xfer_completion);
> > 
> > -	} else if (pending & (SPI_BIT(RXBUFF) | SPI_BIT(ENDRX))) {
> > +	} else if (pending & (SPI_BIT(RXBUFF))) {
> >  		ret = IRQ_HANDLED;
> > 
> >  		spi_writel(as, IDR, pending);
> > --
> > 1.9.3
> > 
> > 
> > _______________________________________________
> > linux-arm-kernel mailing list
> > linux-arm-kernel at lists.infradead.org
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> 
> Best Regards,
> Wenyou Yang
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list