summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/cadence_qspi.c8
-rw-r--r--drivers/spi/cadence_qspi.h5
-rw-r--r--drivers/spi/cadence_qspi_apb.c119
3 files changed, 131 insertions, 1 deletions
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 849bd930edf..599596f9f08 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -210,6 +210,7 @@ static int cadence_spi_probe(struct udevice *bus)
priv->regbase = plat->regbase;
priv->ahbbase = plat->ahbbase;
+ priv->is_dma = plat->is_dma;
priv->is_decoded_cs = plat->is_decoded_cs;
priv->fifo_depth = plat->fifo_depth;
priv->fifo_width = plat->fifo_width;
@@ -347,7 +348,10 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
case CQSPI_READ:
err = cadence_qspi_apb_read_setup(priv, op);
if (!err) {
- err = cadence_qspi_apb_dma_read(priv, op);
+ if (priv->is_dma)
+ err = cadence_qspi_apb_dma_read(priv, op);
+ else
+ err = cadence_qspi_apb_read_execute(priv, op);
}
break;
case CQSPI_WRITE:
@@ -408,6 +412,8 @@ static int cadence_spi_of_to_plat(struct udevice *bus)
if (plat->ahbsize >= SZ_8M)
priv->use_dac_mode = true;
+ plat->is_dma = dev_read_bool(bus, "cdns,is-dma");
+
/* All other parameters are embedded in the child node */
subnode = cadence_qspi_get_subnode(bus);
if (!ofnode_valid(subnode)) {
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 10c4ad14cc0..879e7f8dbfb 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -223,6 +223,8 @@ struct cadence_spi_plat {
u32 tchsh_ns;
u32 tslch_ns;
u32 quirks;
+
+ bool is_dma;
};
struct cadence_spi_priv {
@@ -259,6 +261,7 @@ struct cadence_spi_priv {
bool ddr_init;
bool is_decoded_cs;
bool use_dac_mode;
+ bool is_dma;
/* Transaction protocol parameters. */
u8 inst_width;
@@ -289,6 +292,8 @@ int cadence_qspi_apb_command_write(struct cadence_spi_priv *priv,
int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
+int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv,
+ const struct spi_mem_op *op);
int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op);
int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv,
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 58be017720b..4696c09f754 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -667,6 +667,125 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_priv *priv,
return 0;
}
+static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_priv *priv)
+{
+ u32 reg = readl(priv->regbase + CQSPI_REG_SDRAMLEVEL);
+ reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
+ return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
+}
+
+static int cadence_qspi_wait_for_data(struct cadence_spi_priv *priv)
+{
+ unsigned int timeout = 10000;
+ u32 reg;
+
+ while (timeout--) {
+ reg = cadence_qspi_get_rd_sram_level(priv);
+ if (reg)
+ return reg;
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int
+cadence_qspi_apb_indirect_read_execute(struct cadence_spi_priv *priv,
+ unsigned int n_rx, u8 *rxbuf)
+{
+ unsigned int remaining = n_rx;
+ unsigned int bytes_to_read = 0;
+ int ret;
+
+ writel(n_rx, priv->regbase + CQSPI_REG_INDIRECTRDBYTES);
+
+ /* Start the indirect read transfer */
+ writel(CQSPI_REG_INDIRECTRD_START,
+ priv->regbase + CQSPI_REG_INDIRECTRD);
+
+ while (remaining > 0) {
+ ret = cadence_qspi_wait_for_data(priv);
+ if (ret < 0) {
+ printf("Indirect write timed out (%i)\n", ret);
+ goto failrd;
+ }
+
+ bytes_to_read = ret;
+
+ while (bytes_to_read != 0) {
+ bytes_to_read *= priv->fifo_width;
+ bytes_to_read = bytes_to_read > remaining ?
+ remaining : bytes_to_read;
+ /*
+ * Handle non-4-byte aligned access to avoid
+ * data abort.
+ */
+ if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
+ readsb(priv->ahbbase, rxbuf, bytes_to_read);
+ else
+ readsl(priv->ahbbase, rxbuf,
+ bytes_to_read >> 2);
+ rxbuf += bytes_to_read;
+ remaining -= bytes_to_read;
+ bytes_to_read = cadence_qspi_get_rd_sram_level(priv);
+ }
+ }
+
+ /* Check indirect done status */
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
+ CQSPI_REG_INDIRECTRD_DONE, 1, 10, 0);
+ if (ret) {
+ printf("Indirect read completion error (%i)\n", ret);
+ goto failrd;
+ }
+
+ /* Clear indirect completion status */
+ writel(CQSPI_REG_INDIRECTRD_DONE,
+ priv->regbase + CQSPI_REG_INDIRECTRD);
+
+ /* Check indirect done status */
+ ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTRD,
+ CQSPI_REG_INDIRECTRD_DONE, 0, 10, 0);
+ if (ret) {
+ printf("Indirect read clear completion error (%i)\n", ret);
+ goto failrd;
+ }
+
+ /* Wait til QSPI is idle */
+ if (!cadence_qspi_wait_idle(priv->regbase))
+ return -EIO;
+
+ return 0;
+
+failrd:
+ /* Cancel the indirect read */
+ writel(CQSPI_REG_INDIRECTRD_CANCEL,
+ priv->regbase + CQSPI_REG_INDIRECTRD);
+ return ret;
+}
+
+int cadence_qspi_apb_read_execute(struct cadence_spi_priv *priv,
+ const struct spi_mem_op *op)
+{
+ u64 from = op->addr.val;
+ void *buf = op->data.buf.in;
+ size_t len = op->data.nbytes;
+
+ cadence_qspi_apb_enable_linear_mode(true);
+
+ if (priv->use_dac_mode && (from + len < priv->ahbsize)) {
+ if (len < 256 ||
+ dma_memcpy(buf, priv->ahbbase + from, len) < 0) {
+ memcpy_fromio(buf, priv->ahbbase + from, len);
+ }
+ if (!cadence_qspi_wait_idle(priv->regbase))
+ return -EIO;
+ return 0;
+ }
+
+ return cadence_qspi_apb_indirect_read_execute(priv, len, buf);
+}
+
/* Opcode + Address (3/4 bytes) */
int cadence_qspi_apb_write_setup(struct cadence_spi_priv *priv,
const struct spi_mem_op *op)