From 148f77310a9ddf4db5036066458d7aed92cea9ae Mon Sep 17 00:00:00 2001 From: Andy Gross Date: Sun, 31 Jan 2016 21:28:13 -0600 Subject: [PATCH] spi: qup: Fix block mode to work correctly This patch corrects the behavior of the BLOCK transactions. During block transactions, the controller must be read/written to in block size transactions. Signed-off-by: Andy Gross Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf --- drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 141 insertions(+), 40 deletions(-) --- a/drivers/spi/spi-qup.c +++ b/drivers/spi/spi-qup.c @@ -83,6 +83,8 @@ #define QUP_IO_M_MODE_BAM 3 /* QUP_OPERATIONAL fields */ +#define QUP_OP_IN_BLOCK_READ_REQ BIT(13) +#define QUP_OP_OUT_BLOCK_WRITE_REQ BIT(12) #define QUP_OP_MAX_INPUT_DONE_FLAG BIT(11) #define QUP_OP_MAX_OUTPUT_DONE_FLAG BIT(10) #define QUP_OP_IN_SERVICE_FLAG BIT(9) @@ -156,6 +158,12 @@ struct spi_qup { struct dma_slave_config tx_conf; }; +static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag) +{ + u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL); + + return opflag & flag; +} static inline bool spi_qup_is_dma_xfer(int mode) { @@ -217,29 +225,26 @@ static int spi_qup_set_state(struct spi_ return 0; } -static void spi_qup_fifo_read(struct spi_qup *controller, - struct spi_transfer *xfer) +static void spi_qup_read_from_fifo(struct spi_qup *controller, + struct spi_transfer *xfer, u32 num_words) { u8 *rx_buf = xfer->rx_buf; - u32 word, state; - int idx, shift, w_size; - - w_size = controller->w_size; + int i, shift, num_bytes; + u32 word; - while (controller->rx_bytes < xfer->len) { - - state = readl_relaxed(controller->base + QUP_OPERATIONAL); - if (0 == (state & QUP_OP_IN_FIFO_NOT_EMPTY)) - break; + for (; num_words; num_words--) { word = readl_relaxed(controller->base + QUP_INPUT_FIFO); + num_bytes = min_t(int, xfer->len - controller->rx_bytes, + controller->w_size); + if (!rx_buf) { - controller->rx_bytes += w_size; + controller->rx_bytes += num_bytes; continue; } - for (idx = 0; idx < w_size; idx++, controller->rx_bytes++) { + for (i = 0; i < num_bytes; i++, controller->rx_bytes++) { /* * The data format depends on bytes per SPI word: * 4 bytes: 0x12345678 @@ -247,38 +252,80 @@ static void spi_qup_fifo_read(struct spi * 1 byte : 0x00000012 */ shift = BITS_PER_BYTE; - shift *= (w_size - idx - 1); + shift *= (controller->w_size - i - 1); rx_buf[controller->rx_bytes] = word >> shift; } } } -static void spi_qup_fifo_write(struct spi_qup *controller, +static void spi_qup_read(struct spi_qup *controller, struct spi_transfer *xfer) { - const u8 *tx_buf = xfer->tx_buf; - u32 word, state, data; - int idx, w_size; + u32 remainder, words_per_block, num_words; + bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; + + remainder = DIV_ROUND_UP(xfer->len - controller->rx_bytes, + controller->w_size); + words_per_block = controller->in_blk_sz >> 2; + + do { + /* ACK by clearing service flag */ + writel_relaxed(QUP_OP_IN_SERVICE_FLAG, + controller->base + QUP_OPERATIONAL); + + if (is_block_mode) { + num_words = (remainder > words_per_block) ? + words_per_block : remainder; + } else { + if (!spi_qup_is_flag_set(controller, + QUP_OP_IN_FIFO_NOT_EMPTY)) + break; - w_size = controller->w_size; + num_words = 1; + } + + /* read up to the maximum transfer size available */ + spi_qup_read_from_fifo(controller, xfer, num_words); - while (controller->tx_bytes < xfer->len) { + remainder -= num_words; - state = readl_relaxed(controller->base + QUP_OPERATIONAL); - if (state & QUP_OP_OUT_FIFO_FULL) + /* if block mode, check to see if next block is available */ + if (is_block_mode && !spi_qup_is_flag_set(controller, + QUP_OP_IN_BLOCK_READ_REQ)) break; + } while (remainder); + + /* + * Due to extra stickiness of the QUP_OP_IN_SERVICE_FLAG during block + * mode reads, it has to be cleared again at the very end + */ + if (is_block_mode && spi_qup_is_flag_set(controller, + 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, + struct spi_transfer *xfer, u32 num_words) +{ + const u8 *tx_buf = xfer->tx_buf; + int i, num_bytes; + u32 word, data; + + for (; num_words; num_words--) { word = 0; - for (idx = 0; idx < w_size; idx++, controller->tx_bytes++) { - if (!tx_buf) { - controller->tx_bytes += w_size; - break; + num_bytes = min_t(int, xfer->len - controller->tx_bytes, + controller->w_size); + if (tx_buf) + for (i = 0; i < num_bytes; i++) { + data = tx_buf[controller->tx_bytes + i]; + word |= data << (BITS_PER_BYTE * (3 - i)); } - data = tx_buf[controller->tx_bytes]; - word |= data << (BITS_PER_BYTE * (3 - idx)); - } + controller->tx_bytes += num_bytes; writel_relaxed(word, controller->base + QUP_OUTPUT_FIFO); } @@ -291,6 +338,44 @@ static void spi_qup_dma_done(void *data) complete(done); } +static void spi_qup_write(struct spi_qup *controller, + struct spi_transfer *xfer) +{ + bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK; + u32 remainder, words_per_block, num_words; + + remainder = DIV_ROUND_UP(xfer->len - controller->tx_bytes, + controller->w_size); + words_per_block = controller->out_blk_sz >> 2; + + do { + /* ACK by clearing service flag */ + writel_relaxed(QUP_OP_OUT_SERVICE_FLAG, + controller->base + QUP_OPERATIONAL); + + if (is_block_mode) { + num_words = (remainder > words_per_block) ? + words_per_block : remainder; + } else { + if (spi_qup_is_flag_set(controller, + QUP_OP_OUT_FIFO_FULL)) + break; + + num_words = 1; + } + + spi_qup_write_to_fifo(controller, xfer, num_words); + + remainder -= num_words; + + /* if block mode, check to see if next block is available */ + if (is_block_mode && !spi_qup_is_flag_set(controller, + QUP_OP_OUT_BLOCK_WRITE_REQ)) + break; + + } while (remainder); +} + static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer, enum dma_transfer_direction dir, dma_async_tx_callback callback, @@ -348,11 +433,13 @@ unsigned long timeout) return ret; } - if (xfer->rx_buf) - rx_done = spi_qup_dma_done; + if (!qup->qup_v1) { + if (xfer->rx_buf) + rx_done = spi_qup_dma_done; - if (xfer->tx_buf) - tx_done = spi_qup_dma_done; + if (xfer->tx_buf) + tx_done = spi_qup_dma_done; + } if (xfer->rx_buf) { ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done, @@ -401,7 +488,7 @@ static int spi_qup_do_pio(struct spi_mas } if (qup->mode == QUP_IO_M_MODE_FIFO) - spi_qup_fifo_write(qup, xfer); + spi_qup_write(qup, xfer); ret = spi_qup_set_state(qup, QUP_STATE_RUN); if (ret) { @@ -434,10 +521,11 @@ static irqreturn_t spi_qup_qup_irq(int i writel_relaxed(qup_err, controller->base + QUP_ERROR_FLAGS); writel_relaxed(spi_err, controller->base + SPI_ERROR_FLAGS); - writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); if (!xfer) { - dev_err_ratelimited(controller->dev, "unexpected irq %08x %08x %08x\n", + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + dev_err_ratelimited(controller->dev, + "unexpected irq %08x %08x %08x\n", qup_err, spi_err, opflags); return IRQ_HANDLED; } @@ -463,12 +551,20 @@ static irqreturn_t spi_qup_qup_irq(int i error = -EIO; } - if (!spi_qup_is_dma_xfer(controller->mode)) { + if (spi_qup_is_dma_xfer(controller->mode)) { + writel_relaxed(opflags, controller->base + QUP_OPERATIONAL); + if (opflags & QUP_OP_IN_SERVICE_FLAG && + opflags & QUP_OP_MAX_INPUT_DONE_FLAG) + complete(&controller->done); + if (opflags & QUP_OP_OUT_SERVICE_FLAG && + opflags & QUP_OP_MAX_OUTPUT_DONE_FLAG) + complete(&controller->dma_tx_done); + } else { if (opflags & QUP_OP_IN_SERVICE_FLAG) - spi_qup_fifo_read(controller, xfer); + spi_qup_read(controller, xfer); if (opflags & QUP_OP_OUT_SERVICE_FLAG) - spi_qup_fifo_write(controller, xfer); + spi_qup_write(controller, xfer); } spin_lock_irqsave(&controller->lock, flags); @@ -476,6 +572,9 @@ static irqreturn_t spi_qup_qup_irq(int i controller->xfer = xfer; spin_unlock_irqrestore(&controller->lock, flags); + /* re-read opflags as flags may have changed due to actions above */ + opflags = readl_relaxed(controller->base + QUP_OPERATIONAL); + if ((controller->rx_bytes == xfer->len && (opflags & QUP_OP_MAX_INPUT_DONE_FLAG)) || error) complete(&controller->done); @@ -519,11 +618,13 @@ static int spi_qup_io_config(struct spi_ /* must be zero for FIFO */ writel_relaxed(0, controller->base + QUP_MX_INPUT_CNT); writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT); - controller->use_dma = 0; } else if (spi->master->can_dma && spi->master->can_dma(spi->master, spi, xfer) && spi->master->cur_msg_mapped) { controller->mode = QUP_IO_M_MODE_BAM; + writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT); + writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT); + /* must be zero for BLOCK and BAM */ writel_relaxed(0, controller->base + QUP_MX_READ_CNT); writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);