This patch adds an implementation of exec_op, which support octal
mode and quad mode for reading flash and support existing single
mode for reading and writing flash concurrently.

Signed-off-by: zhengxunli <[email protected]>
---
 drivers/spi/renesas_rpc_spi.c | 144 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c
index 1057651..ee8d49c 100644
--- a/drivers/spi/renesas_rpc_spi.c
+++ b/drivers/spi/renesas_rpc_spi.c
@@ -16,6 +16,7 @@
 #include <linux/bug.h>
 #include <linux/errno.h>
 #include <spi.h>
+#include <spi-mem.h>
 #include <wait_bit.h>
 
 #define RPC_CMNCR              0x0000  /* R/W */
@@ -367,6 +368,144 @@ err:
        return ret;
 }
 
+static int rpc_spi_exec_mem_op(struct spi_slave *slave,
+                              const struct spi_mem_op *op)
+{
+       struct rpc_spi_priv *priv;
+       struct udevice *bus;
+       u32 wloop = (op->data.dir == SPI_MEM_DATA_OUT) ?
+                    DIV_ROUND_UP(op->data.nbytes, 4) : 0;
+       u32 smenr, smcr, cmncr;
+       u32 *datout;
+       int ret = 0;
+
+       bus = slave->dev->parent;
+       priv = dev_get_priv(bus);
+
+       if (op->cmd.buswidth == 2 || op->addr.buswidth == 2 ||
+           op->data.buswidth == 2 || op->addr.nbytes > 4)
+               return -ENOTSUPP;
+
+       smenr = 0;
+
+       if (op->data.dir == SPI_MEM_DATA_OUT ||
+           op->data.dir == SPI_MEM_NO_DATA) {
+               rpc_spi_claim_bus(slave->dev, true);
+
+               writel(0, priv->regs + RPC_SMCR);
+
+               /* Commnad(1) */
+               writel(RPC_SMCMR_CMD(op->cmd.opcode), priv->regs + RPC_SMCMR);
+
+               smenr |= RPC_SMENR_CDE;
+
+               smenr |= RPC_SMENR_CDB(fls(op->cmd.buswidth) - 1);
+
+               /* Address(3 or 4) */
+               if (op->addr.nbytes) {
+                       writel(op->addr.val, priv->regs + RPC_SMADR);
+                       if (op->addr.nbytes == 3)
+                               smenr |= RPC_SMENR_ADE(0x7);
+                       else
+                               smenr |= RPC_SMENR_ADE(0xf);
+                       smenr |= RPC_SMENR_ADB(fls(op->addr.buswidth) - 1);
+               } else {
+                       writel(0, priv->regs + RPC_SMADR);
+               }
+
+               /* Dummy(0) */
+               writel(0, priv->regs + RPC_SMDMCR);
+
+               writel(0, priv->regs + RPC_SMOPR);
+
+               writel(0, priv->regs + RPC_SMDRENR);
+
+               /* Data(n) */
+               if (op->data.nbytes) {
+                       datout = (u32 *)op->data.buf.out;
+
+                       smenr |= RPC_SMENR_SPIDE(0xf);
+
+                       smenr |= RPC_SMENR_SPIDB(fls(op->data.buswidth) - 1);
+
+                       while (wloop--) {
+                               smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE;
+                               if (wloop >= 1)
+                                       smcr |= RPC_SMCR_SSLKP;
+                               writel(smenr, priv->regs + RPC_SMENR);
+                               writel(*datout, priv->regs + RPC_SMWDR0);
+                               writel(smcr, priv->regs + RPC_SMCR);
+                               ret = rpc_spi_wait_tend(slave->dev);
+                               if (ret)
+                                       goto err;
+                               datout++;
+                               smenr = RPC_SMENR_SPIDE(0xf);
+                       }
+
+                       ret = rpc_spi_wait_sslf(slave->dev);
+               } else {
+                       writel(smenr, priv->regs + RPC_SMENR);
+                       writel(RPC_SMCR_SPIE, priv->regs + RPC_SMCR);
+                       ret = rpc_spi_wait_tend(slave->dev);
+               }
+       } else {        /* Read data only, using DRx ext access */
+               rpc_spi_claim_bus(slave->dev, false);
+
+               if (op->cmd.buswidth > 4 || op->addr.buswidth > 4 ||
+                   op->data.buswidth > 4) {
+                       cmncr = readl(priv->regs + RPC_CMNCR);
+                       cmncr |= RPC_CMNCR_BSZ(1);
+                       writel(cmncr, priv->regs + RPC_CMNCR);
+               }
+
+               /* Command(1) */
+               writel(RPC_DRCMR_CMD(op->cmd.opcode), priv->regs + RPC_DRCMR);
+
+               smenr |= RPC_DRENR_CDE;
+
+               smenr |= RPC_DRENR_CDB(fls(op->cmd.buswidth) - 1);
+
+               /* Address(3 or 4) */
+               if (op->addr.nbytes) {
+                       if (op->addr.nbytes == 3)
+                               smenr |= RPC_DRENR_ADE(0x7);
+                       else
+                               smenr |= RPC_DRENR_ADE(0xf);
+                       smenr |= RPC_DRENR_ADB(fls(op->addr.buswidth) - 1);
+               }
+
+               /* Dummy(n) */
+               if (op->dummy.nbytes) {
+                       writel(op->dummy.nbytes, priv->regs + RPC_DRDMCR);
+                       smenr |= RPC_DRENR_DME;
+               } else {
+                       writel(0, priv->regs + RPC_DRDMCR);
+               }
+
+               writel(0, priv->regs + RPC_DROPR);
+
+               if (op->data.buswidth > 4)
+                       smenr |= RPC_DRENR_SPIDB(2);
+               else
+                       smenr |= RPC_DRENR_SPIDB(fls(op->data.buswidth) - 1);
+
+               writel(smenr, priv->regs + RPC_DRENR);
+
+               if (op->data.nbytes) {
+                       memcpy_fromio(op->data.buf.in,
+                                     (void *)(priv->extr + op->addr.val),
+                                     op->data.nbytes);
+               } else {
+                       readl(priv->extr);      /* Dummy read */
+               }
+       }
+
+err:
+       rpc_spi_release_bus(slave->dev);
+
+       return ret;
+}
+
 static int rpc_spi_set_speed(struct udevice *bus, uint speed)
 {
        /* This is a SPI NOR controller, do nothing. */
@@ -441,10 +580,15 @@ static int rpc_spi_of_to_plat(struct udevice *bus)
        return 0;
 }
 
+static const struct spi_controller_mem_ops rpc_spi_mem_ops = {
+       .exec_op = rpc_spi_exec_mem_op,
+};
+
 static const struct dm_spi_ops rpc_spi_ops = {
        .xfer           = rpc_spi_xfer,
        .set_speed      = rpc_spi_set_speed,
        .set_mode       = rpc_spi_set_mode,
+       .mem_ops        = &rpc_spi_mem_ops,
 };
 
 static const struct udevice_id rpc_spi_ids[] = {
-- 
1.9.1

Reply via email to