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:
parent
8e09de297f
commit
74fedbe377
|
@ -149,9 +149,6 @@ void rockchip_spi_init(unsigned int bus, unsigned int speed_hz)
|
||||||
/* First Bit Mode */
|
/* First Bit Mode */
|
||||||
ctrlr0 |= (SPI_FBM_MSB << SPI_FBM_OFFSET);
|
ctrlr0 |= (SPI_FBM_MSB << SPI_FBM_OFFSET);
|
||||||
|
|
||||||
/* Byte and Halfword Transform */
|
|
||||||
ctrlr0 |= (SPI_APB_8BIT << SPI_HALF_WORLD_TX_OFFSET);
|
|
||||||
|
|
||||||
/* Frame Format */
|
/* Frame Format */
|
||||||
ctrlr0 |= (SPI_FRF_SPI << SPI_FRF_OFFSET);
|
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 */
|
/* 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)
|
unsigned int *bytes_out, void *din, unsigned int *bytes_in)
|
||||||
{
|
{
|
||||||
uint8_t *in_buf = din;
|
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.
|
* sychronizing with the SPI clock which is pretty slow.
|
||||||
*/
|
*/
|
||||||
if (*bytes_in && !(sr & SR_RF_EMPT)) {
|
if (*bytes_in && !(sr & SR_RF_EMPT)) {
|
||||||
int todo = read32(®s->rxflr) & RXFLR_LEVEL_MASK;
|
int fifo = read32(®s->rxflr) & RXFLR_LEVEL_MASK;
|
||||||
|
int val;
|
||||||
|
|
||||||
*bytes_in -= todo;
|
if (use_16bit)
|
||||||
xferred = todo;
|
xferred = fifo * 2;
|
||||||
while (todo-- > 0)
|
else
|
||||||
*in_buf++ = read32(®s->rxdr) & 0xff;
|
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;
|
min_xfer -= xferred;
|
||||||
|
@ -290,12 +298,31 @@ int spi_xfer(struct spi_slave *slave, const void *dout,
|
||||||
* seems to work fine.
|
* seems to work fine.
|
||||||
*/
|
*/
|
||||||
while (bytes_out || bytes_in) {
|
while (bytes_out || bytes_in) {
|
||||||
unsigned int in_now = MIN(bytes_in, 0xffff);
|
unsigned int in_now = MIN(bytes_in, 0xfffe);
|
||||||
unsigned int out_now = MIN(bytes_out, 0xffff);
|
unsigned int out_now = MIN(bytes_out, 0xfffe);
|
||||||
unsigned int in_rem, out_rem;
|
unsigned int in_rem, out_rem;
|
||||||
|
unsigned int mask;
|
||||||
|
bool use_16bit;
|
||||||
|
|
||||||
rockchip_spi_enable_chip(regs, 0);
|
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
|
/* Enable/disable transmitter and receiver as needed to
|
||||||
* avoid sending or reading spurious bits. */
|
* avoid sending or reading spurious bits. */
|
||||||
set_transfer_mode(regs, bytes_out, bytes_in);
|
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;
|
in_rem = in_now;
|
||||||
out_rem = out_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)
|
if (ret < 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue