diff options
author | Simon Glass <sjg@chromium.org> | 2016-09-05 11:10:26 -0600 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2016-10-06 21:50:26 +0200 |
commit | 74fedbe3771febf7702a30fc4aa2e039040ff908 (patch) | |
tree | 17784c012e40946abe1b874f3af625bd1ef19d89 /src/soc | |
parent | 8e09de297fce1de0693d2833d7a15f76fa2ce18a (diff) | |
download | coreboot-74fedbe3771febf7702a30fc4aa2e039040ff908.tar.xz |
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>
Reviewed-on: https://review.coreboot.org/16712
Tested-by: build bot (Jenkins)
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'src/soc')
-rw-r--r-- | src/soc/rockchip/common/spi.c | 53 |
1 files changed, 40 insertions, 13 deletions
diff --git a/src/soc/rockchip/common/spi.c b/src/soc/rockchip/common/spi.c index 7dcaaadf19..57e9ca117e 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; |