[coreboot-gerrit] New patch to review for coreboot: 0c98463 ipq806x: Break apart large transfers in spi_xfer()

Marc Jones (marc.jones@se-eng.com) gerrit at coreboot.org
Fri Mar 13 23:45:40 CET 2015


Marc Jones (marc.jones at 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 at 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 at chromium.org>
    Original-Signed-off-by: Vadim Bendebury <vbendeb at chromium.org>
    Original-Reviewed-on: https://chromium-review.googlesource.com/209838
    Original-Reviewed-by: Trevor Bourget <tbourget at codeaurora.org>
    Original-Tested-by: Trevor Bourget <tbourget at codeaurora.org>
    (cherry picked from commit 5ec28de11f12c2438356f45ce978a17fbb603bf7)
    Signed-off-by: Marc Jones <marc.jones at 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);
 



More information about the coreboot-gerrit mailing list