Merge branch 'next_mtd/rpc-spi' of https://source.denx.de/u-boot/custodians/u-boot-sh into next

This commit is contained in:
Tom Rini 2023-06-10 14:07:49 -04:00
commit cc5a940923
2 changed files with 105 additions and 103 deletions

View file

@ -382,7 +382,7 @@ config SPI_QUP
config RENESAS_RPC_SPI config RENESAS_RPC_SPI
bool "Renesas RPC SPI driver" bool "Renesas RPC SPI driver"
depends on RCAR_64 || RZA1 depends on RCAR_64 || RZA1
imply SPI_FLASH_BAR imply SPI_FLASH_SFDP_SUPPORT
help help
Enable the Renesas RPC SPI driver, used to access SPI NOR flash Enable the Renesas RPC SPI driver, used to access SPI NOR flash
on Renesas RCar Gen3 SoCs. This uses driver model and requires a on Renesas RCar Gen3 SoCs. This uses driver model and requires a

View file

@ -17,6 +17,7 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <spi.h> #include <spi.h>
#include <spi-mem.h>
#include <wait_bit.h> #include <wait_bit.h>
#define RPC_CMNCR 0x0000 /* R/W */ #define RPC_CMNCR 0x0000 /* R/W */
@ -140,6 +141,7 @@
#define PRC_PHYCNT_EXDS BIT(21) #define PRC_PHYCNT_EXDS BIT(21)
#define RPC_PHYCNT_OCT BIT(20) #define RPC_PHYCNT_OCT BIT(20)
#define RPC_PHYCNT_STRTIM(v) (((v) & 0x7) << 15) #define RPC_PHYCNT_STRTIM(v) (((v) & 0x7) << 15)
#define RPC_PHYCNT_STRTIM2(v) ((((v) & 0x7) << 15) | (((v) & 0x8) << 24))
#define RPC_PHYCNT_WBUF2 BIT(4) #define RPC_PHYCNT_WBUF2 BIT(4)
#define RPC_PHYCNT_WBUF BIT(2) #define RPC_PHYCNT_WBUF BIT(2)
#define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0) #define RPC_PHYCNT_MEM(v) (((v) & 0x3) << 0)
@ -167,10 +169,6 @@ struct rpc_spi_priv {
fdt_addr_t regs; fdt_addr_t regs;
fdt_addr_t extr; fdt_addr_t extr;
struct clk clk; struct clk clk;
u8 cmdcopy[8];
u32 cmdlen;
bool cmdstarted;
}; };
static int rpc_spi_wait_sslf(struct udevice *dev) static int rpc_spi_wait_sslf(struct udevice *dev)
@ -202,18 +200,35 @@ static void rpc_spi_flush_read_cache(struct udevice *dev)
} }
static u32 rpc_spi_get_strobe_delay(void)
{
#ifndef CONFIG_RZA1
u32 cpu_type = rmobile_get_cpu_type();
/*
* NOTE: RPC_PHYCNT_STRTIM value:
* 0: On H3 ES1.x (not supported in mainline U-Boot)
* 6: On M3 ES1.x
* 7: On other R-Car Gen3
* 15: On R-Car Gen4
*/
if (cpu_type == RMOBILE_CPU_TYPE_R8A7796 && rmobile_get_cpu_rev_integer() == 1)
return RPC_PHYCNT_STRTIM(6);
else if (cpu_type == RMOBILE_CPU_TYPE_R8A779F0 ||
cpu_type == RMOBILE_CPU_TYPE_R8A779G0)
return RPC_PHYCNT_STRTIM2(15);
else
#endif
return RPC_PHYCNT_STRTIM(7);
}
static int rpc_spi_claim_bus(struct udevice *dev, bool manual) static int rpc_spi_claim_bus(struct udevice *dev, bool manual)
{ {
struct udevice *bus = dev->parent; struct udevice *bus = dev->parent;
struct rpc_spi_priv *priv = dev_get_priv(bus); struct rpc_spi_priv *priv = dev_get_priv(bus);
/* /* NOTE: The 0x260 are undocumented bits, but they must be set. */
* NOTE: The 0x260 are undocumented bits, but they must be set. writel(RPC_PHYCNT_CAL | rpc_spi_get_strobe_delay() | 0x260,
* NOTE: On H3 ES1.x (not supported in mainline U-Boot), the
* RPC_PHYCNT_STRTIM shall be 0, while on newer parts, the
* RPC_PHYCNT_STRTIM shall be 6.
*/
writel(RPC_PHYCNT_CAL | RPC_PHYCNT_STRTIM(6) | 0x260,
priv->regs + RPC_PHYCNT); priv->regs + RPC_PHYCNT);
writel((manual ? RPC_CMNCR_MD : 0) | RPC_CMNCR_SFDE | writel((manual ? RPC_CMNCR_MD : 0) | RPC_CMNCR_SFDE |
RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | RPC_CMNCR_BSZ(0), RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ | RPC_CMNCR_BSZ(0),
@ -233,79 +248,91 @@ static int rpc_spi_release_bus(struct udevice *dev)
struct rpc_spi_priv *priv = dev_get_priv(bus); struct rpc_spi_priv *priv = dev_get_priv(bus);
/* NOTE: The 0x260 are undocumented bits, but they must be set. */ /* NOTE: The 0x260 are undocumented bits, but they must be set. */
writel(RPC_PHYCNT_STRTIM(6) | 0x260, priv->regs + RPC_PHYCNT); writel(rpc_spi_get_strobe_delay() | 0x260, priv->regs + RPC_PHYCNT);
rpc_spi_flush_read_cache(dev); rpc_spi_flush_read_cache(dev);
return 0; return 0;
} }
static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen, static int rpc_spi_mem_exec_op(struct spi_slave *spi,
const void *dout, void *din, unsigned long flags) 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); struct rpc_spi_priv *priv = dev_get_priv(bus);
u32 wlen = dout ? (bitlen / 8) : 0; const void *dout = op->data.buf.out ? op->data.buf.out : NULL;
u32 rlen = din ? (bitlen / 8) : 0; void *din = op->data.buf.in ? op->data.buf.in : NULL;
u32 wloop = DIV_ROUND_UP(wlen, 4);
u32 smenr, smcr, offset;
int ret = 0; int ret = 0;
u32 offset = 0;
if (!priv->cmdstarted) { u32 smenr, smcr;
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);
smenr = 0; smenr = 0;
offset = op->addr.val;
if (wlen || (!rlen && !wlen) || flags == SPI_XFER_ONCE) { switch (op->data.dir) {
if (wlen && flags == SPI_XFER_END) case SPI_MEM_DATA_IN:
smenr = RPC_SMENR_SPIDE(0xf); rpc_spi_claim_bus(spi->dev, false);
rpc_spi_claim_bus(dev, true); 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);
}
writel(0, priv->regs + RPC_DRDMCR);
if (op->dummy.nbytes) {
writel(8 * op->dummy.nbytes - 1, priv->regs + RPC_DRDMCR);
smenr |= RPC_DRENR_DME;
}
writel(0, priv->regs + RPC_DROPR);
writel(smenr, priv->regs + RPC_DRENR);
memcpy_fromio(din, (void *)(priv->extr + offset), op->data.nbytes);
rpc_spi_release_bus(spi->dev);
break;
case SPI_MEM_DATA_OUT:
case SPI_MEM_NO_DATA:
rpc_spi_claim_bus(spi->dev, true);
writel(0, priv->regs + RPC_SMCR); 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;
if (priv->cmdlen >= 1) { /* Command(1) */ writel(0, priv->regs + RPC_SMADR);
writel(RPC_SMCMR_CMD(priv->cmdcopy[0]), if (op->addr.nbytes == 4)
priv->regs + RPC_SMCMR); smenr |= RPC_SMENR_ADE(0xF);
smenr |= RPC_SMENR_CDE; else if (op->addr.nbytes == 3)
} else { smenr |= RPC_SMENR_ADE(0x7);
writel(0, priv->regs + RPC_SMCMR); else
} smenr |= RPC_SMENR_ADE(0);
writel(offset, priv->regs + RPC_SMADR);
if (priv->cmdlen >= 4) { /* Address(3) */ writel(0, priv->regs + RPC_SMDMCR);
writel(offset, priv->regs + RPC_SMADR); if (op->dummy.nbytes) {
smenr |= RPC_SMENR_ADE(7); writel(8 * op->dummy.nbytes - 1, priv->regs + RPC_SMDMCR);
} else {
writel(0, priv->regs + RPC_SMADR);
}
if (priv->cmdlen >= 5) { /* Dummy(n) */
writel(8 * (priv->cmdlen - 4) - 1,
priv->regs + RPC_SMDMCR);
smenr |= RPC_SMENR_DME; smenr |= RPC_SMENR_DME;
} else {
writel(0, priv->regs + RPC_SMDMCR);
} }
writel(0, priv->regs + RPC_SMOPR); writel(0, priv->regs + RPC_SMOPR);
writel(0, priv->regs + RPC_SMDRENR); writel(0, priv->regs + RPC_SMDRENR);
if (wlen && flags == SPI_XFER_END) { if (dout && op->data.nbytes) {
u32 *datout = (u32 *)dout; u32 *datout = (u32 *)dout;
u32 wloop = DIV_ROUND_UP(op->data.nbytes, 4);
smenr |= RPC_SMENR_SPIDE(0xF);
while (wloop--) { while (wloop--) {
smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE; smcr = RPC_SMCR_SPIWE | RPC_SMCR_SPIE;
@ -314,57 +341,28 @@ static int rpc_spi_xfer(struct udevice *dev, unsigned int bitlen,
writel(smenr, priv->regs + RPC_SMENR); writel(smenr, priv->regs + RPC_SMENR);
writel(*datout, priv->regs + RPC_SMWDR0); writel(*datout, priv->regs + RPC_SMWDR0);
writel(smcr, priv->regs + RPC_SMCR); writel(smcr, priv->regs + RPC_SMCR);
ret = rpc_spi_wait_tend(dev); ret = rpc_spi_wait_tend(spi->dev);
if (ret) if (ret) {
goto err; rpc_spi_release_bus(spi->dev);
return ret;
}
datout++; 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 { } else {
writel(smenr, priv->regs + RPC_SMENR); writel(smenr, priv->regs + RPC_SMENR);
writel(RPC_SMCR_SPIE, priv->regs + RPC_SMCR); 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) */ rpc_spi_release_bus(spi->dev);
smenr |= RPC_DRENR_ADE(7); break;
default:
if (priv->cmdlen >= 5) { /* Dummy(n) */ break;
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 */
} }
err:
priv->cmdstarted = false;
rpc_spi_release_bus(dev);
return ret; return ret;
} }
@ -380,6 +378,10 @@ static int rpc_spi_set_mode(struct udevice *bus, uint mode)
return 0; 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) static int rpc_spi_bind(struct udevice *parent)
{ {
const void *fdt = gd->fdt_blob; const void *fdt = gd->fdt_blob;
@ -443,9 +445,9 @@ static int rpc_spi_of_to_plat(struct udevice *bus)
} }
static const struct dm_spi_ops rpc_spi_ops = { static const struct dm_spi_ops rpc_spi_ops = {
.xfer = rpc_spi_xfer,
.set_speed = rpc_spi_set_speed, .set_speed = rpc_spi_set_speed,
.set_mode = rpc_spi_set_mode, .set_mode = rpc_spi_set_mode,
.mem_ops = &rpc_spi_mem_ops
}; };
static const struct udevice_id rpc_spi_ids[] = { static const struct udevice_id rpc_spi_ids[] = {