Patrick Georgi (pgeorgi@google.com) just uploaded a new patch set to gerrit, which you can find at https://review.coreboot.org/16712
-gerrit
commit 237f0464b41ebd416a52a5a36253ea0fc6d3abcc Author: Simon Glass sjg@chromium.org Date: Mon Sep 5 11:10:26 2016 -0600
rockchip: spi: Add support for 16-bit APB reads
With a SPI clock above about 24MHz the APB cannot keep up when doing individual byte transfers. Adjust the driver to use 16-bit reads when it can, to remove this bottleneck.
Any transaction which involves writing bytes still uses 8-bit transfers, to simplify the code. These are the transfers that are not time-critical since they tend to be small. The case that really matters is reading from SPI flash.
In general we can use 16-bit reads anytime we are transferring an even number of bytes. If the code detects an odd number of bytes, it tries to perform the operation in two steps: once in 16-bit mode with an even number of bytes, and once in 8-bit mode for the final byte. This allow us to use 16-bit reads even if asked to transfer (for example) 0xf423 bytes.
The limit on in_now and out_now is adjusted to 0xfffe to avoid an extra transfer when transferring ~>=64KB.
CQ-DEPEND=CL:383232 BUG=chrome-os-partner:56556 BRANCH=none TEST=boot on gru and see that things still work correctly. I tested (with extra debugging) that the 16-bit case is being picked when it should be.
Change-Id: If5effae9a84e4de06537fd594bedf7f01d6a9c88 Signed-off-by: Patrick Georgi pgeorgi@chromium.org Original-Commit-Id: ec250b4931c7d99cc014e32ab597fca948299d08 Original-Change-Id: Idc5b7e5d82cdbdc1e8fe8b2d6da819edf2d5570c Original-Signed-off-by: Simon Glass sjg@chromium.org Original-Reviewed-on: https://chromium-review.googlesource.com/381312 Original-Commit-Ready: Julius Werner jwerner@chromium.org Original-Tested-by: Julius Werner jwerner@chromium.org Original-Reviewed-by: Julius Werner jwerner@chromium.org --- src/soc/rockchip/common/spi.c | 53 ++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-)
diff --git a/src/soc/rockchip/common/spi.c b/src/soc/rockchip/common/spi.c index 7dcaaad..57e9ca1 100644 --- a/src/soc/rockchip/common/spi.c +++ b/src/soc/rockchip/common/spi.c @@ -149,9 +149,6 @@ void rockchip_spi_init(unsigned int bus, unsigned int speed_hz) /* First Bit Mode */ ctrlr0 |= (SPI_FBM_MSB << SPI_FBM_OFFSET);
- /* Byte and Halfword Transform */ - ctrlr0 |= (SPI_APB_8BIT << SPI_HALF_WORLD_TX_OFFSET); - /* Frame Format */ ctrlr0 |= (SPI_FRF_SPI << SPI_FRF_OFFSET);
@@ -220,7 +217,7 @@ static void set_transfer_mode(struct rockchip_spi *regs, }
/* returns 0 to indicate success, <0 otherwise */ -static int do_xfer(struct rockchip_spi *regs, const void *dout, +static int do_xfer(struct rockchip_spi *regs, bool use_16bit, const void *dout, unsigned int *bytes_out, void *din, unsigned int *bytes_in) { uint8_t *in_buf = din; @@ -251,12 +248,23 @@ static int do_xfer(struct rockchip_spi *regs, const void *dout, * sychronizing with the SPI clock which is pretty slow. */ if (*bytes_in && !(sr & SR_RF_EMPT)) { - int todo = read32(®s->rxflr) & RXFLR_LEVEL_MASK; - - *bytes_in -= todo; - xferred = todo; - while (todo-- > 0) - *in_buf++ = read32(®s->rxdr) & 0xff; + int fifo = read32(®s->rxflr) & RXFLR_LEVEL_MASK; + int val; + + if (use_16bit) + xferred = fifo * 2; + else + xferred = fifo; + *bytes_in -= xferred; + while (fifo-- > 0) { + val = read32(®s->rxdr); + if (use_16bit) { + *in_buf++ = val & 0xff; + *in_buf++ = (val >> 8) & 0xff; + } else { + *in_buf++ = val & 0xff; + } + } }
min_xfer -= xferred; @@ -290,12 +298,31 @@ int spi_xfer(struct spi_slave *slave, const void *dout, * 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_now = MIN(bytes_in, 0xfffe); + unsigned int out_now = MIN(bytes_out, 0xfffe); unsigned int in_rem, out_rem; + unsigned int mask; + bool use_16bit;
rockchip_spi_enable_chip(regs, 0);
+ /* + * Use 16-bit transfers for higher-speed reads. If we are + * transferring an odd number of bytes, try to make it even. + */ + use_16bit = false; + if (bytes_out == 0) { + if ((in_now & 1) && in_now > 1) + in_now--; + if (!(in_now & 1)) + use_16bit = true; + } + mask = SPI_APB_8BIT << SPI_HALF_WORLD_TX_OFFSET; + if (use_16bit) + clrbits_le32(®s->ctrlr0, mask); + else + setbits_le32(®s->ctrlr0, mask); + /* Enable/disable transmitter and receiver as needed to * avoid sending or reading spurious bits. */ set_transfer_mode(regs, bytes_out, bytes_in); @@ -307,7 +334,7 @@ int spi_xfer(struct spi_slave *slave, const void *dout,
in_rem = in_now; out_rem = out_now; - ret = do_xfer(regs, dout, &out_rem, din, &in_rem); + ret = do_xfer(regs, use_16bit, dout, &out_rem, din, &in_rem); if (ret < 0) break;