Marc Jones (marc.jones@se-eng.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/8676
-gerrit
commit 0c98463e1684e9aae09c2aac5855a5d1138cab4a Author: David Hendricks dhendrix@chromium.org Date: Fri Jul 25 12:59:48 2014 -0700
ipq806x: Break apart large transfers in spi_xfer()
The current spi_xfer() function sets the count in hardware and then loops while waiting for the requested number of bytes to be sent or received. However, the number of bytes to be transferred may exceed the maximum count that can be programmed into the controller.
This patch re-factors spi_xfer() to split the low-level FIFO handling portions for transmit/receive into their own functions to be called by loops in spi_xfer() which will break large transfers into smaller ones.
BUG=chrome-os-partner:30904 BRANCH=storm TEST=built and booted with a >64KB payload on Storm
Original-Change-Id: I70743487996cf08cfc602449f2181a7fcd99bfa4 Original-Signed-off-by: David Hendricks dhendrix@chromium.org Original-Signed-off-by: Vadim Bendebury vbendeb@chromium.org Original-Reviewed-on: https://chromium-review.googlesource.com/209838 Original-Reviewed-by: Trevor Bourget tbourget@codeaurora.org Original-Tested-by: Trevor Bourget tbourget@codeaurora.org (cherry picked from commit 5ec28de11f12c2438356f45ce978a17fbb603bf7) Signed-off-by: Marc Jones marc.jones@se-eng.com
Change-Id: I0033e0dd96006cfd30a7a4f5e5a052f677e05108 --- src/soc/qualcomm/ipq806x/spi.c | 113 +++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 38 deletions(-)
diff --git a/src/soc/qualcomm/ipq806x/spi.c b/src/soc/qualcomm/ipq806x/spi.c index f1a180b..2c16cb6 100644 --- a/src/soc/qualcomm/ipq806x/spi.c +++ b/src/soc/qualcomm/ipq806x/spi.c @@ -34,6 +34,11 @@
#define GSBI_IDX_TO_GSBI(idx) (idx + 5)
+ +/* MX_INPUT_COUNT and MX_OUTPUT_COUNT are 16-bits. Zero has a special meaning + * (count function disabled) and does not hold significance in the count. */ +#define MAX_PACKET_COUNT ((64 * KiB) - 1) + /* * TLMM Configuration for SPI NOR * gsbi_pin_conf[bus_num][GPIO_NUM, FUNC_SEL, I/O, @@ -644,12 +649,66 @@ void spi_release_bus(struct spi_slave *slave) ds->initialized = 0; }
+static int spi_xfer_tx_packet(struct ipq_spi_slave *ds, + const uint8_t *dout, unsigned out_bytes) +{ + int ret; + + writel_i(out_bytes, ds->regs->qup_mx_output_count); + + ret = config_spi_state(ds, SPI_RUN_STATE); + if (ret) + return ret; + + while (out_bytes) { + if (readl_i(ds->regs->qup_operational) & QUP_OUTPUT_FIFO_FULL) + continue; + + writel_i(*dout++, ds->regs->qup_output_fifo); + out_bytes--; + + /* Wait for output FIFO to drain. */ + if (!out_bytes) + while (readl_i(ds->regs->qup_operational) & + QUP_OUTPUT_FIFO_NOT_EMPTY) + ; + } + + return config_spi_state(ds, SPI_RESET_STATE); +} + +static int spi_xfer_rx_packet(struct ipq_spi_slave *ds, + uint8_t *din, unsigned in_bytes) +{ + int ret; + + writel_i(in_bytes, ds->regs->qup_mx_input_count); + writel_i(in_bytes, ds->regs->qup_mx_output_count); + + ret = config_spi_state(ds, SPI_RUN_STATE); + if (ret) + return ret; + + /* Seed clocking */ + writel_i(0xff, ds->regs->qup_output_fifo); + while (in_bytes) { + if (!(readl_i(ds->regs->qup_operational) & + QUP_INPUT_FIFO_NOT_EMPTY)) + continue; + /* Keep it clocking */ + writel_i(0xff, ds->regs->qup_output_fifo); + + *din++ = readl_i(ds->regs->qup_input_fifo) & 0xff; + in_bytes--; + } + + return config_spi_state(ds, SPI_RESET_STATE); +} + int spi_xfer(struct spi_slave *slave, const void *dout, unsigned out_bytes, void *din, unsigned in_bytes) { int ret; - uint8_t* dbuf; - const uint8_t* dobuf; struct ipq_spi_slave *ds = to_ipq_spi(slave);
/* Assert the chip select */ @@ -669,29 +728,17 @@ int spi_xfer(struct spi_slave *slave, const void *dout, clrsetbits_le32_i(ds->regs->qup_config, SPI_QUP_CONF_OUTPUT_MSK, SPI_QUP_CONF_OUTPUT_ENA);
- writel_i(out_bytes, ds->regs->qup_mx_output_count); - - ret = config_spi_state(ds, SPI_RUN_STATE); - if (ret) - goto out; - - dobuf = dout; /* Alias to make it possible to use pointer autoinc. */ - while (out_bytes) { - if (readl_i(ds->regs->qup_operational) & QUP_OUTPUT_FIFO_FULL) - continue; + unsigned todo = MIN(out_bytes, MAX_PACKET_COUNT);
- writel_i(*dobuf++, ds->regs->qup_output_fifo); - out_bytes--; + ret = spi_xfer_tx_packet(ds, dout, todo); + if (ret) + break;
- /* Wait for output FIFO to drain. */ - if (!out_bytes) - while (readl_i(ds->regs->qup_operational) & - QUP_OUTPUT_FIFO_NOT_EMPTY) - ; + out_bytes -= todo; + dout += todo; }
- ret = config_spi_state(ds, SPI_RESET_STATE); if (ret) goto out;
@@ -703,28 +750,18 @@ spi_receive: clrsetbits_le32_i(ds->regs->qup_config, SPI_QUP_CONF_INPUT_MSK, SPI_QUP_CONF_INPUT_ENA);
- writel_i(in_bytes, ds->regs->qup_mx_input_count); - writel_i(in_bytes, ds->regs->qup_mx_output_count); - - ret = config_spi_state(ds, SPI_RUN_STATE); - if (ret) - goto out; - - /* Seed clocking */ - writel_i(0xff, ds->regs->qup_output_fifo); - dbuf = din; /* Alias for pointer autoincrement again. */ while (in_bytes) { - if (!(readl_i(ds->regs->qup_operational) & - QUP_INPUT_FIFO_NOT_EMPTY)) - continue; - /* Keep it clocking */ - writel_i(0xff, ds->regs->qup_output_fifo); + unsigned todo = MIN(in_bytes, MAX_PACKET_COUNT);
- *dbuf++ = readl_i(ds->regs->qup_input_fifo) & 0xff; - in_bytes--; + ret = spi_xfer_rx_packet(ds, din, todo); + if (ret) + break; + + in_bytes -= todo; + din += todo; }
- out: +out: /* Deassert CS */ CS_change(ds->slave.bus, ds->slave.cs, CS_DEASSERT);