armv7/exynos5420: Apply new implementation for SPI transmission.
Switch spi_xfer and exynos_spi_read to use the new spi_rx_tx function. Change-Id: I01ab43509df1319672bec30dd111f98001d655d0 Signed-off-by: Hung-Te Lin <hungte@chromium.org> Signed-off-by: Gabe Black <gabeblack@chromium.org> Reviewed-on: http://review.coreboot.org/3714 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
864420766a
commit
a965a37810
|
@ -153,9 +153,7 @@ static void exynos_spi_request_bytes(struct exynos_spi *regs, int count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
|
static int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
|
||||||
const uint8_t *txp, int tx_bytes);
|
|
||||||
int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
|
|
||||||
const uint8_t *txp, int tx_bytes)
|
const uint8_t *txp, int tx_bytes)
|
||||||
{
|
{
|
||||||
struct exynos_spi_slave *espi = to_exynos_spi(slave);
|
struct exynos_spi_slave *espi = to_exynos_spi(slave);
|
||||||
|
@ -237,46 +235,6 @@ int spi_rx_tx(struct spi_slave *slave, uint8_t *rxp, int rx_bytes,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void exynos_spi_rx_tx(struct exynos_spi *regs, int todo,
|
|
||||||
void *dinp, void const *doutp, int i)
|
|
||||||
{
|
|
||||||
int rx_lvl, tx_lvl;
|
|
||||||
unsigned int *rxp = (unsigned int *)(dinp + (i * (32 * 1024)));
|
|
||||||
unsigned int out_bytes, in_bytes;
|
|
||||||
|
|
||||||
// TODO In currrent implementation, every read/write must be aligned to
|
|
||||||
// 4 bytes, otherwise you may get timeout or other unexpected results.
|
|
||||||
ASSERT(todo % 4 == 0);
|
|
||||||
|
|
||||||
out_bytes = in_bytes = todo;
|
|
||||||
exynos_spi_soft_reset(regs);
|
|
||||||
writel(((todo * 8) / 32) | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
|
||||||
|
|
||||||
while (in_bytes) {
|
|
||||||
uint32_t spi_sts;
|
|
||||||
int temp;
|
|
||||||
|
|
||||||
spi_sts = readl(®s->spi_sts);
|
|
||||||
rx_lvl = ((spi_sts >> 15) & 0x7f);
|
|
||||||
tx_lvl = ((spi_sts >> 6) & 0x7f);
|
|
||||||
while (tx_lvl < 32 && out_bytes) {
|
|
||||||
// TODO The "writing" (tx) is not supported now; that's
|
|
||||||
// why we write garbage to keep driving FIFO clock.
|
|
||||||
temp = 0xffffffff;
|
|
||||||
writel(temp, ®s->tx_data);
|
|
||||||
out_bytes -= 4;
|
|
||||||
tx_lvl += 4;
|
|
||||||
}
|
|
||||||
while (rx_lvl >= 4 && in_bytes) {
|
|
||||||
temp = readl(®s->rx_data);
|
|
||||||
if (rxp)
|
|
||||||
*rxp++ = temp;
|
|
||||||
in_bytes -= 4;
|
|
||||||
rx_lvl -= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int spi_claim_bus(struct spi_slave *slave)
|
int spi_claim_bus(struct spi_slave *slave)
|
||||||
{
|
{
|
||||||
struct exynos_spi_slave *espi = to_exynos_spi(slave);
|
struct exynos_spi_slave *espi = to_exynos_spi(slave);
|
||||||
|
@ -304,32 +262,70 @@ int spi_claim_bus(struct spi_slave *slave)
|
||||||
int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout,
|
int spi_xfer(struct spi_slave *slave, const void *dout, unsigned int bitsout,
|
||||||
void *din, unsigned int bitsin)
|
void *din, unsigned int bitsin)
|
||||||
{
|
{
|
||||||
// TODO(hungte) Invoke exynos_spi_rx_tx to transfer data.
|
unsigned int out_bytes = bitsout / 8, in_bytes = bitsin / 8;
|
||||||
return -1;
|
uint8_t *out_ptr = (uint8_t *)dout, *in_ptr = (uint8_t *)din;
|
||||||
|
int offset, todo, len;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ASSERT(bitsout % 8 == 0 && bitsin % 8 == 0);
|
||||||
|
len = MAX(out_bytes, in_bytes);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Exynos SPI limits each transfer to (2^16-1=65535) bytes. To keep
|
||||||
|
* things simple (especially for word-width transfer mode), allow a
|
||||||
|
* maximum of (2^16-4=65532) bytes. We could allow more in word mode,
|
||||||
|
* but the performance difference is small.
|
||||||
|
*/
|
||||||
|
spi_cs_activate(slave);
|
||||||
|
for (offset = 0; !ret && (offset < len); offset += todo) {
|
||||||
|
todo = min(len - offset, (1 << 16) - 4);
|
||||||
|
ret = spi_rx_tx(slave, in_ptr, MIN(in_bytes, todo), out_ptr,
|
||||||
|
MIN(out_bytes, todo));
|
||||||
|
// Adjust remaining bytes and pointers.
|
||||||
|
if (in_bytes >= todo) {
|
||||||
|
in_bytes -= todo;
|
||||||
|
in_ptr += todo;
|
||||||
|
} else {
|
||||||
|
in_bytes = 0;
|
||||||
|
in_ptr = NULL;
|
||||||
|
}
|
||||||
|
if (out_bytes >= todo) {
|
||||||
|
out_bytes -= todo;
|
||||||
|
out_ptr += todo;
|
||||||
|
} else {
|
||||||
|
out_bytes = 0;
|
||||||
|
out_ptr = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spi_cs_deactivate(slave);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int exynos_spi_read(struct spi_slave *slave, void *dest, uint32_t len,
|
static int exynos_spi_read(struct spi_slave *slave, void *dest, uint32_t len,
|
||||||
uint32_t off)
|
uint32_t off)
|
||||||
{
|
{
|
||||||
struct exynos_spi *regs = to_exynos_spi(slave)->regs;
|
struct exynos_spi *regs = to_exynos_spi(slave)->regs;
|
||||||
int upto, todo;
|
int rv;
|
||||||
int i;
|
|
||||||
clrbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT); /* CS low */
|
|
||||||
|
|
||||||
/* Send read instruction (0x3h) followed by a 24 bit addr */
|
// TODO(hungte) Merge the "read address" command into spi_xfer calls
|
||||||
writel((SF_READ_DATA_CMD << 24) | off, ®s->tx_data);
|
// (full-duplex mode).
|
||||||
|
|
||||||
/* waiting for TX done */
|
spi_cs_activate(slave);
|
||||||
while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE));
|
|
||||||
|
|
||||||
for (upto = 0, i = 0; upto < len; upto += todo, i++) {
|
// Specify read address (in word-width mode).
|
||||||
todo = MIN(len - upto, (1 << 15));
|
ASSERT(off < (1 << 24));
|
||||||
exynos_spi_rx_tx(regs, todo, dest, (void *)(off), i);
|
exynos_spi_request_bytes(regs, sizeof(off), sizeof(off));
|
||||||
|
writel(htonl((SF_READ_DATA_CMD << 24) | off), ®s->tx_data);
|
||||||
|
while (!(readl(®s->spi_sts) & SPI_ST_TX_DONE)) {
|
||||||
|
/* Wait for TX done */
|
||||||
}
|
}
|
||||||
|
|
||||||
setbits_le32(®s->cs_reg, SPI_SLAVE_SIG_INACT);/* make the CS high */
|
// Now, safe to transfer.
|
||||||
|
rv = spi_xfer(slave, NULL, 0, dest, len * 8);
|
||||||
|
spi_cs_deactivate(slave);
|
||||||
|
|
||||||
return len;
|
return (rv == 0) ? len : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void spi_release_bus(struct spi_slave *slave)
|
void spi_release_bus(struct spi_slave *slave)
|
||||||
|
@ -378,7 +374,8 @@ static void *exynos_spi_cbfs_map(struct cbfs_media *media, size_t offset,
|
||||||
size_t count) {
|
size_t count) {
|
||||||
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
|
struct exynos_spi_media *spi = (struct exynos_spi_media*)media->context;
|
||||||
DEBUG_SPI("exynos_spi_cbfs_map\n");
|
DEBUG_SPI("exynos_spi_cbfs_map\n");
|
||||||
// See exynos_spi_rx_tx for I/O alignment limitation.
|
// exynos: spi_rx_tx may work in 4 byte-width-transmission mode and
|
||||||
|
// requires buffer memory address to be aligned.
|
||||||
if (count % 4)
|
if (count % 4)
|
||||||
count += 4 - (count % 4);
|
count += 4 - (count % 4);
|
||||||
return cbfs_simple_buffer_map(&spi->buffer, media, offset, count);
|
return cbfs_simple_buffer_map(&spi->buffer, media, offset, count);
|
||||||
|
|
Loading…
Reference in New Issue