From 68ec2fce2bcf0f9d8f02d80feb4d6bc4385e9047 Mon Sep 17 00:00:00 2001 From: David Hendricks Date: Fri, 25 Jul 2014 12:59:48 -0700 Subject: 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 Original-Signed-off-by: Vadim Bendebury Original-Reviewed-on: https://chromium-review.googlesource.com/209838 Original-Reviewed-by: Trevor Bourget Original-Tested-by: Trevor Bourget (cherry picked from commit 5ec28de11f12c2438356f45ce978a17fbb603bf7) Signed-off-by: Marc Jones Change-Id: I0033e0dd96006cfd30a7a4f5e5a052f677e05108 Reviewed-on: http://review.coreboot.org/8676 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer --- src/soc/qualcomm/ipq806x/spi.c | 113 +++++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 38 deletions(-) (limited to 'src') 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); -- cgit v1.2.3