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>
This commit is contained in:
Simon Glass 2016-09-05 11:10:26 -06:00 committed by Patrick Georgi
parent 8e09de297f
commit 74fedbe377
1 changed files with 39 additions and 12 deletions

View File

@ -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;
int fifo = read32(&regs->rxflr) & RXFLR_LEVEL_MASK;
int val;
*bytes_in -= todo;
xferred = todo;
while (todo-- > 0)
*in_buf++ = read32(&regs->rxdr) & 0xff;
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;