diff options
author | John Crispin <john@phrozen.org> | 2017-03-13 13:30:10 +0100 |
---|---|---|
committer | Felix Fietkau <nbd@nbd.name> | 2017-03-13 13:40:55 +0100 |
commit | 1adf51702e94063fb058ce13226ec5a04d15cfd4 (patch) | |
tree | 00fb0f446f516289abb93ff78f103294254545c3 /target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch | |
parent | 99b6aefd638c7934136ffcde271e6fc3f4f6c464 (diff) | |
download | mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.zip mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.tar.gz mtk-20170518-1adf51702e94063fb058ce13226ec5a04d15cfd4.tar.bz2 |
ipq806x: clean up patches, port missing patches from 4.4
Signed-off-by: John Crispin <john@phrozen.org>
Signed-off-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch')
-rw-r--r-- | target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch new file mode 100644 index 0000000..e7d0d65 --- /dev/null +++ b/target/linux/ipq806x/patches-4.9/0006-spi-qup-Fix-block-mode-to-work-correctly.patch @@ -0,0 +1,310 @@ +From b56c1e35cc550fd014fa601ca56b964d88fd44a9 Mon Sep 17 00:00:00 2001 +From: Andy Gross <andy.gross@linaro.org> +Date: Sun, 31 Jan 2016 21:28:13 -0600 +Subject: [PATCH 06/69] 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 <andy.gross@linaro.org> +--- + drivers/spi/spi-qup.c | 182 +++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 142 insertions(+), 40 deletions(-) + +--- a/drivers/spi/spi-qup.c ++++ b/drivers/spi/spi-qup.c +@@ -82,6 +82,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) +@@ -155,6 +157,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) + { +@@ -216,29 +224,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 +@@ -246,38 +251,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); + } +@@ -290,6 +337,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, +@@ -347,11 +432,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, +@@ -398,7 +485,8 @@ static int spi_qup_do_pio(struct spi_mas + return ret; + } + +- spi_qup_fifo_write(qup, xfer); ++ if (qup->mode == QUP_IO_M_MODE_FIFO) ++ spi_qup_write(qup, xfer); + + ret = spi_qup_set_state(qup, QUP_STATE_RUN); + if (ret) { +@@ -431,10 +519,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; + } +@@ -460,12 +549,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); +@@ -473,6 +570,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); +@@ -516,11 +616,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); +- + } 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); + |