aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
diff options
context:
space:
mode:
authorJohn Crispin <john@phrozen.org>2016-08-11 16:34:08 +0200
committerJohn Crispin <john@phrozen.org>2016-08-18 09:49:18 +0200
commitd7e4b9babb6ce8bf66c4c2e721b78c30d09afdda (patch)
tree7808c469b98212bd9da8b8aa7132829946664a3c /target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
parent5e563262f12d919f5062b5e47d60d0be64d33d35 (diff)
downloadupstream-d7e4b9babb6ce8bf66c4c2e721b78c30d09afdda.tar.gz
upstream-d7e4b9babb6ce8bf66c4c2e721b78c30d09afdda.tar.bz2
upstream-d7e4b9babb6ce8bf66c4c2e721b78c30d09afdda.zip
ipq806x: sync with latest patches sent by QCA
Signed-off-by: John Crispin <john@phrozen.org>
Diffstat (limited to 'target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch')
-rw-r--r--target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch317
1 files changed, 317 insertions, 0 deletions
diff --git a/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
new file mode 100644
index 0000000000..7a05ecdba5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/713-spi-qup-Fix-block-mode-to-work-correctly.patch
@@ -0,0 +1,317 @@
+From 148f77310a9ddf4db5036066458d7aed92cea9ae 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] 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>
+
+Change-Id: I4b4f4d25be57e6e8148f6f0d24bed376eb287ecf
+---
+ drivers/spi/spi-qup.c | 181 +++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 141 insertions(+), 40 deletions(-)
+
+diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
+index 089c5e8..e487416 100644
+--- 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_qup *controller, u32 state)
+ 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;
+-
+- while (controller->rx_bytes < xfer->len) {
++ int i, shift, num_bytes;
++ u32 word;
+
+- 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_qup *controller,
+ * 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;
++
++ num_words = 1;
++ }
+
+- w_size = controller->w_size;
++ /* 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_master *master, struct spi_transfer *xfer,
+ }
+
+ 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 irq, void *dev_id)
+
+ 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 irq, void *dev_id)
+ 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 irq, void *dev_id)
+ 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_device *spi, struct spi_transfer *xfer)
+ /* 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);
+
+--
+2.7.2
+