[PATCH 4/4] driver: spi: renesas_rpc_spi: Add mem_ops
zhengxunli at mxic.com.tw
zhengxunli at mxic.com.tw
Fri Nov 27 10:03:15 CET 2020
From: zhengxun <zhengxunli at mxic.com.tw>
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 at mxic.com.tw>
---
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 d0ff918af8..4424cf0209 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_ofdata_to_platdata(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[] = {
--
2.17.1
More information about the U-Boot
mailing list