From 74fedbe3771febf7702a30fc4aa2e039040ff908 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 5 Sep 2016 11:10:26 -0600 Subject: 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 Original-Commit-Id: ec250b4931c7d99cc014e32ab597fca948299d08 Original-Change-Id: Idc5b7e5d82cdbdc1e8fe8b2d6da819edf2d5570c Original-Signed-off-by: Simon Glass Original-Reviewed-on: https://chromium-review.googlesource.com/381312 Original-Commit-Ready: Julius Werner Original-Tested-by: Julius Werner Original-Reviewed-by: Julius Werner Reviewed-on: https://review.coreboot.org/16712 Tested-by: build bot (Jenkins) Reviewed-by: Paul Menzel Reviewed-by: Martin Roth --- 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 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; -- cgit v1.2.3