diff options
author | David Hendricks <dhendrix@chromium.org> | 2014-07-25 12:59:48 -0700 |
---|---|---|
committer | Marc Jones <marc.jones@se-eng.com> | 2015-03-17 16:54:26 +0100 |
commit | 68ec2fce2bcf0f9d8f02d80feb4d6bc4385e9047 (patch) | |
tree | 111b913c751870ffdcf4683209145c3bd30f13ac /src/soc | |
parent | c4f08f7f9c56f5f988ecdd86323390c0f5b4232b (diff) |
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
Reviewed-on: http://review.coreboot.org/8676
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Diffstat (limited to 'src/soc')
-rw-r--r-- | src/soc/qualcomm/ipq806x/spi.c | 113 |
1 files changed, 75 insertions, 38 deletions
diff --git a/src/soc/qualcomm/ipq806x/spi.c b/src/soc/qualcomm/ipq806x/spi.c index f1a180ba88..2c16cb6388 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); |