diff options
Diffstat (limited to 'drivers/spi/spi-armada-3700.c')
-rw-r--r-- | drivers/spi/spi-armada-3700.c | 118 |
1 files changed, 100 insertions, 18 deletions
diff --git a/drivers/spi/spi-armada-3700.c b/drivers/spi/spi-armada-3700.c index 77fe55ce790c..7dcb14d303eb 100644 --- a/drivers/spi/spi-armada-3700.c +++ b/drivers/spi/spi-armada-3700.c @@ -27,6 +27,8 @@ #define DRIVER_NAME "armada_3700_spi" +#define A3700_SPI_MAX_SPEED_HZ 100000000 +#define A3700_SPI_MAX_PRESCALE 30 #define A3700_SPI_TIMEOUT 10 /* SPI Register Offest */ @@ -79,6 +81,7 @@ #define A3700_SPI_BYTE_LEN BIT(5) #define A3700_SPI_CLK_PRESCALE BIT(0) #define A3700_SPI_CLK_PRESCALE_MASK (0x1f) +#define A3700_SPI_CLK_EVEN_OFFS (0x10) #define A3700_SPI_WFIFO_THRS_BIT 28 #define A3700_SPI_RFIFO_THRS_BIT 24 @@ -183,12 +186,15 @@ static int a3700_spi_pin_mode_set(struct a3700_spi *a3700_spi, return 0; } -static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi) +static void a3700_spi_fifo_mode_set(struct a3700_spi *a3700_spi, bool enable) { u32 val; val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); - val |= A3700_SPI_FIFO_MODE; + if (enable) + val |= A3700_SPI_FIFO_MODE; + else + val &= ~A3700_SPI_FIFO_MODE; spireg_write(a3700_spi, A3700_SPI_IF_CFG_REG, val); } @@ -220,6 +226,13 @@ static void a3700_spi_clock_set(struct a3700_spi *a3700_spi, prescale = DIV_ROUND_UP(clk_get_rate(a3700_spi->clk), speed_hz); + /* For prescaler values over 15, we can only set it by steps of 2. + * Starting from A3700_SPI_CLK_EVEN_OFFS, we set values from 0 up to + * 30. We only use this range from 16 to 30. + */ + if (prescale > 15) + prescale = A3700_SPI_CLK_EVEN_OFFS + DIV_ROUND_UP(prescale, 2); + val = spireg_read(a3700_spi, A3700_SPI_IF_CFG_REG); val = val & ~A3700_SPI_CLK_PRESCALE_MASK; @@ -289,7 +302,7 @@ static int a3700_spi_init(struct a3700_spi *a3700_spi) a3700_spi_deactivate_cs(a3700_spi, i); /* Enable FIFO mode */ - a3700_spi_fifo_mode_set(a3700_spi); + a3700_spi_fifo_mode_set(a3700_spi, true); /* Set SPI mode */ a3700_spi_mode_set(a3700_spi, master->mode_bits); @@ -408,15 +421,20 @@ static void a3700_spi_transfer_setup(struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi; - unsigned int byte_len; a3700_spi = spi_master_get_devdata(spi->master); a3700_spi_clock_set(a3700_spi, xfer->speed_hz); - byte_len = xfer->bits_per_word >> 3; + /* Use 4 bytes long transfers. Each transfer method has its way to deal + * with the remaining bytes for non 4-bytes aligned transfers. + */ + a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_fifo_thres_set(a3700_spi, byte_len); + /* Initialize the working buffers */ + a3700_spi->tx_buf = xfer->tx_buf; + a3700_spi->rx_buf = xfer->rx_buf; + a3700_spi->buf_len = xfer->len; } static void a3700_spi_set_cs(struct spi_device *spi, bool enable) @@ -483,7 +501,7 @@ static int a3700_spi_fifo_write(struct a3700_spi *a3700_spi) u32 val; while (!a3700_is_wfifo_full(a3700_spi) && a3700_spi->buf_len) { - val = cpu_to_le32(*(u32 *)a3700_spi->tx_buf); + val = *(u32 *)a3700_spi->tx_buf; spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val); a3700_spi->buf_len -= 4; a3700_spi->tx_buf += 4; @@ -506,9 +524,8 @@ static int a3700_spi_fifo_read(struct a3700_spi *a3700_spi) while (!a3700_is_rfifo_empty(a3700_spi) && a3700_spi->buf_len) { val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG); if (a3700_spi->buf_len >= 4) { - u32 data = le32_to_cpu(val); - memcpy(a3700_spi->rx_buf, &data, 4); + memcpy(a3700_spi->rx_buf, &val, 4); a3700_spi->buf_len -= 4; a3700_spi->rx_buf += 4; @@ -571,27 +588,26 @@ static int a3700_spi_prepare_message(struct spi_master *master, if (ret) return ret; - a3700_spi_bytelen_set(a3700_spi, 4); - a3700_spi_mode_set(a3700_spi, spi->mode); return 0; } -static int a3700_spi_transfer_one(struct spi_master *master, +static int a3700_spi_transfer_one_fifo(struct spi_master *master, struct spi_device *spi, struct spi_transfer *xfer) { struct a3700_spi *a3700_spi = spi_master_get_devdata(master); int ret = 0, timeout = A3700_SPI_TIMEOUT; - unsigned int nbits = 0; + unsigned int nbits = 0, byte_len; u32 val; - a3700_spi_transfer_setup(spi, xfer); + /* Make sure we use FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, true); - a3700_spi->tx_buf = xfer->tx_buf; - a3700_spi->rx_buf = xfer->rx_buf; - a3700_spi->buf_len = xfer->len; + /* Configure FIFO thresholds */ + byte_len = xfer->bits_per_word >> 3; + a3700_spi_fifo_thres_set(a3700_spi, byte_len); if (xfer->tx_buf) nbits = xfer->tx_nbits; @@ -607,6 +623,11 @@ static int a3700_spi_transfer_one(struct spi_master *master, a3700_spi_header_set(a3700_spi); if (xfer->rx_buf) { + /* Clear WFIFO, since it's last 2 bytes are shifted out during + * a read operation + */ + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, 0); + /* Set read data length */ spireg_write(a3700_spi, A3700_SPI_IF_DIN_CNT_REG, a3700_spi->buf_len); @@ -721,6 +742,63 @@ out: return ret; } +static int a3700_spi_transfer_one_full_duplex(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct a3700_spi *a3700_spi = spi_master_get_devdata(master); + u32 val; + + /* Disable FIFO mode */ + a3700_spi_fifo_mode_set(a3700_spi, false); + + while (a3700_spi->buf_len) { + + /* When we have less than 4 bytes to transfer, switch to 1 byte + * mode. This is reset after each transfer + */ + if (a3700_spi->buf_len < 4) + a3700_spi_bytelen_set(a3700_spi, 1); + + if (a3700_spi->byte_len == 1) + val = *a3700_spi->tx_buf; + else + val = *(u32 *)a3700_spi->tx_buf; + + spireg_write(a3700_spi, A3700_SPI_DATA_OUT_REG, val); + + /* Wait for all the data to be shifted in / out */ + while (!(spireg_read(a3700_spi, A3700_SPI_IF_CTRL_REG) & + A3700_SPI_XFER_DONE)) + cpu_relax(); + + val = spireg_read(a3700_spi, A3700_SPI_DATA_IN_REG); + + memcpy(a3700_spi->rx_buf, &val, a3700_spi->byte_len); + + a3700_spi->buf_len -= a3700_spi->byte_len; + a3700_spi->tx_buf += a3700_spi->byte_len; + a3700_spi->rx_buf += a3700_spi->byte_len; + + } + + spi_finalize_current_transfer(master); + + return 0; +} + +static int a3700_spi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + a3700_spi_transfer_setup(spi, xfer); + + if (xfer->tx_buf && xfer->rx_buf) + return a3700_spi_transfer_one_full_duplex(master, spi, xfer); + + return a3700_spi_transfer_one_fifo(master, spi, xfer); +} + static int a3700_spi_unprepare_message(struct spi_master *master, struct spi_message *message) { @@ -770,7 +848,6 @@ static int a3700_spi_probe(struct platform_device *pdev) master->transfer_one = a3700_spi_transfer_one; master->unprepare_message = a3700_spi_unprepare_message; master->set_cs = a3700_spi_set_cs; - master->flags = SPI_MASTER_HALF_DUPLEX; master->mode_bits |= (SPI_RX_DUAL | SPI_TX_DUAL | SPI_RX_QUAD | SPI_TX_QUAD); @@ -810,6 +887,11 @@ static int a3700_spi_probe(struct platform_device *pdev) goto error; } + master->max_speed_hz = min_t(unsigned long, A3700_SPI_MAX_SPEED_HZ, + clk_get_rate(spi->clk)); + master->min_speed_hz = DIV_ROUND_UP(clk_get_rate(spi->clk), + A3700_SPI_MAX_PRESCALE); + ret = a3700_spi_init(spi); if (ret) goto error_clk; |