diff mbox series

driver: spi: renesas_rpc_spi: Add mem_ops

Message ID 20210322172147.12018-1-zhengxunli.mxic@gmail.com
State New
Delegated to: Marek Vasut
Headers show
Series driver: spi: renesas_rpc_spi: Add mem_ops | expand

Commit Message

Zhengxun March 22, 2021, 5:21 p.m. UTC
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: zhengxun <zhengxunli.mxic@gmail.com>
---
 drivers/spi/renesas_rpc_spi.c | 144 ++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)
diff mbox series

Patch

diff --git a/drivers/spi/renesas_rpc_spi.c b/drivers/spi/renesas_rpc_spi.c
index 26b6aa85c9..b317995dae 100644
--- a/drivers/spi/renesas_rpc_spi.c
+++ b/drivers/spi/renesas_rpc_spi.c
@@ -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 */
@@ -368,6 +369,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. */
@@ -442,10 +581,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[] = {