mtd: spi: renesas: Add 4 bytes address mode support
authorCong Dang <cong.dang.xn@renesas.com>
Wed, 24 Aug 2022 23:06:54 +0000 (06:06 +0700)
committerMarek Vasut <marek.vasut+renesas@mailbox.org>
Sat, 10 Jun 2023 09:50:45 +0000 (11:50 +0200)
This patch adds 4-byte address mode support. Because traditional access
based on FIFO/shift register, it's complex to specify information like
opcode, address length, dummy bytes etc to flash. Replace the traditional
access by spi-mem layer which is essential to make 4-byte address mode
support possible.

Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
Signed-off-by: Cong Dang <cong.dang.xn@renesas.com>
Signed-off-by: Hai Pham <hai.pham.ud@renesas.com>
Signed-off-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
drivers/spi/renesas_rpc_spi.c

index af1b368..51c37d7 100644 (file)
@@ -17,6 +17,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 */
@@ -168,10 +169,6 @@ struct rpc_spi_priv {
        fdt_addr_t      regs;
        fdt_addr_t      extr;
        struct clk      clk;
-
-       u8              cmdcopy[8];
-       u32             cmdlen;
-       bool            cmdstarted;
 };
 
 static int rpc_spi_wait_sslf(struct udevice *dev)
@@ -258,72 +255,84 @@ static int rpc_spi_release_bus(struct udevice *dev)
        return 0;
 }
 
-static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen,
-                       const void *dout, void *din, unsigned long flags)
+static int rpc_spi_mem_exec_op(struct spi_slave *spi,
+                              const struct spi_mem_op *op)
 {
-       struct udevice *bus = dev->parent;
+       struct udevice *bus = spi->dev->parent;
        struct rpc_spi_priv *priv = dev_get_priv(bus);
-       u32 wlen = dout ? (bitlen / 8) : 0;
-       u32 rlen = din ? (bitlen / 8) : 0;
-       u32 wloop = DIV_ROUND_UP(wlen, 4);
-       u32 smenr, smcr, offset;
+       const void *dout = op->data.buf.out ? op->data.buf.out : NULL;
+       void *din = op->data.buf.in ? op->data.buf.in : NULL;
        int ret = 0;
-
-       if (!priv->cmdstarted) {
-               if (!wlen || rlen)
-                       BUG();
-
-               memcpy(priv->cmdcopy, dout, wlen);
-               priv->cmdlen = wlen;
-
-               /* Command transfer start */
-               priv->cmdstarted = true;
-               if (!(flags & SPI_XFER_END))
-                       return 0;
-       }
-
-       offset = (priv->cmdcopy[1] << 16) | (priv->cmdcopy[2] << 8) |
-                (priv->cmdcopy[3] << 0);
+       u32 offset = 0;
+       u32 smenr, smcr;
 
        smenr = 0;
+       offset = op->addr.val;
+
+       switch (op->data.dir) {
+       case SPI_MEM_DATA_IN:
+               rpc_spi_claim_bus(spi->dev, false);
+
+               writel(0, priv->regs + RPC_DRCMR);
+               writel(RPC_DRCMR_CMD(op->cmd.opcode), priv->regs + RPC_DRCMR);
+               smenr |= RPC_DRENR_CDE;
+
+               writel(0, priv->regs + RPC_DREAR);
+               if (op->addr.nbytes == 4) {
+                       writel(RPC_DREAR_EAV(offset >> 25) | RPC_DREAR_EAC(1),
+                              priv->regs + RPC_DREAR);
+                       smenr |= RPC_DRENR_ADE(0xF);
+               } else if (op->addr.nbytes == 3) {
+                       smenr |= RPC_DRENR_ADE(0x7);
+               } else {
+                       smenr |= RPC_DRENR_ADE(0);
+               }
 
-       if (wlen || (!rlen && !wlen) || flags == SPI_XFER_ONCE) {
-               if (wlen && flags == SPI_XFER_END)
-                       smenr = RPC_SMENR_SPIDE(0xf);
+               writel(0, priv->regs + RPC_DRDMCR);
+               if (op->dummy.nbytes) {
+                       writel(8 * op->dummy.nbytes - 1, priv->regs + RPC_DRDMCR);
+                       smenr |= RPC_DRENR_DME;
+               }
 
-               rpc_spi_claim_bus(dev, true);
+               writel(0, priv->regs + RPC_DROPR);
+               writel(smenr, priv->regs + RPC_DRENR);
 
-               writel(0, priv->regs + RPC_SMCR);
+               memcpy_fromio(din, (void *)(priv->extr + offset), op->data.nbytes);
 
-               if (priv->cmdlen >= 1) {        /* Command(1) */
-                       writel(RPC_SMCMR_CMD(priv->cmdcopy[0]),
-                              priv->regs + RPC_SMCMR);
-                       smenr |= RPC_SMENR_CDE;
-               } else {
-                       writel(0, priv->regs + RPC_SMCMR);
-               }
+               rpc_spi_release_bus(spi->dev);
+               break;
+       case SPI_MEM_DATA_OUT:
+       case SPI_MEM_NO_DATA:
+               rpc_spi_claim_bus(spi->dev, true);
 
-               if (priv->cmdlen >= 4) {        /* Address(3) */
-                       writel(offset, priv->regs + RPC_SMADR);
-                       smenr |= RPC_SMENR_ADE(7);
-               } else {
-                       writel(0, priv->regs + RPC_SMADR);
-               }
+               writel(0, priv->regs + RPC_SMCR);
+               writel(0, priv->regs + RPC_SMCMR);
+               writel(RPC_SMCMR_CMD(op->cmd.opcode), priv->regs + RPC_SMCMR);
+               smenr |= RPC_SMENR_CDE;
+
+               writel(0, priv->regs + RPC_SMADR);
+               if (op->addr.nbytes == 4)
+                       smenr |= RPC_SMENR_ADE(0xF);
+               else if (op->addr.nbytes == 3)
+                       smenr |= RPC_SMENR_ADE(0x7);
+               else
+                       smenr |= RPC_SMENR_ADE(0);
+               writel(offset, priv->regs + RPC_SMADR);
 
-               if (priv->cmdlen >= 5) {        /* Dummy(n) */
-                       writel(8 * (priv->cmdlen - 4) - 1,
-                              priv->regs + RPC_SMDMCR);
+               writel(0, priv->regs + RPC_SMDMCR);
+               if (op->dummy.nbytes) {
+                       writel(8 * op->dummy.nbytes - 1, priv->regs + RPC_SMDMCR);
                        smenr |= RPC_SMENR_DME;
-               } else {
-                       writel(0, priv->regs + RPC_SMDMCR);
                }
 
                writel(0, priv->regs + RPC_SMOPR);
-
                writel(0, priv->regs + RPC_SMDRENR);
 
-               if (wlen && flags == SPI_XFER_END) {
+               if (dout && op->data.nbytes) {
                        u32 *datout = (u32 *)dout;
+                       u32 wloop = DIV_ROUND_UP(op->data.nbytes, 4);
+
+                       smenr |= RPC_SMENR_SPIDE(0xF);
 
                        while (wloop--) {
                                smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE;
@@ -332,57 +341,28 @@ static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen,
                                writel(smenr, priv->regs + RPC_SMENR);
                                writel(*datout, priv->regs + RPC_SMWDR0);
                                writel(smcr, priv->regs + RPC_SMCR);
-                               ret = rpc_spi_wait_tend(dev);
-                               if (ret)
-                                       goto err;
+                               ret = rpc_spi_wait_tend(spi->dev);
+                               if (ret) {
+                                       rpc_spi_release_bus(spi->dev);
+                                       return ret;
+                               }
                                datout++;
-                               smenr = RPC_SMENR_SPIDE(0xf);
+                               smenr &= (~RPC_SMENR_CDE & ~RPC_SMENR_ADE(0xF));
                        }
 
-                       ret = rpc_spi_wait_sslf(dev);
-
+                       ret = rpc_spi_wait_sslf(spi->dev);
                } else {
                        writel(smenr, priv->regs + RPC_SMENR);
                        writel(RPC_SMCR_SPIE, priv->regs + RPC_SMCR);
-                       ret = rpc_spi_wait_tend(dev);
+                       ret = rpc_spi_wait_tend(spi->dev);
                }
-       } else {        /* Read data only, using DRx ext access */
-               rpc_spi_claim_bus(dev, false);
 
-               if (priv->cmdlen >= 1) {        /* Command(1) */
-                       writel(RPC_DRCMR_CMD(priv->cmdcopy[0]),
-                              priv->regs + RPC_DRCMR);
-                       smenr |= RPC_DRENR_CDE;
-               } else {
-                       writel(0, priv->regs + RPC_DRCMR);
-               }
-
-               if (priv->cmdlen >= 4)          /* Address(3) */
-                       smenr |= RPC_DRENR_ADE(7);
-
-               if (priv->cmdlen >= 5) {        /* Dummy(n) */
-                       writel(8 * (priv->cmdlen - 4) - 1,
-                              priv->regs + RPC_DRDMCR);
-                       smenr |= RPC_DRENR_DME;
-               } else {
-                       writel(0, priv->regs + RPC_DRDMCR);
-               }
-
-               writel(0, priv->regs + RPC_DROPR);
-
-               writel(smenr, priv->regs + RPC_DRENR);
-
-               if (rlen)
-                       memcpy_fromio(din, (void *)(priv->extr + offset), rlen);
-               else
-                       readl(priv->extr);      /* Dummy read */
+               rpc_spi_release_bus(spi->dev);
+               break;
+       default:
+               break;
        }
 
-err:
-       priv->cmdstarted = false;
-
-       rpc_spi_release_bus(dev);
-
        return ret;
 }
 
@@ -398,6 +378,10 @@ static int rpc_spi_set_mode(struct udevice *bus, uint mode)
        return 0;
 }
 
+static const struct spi_controller_mem_ops rpc_spi_mem_ops = {
+       .exec_op        = rpc_spi_mem_exec_op
+};
+
 static int rpc_spi_bind(struct udevice *parent)
 {
        const void *fdt = gd->fdt_blob;
@@ -461,9 +445,9 @@ static int rpc_spi_of_to_plat(struct udevice *bus)
 }
 
 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[] = {