summaryrefslogtreecommitdiff
path: root/src/soc
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2016-09-05 11:10:26 -0600
committerPatrick Georgi <pgeorgi@google.com>2016-10-06 21:50:26 +0200
commit74fedbe3771febf7702a30fc4aa2e039040ff908 (patch)
tree17784c012e40946abe1b874f3af625bd1ef19d89 /src/soc
parent8e09de297fce1de0693d2833d7a15f76fa2ce18a (diff)
downloadcoreboot-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.c53
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(&regs->rxflr) & RXFLR_LEVEL_MASK;
-
- *bytes_in -= todo;
- xferred = todo;
- while (todo-- > 0)
- *in_buf++ = read32(&regs->rxdr) & 0xff;
+ int fifo = read32(&regs->rxflr) & RXFLR_LEVEL_MASK;
+ int val;
+
+ if (use_16bit)
+ xferred = fifo * 2;
+ else
+ xferred = fifo;
+ *bytes_in -= xferred;
+ while (fifo-- > 0) {
+ val = read32(&regs->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(&regs->ctrlr0, mask);
+ else
+ setbits_le32(&regs->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;