|
@@ -204,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs,
|
|
*
|
|
*
|
|
* @param regs SPI peripheral registers
|
|
* @param regs SPI peripheral registers
|
|
* @param count Number of bytes to transfer
|
|
* @param count Number of bytes to transfer
|
|
|
|
+ * @param step Number of bytes to transfer in each packet (1 or 4)
|
|
*/
|
|
*/
|
|
-static void spi_request_bytes(struct exynos_spi *regs, int count)
|
|
|
|
|
|
+static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
|
|
{
|
|
{
|
|
|
|
+ /* For word address we need to swap bytes */
|
|
|
|
+ if (step == 4) {
|
|
|
|
+ setbits_le32(®s->mode_cfg,
|
|
|
|
+ SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
|
|
|
+ count /= 4;
|
|
|
|
+ setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
|
|
|
|
+ SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
|
|
|
|
+ SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
|
|
|
|
+ } else {
|
|
|
|
+ /* Select byte access and clear the swap configuration */
|
|
|
|
+ clrbits_le32(®s->mode_cfg,
|
|
|
|
+ SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
|
|
|
+ writel(0, ®s->swap_cfg);
|
|
|
|
+ }
|
|
|
|
+
|
|
assert(count && count < (1 << 16));
|
|
assert(count && count < (1 << 16));
|
|
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
|
|
|
+
|
|
writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
|
writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -224,17 +241,27 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
|
int toread;
|
|
int toread;
|
|
unsigned start = get_timer(0);
|
|
unsigned start = get_timer(0);
|
|
int stopping;
|
|
int stopping;
|
|
|
|
+ int step;
|
|
|
|
|
|
out_bytes = in_bytes = todo;
|
|
out_bytes = in_bytes = todo;
|
|
|
|
|
|
stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) &&
|
|
stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) &&
|
|
!(spi_slave->mode & SPI_SLAVE);
|
|
!(spi_slave->mode & SPI_SLAVE);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Try to transfer words if we can. This helps read performance at
|
|
|
|
+ * SPI clock speeds above about 20MHz.
|
|
|
|
+ */
|
|
|
|
+ step = 1;
|
|
|
|
+ if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
|
|
|
|
+ !spi_slave->skip_preamble)
|
|
|
|
+ step = 4;
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* If there's something to send, do a software reset and set a
|
|
* If there's something to send, do a software reset and set a
|
|
* transaction size.
|
|
* transaction size.
|
|
*/
|
|
*/
|
|
- spi_request_bytes(regs, todo);
|
|
|
|
|
|
+ spi_request_bytes(regs, todo, step);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Bytes are transmitted/received in pairs. Wait to receive all the
|
|
* Bytes are transmitted/received in pairs. Wait to receive all the
|
|
@@ -247,14 +274,26 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
|
|
|
|
|
/* Keep the fifos full/empty. */
|
|
/* Keep the fifos full/empty. */
|
|
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
|
|
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Don't completely fill the txfifo, since we don't want our
|
|
|
|
+ * rxfifo to overflow, and it may already contain data.
|
|
|
|
+ */
|
|
while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
|
|
while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
|
|
- temp = txp ? *txp++ : 0xff;
|
|
|
|
|
|
+ if (!txp)
|
|
|
|
+ temp = -1;
|
|
|
|
+ else if (step == 4)
|
|
|
|
+ temp = *(uint32_t *)txp;
|
|
|
|
+ else
|
|
|
|
+ temp = *txp;
|
|
writel(temp, ®s->tx_data);
|
|
writel(temp, ®s->tx_data);
|
|
- out_bytes--;
|
|
|
|
- tx_lvl++;
|
|
|
|
|
|
+ out_bytes -= step;
|
|
|
|
+ if (txp)
|
|
|
|
+ txp += step;
|
|
|
|
+ tx_lvl += step;
|
|
}
|
|
}
|
|
- if (rx_lvl > 0) {
|
|
|
|
- while (rx_lvl > 0) {
|
|
|
|
|
|
+ if (rx_lvl >= step) {
|
|
|
|
+ while (rx_lvl >= step) {
|
|
temp = readl(®s->rx_data);
|
|
temp = readl(®s->rx_data);
|
|
if (spi_slave->skip_preamble) {
|
|
if (spi_slave->skip_preamble) {
|
|
if (temp == SPI_PREAMBLE_END_BYTE) {
|
|
if (temp == SPI_PREAMBLE_END_BYTE) {
|
|
@@ -262,12 +301,15 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
|
stopping = 0;
|
|
stopping = 0;
|
|
}
|
|
}
|
|
} else {
|
|
} else {
|
|
- if (rxp || stopping)
|
|
|
|
- *rxp++ = temp;
|
|
|
|
- in_bytes--;
|
|
|
|
|
|
+ if (rxp || stopping) {
|
|
|
|
+ *rxp = temp;
|
|
|
|
+ rxp += step;
|
|
|
|
+ }
|
|
|
|
+ in_bytes -= step;
|
|
}
|
|
}
|
|
- toread--;
|
|
|
|
- rx_lvl--;
|
|
|
|
|
|
+ toread -= step;
|
|
|
|
+ rx_lvl -= step;
|
|
|
|
+ }
|
|
} else if (!toread) {
|
|
} else if (!toread) {
|
|
/*
|
|
/*
|
|
* We have run out of input data, but haven't read
|
|
* We have run out of input data, but haven't read
|
|
@@ -279,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
|
|
out_bytes = in_bytes;
|
|
out_bytes = in_bytes;
|
|
toread = in_bytes;
|
|
toread = in_bytes;
|
|
txp = NULL;
|
|
txp = NULL;
|
|
- spi_request_bytes(regs, toread);
|
|
|
|
|
|
+ spi_request_bytes(regs, toread, step);
|
|
}
|
|
}
|
|
if (spi_slave->skip_preamble && get_timer(start) > 100) {
|
|
if (spi_slave->skip_preamble && get_timer(start) > 100) {
|
|
printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
|
|
printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
|
|
@@ -323,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
|
if ((flags & SPI_XFER_BEGIN))
|
|
if ((flags & SPI_XFER_BEGIN))
|
|
spi_cs_activate(slave);
|
|
spi_cs_activate(slave);
|
|
|
|
|
|
- /* Exynos SPI limits each transfer to 65535 bytes */
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Exynos SPI limits each transfer to 65535 transfers. To keep
|
|
|
|
+ * things simple, allow a maximum of 65532 bytes. We could allow
|
|
|
|
+ * more in word mode, but the performance difference is small.
|
|
|
|
+ */
|
|
bytelen = bitlen / 8;
|
|
bytelen = bitlen / 8;
|
|
for (upto = 0; !ret && upto < bytelen; upto += todo) {
|
|
for (upto = 0; !ret && upto < bytelen; upto += todo) {
|
|
- todo = min(bytelen - upto, (1 << 16) - 1);
|
|
|
|
|
|
+ todo = min(bytelen - upto, (1 << 16) - 4);
|
|
ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
|
|
ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
|
|
if (ret)
|
|
if (ret)
|
|
break;
|
|
break;
|