[PATCH] driver: spi: renesas_rpc_spi: Add mem_ops

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 --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[] = {
participants (1)
-
zhengxun