aboutsummaryrefslogtreecommitdiff
path: root/drivers/spi/spi-sifive.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-sifive.c')
-rw-r--r--drivers/spi/spi-sifive.c57
1 files changed, 37 insertions, 20 deletions
diff --git a/drivers/spi/spi-sifive.c b/drivers/spi/spi-sifive.c
index 0ea4930..4cab039 100644
--- a/drivers/spi/spi-sifive.c
+++ b/drivers/spi/spi-sifive.c
@@ -11,6 +11,7 @@
#include <dm/device_compat.h>
#include <malloc.h>
#include <spi-mem.h>
+#include <wait_bit.h>
#include <asm/io.h>
#include <linux/log2.h>
#include <clk.h>
@@ -134,8 +135,8 @@ static void sifive_spi_clear_cs(struct sifive_spi *spi)
}
static void sifive_spi_prep_transfer(struct sifive_spi *spi,
- bool is_rx_xfer,
- struct dm_spi_slave_platdata *slave_plat)
+ struct dm_spi_slave_platdata *slave_plat,
+ u8 *rx_ptr)
{
u32 cr;
@@ -167,7 +168,7 @@ static void sifive_spi_prep_transfer(struct sifive_spi *spi,
/* SPI direction in/out ? */
cr &= ~SIFIVE_SPI_FMT_DIR;
- if (!is_rx_xfer)
+ if (!rx_ptr)
cr |= SIFIVE_SPI_FMT_DIR;
writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
@@ -198,13 +199,19 @@ static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
}
+static int sifive_spi_wait(struct sifive_spi *spi, u32 bit)
+{
+ return wait_for_bit_le32(spi->regs + SIFIVE_SPI_REG_IP,
+ bit, true, 100, false);
+}
+
static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct sifive_spi *spi = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
- const unsigned char *tx_ptr = dout;
+ const u8 *tx_ptr = dout;
u8 *rx_ptr = din;
u32 remaining_len;
int ret;
@@ -217,31 +224,37 @@ static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
return ret;
}
- sifive_spi_prep_transfer(spi, true, slave_plat);
+ sifive_spi_prep_transfer(spi, slave_plat, rx_ptr);
remaining_len = bitlen / 8;
while (remaining_len) {
- int n_words, tx_words, rx_words;
-
- n_words = min(remaining_len, spi->fifo_depth);
+ unsigned int n_words = min(remaining_len, spi->fifo_depth);
+ unsigned int tx_words, rx_words;
/* Enqueue n_words for transmission */
- if (tx_ptr) {
- for (tx_words = 0; tx_words < n_words; ++tx_words) {
- sifive_spi_tx(spi, tx_ptr);
- sifive_spi_rx(spi, NULL);
- tx_ptr++;
- }
+ for (tx_words = 0; tx_words < n_words; tx_words++) {
+ if (!tx_ptr)
+ sifive_spi_tx(spi, NULL);
+ else
+ sifive_spi_tx(spi, tx_ptr++);
}
- /* Read out all the data from the RX FIFO */
if (rx_ptr) {
- for (rx_words = 0; rx_words < n_words; ++rx_words) {
- sifive_spi_tx(spi, NULL);
- sifive_spi_rx(spi, rx_ptr);
- rx_ptr++;
- }
+ /* Wait for transmission + reception to complete */
+ writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
+ ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);
+ if (ret)
+ return ret;
+
+ /* Read out all the data from the RX FIFO */
+ for (rx_words = 0; rx_words < n_words; rx_words++)
+ sifive_spi_rx(spi, rx_ptr++);
+ } else {
+ /* Wait for transmission to complete */
+ ret = sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
+ if (ret)
+ return ret;
}
remaining_len -= n_words;
@@ -395,6 +408,10 @@ static void sifive_spi_init_hw(struct sifive_spi *spi)
/* Watermark interrupts are disabled by default */
writel(0, spi->regs + SIFIVE_SPI_REG_IE);
+ /* Default watermark FIFO threshold values */
+ writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
+ writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);
+
/* Set CS/SCK Delays and Inactive Time to defaults */
writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
spi->regs + SIFIVE_SPI_REG_DELAY0);