2017-07-28 06:53:17

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 00/14] spi: qup: Fixes and add support for >64k transfers

v6:
Fix git bisect-ability issues in
spi: qup: Add completion timeout
spi: qup: call io_config in mode specific function
spi: qup: allow multiple DMA transactions per spi xfer

v5:
Incorporate feedback from Mark Brown and Geert Uytterhoeven

spi: qup: Enable chip select support
Update commit log. Include the commit's description

Removed "spi: qup: Add completion structures for DMA". This
introduced separate completion structures for DMA based rx/tx
and FIFO based i/os. This was not needed.

Added "spi: qup: Fix QUP version identify method" to identify
qup version using of_device_get_match_data instead of
of_device_is_compatible.

v4:
Discard patch #15, 'spi: qup: support for qup v1 dma'.
This depends on ADM driver, which is not upstreamed yet.

v3:
Fix git bisect-ability issues in
spi: qup: Add completion structures for DMA
spi: qup: Add completion timeout
spi: qup: Place the QUP in run mode before DMA
spi: qup: Fix transaction done signaling

v2:
Incorporate feedback from Andy Gross, Sricharan, Stanimir Varbanov

Modified the QUP-v1 dma completion sequence to QUP-v2 as per feedback.

Removed code that used controller->xfer to identify extra interrupts,
since with the fixes done to handle i/o completion we don't see
extra interrupts.

v1:
This series fixes some existing issues in the code for both
interrupt and dma mode. Patches 1 - 11 are the fixes.
Random failures/timeout are observed without these fixes.
Also, the current driver does not support block transfers > 64K
and the driver quietly fails. Patches 12 - 18 add support for this
in both interrupt and dma mode.

The entire series has been tested on ipq4019 with
SPI-NOR flash for block sizes > 64k.

Varadarajan Narayanan (14):
spi: qup: Enable chip select support
spi: qup: Setup DMA mode correctly
spi: qup: Add completion timeout
spi: qup: Place the QUP in run mode before DMA
spi: qup: Fix error handling in spi_qup_prep_sg
spi: qup: Fix transaction done signaling
spi: qup: Do block sized read/write in block mode
spi: qup: refactor spi_qup_io_config into two functions
spi: qup: call io_config in mode specific function
spi: qup: allow block mode to generate multiple transactions
spi: qup: refactor spi_qup_prep_sg
spi: qup: allow multiple DMA transactions per spi xfer
spi: qup: Ensure done detection
spi: qup: Fix QUP version identify method

drivers/spi/spi-qup.c | 566 ++++++++++++++++++++++++++++++++++----------------
1 file changed, 392 insertions(+), 174 deletions(-)

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2017-07-28 06:53:23

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 01/14] spi: qup: Enable chip select support

Enable chip select support for QUP versions later than v1. The
chip select support was broken in QUP version 1. Hence the chip
select support was removed earlier in an earlier commit
(4a8573abe "spi: qup: Remove chip select function"). Since the
chip select support is functional in recent versions of QUP,
re-enabling it for QUP versions later than v1.

Signed-off-by: Sham Muthayyan <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 1bfa889..c0d4def 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -750,6 +750,24 @@ static int spi_qup_init_dma(struct spi_master *master, resource_size_t base)
return ret;
}

+static void spi_qup_set_cs(struct spi_device *spi, bool val)
+{
+ struct spi_qup *controller;
+ u32 spi_ioc;
+ u32 spi_ioc_orig;
+
+ controller = spi_master_get_devdata(spi->master);
+ spi_ioc = readl_relaxed(controller->base + SPI_IO_CONTROL);
+ spi_ioc_orig = spi_ioc;
+ if (!val)
+ spi_ioc |= SPI_IO_C_FORCE_CS;
+ else
+ spi_ioc &= ~SPI_IO_C_FORCE_CS;
+
+ if (spi_ioc != spi_ioc_orig)
+ writel_relaxed(spi_ioc, controller->base + SPI_IO_CONTROL);
+}
+
static int spi_qup_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -846,6 +864,9 @@ static int spi_qup_probe(struct platform_device *pdev)
if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
controller->qup_v1 = 1;

+ if (!controller->qup_v1)
+ master->set_cs = spi_qup_set_cs;
+
spin_lock_init(&controller->lock);
init_completion(&controller->done);

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:30

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 03/14] spi: qup: Add completion timeout

Add i/o completion timeout for DMA and PIO modes.

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index abe799b..fdd34c3 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -331,8 +331,10 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
+static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
+ unsigned long timeout)
{
+ struct spi_qup *qup = spi_master_get_devdata(master);
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
int ret;

@@ -357,10 +359,14 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
dma_async_issue_pending(master->dma_tx);
}

+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
return 0;
}

-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
+static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
+ unsigned long timeout)
{
struct spi_qup *qup = spi_master_get_devdata(master);
int ret;
@@ -379,6 +385,9 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)

spi_qup_fifo_write(qup, xfer);

+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
return 0;
}

@@ -632,9 +641,9 @@ static int spi_qup_transfer_one(struct spi_master *master,
spin_unlock_irqrestore(&controller->lock, flags);

if (spi_qup_is_dma_xfer(controller->mode))
- ret = spi_qup_do_dma(master, xfer);
+ ret = spi_qup_do_dma(master, xfer, timeout);
else
- ret = spi_qup_do_pio(master, xfer);
+ ret = spi_qup_do_pio(master, xfer, timeout);

if (ret)
goto exit;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:40

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 06/14] spi: qup: Fix transaction done signaling

Wait to signal done until we get all of the interrupts we are expecting
to get for a transaction. If we don't wait for the input done flag, we
can be in between transactions when the done flag comes in and this can
mess up the next transaction.

While here cleaning up the code which sets controller->xfer = NULL and
restores it in the ISR. This looks to be some debug code which is not
required.

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 27 +++++----------------------
1 file changed, 5 insertions(+), 22 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index ef95294..a7c630c 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -409,29 +409,16 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
{
struct spi_qup *controller = dev_id;
- struct spi_transfer *xfer;
+ struct spi_transfer *xfer = controller->xfer;
u32 opflags, qup_err, spi_err;
- unsigned long flags;
int error = 0;

- spin_lock_irqsave(&controller->lock, flags);
- xfer = controller->xfer;
- controller->xfer = NULL;
- spin_unlock_irqrestore(&controller->lock, flags);
-
qup_err = readl_relaxed(controller->base + QUP_ERROR_FLAGS);
spi_err = readl_relaxed(controller->base + SPI_ERROR_FLAGS);
opflags = readl_relaxed(controller->base + QUP_OPERATIONAL);

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",
- qup_err, spi_err, opflags);
- return IRQ_HANDLED;
- }

if (qup_err) {
if (qup_err & QUP_ERROR_OUTPUT_OVER_RUN)
@@ -455,7 +442,9 @@ 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);
+ } else {
if (opflags & QUP_OP_IN_SERVICE_FLAG)
spi_qup_fifo_read(controller, xfer);

@@ -463,12 +452,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
spi_qup_fifo_write(controller, xfer);
}

- spin_lock_irqsave(&controller->lock, flags);
- controller->error = error;
- controller->xfer = xfer;
- spin_unlock_irqrestore(&controller->lock, flags);
-
- if (controller->rx_bytes == xfer->len || error)
+ if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
complete(&controller->done);

return IRQ_HANDLED;
@@ -666,7 +650,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
exit:
spi_qup_set_state(controller, QUP_STATE_RESET);
spin_lock_irqsave(&controller->lock, flags);
- controller->xfer = NULL;
if (!ret)
ret = controller->error;
spin_unlock_irqrestore(&controller->lock, flags);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:45

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 07/14] spi: qup: Do block sized read/write in block mode

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 <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 151 +++++++++++++++++++++++++++++++++++++++-----------
1 file changed, 119 insertions(+), 32 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index a7c630c..8cfa112 100644
--- 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)
@@ -154,6 +156,13 @@ 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) != 0;
+}
+
static inline bool spi_qup_is_dma_xfer(int mode)
{
if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
@@ -214,29 +223,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;
+ int i, shift, num_bytes;
+ u32 word;

- w_size = controller->w_size;
-
- 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
@@ -244,38 +250,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;

- w_size = controller->w_size;
+ num_words = 1;
+ }

- while (controller->tx_bytes < xfer->len) {
+ /* read up to the maximum transfer size available */
+ spi_qup_read_from_fifo(controller, xfer, num_words);

- state = readl_relaxed(controller->base + QUP_OPERATIONAL);
- if (state & QUP_OP_OUT_FIFO_FULL)
+ 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_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);
}
@@ -288,6 +336,44 @@ static void spi_qup_dma_done(void *data)
complete(&qup->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)
@@ -391,7 +477,8 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
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) {
@@ -446,10 +533,10 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
} 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);
}

if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:51

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 08/14] spi: qup: refactor spi_qup_io_config into two functions

This is in preparation for handling transactions larger than
64K-1 bytes in block mode, which is currently unsupported and
quietly fails.

We need to break these into two functions 1) prep is
called once per spi_message and 2) io_config is called
once per spi-qup bus transaction

This is just refactoring, there should be no functional
change

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 91 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 62 insertions(+), 29 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 8cfa112..ff5aa08 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -545,12 +545,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}

-/* set clock freq ... bits per word */
-static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+/* set clock freq ... bits per word, determine mode */
+static int spi_qup_io_prep(struct spi_device *spi, struct spi_transfer *xfer)
{
struct spi_qup *controller = spi_master_get_devdata(spi->master);
- u32 config, iomode, control;
- int ret, n_words;
+ int ret;

if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
dev_err(controller->dev, "too big size for loopback %d > %d\n",
@@ -565,32 +564,56 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
return -EIO;
}

- if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
- dev_err(controller->dev, "cannot set RESET state\n");
- return -EIO;
- }
-
controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
controller->n_words = xfer->len / controller->w_size;
- n_words = controller->n_words;
-
- if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {

+ if (controller->n_words <= (controller->in_fifo_sz / sizeof(u32)))
controller->mode = QUP_IO_M_MODE_FIFO;
+ 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;
+ else
+ controller->mode = QUP_IO_M_MODE_BLOCK;
+
+ return 0;
+}
+
+/* prep qup for another spi transaction of specific type */
+static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
+{
+ struct spi_qup *controller = spi_master_get_devdata(spi->master);
+ u32 config, iomode, control;
+ unsigned long flags;
+
+ spin_lock_irqsave(&controller->lock, flags);
+ controller->xfer = xfer;
+ controller->error = 0;
+ controller->rx_bytes = 0;
+ controller->tx_bytes = 0;
+ spin_unlock_irqrestore(&controller->lock, flags);
+
+
+ if (spi_qup_set_state(controller, QUP_STATE_RESET)) {
+ dev_err(controller->dev, "cannot set RESET state\n");
+ return -EIO;
+ }

- writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
- writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
+ switch (controller->mode) {
+ case QUP_IO_M_MODE_FIFO:
+ writel_relaxed(controller->n_words,
+ controller->base + QUP_MX_READ_CNT);
+ writel_relaxed(controller->n_words,
+ controller->base + QUP_MX_WRITE_CNT);
/* 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);
+ break;
+ case QUP_IO_M_MODE_BAM:
+ writel_relaxed(controller->n_words,
+ controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(controller->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);
@@ -608,19 +631,25 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
if (xfer->tx_buf)
writel_relaxed(0, input_cnt);
else
- writel_relaxed(n_words, input_cnt);
+ writel_relaxed(controller->n_words, input_cnt);

writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
}
- } else {
-
- controller->mode = QUP_IO_M_MODE_BLOCK;
-
- writel_relaxed(n_words, controller->base + QUP_MX_INPUT_CNT);
- writel_relaxed(n_words, controller->base + QUP_MX_OUTPUT_CNT);
+ break;
+ case QUP_IO_M_MODE_BLOCK:
+ reinit_completion(&controller->done);
+ writel_relaxed(controller->n_words,
+ controller->base + QUP_MX_INPUT_CNT);
+ writel_relaxed(controller->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);
+ break;
+ default:
+ dev_err(controller->dev, "unknown mode = %d\n",
+ controller->mode);
+ return -EIO;
}

iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
@@ -709,6 +738,10 @@ static int spi_qup_transfer_one(struct spi_master *master,
unsigned long timeout, flags;
int ret = -EIO;

+ ret = spi_qup_io_prep(spi, xfer);
+ if (ret)
+ return ret;
+
ret = spi_qup_io_config(spi, xfer);
if (ret)
return ret;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:59

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 10/14] spi: qup: allow block mode to generate multiple transactions

This let's you write more to the SPI bus than 64K-1 which is important
if the block size of a SPI device is >= 64K or some other device wants
to do something larger.

This has the benefit of completely removing spi_message from the spi-qup
transactions

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 128 +++++++++++++++++++++++++++++++-------------------
1 file changed, 80 insertions(+), 48 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 1aa6078..707b1ec 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -120,7 +120,7 @@

#define SPI_NUM_CHIPSELECTS 4

-#define SPI_MAX_DMA_XFER (SZ_64K - 64)
+#define SPI_MAX_XFER (SZ_64K - 64)

/* high speed mode is when bus rate is greater then 26MHz */
#define SPI_HS_MIN_RATE 26000000
@@ -149,6 +149,8 @@ struct spi_qup {
int n_words;
int tx_bytes;
int rx_bytes;
+ const u8 *tx_buf;
+ u8 *rx_buf;
int qup_v1;

int mode;
@@ -173,6 +175,12 @@ static inline bool spi_qup_is_dma_xfer(int mode)
return false;
}

+/* get's the transaction size length */
+static inline unsigned int spi_qup_len(struct spi_qup *controller)
+{
+ return controller->n_words * controller->w_size;
+}
+
static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
{
u32 opstate = readl_relaxed(controller->base + QUP_STATE);
@@ -225,10 +233,9 @@ static int spi_qup_set_state(struct spi_qup *controller, u32 state)
return 0;
}

-static void spi_qup_read_from_fifo(struct spi_qup *controller,
- struct spi_transfer *xfer, u32 num_words)
+static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
{
- u8 *rx_buf = xfer->rx_buf;
+ u8 *rx_buf = controller->rx_buf;
int i, shift, num_bytes;
u32 word;

@@ -236,8 +243,9 @@ static void spi_qup_read_from_fifo(struct spi_qup *controller,

word = readl_relaxed(controller->base + QUP_INPUT_FIFO);

- num_bytes = min_t(int, xfer->len - controller->rx_bytes,
- controller->w_size);
+ num_bytes = min_t(int, spi_qup_len(controller) -
+ controller->rx_bytes,
+ controller->w_size);

if (!rx_buf) {
controller->rx_bytes += num_bytes;
@@ -258,13 +266,12 @@ static void spi_qup_read_from_fifo(struct spi_qup *controller,
}
}

-static void spi_qup_read(struct spi_qup *controller,
- struct spi_transfer *xfer)
+static void spi_qup_read(struct spi_qup *controller)
{
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,
+ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->rx_bytes,
controller->w_size);
words_per_block = controller->in_blk_sz >> 2;

@@ -285,7 +292,7 @@ static void spi_qup_read(struct spi_qup *controller,
}

/* read up to the maximum transfer size available */
- spi_qup_read_from_fifo(controller, xfer, num_words);
+ spi_qup_read_from_fifo(controller, num_words);

remainder -= num_words;

@@ -307,18 +314,18 @@ static void spi_qup_read(struct spi_qup *controller,

}

-static void spi_qup_write_to_fifo(struct spi_qup *controller,
- struct spi_transfer *xfer, u32 num_words)
+static void spi_qup_write_to_fifo(struct spi_qup *controller, u32 num_words)
{
- const u8 *tx_buf = xfer->tx_buf;
+ const u8 *tx_buf = controller->tx_buf;
int i, num_bytes;
u32 word, data;

for (; num_words; num_words--) {
word = 0;

- num_bytes = min_t(int, xfer->len - controller->tx_bytes,
- controller->w_size);
+ num_bytes = min_t(int, spi_qup_len(controller) -
+ controller->tx_bytes,
+ controller->w_size);
if (tx_buf)
for (i = 0; i < num_bytes; i++) {
data = tx_buf[controller->tx_bytes + i];
@@ -338,13 +345,12 @@ static void spi_qup_dma_done(void *data)
complete(&qup->done);
}

-static void spi_qup_write(struct spi_qup *controller,
- struct spi_transfer *xfer)
+static void spi_qup_write(struct spi_qup *controller)
{
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,
+ remainder = DIV_ROUND_UP(spi_qup_len(controller) - controller->tx_bytes,
controller->w_size);
words_per_block = controller->out_blk_sz >> 2;

@@ -364,7 +370,7 @@ static void spi_qup_write(struct spi_qup *controller,
num_words = 1;
}

- spi_qup_write_to_fifo(controller, xfer, num_words);
+ spi_qup_write_to_fifo(controller, num_words);

remainder -= num_words;

@@ -471,36 +477,62 @@ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
{
struct spi_master *master = spi->master;
struct spi_qup *qup = spi_master_get_devdata(master);
- int ret;
+ int ret, n_words, iterations, offset = 0;

- ret = spi_qup_io_config(spi, xfer);
- if (ret)
- return ret;
+ n_words = qup->n_words;
+ iterations = n_words / SPI_MAX_XFER; /* round down */
+ qup->rx_buf = xfer->rx_buf;
+ qup->tx_buf = xfer->tx_buf;

- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
- if (ret) {
- dev_warn(qup->dev, "cannot set RUN state\n");
- return ret;
- }
+ do {
+ if (iterations)
+ qup->n_words = SPI_MAX_XFER;
+ else
+ qup->n_words = n_words % SPI_MAX_XFER;

- ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
- if (ret) {
- dev_warn(qup->dev, "cannot set PAUSE state\n");
- return ret;
- }
+ if (qup->tx_buf && offset)
+ qup->tx_buf = xfer->tx_buf + offset * SPI_MAX_XFER;

- if (qup->mode == QUP_IO_M_MODE_FIFO)
- spi_qup_write(qup, xfer);
+ if (qup->rx_buf && offset)
+ qup->rx_buf = xfer->rx_buf + offset * SPI_MAX_XFER;

- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
- if (ret) {
- dev_warn(qup->dev, "%s(%d): cannot set RUN state\n",
- __func__, __LINE__);
- return ret;
- }
+ /*
+ * if the transaction is small enough, we need
+ * to fallback to FIFO mode
+ */
+ if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
+ qup->mode = QUP_IO_M_MODE_FIFO;

- if (!wait_for_completion_timeout(&qup->done, timeout))
- return -ETIMEDOUT;
+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set RUN state\n");
+ return ret;
+ }
+
+ ret = spi_qup_set_state(qup, QUP_STATE_PAUSE);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set PAUSE state\n");
+ return ret;
+ }
+
+ if (qup->mode == QUP_IO_M_MODE_FIFO)
+ spi_qup_write(qup);
+
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set RUN state\n");
+ return ret;
+ }
+
+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
+ offset++;
+ } while (iterations--);

return 0;
}
@@ -508,7 +540,6 @@ static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
{
struct spi_qup *controller = dev_id;
- struct spi_transfer *xfer = controller->xfer;
u32 opflags, qup_err, spi_err;
int error = 0;

@@ -545,10 +576,10 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
} else {
if (opflags & QUP_OP_IN_SERVICE_FLAG)
- spi_qup_read(controller, xfer);
+ spi_qup_read(controller);

if (opflags & QUP_OP_OUT_SERVICE_FLAG)
- spi_qup_write(controller, xfer);
+ spi_qup_write(controller);
}

if ((opflags & QUP_OP_MAX_INPUT_DONE_FLAG) || error)
@@ -755,7 +786,8 @@ static int spi_qup_transfer_one(struct spi_master *master,
return ret;

timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
- timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
+ timeout = DIV_ROUND_UP(min_t(unsigned long, SPI_MAX_XFER,
+ xfer->len) * 8, timeout);
timeout = 100 * msecs_to_jiffies(timeout);

reinit_completion(&controller->done);
@@ -969,7 +1001,7 @@ static int spi_qup_probe(struct platform_device *pdev)
master->dev.of_node = pdev->dev.of_node;
master->auto_runtime_pm = true;
master->dma_alignment = dma_get_cache_alignment();
- master->max_dma_len = SPI_MAX_DMA_XFER;
+ master->max_dma_len = SPI_MAX_XFER;

platform_set_drvdata(pdev, master);

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:54:03

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 11/14] spi: qup: refactor spi_qup_prep_sg

Take specific sgl and nent to be prepared. This is in
preparation for splitting DMA into multiple transacations, this
contains no code changes just refactoring.

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 707b1ec..1af3b41 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -382,27 +382,20 @@ static void spi_qup_write(struct spi_qup *controller)
} while (remainder);
}

-static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
- enum dma_transfer_direction dir,
+static int spi_qup_prep_sg(struct spi_master *master, struct scatterlist *sgl,
+ unsigned int nents, enum dma_transfer_direction dir,
dma_async_tx_callback callback)
{
struct spi_qup *qup = spi_master_get_devdata(master);
unsigned long flags = DMA_PREP_INTERRUPT | DMA_PREP_FENCE;
struct dma_async_tx_descriptor *desc;
- struct scatterlist *sgl;
struct dma_chan *chan;
dma_cookie_t cookie;
- unsigned int nents;

- if (dir == DMA_MEM_TO_DEV) {
+ if (dir == DMA_MEM_TO_DEV)
chan = master->dma_tx;
- nents = xfer->tx_sg.nents;
- sgl = xfer->tx_sg.sgl;
- } else {
+ else
chan = master->dma_rx;
- nents = xfer->rx_sg.nents;
- sgl = xfer->rx_sg.sgl;
- }

desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
if (IS_ERR_OR_NULL(desc))
@@ -451,7 +444,9 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
}

if (xfer->rx_buf) {
- ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
+ ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
+ xfer->rx_sg.nents, DMA_DEV_TO_MEM,
+ rx_done);
if (ret)
return ret;

@@ -459,7 +454,9 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
}

if (xfer->tx_buf) {
- ret = spi_qup_prep_sg(master, xfer, DMA_MEM_TO_DEV, tx_done);
+ ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
+ xfer->tx_sg.nents, DMA_MEM_TO_DEV,
+ tx_done);
if (ret)
return ret;

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:54:11

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 13/14] spi: qup: Ensure done detection

This patch fixes an issue where a SPI transaction has completed, but the
done condition is missed. This occurs because at the time of interrupt the
MAX_INPUT_DONE_FLAG is not asserted. However, in the process of reading
blocks of data from the FIFO, the last portion of data comes in.

The opflags read at the beginning of the irq handler no longer matches the
current opflag state. To get around this condition, the block read
function should update the opflags so that done detection is correct after
the return.

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 3c2c2c0..4c3c938 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -266,7 +266,7 @@ static void spi_qup_read_from_fifo(struct spi_qup *controller, u32 num_words)
}
}

-static void spi_qup_read(struct spi_qup *controller)
+static void spi_qup_read(struct spi_qup *controller, u32 *opflags)
{
u32 remainder, words_per_block, num_words;
bool is_block_mode = controller->mode == QUP_IO_M_MODE_BLOCK;
@@ -305,10 +305,12 @@ static void spi_qup_read(struct spi_qup *controller)

/*
* 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
+ * reads, it has to be cleared again at the very end. However, be sure
+ * to refresh opflags value because MAX_INPUT_DONE_FLAG may now be
+ * present and this is used to determine if transaction is complete
*/
- if (is_block_mode && spi_qup_is_flag_set(controller,
- QUP_OP_MAX_INPUT_DONE_FLAG))
+ *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);

@@ -613,7 +615,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
writel_relaxed(opflags, controller->base + QUP_OPERATIONAL);
} else {
if (opflags & QUP_OP_IN_SERVICE_FLAG)
- spi_qup_read(controller);
+ spi_qup_read(controller, &opflags);

if (opflags & QUP_OP_OUT_SERVICE_FLAG)
spi_qup_write(controller);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:54:28

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 14/14] spi: qup: Fix QUP version identify method

Use of_device_get_match_data to identify QUP version instead
of of_device_is_compatible.

Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 4c3c938..1364516 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -19,6 +19,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
@@ -1058,9 +1059,7 @@ static int spi_qup_probe(struct platform_device *pdev)
else if (!ret)
master->can_dma = spi_qup_can_dma;

- /* set v1 flag if device is version 1 */
- if (of_device_is_compatible(dev->of_node, "qcom,spi-qup-v1.1.1"))
- controller->qup_v1 = 1;
+ controller->qup_v1 = (int)of_device_get_match_data(dev);

if (!controller->qup_v1)
master->set_cs = spi_qup_set_cs;
@@ -1256,7 +1255,7 @@ static int spi_qup_remove(struct platform_device *pdev)
}

static const struct of_device_id spi_qup_dt_match[] = {
- { .compatible = "qcom,spi-qup-v1.1.1", },
+ { .compatible = "qcom,spi-qup-v1.1.1", .data = (void *)1, },
{ .compatible = "qcom,spi-qup-v2.1.1", },
{ .compatible = "qcom,spi-qup-v2.2.1", },
{ }
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:54:51

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 12/14] spi: qup: allow multiple DMA transactions per spi xfer

Much like the block mode changes, we are breaking up DMA transactions
into 64K chunks so we can reset the QUP engine.

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 92 ++++++++++++++++++++++++++++++++++++---------------
1 file changed, 66 insertions(+), 26 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 1af3b41..3c2c2c0 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -418,12 +418,35 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

+static u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max,
+ u32 *nents)
+{
+ struct scatterlist *sg;
+ u32 total = 0;
+
+ *nents = 0;
+
+ for (sg = sgl; sg; sg = sg_next(sg)) {
+ unsigned int len = sg_dma_len(sg);
+
+ /* check for overflow as well as limit */
+ if (((total + len) < total) || ((total + len) > max))
+ break;
+
+ total += len;
+ (*nents)++;
+ }
+
+ return total;
+}
+
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
struct spi_master *master = spi->master;
struct spi_qup *qup = spi_master_get_devdata(master);
+ struct scatterlist *tx_sgl, *rx_sgl;
int ret;

if (xfer->rx_buf)
@@ -431,40 +454,57 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
else if (xfer->tx_buf)
tx_done = spi_qup_dma_done;

- ret = spi_qup_io_config(spi, xfer);
- if (ret)
- return ret;
+ rx_sgl = xfer->rx_sg.sgl;
+ tx_sgl = xfer->tx_sg.sgl;

- /* before issuing the descriptors, set the QUP to run */
- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
- if (ret) {
- dev_warn(qup->dev, "%s(%d): cannot set RUN state\n",
- __func__, __LINE__);
- return ret;
- }
+ do {
+ u32 rx_nents, tx_nents;
+
+ if (rx_sgl)
+ qup->n_words = spi_qup_sgl_get_nents_len(rx_sgl,
+ SPI_MAX_XFER, &rx_nents) / qup->w_size;
+ if (tx_sgl)
+ qup->n_words = spi_qup_sgl_get_nents_len(tx_sgl,
+ SPI_MAX_XFER, &tx_nents) / qup->w_size;
+ if (!qup->n_words)
+ return -EIO;

- if (xfer->rx_buf) {
- ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
- xfer->rx_sg.nents, DMA_DEV_TO_MEM,
- rx_done);
+ ret = spi_qup_io_config(spi, xfer);
if (ret)
return ret;

- dma_async_issue_pending(master->dma_rx);
- }
-
- if (xfer->tx_buf) {
- ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
- xfer->tx_sg.nents, DMA_MEM_TO_DEV,
- tx_done);
- if (ret)
+ /* before issuing the descriptors, set the QUP to run */
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set RUN state\n");
return ret;
+ }
+ if (rx_sgl) {
+ ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
+ DMA_DEV_TO_MEM, rx_done);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(master->dma_rx);
+ }

- dma_async_issue_pending(master->dma_tx);
- }
+ if (tx_sgl) {
+ ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
+ DMA_MEM_TO_DEV, tx_done);
+ if (ret)
+ return ret;
+
+ dma_async_issue_pending(master->dma_tx);
+ }
+
+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
+ for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl))
+ ;
+ for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl))
+ ;

- if (!wait_for_completion_timeout(&qup->done, timeout))
- return -ETIMEDOUT;
+ } while (rx_sgl || tx_sgl);

return 0;
}
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:55:19

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 09/14] spi: qup: call io_config in mode specific function

DMA transactions should only only need to call io_config only once, but
block mode might call it several times to setup several transactions so
it can handle reads/writes larger than the max size per transaction, so
we move the call to the do_ functions.

This is just refactoring, there should be no functional change

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index ff5aa08..1aa6078 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -156,6 +156,8 @@ struct spi_qup {
struct dma_slave_config tx_conf;
};

+static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer);
+
static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
{
u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
@@ -417,11 +419,12 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
+static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
- struct spi_qup *qup = spi_master_get_devdata(master);
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ struct spi_master *master = spi->master;
+ struct spi_qup *qup = spi_master_get_devdata(master);
int ret;

if (xfer->rx_buf)
@@ -429,6 +432,10 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
else if (xfer->tx_buf)
tx_done = spi_qup_dma_done;

+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
/* before issuing the descriptors, set the QUP to run */
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
if (ret) {
@@ -459,12 +466,17 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
return 0;
}

-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
+static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
+ struct spi_master *master = spi->master;
struct spi_qup *qup = spi_master_get_devdata(master);
int ret;

+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
if (ret) {
dev_warn(qup->dev, "cannot set RUN state\n");
@@ -742,10 +754,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
if (ret)
return ret;

- ret = spi_qup_io_config(spi, xfer);
- if (ret)
- return ret;
-
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
timeout = 100 * msecs_to_jiffies(timeout);
@@ -760,9 +768,9 @@ static int spi_qup_transfer_one(struct spi_master *master,
spin_unlock_irqrestore(&controller->lock, flags);

if (spi_qup_is_dma_xfer(controller->mode))
- ret = spi_qup_do_dma(master, xfer, timeout);
+ ret = spi_qup_do_dma(spi, xfer, timeout);
else
- ret = spi_qup_do_pio(master, xfer, timeout);
+ ret = spi_qup_do_pio(spi, xfer, timeout);

if (ret)
goto exit;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:53:38

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 05/14] spi: qup: Fix error handling in spi_qup_prep_sg

Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index f1aa5c1..ef95294 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -311,8 +311,8 @@ static int spi_qup_prep_sg(struct spi_master *master, struct spi_transfer *xfer,
}

desc = dmaengine_prep_slave_sg(chan, sgl, nents, dir, flags);
- if (!desc)
- return -EINVAL;
+ if (IS_ERR_OR_NULL(desc))
+ return desc ? PTR_ERR(desc) : -EINVAL;

desc->callback = callback;
desc->callback_param = qup;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:56:47

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 04/14] spi: qup: Place the QUP in run mode before DMA

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index fdd34c3..f1aa5c1 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -343,6 +343,14 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
else if (xfer->tx_buf)
tx_done = spi_qup_dma_done;

+ /* before issuing the descriptors, set the QUP to run */
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "%s(%d): cannot set RUN state\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
if (xfer->rx_buf) {
ret = spi_qup_prep_sg(master, xfer, DMA_DEV_TO_MEM, rx_done);
if (ret)
@@ -385,6 +393,13 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,

spi_qup_fifo_write(qup, xfer);

+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "%s(%d): cannot set RUN state\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
if (!wait_for_completion_timeout(&qup->done, timeout))
return -ETIMEDOUT;

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-07-28 06:57:09

by Varadarajan Narayanan

[permalink] [raw]
Subject: [PATCH v6 02/14] spi: qup: Setup DMA mode correctly

To operate in DMA mode, the buffer should be aligned and
the size of the transfer should be a multiple of block size
(for v1). And the no. of words being transferred should
be programmed in the count registers appropriately.

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
---
drivers/spi/spi-qup.c | 118 +++++++++++++++++++++++---------------------------
1 file changed, 55 insertions(+), 63 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index c0d4def..abe799b 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -149,11 +149,18 @@ struct spi_qup {
int rx_bytes;
int qup_v1;

- int use_dma;
+ int mode;
struct dma_slave_config rx_conf;
struct dma_slave_config tx_conf;
};

+static inline bool spi_qup_is_dma_xfer(int mode)
+{
+ if (mode == QUP_IO_M_MODE_DMOV || mode == QUP_IO_M_MODE_BAM)
+ return true;
+
+ return false;
+}

static inline bool spi_qup_is_valid_state(struct spi_qup *controller)
{
@@ -424,7 +431,7 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
error = -EIO;
}

- if (!controller->use_dma) {
+ if (!spi_qup_is_dma_xfer(controller->mode)) {
if (opflags & QUP_OP_IN_SERVICE_FLAG)
spi_qup_fifo_read(controller, xfer);

@@ -443,34 +450,11 @@ static irqreturn_t spi_qup_qup_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}

-static u32
-spi_qup_get_mode(struct spi_master *master, struct spi_transfer *xfer)
-{
- struct spi_qup *qup = spi_master_get_devdata(master);
- u32 mode;
-
- qup->w_size = 4;
-
- if (xfer->bits_per_word <= 8)
- qup->w_size = 1;
- else if (xfer->bits_per_word <= 16)
- qup->w_size = 2;
-
- qup->n_words = xfer->len / qup->w_size;
-
- if (qup->n_words <= (qup->in_fifo_sz / sizeof(u32)))
- mode = QUP_IO_M_MODE_FIFO;
- else
- mode = QUP_IO_M_MODE_BLOCK;
-
- return mode;
-}
-
/* set clock freq ... bits per word */
static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
{
struct spi_qup *controller = spi_master_get_devdata(spi->master);
- u32 config, iomode, mode, control;
+ u32 config, iomode, control;
int ret, n_words;

if (spi->mode & SPI_LOOP && xfer->len > controller->in_fifo_sz) {
@@ -491,25 +475,30 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
return -EIO;
}

- mode = spi_qup_get_mode(spi->master, xfer);
+ controller->w_size = DIV_ROUND_UP(xfer->bits_per_word, 8);
+ controller->n_words = xfer->len / controller->w_size;
n_words = controller->n_words;

- if (mode == QUP_IO_M_MODE_FIFO) {
+ if (n_words <= (controller->in_fifo_sz / sizeof(u32))) {
+
+ controller->mode = QUP_IO_M_MODE_FIFO;
+
writel_relaxed(n_words, controller->base + QUP_MX_READ_CNT);
writel_relaxed(n_words, controller->base + QUP_MX_WRITE_CNT);
/* 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 (!controller->use_dma) {
+ } 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);
- } else {
- mode = QUP_IO_M_MODE_BAM;
- writel_relaxed(0, controller->base + QUP_MX_READ_CNT);
- writel_relaxed(0, controller->base + QUP_MX_WRITE_CNT);

if (!controller->qup_v1) {
void __iomem *input_cnt;
@@ -528,19 +517,28 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)

writel_relaxed(0, controller->base + QUP_MX_OUTPUT_CNT);
}
+ } else {
+
+ controller->mode = QUP_IO_M_MODE_BLOCK;
+
+ 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);
}

iomode = readl_relaxed(controller->base + QUP_IO_M_MODES);
/* Set input and output transfer mode */
iomode &= ~(QUP_IO_M_INPUT_MODE_MASK | QUP_IO_M_OUTPUT_MODE_MASK);

- if (!controller->use_dma)
+ if (!spi_qup_is_dma_xfer(controller->mode))
iomode &= ~(QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN);
else
iomode |= QUP_IO_M_PACK_EN | QUP_IO_M_UNPACK_EN;

- iomode |= (mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
- iomode |= (mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);
+ iomode |= (controller->mode << QUP_IO_M_OUTPUT_MODE_MASK_SHIFT);
+ iomode |= (controller->mode << QUP_IO_M_INPUT_MODE_MASK_SHIFT);

writel_relaxed(iomode, controller->base + QUP_IO_M_MODES);

@@ -581,7 +579,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
config |= xfer->bits_per_word - 1;
config |= QUP_CONFIG_SPI_MODE;

- if (controller->use_dma) {
+ if (spi_qup_is_dma_xfer(controller->mode)) {
if (!xfer->tx_buf)
config |= QUP_CONFIG_NO_OUTPUT;
if (!xfer->rx_buf)
@@ -599,7 +597,7 @@ static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer)
* status change in BAM mode
*/

- if (mode == QUP_IO_M_MODE_BAM)
+ if (spi_qup_is_dma_xfer(controller->mode))
mask = QUP_OP_IN_SERVICE_FLAG | QUP_OP_OUT_SERVICE_FLAG;

writel_relaxed(mask, controller->base + QUP_OPERATIONAL_MASK);
@@ -633,7 +631,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
controller->tx_bytes = 0;
spin_unlock_irqrestore(&controller->lock, flags);

- if (controller->use_dma)
+ if (spi_qup_is_dma_xfer(controller->mode))
ret = spi_qup_do_dma(master, xfer);
else
ret = spi_qup_do_pio(master, xfer);
@@ -641,14 +639,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
if (ret)
goto exit;

- if (spi_qup_set_state(controller, QUP_STATE_RUN)) {
- dev_warn(controller->dev, "cannot set EXECUTE state\n");
- goto exit;
- }
-
- if (!wait_for_completion_timeout(&controller->done, timeout))
- ret = -ETIMEDOUT;
-
exit:
spi_qup_set_state(controller, QUP_STATE_RESET);
spin_lock_irqsave(&controller->lock, flags);
@@ -657,7 +647,7 @@ static int spi_qup_transfer_one(struct spi_master *master,
ret = controller->error;
spin_unlock_irqrestore(&controller->lock, flags);

- if (ret && controller->use_dma)
+ if (ret && spi_qup_is_dma_xfer(controller->mode))
spi_qup_dma_terminate(master, xfer);

return ret;
@@ -668,26 +658,28 @@ static bool spi_qup_can_dma(struct spi_master *master, struct spi_device *spi,
{
struct spi_qup *qup = spi_master_get_devdata(master);
size_t dma_align = dma_get_cache_alignment();
- u32 mode;
-
- qup->use_dma = 0;
+ int n_words;

- if (xfer->rx_buf && (xfer->len % qup->in_blk_sz ||
- IS_ERR_OR_NULL(master->dma_rx) ||
- !IS_ALIGNED((size_t)xfer->rx_buf, dma_align)))
- return false;
+ if (xfer->rx_buf) {
+ if (!IS_ALIGNED((size_t)xfer->rx_buf, dma_align) ||
+ IS_ERR_OR_NULL(master->dma_rx))
+ return false;
+ if (qup->qup_v1 && (xfer->len % qup->in_blk_sz))
+ return false;
+ }

- if (xfer->tx_buf && (xfer->len % qup->out_blk_sz ||
- IS_ERR_OR_NULL(master->dma_tx) ||
- !IS_ALIGNED((size_t)xfer->tx_buf, dma_align)))
- return false;
+ if (xfer->tx_buf) {
+ if (!IS_ALIGNED((size_t)xfer->tx_buf, dma_align) ||
+ IS_ERR_OR_NULL(master->dma_tx))
+ return false;
+ if (qup->qup_v1 && (xfer->len % qup->out_blk_sz))
+ return false;
+ }

- mode = spi_qup_get_mode(master, xfer);
- if (mode == QUP_IO_M_MODE_FIFO)
+ n_words = xfer->len / DIV_ROUND_UP(xfer->bits_per_word, 8);
+ if (n_words <= (qup->in_fifo_sz / sizeof(u32)))
return false;

- qup->use_dma = 1;
-
return true;
}

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 11:18:17

by Mark Brown

[permalink] [raw]
Subject: Applied "spi: qup: allow multiple DMA transactions per spi xfer" to the spi tree

The patch

spi: qup: allow multiple DMA transactions per spi xfer

has been applied to the spi tree at

git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 5884e17ef3cb3dac2e83e466246cf033bfba0e2f Mon Sep 17 00:00:00 2001
From: Varadarajan Narayanan <[email protected]>
Date: Fri, 28 Jul 2017 12:22:59 +0530
Subject: [PATCH] spi: qup: allow multiple DMA transactions per spi xfer

Much like the block mode changes, we are breaking up DMA transactions
into 64K chunks so we can reset the QUP engine.

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-qup.c | 92 ++++++++++++++++++++++++++++++++++++---------------
1 file changed, 66 insertions(+), 26 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index 1af3b41ac12d..3c2c2c0ed9ab 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -418,12 +418,35 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

+static u32 spi_qup_sgl_get_nents_len(struct scatterlist *sgl, u32 max,
+ u32 *nents)
+{
+ struct scatterlist *sg;
+ u32 total = 0;
+
+ *nents = 0;
+
+ for (sg = sgl; sg; sg = sg_next(sg)) {
+ unsigned int len = sg_dma_len(sg);
+
+ /* check for overflow as well as limit */
+ if (((total + len) < total) || ((total + len) > max))
+ break;
+
+ total += len;
+ (*nents)++;
+ }
+
+ return total;
+}
+
static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
struct spi_master *master = spi->master;
struct spi_qup *qup = spi_master_get_devdata(master);
+ struct scatterlist *tx_sgl, *rx_sgl;
int ret;

if (xfer->rx_buf)
@@ -431,40 +454,57 @@ static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
else if (xfer->tx_buf)
tx_done = spi_qup_dma_done;

- ret = spi_qup_io_config(spi, xfer);
- if (ret)
- return ret;
+ rx_sgl = xfer->rx_sg.sgl;
+ tx_sgl = xfer->tx_sg.sgl;

- /* before issuing the descriptors, set the QUP to run */
- ret = spi_qup_set_state(qup, QUP_STATE_RUN);
- if (ret) {
- dev_warn(qup->dev, "%s(%d): cannot set RUN state\n",
- __func__, __LINE__);
- return ret;
- }
+ do {
+ u32 rx_nents, tx_nents;
+
+ if (rx_sgl)
+ qup->n_words = spi_qup_sgl_get_nents_len(rx_sgl,
+ SPI_MAX_XFER, &rx_nents) / qup->w_size;
+ if (tx_sgl)
+ qup->n_words = spi_qup_sgl_get_nents_len(tx_sgl,
+ SPI_MAX_XFER, &tx_nents) / qup->w_size;
+ if (!qup->n_words)
+ return -EIO;

- if (xfer->rx_buf) {
- ret = spi_qup_prep_sg(master, xfer->rx_sg.sgl,
- xfer->rx_sg.nents, DMA_DEV_TO_MEM,
- rx_done);
+ ret = spi_qup_io_config(spi, xfer);
if (ret)
return ret;

- dma_async_issue_pending(master->dma_rx);
- }
-
- if (xfer->tx_buf) {
- ret = spi_qup_prep_sg(master, xfer->tx_sg.sgl,
- xfer->tx_sg.nents, DMA_MEM_TO_DEV,
- tx_done);
- if (ret)
+ /* before issuing the descriptors, set the QUP to run */
+ ret = spi_qup_set_state(qup, QUP_STATE_RUN);
+ if (ret) {
+ dev_warn(qup->dev, "cannot set RUN state\n");
return ret;
+ }
+ if (rx_sgl) {
+ ret = spi_qup_prep_sg(master, rx_sgl, rx_nents,
+ DMA_DEV_TO_MEM, rx_done);
+ if (ret)
+ return ret;
+ dma_async_issue_pending(master->dma_rx);
+ }

- dma_async_issue_pending(master->dma_tx);
- }
+ if (tx_sgl) {
+ ret = spi_qup_prep_sg(master, tx_sgl, tx_nents,
+ DMA_MEM_TO_DEV, tx_done);
+ if (ret)
+ return ret;
+
+ dma_async_issue_pending(master->dma_tx);
+ }
+
+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
+ for (; rx_sgl && rx_nents--; rx_sgl = sg_next(rx_sgl))
+ ;
+ for (; tx_sgl && tx_nents--; tx_sgl = sg_next(tx_sgl))
+ ;

- if (!wait_for_completion_timeout(&qup->done, timeout))
- return -ETIMEDOUT;
+ } while (rx_sgl || tx_sgl);

return 0;
}
--
2.13.2

2017-08-08 11:20:01

by Mark Brown

[permalink] [raw]
Subject: Applied "spi: qup: Add completion timeout" to the spi tree

The patch

spi: qup: Add completion timeout

has been applied to the spi tree at

git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 5f13fd60b1e709b9387877b0b65d605df9fff1d6 Mon Sep 17 00:00:00 2001
From: Varadarajan Narayanan <[email protected]>
Date: Fri, 28 Jul 2017 12:22:50 +0530
Subject: [PATCH] spi: qup: Add completion timeout

Add i/o completion timeout for DMA and PIO modes.

Signed-off-by: Andy Gross <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-qup.c | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index abe799bbc67f..fdd34c3ce426 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -331,8 +331,10 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
+static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
+ unsigned long timeout)
{
+ struct spi_qup *qup = spi_master_get_devdata(master);
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
int ret;

@@ -357,10 +359,14 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer)
dma_async_issue_pending(master->dma_tx);
}

+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
return 0;
}

-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)
+static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
+ unsigned long timeout)
{
struct spi_qup *qup = spi_master_get_devdata(master);
int ret;
@@ -379,6 +385,9 @@ static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer)

spi_qup_fifo_write(qup, xfer);

+ if (!wait_for_completion_timeout(&qup->done, timeout))
+ return -ETIMEDOUT;
+
return 0;
}

@@ -632,9 +641,9 @@ static int spi_qup_transfer_one(struct spi_master *master,
spin_unlock_irqrestore(&controller->lock, flags);

if (spi_qup_is_dma_xfer(controller->mode))
- ret = spi_qup_do_dma(master, xfer);
+ ret = spi_qup_do_dma(master, xfer, timeout);
else
- ret = spi_qup_do_pio(master, xfer);
+ ret = spi_qup_do_pio(master, xfer, timeout);

if (ret)
goto exit;
--
2.13.2

2017-08-08 11:21:42

by Mark Brown

[permalink] [raw]
Subject: Applied "spi: qup: call io_config in mode specific function" to the spi tree

The patch

spi: qup: call io_config in mode specific function

has been applied to the spi tree at

git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark

>From 3b5ea2c981dd470882bdc83e548dd15a942dbf4d Mon Sep 17 00:00:00 2001
From: Varadarajan Narayanan <[email protected]>
Date: Fri, 28 Jul 2017 12:22:56 +0530
Subject: [PATCH] spi: qup: call io_config in mode specific function

DMA transactions should only only need to call io_config only once, but
block mode might call it several times to setup several transactions so
it can handle reads/writes larger than the max size per transaction, so
we move the call to the do_ functions.

This is just refactoring, there should be no functional change

Signed-off-by: Matthew McClintock <[email protected]>
Signed-off-by: Varadarajan Narayanan <[email protected]>
Signed-off-by: Mark Brown <[email protected]>
---
drivers/spi/spi-qup.c | 26 +++++++++++++++++---------
1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-qup.c b/drivers/spi/spi-qup.c
index ff5aa08b5725..1aa60785bf98 100644
--- a/drivers/spi/spi-qup.c
+++ b/drivers/spi/spi-qup.c
@@ -156,6 +156,8 @@ struct spi_qup {
struct dma_slave_config tx_conf;
};

+static int spi_qup_io_config(struct spi_device *spi, struct spi_transfer *xfer);
+
static inline bool spi_qup_is_flag_set(struct spi_qup *controller, u32 flag)
{
u32 opflag = readl_relaxed(controller->base + QUP_OPERATIONAL);
@@ -417,11 +419,12 @@ static void spi_qup_dma_terminate(struct spi_master *master,
dmaengine_terminate_all(master->dma_rx);
}

-static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
+static int spi_qup_do_dma(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
- struct spi_qup *qup = spi_master_get_devdata(master);
dma_async_tx_callback rx_done = NULL, tx_done = NULL;
+ struct spi_master *master = spi->master;
+ struct spi_qup *qup = spi_master_get_devdata(master);
int ret;

if (xfer->rx_buf)
@@ -429,6 +432,10 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
else if (xfer->tx_buf)
tx_done = spi_qup_dma_done;

+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
/* before issuing the descriptors, set the QUP to run */
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
if (ret) {
@@ -459,12 +466,17 @@ static int spi_qup_do_dma(struct spi_master *master, struct spi_transfer *xfer,
return 0;
}

-static int spi_qup_do_pio(struct spi_master *master, struct spi_transfer *xfer,
+static int spi_qup_do_pio(struct spi_device *spi, struct spi_transfer *xfer,
unsigned long timeout)
{
+ struct spi_master *master = spi->master;
struct spi_qup *qup = spi_master_get_devdata(master);
int ret;

+ ret = spi_qup_io_config(spi, xfer);
+ if (ret)
+ return ret;
+
ret = spi_qup_set_state(qup, QUP_STATE_RUN);
if (ret) {
dev_warn(qup->dev, "cannot set RUN state\n");
@@ -742,10 +754,6 @@ static int spi_qup_transfer_one(struct spi_master *master,
if (ret)
return ret;

- ret = spi_qup_io_config(spi, xfer);
- if (ret)
- return ret;
-
timeout = DIV_ROUND_UP(xfer->speed_hz, MSEC_PER_SEC);
timeout = DIV_ROUND_UP(xfer->len * 8, timeout);
timeout = 100 * msecs_to_jiffies(timeout);
@@ -760,9 +768,9 @@ static int spi_qup_transfer_one(struct spi_master *master,
spin_unlock_irqrestore(&controller->lock, flags);

if (spi_qup_is_dma_xfer(controller->mode))
- ret = spi_qup_do_dma(master, xfer, timeout);
+ ret = spi_qup_do_dma(spi, xfer, timeout);
else
- ret = spi_qup_do_pio(master, xfer, timeout);
+ ret = spi_qup_do_pio(spi, xfer, timeout);

if (ret)
goto exit;
--
2.13.2