Skip to content

Commit

Permalink
spi: spi-xilinx: Remove ISR race condition
Browse files Browse the repository at this point in the history
The ISR currently consumes the rx buffer data and re-enables transmission
from within interrupt context. This is bad because if the interrupt
occurs again before the ISR exits, the new interrupt will be erroneously
cleared by the still completing ISR.

Simplified the ISR by just setting the completion variable and exiting with
no action. Then just looped the transmit functionality in
xilinx_spi_txrx_bufs().

Signed-off-by: Peter Crosthwaite <[email protected]>
Signed-off-by: Michal Simek <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
pete128 authored and broonie committed Jun 4, 2013
1 parent d683b96 commit 68c315b
Showing 1 changed file with 35 additions and 39 deletions.
74 changes: 35 additions & 39 deletions drivers/spi/spi-xilinx.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,6 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
{
struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
u32 ipif_ier;
u16 cr;

/* We get here with transmitter inhibited */

Expand All @@ -276,7 +275,6 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
xspi->remaining_bytes = t->len;
INIT_COMPLETION(xspi->done);

xilinx_spi_fill_tx_fifo(xspi);

/* Enable the transmit empty interrupt, which we use to determine
* progress on the transmission.
Expand All @@ -285,12 +283,41 @@ static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
xspi->write_fn(ipif_ier | XSPI_INTR_TX_EMPTY,
xspi->regs + XIPIF_V123B_IIER_OFFSET);

/* Start the transfer by not inhibiting the transmitter any longer */
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
~XSPI_CR_TRANS_INHIBIT;
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
for (;;) {
u16 cr;
u8 sr;

xilinx_spi_fill_tx_fifo(xspi);

/* Start the transfer by not inhibiting the transmitter any
* longer
*/
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET) &
~XSPI_CR_TRANS_INHIBIT;
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);

wait_for_completion(&xspi->done);

/* A transmit has just completed. Process received data and
* check for more data to transmit. Always inhibit the
* transmitter while the Isr refills the transmit register/FIFO,
* or make sure it is stopped if we're done.
*/
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
xspi->regs + XSPI_CR_OFFSET);

/* Read out all the data from the Rx FIFO */
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
xspi->rx_fn(xspi);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}

wait_for_completion(&xspi->done);
/* See if there is more data to send */
if (!xspi->remaining_bytes > 0)
break;
}

/* Disable the transmit empty interrupt */
xspi->write_fn(ipif_ier, xspi->regs + XIPIF_V123B_IIER_OFFSET);
Expand All @@ -314,38 +341,7 @@ static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
xspi->write_fn(ipif_isr, xspi->regs + XIPIF_V123B_IISR_OFFSET);

if (ipif_isr & XSPI_INTR_TX_EMPTY) { /* Transmission completed */
u16 cr;
u8 sr;

/* A transmit has just completed. Process received data and
* check for more data to transmit. Always inhibit the
* transmitter while the Isr refills the transmit register/FIFO,
* or make sure it is stopped if we're done.
*/
cr = xspi->read_fn(xspi->regs + XSPI_CR_OFFSET);
xspi->write_fn(cr | XSPI_CR_TRANS_INHIBIT,
xspi->regs + XSPI_CR_OFFSET);

/* Read out all the data from the Rx FIFO */
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
xspi->rx_fn(xspi);
sr = xspi->read_fn(xspi->regs + XSPI_SR_OFFSET);
}

/* See if there is more data to send */
if (xspi->remaining_bytes > 0) {
xilinx_spi_fill_tx_fifo(xspi);
/* Start the transfer by not inhibiting the
* transmitter any longer
*/
xspi->write_fn(cr, xspi->regs + XSPI_CR_OFFSET);
} else {
/* No more data to send.
* Indicate the transfer is completed.
*/
complete(&xspi->done);
}
complete(&xspi->done);
}

return IRQ_HANDLED;
Expand Down

0 comments on commit 68c315b

Please sign in to comment.