[coreboot-gerrit] Patch set updated for coreboot: 21e7396 rk3288: Re-write spi_xfer() to support full duplex

Patrick Georgi (pgeorgi@google.com) gerrit at coreboot.org
Tue Apr 7 13:55:23 CEST 2015


Patrick Georgi (pgeorgi at google.com) just uploaded a new patch set to gerrit, which you can find at http://review.coreboot.org/9338

-gerrit

commit 21e7396a8bd34e93ad7013a7aa677e87a9cff766
Author: David Hendricks <dhendrix at chromium.org>
Date:   Fri Sep 12 18:58:47 2014 -0700

    rk3288: Re-write spi_xfer() to support full duplex
    
    This change re-writes the spi_xfer() function to support full-duplex
    transfers.
    
    Even though the code looks much different, the same basic algorithm
    for setting up the transfer is used. The main difference is that
    reads from rxdr and writes to txdr occur simultaneously and accounting
    is more complicated, so I separated the higher-level accounting
    portion from the low-level FIFO handling portion to simplify things.
    
    BUG=chrome-os-partner:31850
    BRANCH=none
    TEST=Loaded content from SPI ROM fine, needs testing w/ EC
    
    Change-Id: Ic109a02daf52ba694b63a73fec1a72b3c5c0fd71
    Signed-off-by: Patrick Georgi <pgeorgi at chromium.org>
    Original-Commit-Id: 6a14f5ff8ed04d62e8de6ad2f468b763ffb8213c
    Original-Signed-off-by: David Hendricks <dhendrix at chromium.org>
    Original-Change-Id: I33d2f5179360baf94627c86b57d12f032897caf5
    Original-Reviewed-on: https://chromium-review.googlesource.com/218881
    Original-Reviewed-by: Julius Werner <jwerner at chromium.org>
---
 src/soc/rockchip/rk3288/spi.c | 182 ++++++++++++++++++++++++++++--------------
 1 file changed, 122 insertions(+), 60 deletions(-)

diff --git a/src/soc/rockchip/rk3288/spi.c b/src/soc/rockchip/rk3288/spi.c
index a74d423..796107e 100644
--- a/src/soc/rockchip/rk3288/spi.c
+++ b/src/soc/rockchip/rk3288/spi.c
@@ -171,77 +171,121 @@ static int rockchip_spi_wait_till_not_busy(struct rockchip_spi *regs)
 	return -1;
 }
 
-int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int sout,
-	     void *din, unsigned int sin)
+static void set_tmod(struct rockchip_spi *regs, unsigned int tmod)
+{
+	clrsetbits_le32(&regs->ctrlr0, SPI_TMOD_MASK << SPI_TMOD_OFFSET,
+				      tmod << SPI_TMOD_OFFSET);
+}
+
+static void set_transfer_mode(struct rockchip_spi *regs,
+		unsigned int sout, unsigned int sin)
+{
+	if (!sin && !sout)
+		return;
+	else if (sin && sout)
+		set_tmod(regs, SPI_TMOD_TR);	/* tx and rx */
+	else if (!sin)
+		set_tmod(regs, SPI_TMOD_TO);	/* tx only */
+	else if (!sout)
+		set_tmod(regs, SPI_TMOD_RO);	/* rx only */
+}
+
+/* returns 0 to indicate success, <0 otherwise */
+static int do_xfer(struct spi_slave *slave, const void *dout,
+	unsigned int *bytes_out, void *din, unsigned int *bytes_in)
 {
-	unsigned int len;
-	unsigned int bytes_remaining;
-	uint8_t *p;
 	struct rockchip_spi *regs = to_rockchip_spi(slave)->regs;
+	uint8_t *in_buf = din;
+	uint8_t *out_buf = (uint8_t *)dout;
+	unsigned int min_xfer;
+
+	if (*bytes_out == 0)
+		min_xfer = *bytes_in;
+	else if (*bytes_in == 0)
+		min_xfer = *bytes_out;
+	else
+		min_xfer = MIN(*bytes_in, *bytes_out);
 
-	if (dout) {
-		len = sout;
-		p = (uint8_t *) dout;
-		bytes_remaining = len;
-		writel(0, &regs->spienr);	/*disable spi */
-		writel(len - 1, &regs->ctrlr1);	/*wrtie len */
-
-		/*tx only */
-		clrsetbits_le32(&regs->ctrlr0, SPI_TMOD_MASK << SPI_TMOD_OFFSET,
-					      SPI_TMOD_TO << SPI_TMOD_OFFSET);
-		writel(1, &regs->spienr);/*enable spi */
-		while (bytes_remaining) {
-			if ((readl(&regs->txflr) & 0x3f) < SPI_FIFO_DEPTH) {
-				writel(*p++, &regs->txdr);
-				bytes_remaining--;
-			}
+	while (min_xfer) {
+		uint32_t sr = readl(&regs->sr);
+		int xferred = 0;	/* in either (or both) directions */
+
+		if (*bytes_out && !(sr & SR_TF_FULL)) {
+			writel(*out_buf, &regs->txdr);
+			out_buf++;
+			*bytes_out -= 1;
+			xferred = 1;
 		}
-		if (rockchip_spi_wait_till_not_busy(regs))
-			return -1;
-	}
-	if (din) {
-		len = sin;
-		p = (uint8_t *) din;
-		writel(0, &regs->spienr);	/*disable spi */
-		writel(len - 1, &regs->ctrlr1);	/*write len */
-
-		/*rx only */
-		clrsetbits_le32(&regs->ctrlr0, SPI_TMOD_MASK << SPI_TMOD_OFFSET,
-					      SPI_TMOD_RO << SPI_TMOD_OFFSET);
-		while (len) {
-			writel(0, &regs->spienr);/*disable spi */
-			bytes_remaining = MIN(len, 0xffff);
-			writel(bytes_remaining - 1, &regs->ctrlr1);
-			len -= bytes_remaining;
-			writel(1, &regs->spienr);/*enable spi */
-			while (bytes_remaining) {
-				if (readl(&regs->rxflr) & 0x3f) {
-					*p = readl(&regs->rxdr) & 0xff;
-					p += 1;
-					bytes_remaining--;
-				}
-			}
+
+		if (*bytes_in && !(sr & SR_RF_EMPT)) {
+			*in_buf = readl(&regs->rxdr) & 0xff;
+			in_buf++;
+			*bytes_in -= 1;
+			xferred = 1;
 		}
-		if (rockchip_spi_wait_till_not_busy(regs))
-			return -1;
+
+		min_xfer -= xferred;
+	}
+
+	if (rockchip_spi_wait_till_not_busy(regs)) {
+		printk(BIOS_ERR, "Timed out waiting on SPI transfer\n");
+		return -1;
 	}
+
 	return 0;
 }
 
-static int rockchip_spi_read(struct spi_slave *slave, void *dest, uint32_t len,
-			     uint32_t off)
+int spi_xfer(struct spi_slave *slave, const void *dout,
+		unsigned int bytes_out, void *din, unsigned int bytes_in)
 {
-	unsigned int cmd;
+	struct rockchip_spi *regs = to_rockchip_spi(slave)->regs;
+	int ret = 0;
+
+	/*
+	 * RK3288 SPI controller can transfer up to 65536 data frames (bytes
+	 * in our case) continuously. Break apart large requests as necessary.
+	 *
+	 * FIXME: And by 65536, we really mean 65535. If 0xffff is written to
+	 * ctrlr1, all bytes that we see in rxdr end up being 0x00. 0xffff - 1
+	 * seems to work fine.
+	 */
+	while (bytes_out || bytes_in) {
+		unsigned int in_now = MIN(bytes_in, 0xffff);
+		unsigned int out_now = MIN(bytes_out, 0xffff);
+		unsigned int in_rem, out_rem;
+
+		rockchip_spi_enable_chip(regs, 0);
+
+		/* Enable/disable transmitter and receiver as needed to
+		 * avoid sending or reading spurious bits. */
+		set_transfer_mode(regs, bytes_out, bytes_in);
+
+		/* MAX() in case either counter is 0 */
+		writel(MAX(in_now, out_now) - 1, &regs->ctrlr1);
+
+		rockchip_spi_enable_chip(regs, 1);
+
+		in_rem = in_now;
+		out_rem = out_now;
+		ret = do_xfer(slave, dout, &out_rem, din, &in_rem);
+		if (ret < 0)
+			break;
+
+		if (bytes_out) {
+			unsigned int sent = out_now - out_rem;
+			bytes_out -= sent;
+			dout += sent;
+		}
 
-	spi_claim_bus(slave);
-	cmd = swab32(off) | SF_READ_DATA_CMD;
-	if (spi_xfer(slave, &cmd, sizeof(cmd), dest, len)) {
-		printk(BIOS_DEBUG, "rockchip_spi_read err\n");
-		spi_release_bus(slave);
-		return -1;
+		if (bytes_in) {
+			unsigned int received = in_now - in_rem;
+			bytes_in -= received;
+			din += received;
+		}
 	}
-	spi_release_bus(slave);
-	return len;
+
+	rockchip_spi_enable_chip(regs, 0);
+	return ret < 0 ? ret : 0;
 }
 
 struct rockchip_spi_media {
@@ -262,10 +306,28 @@ static int rockchip_spi_cbfs_close(struct cbfs_media *media)
 static size_t rockchip_spi_cbfs_read(struct cbfs_media *media, void *dest,
 				     size_t offset, size_t count)
 {
+	unsigned int cmd;
 	struct rockchip_spi_media *spi =
 	    (struct rockchip_spi_media *)media->context;
+	int ret;
+
+	spi_claim_bus(spi->slave);
+	cmd = swab32(offset) | SF_READ_DATA_CMD;
+	if (spi_xfer(spi->slave, &cmd, sizeof(cmd), NULL, 0)) {
+		printk(BIOS_DEBUG, "%s: could not send command\n", __func__);
+		ret = 0;
+		goto rockchip_spi_cbfs_read_done;
+	}
+
+	if (spi_xfer(spi->slave, NULL, 0, dest, count)) {
+		printk(BIOS_DEBUG, "%s: could not receive data\n", __func__);
+		ret = 0;
+		goto rockchip_spi_cbfs_read_done;
+	}
 
-	return rockchip_spi_read(spi->slave, dest, count, offset);
+rockchip_spi_cbfs_read_done:
+	spi_release_bus(spi->slave);
+	return ret < 0 ? 0 : count;
 }
 
 static void *rockchip_spi_cbfs_map(struct cbfs_media *media, size_t offset,



More information about the coreboot-gerrit mailing list