|
@@ -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 */
|
|
@@ -184,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);
|
|
|
}
|
|
|
|
|
@@ -297,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);
|
|
@@ -416,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)
|
|
@@ -491,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;
|
|
@@ -514,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;
|
|
@@ -579,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;
|
|
@@ -615,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);
|
|
@@ -729,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)
|
|
|
{
|
|
@@ -778,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);
|
|
|
|
|
@@ -818,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;
|