writel_relaxed(QUP_OP_IN_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL);
+ if (!remainder)
+ goto exit;
+
if (is_block_mode) {
num_words = (remainder > words_per_block) ?
words_per_block : remainder;
* to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
* present and this is used to determine if transaction is complete
*/
- *opflags = readl_relaxed(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);
-
+exit:
+ if (!remainder) {
+ *opflags = readl_relaxed(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)
writel_relaxed(QUP_OP_OUT_SERVICE_FLAG,
controller->base + QUP_OPERATIONAL);
+ /* make sure the interrupt is valid */
+ if (!remainder)
+ return;
+
if (is_block_mode) {
num_words = (remainder > words_per_block) ?
words_per_block : remainder;
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)
{
struct spi_qup *controller = dev_id;
u32 opflags, qup_err, spi_err;
+ unsigned long flags;
int error = 0;
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
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)) {
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
} else {
if (opflags & QUP_OP_OUT_SERVICE_FLAG)
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);
+ 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;
}