Commit a75e91ba authored by Jorge Ramirez-Ortiz's avatar Jorge Ramirez-Ortiz Committed by Mark Brown

spi: qup: fix PIO/DMA transfers.

- DMA/PIO:
  If an error IRQ occurred during PIO or DMA mode make sure to log it so
on completion the transfer can be marked as an error.

- PIO:
  Do not complete a transaction until all data has been transferred or
an error IRQ was flagged.

1) If there was no error IRQ, ignore the done flag IRQ
(QUP_OP_MAX_INPUT_DONE_FLAG) until all data for the transfer has been
processed: not doing so risks completing the transfer returning
uninitialized data in the buffers.

2) Under stress testing we have identified the need to
protect read/write operations against spurious IN/OUT service events.
Signed-off-by: default avatarJorge Ramirez-Ortiz <jorge.ramirez-ortiz@linaro.org>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent f3440d9a
...@@ -281,6 +281,9 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags) ...@@ -281,6 +281,9 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
writel_relaxed(QUP_OP_IN_SERVICE_FLAG, writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL); controller->base + QUP_OPERATIONAL);
if (!remainder)
goto exit;
if (is_block_mode) { if (is_block_mode) {
num_words = (remainder > words_per_block) ? num_words = (remainder > words_per_block) ?
words_per_block : remainder; words_per_block : remainder;
...@@ -310,11 +313,13 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags) ...@@ -310,11 +313,13 @@ static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
* to refresh opflags value because MAX_INPUT_DONE_FLAG may now be * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
* present and this is used to determine if transaction is complete * present and this is used to determine if transaction is complete
*/ */
*opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); exit:
if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG) if (!remainder) {
writel_relaxed(QUP_OP_IN_SERVICE_FLAG, *opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);
controller->base + QUP_OPERATIONAL); if (is_block_mode && *opflags & QUP_OP_MAX_INPUT_DONE_FLAG)
writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL);
}
} }
static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words) static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
...@@ -362,6 +367,10 @@ static void spi_qup_write(struct spi_qup *controller) ...@@ -362,6 +367,10 @@ static void spi_qup_write(struct spi_qup *controller)
writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL); controller->base + QUP_OPERATIONAL);
/* make sure the interrupt is valid */
if (!remainder)
return;
if (is_block_mode) { if (is_block_mode) {
num_words = (remainder > words_per_block) ? num_words = (remainder > words_per_block) ?
words_per_block : remainder; words_per_block : remainder;
...@@ -575,10 +584,24 @@ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer, ...@@ -575,10 +584,24 @@ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
return 0; return 0;
} }
static bool spi_qup_data_pending(struct spi_qup *controller)
{
unsigned int remainder_tx, remainder_rx;
remainder_tx = DIV_ROUND_UP(spi_qup_len(controller) -
controller->tx_bytes, controller->w_size);
remainder_rx = DIV_ROUND_UP(spi_qup_len(controller) -
controller->rx_bytes, controller->w_size);
return remainder_tx || remainder_rx;
}
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
{ {
struct spi_qup *controller = dev_id; struct spi_qup *controller = dev_id;
u32 opflags, qup_err, spi_err; u32 opflags, qup_err, spi_err;
unsigned long flags;
int error = 0; int error = 0;
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS); qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
...@@ -610,6 +633,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) ...@@ -610,6 +633,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
error = -EIO; error = -EIO;
} }
spin_lock_irqsave(&controller->lock, flags);
if (!controller->error)
controller->error = error;
spin_unlock_irqrestore(&controller->lock, flags);
if (spi_qup_is_dma_xfer(controller->mode)) { if (spi_qup_is_dma_xfer(controller->mode)) {
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
} else { } else {
...@@ -618,11 +646,22 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id) ...@@ -618,11 +646,22 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
if (opflags & QUP_OP_OUT_SERVICE_FLAG) if (opflags & QUP_OP_OUT_SERVICE_FLAG)
spi_qup_write(controller); spi_qup_write(controller);
if (!spi_qup_data_pending(controller))
complete(&controller->done);
} }
if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error) if (error)
complete(&controller->done); complete(&controller->done);
if (opflags & QUP_OP_MAX_INPUT_DONE_FLAG) {
if (!spi_qup_is_dma_xfer(controller->mode)) {
if (spi_qup_data_pending(controller))
return IRQ_HANDLED;
}
complete(&controller->done);
}
return IRQ_HANDLED; return IRQ_HANDLED;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment