Loading Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +1 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ properties: - renesas,r8a77970-rpc-if # R-Car V3M - renesas,r8a77980-rpc-if # R-Car V3H - renesas,r8a77995-rpc-if # R-Car D3 - renesas,r8a779a0-rpc-if # R-Car V3U - const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2 device reg: Loading drivers/memory/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ config RENESAS_RPCIF tristate "Renesas RPC-IF driver" depends on ARCH_RENESAS || COMPILE_TEST select REGMAP_MMIO select RESET_CONTROLLER help This supports Renesas R-Car Gen3 or RZ/G2 RPC-IF which provides either SPI host or HyperFlash. You'll have to select individual Loading drivers/memory/renesas-rpc-if.c +123 −36 Original line number Diff line number Diff line Loading @@ -160,10 +160,61 @@ static const struct regmap_access_table rpcif_volatile_table = { .n_yes_ranges = ARRAY_SIZE(rpcif_volatile_ranges), }; /* * Custom accessor functions to ensure SMRDR0 and SMWDR0 are always accessed * with proper width. Requires SMENR_SPIDE to be correctly set before! */ static int rpcif_reg_read(void *context, unsigned int reg, unsigned int *val) { struct rpcif *rpc = context; if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) { u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF); if (spide == 0x8) { *val = readb(rpc->base + reg); return 0; } else if (spide == 0xC) { *val = readw(rpc->base + reg); return 0; } else if (spide != 0xF) { return -EILSEQ; } } *val = readl(rpc->base + reg); return 0; } static int rpcif_reg_write(void *context, unsigned int reg, unsigned int val) { struct rpcif *rpc = context; if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) { u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF); if (spide == 0x8) { writeb(val, rpc->base + reg); return 0; } else if (spide == 0xC) { writew(val, rpc->base + reg); return 0; } else if (spide != 0xF) { return -EILSEQ; } } writel(val, rpc->base + reg); return 0; } static const struct regmap_config rpcif_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .reg_read = rpcif_reg_read, .reg_write = rpcif_reg_write, .fast_io = true, .max_register = RPCIF_PHYINT, .volatile_table = &rpcif_volatile_table, Loading @@ -173,17 +224,15 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct resource *res; void __iomem *base; rpc->dev = dev; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); rpc->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(rpc->base)) return PTR_ERR(rpc->base); rpc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &rpcif_regmap_config); rpc->regmap = devm_regmap_init(&pdev->dev, NULL, rpc, &rpcif_regmap_config); if (IS_ERR(rpc->regmap)) { dev_err(&pdev->dev, "failed to init regmap for rpcif, error %ld\n", Loading Loading @@ -354,20 +403,16 @@ void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs, nbytes = op->data.nbytes; rpc->xferlen = nbytes; rpc->enable |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)) | RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth)); rpc->enable |= RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth)); } } EXPORT_SYMBOL(rpcif_prepare); int rpcif_manual_xfer(struct rpcif *rpc) { u32 smenr, smcr, pos = 0, max = 4; u32 smenr, smcr, pos = 0, max = rpc->bus_size == 2 ? 8 : 4; int ret = 0; if (rpc->bus_size == 2) max = 8; pm_runtime_get_sync(rpc->dev); regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, Loading @@ -378,37 +423,36 @@ int rpcif_manual_xfer(struct rpcif *rpc) regmap_write(rpc->regmap, RPCIF_SMOPR, rpc->option); regmap_write(rpc->regmap, RPCIF_SMDMCR, rpc->dummy); regmap_write(rpc->regmap, RPCIF_SMDRENR, rpc->ddr); regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr); smenr = rpc->enable; switch (rpc->dir) { case RPCIF_DATA_OUT: while (pos < rpc->xferlen) { u32 nbytes = rpc->xferlen - pos; u32 data[2]; u32 bytes_left = rpc->xferlen - pos; u32 nbytes, data[2]; smcr = rpc->smcr | RPCIF_SMCR_SPIE; if (nbytes > max) { nbytes = max; /* nbytes may only be 1, 2, 4, or 8 */ nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left)); if (bytes_left > nbytes) smcr |= RPCIF_SMCR_SSLKP; } smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); memcpy(data, rpc->buffer + pos, nbytes); if (nbytes > 4) { if (nbytes == 8) { regmap_write(rpc->regmap, RPCIF_SMWDR1, data[0]); regmap_write(rpc->regmap, RPCIF_SMWDR0, data[1]); } else if (nbytes > 2) { regmap_write(rpc->regmap, RPCIF_SMWDR0, data[0]); } else { regmap_write(rpc->regmap, RPCIF_SMWDR0, data[0] << 16); data[0]); } regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr + pos); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); regmap_write(rpc->regmap, RPCIF_SMCR, smcr); ret = wait_msg_xfer_end(rpc); if (ret) Loading Loading @@ -448,14 +492,16 @@ int rpcif_manual_xfer(struct rpcif *rpc) break; } while (pos < rpc->xferlen) { u32 nbytes = rpc->xferlen - pos; u32 data[2]; u32 bytes_left = rpc->xferlen - pos; u32 nbytes, data[2]; if (nbytes > max) nbytes = max; /* nbytes may only be 1, 2, 4, or 8 */ nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left)); regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr + pos); smenr &= ~RPCIF_SMENR_SPIDE(0xF); smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); regmap_write(rpc->regmap, RPCIF_SMCR, rpc->smcr | RPCIF_SMCR_SPIE); Loading @@ -463,18 +509,14 @@ int rpcif_manual_xfer(struct rpcif *rpc) if (ret) goto err_out; if (nbytes > 4) { if (nbytes == 8) { regmap_read(rpc->regmap, RPCIF_SMRDR1, &data[0]); regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[1]); } else if (nbytes > 2) { regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[0]); } else { regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[0]); data[0] >>= 16; } memcpy(rpc->buffer + pos, data, nbytes); Loading Loading @@ -502,6 +544,48 @@ int rpcif_manual_xfer(struct rpcif *rpc) } EXPORT_SYMBOL(rpcif_manual_xfer); static void memcpy_fromio_readw(void *to, const void __iomem *from, size_t count) { const int maxw = (IS_ENABLED(CONFIG_64BIT)) ? 8 : 4; u8 buf[2]; if (count && ((unsigned long)from & 1)) { *(u16 *)buf = __raw_readw((void __iomem *)((unsigned long)from & ~1)); *(u8 *)to = buf[1]; from++; to++; count--; } while (count >= 2 && !IS_ALIGNED((unsigned long)from, maxw)) { *(u16 *)to = __raw_readw(from); from += 2; to += 2; count -= 2; } while (count >= maxw) { #ifdef CONFIG_64BIT *(u64 *)to = __raw_readq(from); #else *(u32 *)to = __raw_readl(from); #endif from += maxw; to += maxw; count -= maxw; } while (count >= 2) { *(u16 *)to = __raw_readw(from); from += 2; to += 2; count -= 2; } if (count) { *(u16 *)buf = __raw_readw(from); *(u8 *)to = buf[0]; } } ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf) { loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1); Loading @@ -523,6 +607,9 @@ ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf) regmap_write(rpc->regmap, RPCIF_DRDMCR, rpc->dummy); regmap_write(rpc->regmap, RPCIF_DRDRENR, rpc->ddr); if (rpc->bus_size == 2) memcpy_fromio_readw(buf, rpc->dirmap + from, len); else memcpy_fromio(buf, rpc->dirmap + from, len); pm_runtime_put(rpc->dev); Loading include/memory/renesas-rpc-if.h +1 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ struct rpcif_op { struct rpcif { struct device *dev; void __iomem *base; void __iomem *dirmap; struct regmap *regmap; struct reset_control *rstc; Loading Loading
Documentation/devicetree/bindings/memory-controllers/renesas,rpc-if.yaml +1 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ properties: - renesas,r8a77970-rpc-if # R-Car V3M - renesas,r8a77980-rpc-if # R-Car V3H - renesas,r8a77995-rpc-if # R-Car D3 - renesas,r8a779a0-rpc-if # R-Car V3U - const: renesas,rcar-gen3-rpc-if # a generic R-Car gen3 or RZ/G2 device reg: Loading
drivers/memory/Kconfig +1 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ config RENESAS_RPCIF tristate "Renesas RPC-IF driver" depends on ARCH_RENESAS || COMPILE_TEST select REGMAP_MMIO select RESET_CONTROLLER help This supports Renesas R-Car Gen3 or RZ/G2 RPC-IF which provides either SPI host or HyperFlash. You'll have to select individual Loading
drivers/memory/renesas-rpc-if.c +123 −36 Original line number Diff line number Diff line Loading @@ -160,10 +160,61 @@ static const struct regmap_access_table rpcif_volatile_table = { .n_yes_ranges = ARRAY_SIZE(rpcif_volatile_ranges), }; /* * Custom accessor functions to ensure SMRDR0 and SMWDR0 are always accessed * with proper width. Requires SMENR_SPIDE to be correctly set before! */ static int rpcif_reg_read(void *context, unsigned int reg, unsigned int *val) { struct rpcif *rpc = context; if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) { u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF); if (spide == 0x8) { *val = readb(rpc->base + reg); return 0; } else if (spide == 0xC) { *val = readw(rpc->base + reg); return 0; } else if (spide != 0xF) { return -EILSEQ; } } *val = readl(rpc->base + reg); return 0; } static int rpcif_reg_write(void *context, unsigned int reg, unsigned int val) { struct rpcif *rpc = context; if (reg == RPCIF_SMRDR0 || reg == RPCIF_SMWDR0) { u32 spide = readl(rpc->base + RPCIF_SMENR) & RPCIF_SMENR_SPIDE(0xF); if (spide == 0x8) { writeb(val, rpc->base + reg); return 0; } else if (spide == 0xC) { writew(val, rpc->base + reg); return 0; } else if (spide != 0xF) { return -EILSEQ; } } writel(val, rpc->base + reg); return 0; } static const struct regmap_config rpcif_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, .reg_read = rpcif_reg_read, .reg_write = rpcif_reg_write, .fast_io = true, .max_register = RPCIF_PHYINT, .volatile_table = &rpcif_volatile_table, Loading @@ -173,17 +224,15 @@ int rpcif_sw_init(struct rpcif *rpc, struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct resource *res; void __iomem *base; rpc->dev = dev; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); rpc->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(rpc->base)) return PTR_ERR(rpc->base); rpc->regmap = devm_regmap_init_mmio(&pdev->dev, base, &rpcif_regmap_config); rpc->regmap = devm_regmap_init(&pdev->dev, NULL, rpc, &rpcif_regmap_config); if (IS_ERR(rpc->regmap)) { dev_err(&pdev->dev, "failed to init regmap for rpcif, error %ld\n", Loading Loading @@ -354,20 +403,16 @@ void rpcif_prepare(struct rpcif *rpc, const struct rpcif_op *op, u64 *offs, nbytes = op->data.nbytes; rpc->xferlen = nbytes; rpc->enable |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)) | RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth)); rpc->enable |= RPCIF_SMENR_SPIDB(rpcif_bit_size(op->data.buswidth)); } } EXPORT_SYMBOL(rpcif_prepare); int rpcif_manual_xfer(struct rpcif *rpc) { u32 smenr, smcr, pos = 0, max = 4; u32 smenr, smcr, pos = 0, max = rpc->bus_size == 2 ? 8 : 4; int ret = 0; if (rpc->bus_size == 2) max = 8; pm_runtime_get_sync(rpc->dev); regmap_update_bits(rpc->regmap, RPCIF_PHYCNT, Loading @@ -378,37 +423,36 @@ int rpcif_manual_xfer(struct rpcif *rpc) regmap_write(rpc->regmap, RPCIF_SMOPR, rpc->option); regmap_write(rpc->regmap, RPCIF_SMDMCR, rpc->dummy); regmap_write(rpc->regmap, RPCIF_SMDRENR, rpc->ddr); regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr); smenr = rpc->enable; switch (rpc->dir) { case RPCIF_DATA_OUT: while (pos < rpc->xferlen) { u32 nbytes = rpc->xferlen - pos; u32 data[2]; u32 bytes_left = rpc->xferlen - pos; u32 nbytes, data[2]; smcr = rpc->smcr | RPCIF_SMCR_SPIE; if (nbytes > max) { nbytes = max; /* nbytes may only be 1, 2, 4, or 8 */ nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left)); if (bytes_left > nbytes) smcr |= RPCIF_SMCR_SSLKP; } smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); memcpy(data, rpc->buffer + pos, nbytes); if (nbytes > 4) { if (nbytes == 8) { regmap_write(rpc->regmap, RPCIF_SMWDR1, data[0]); regmap_write(rpc->regmap, RPCIF_SMWDR0, data[1]); } else if (nbytes > 2) { regmap_write(rpc->regmap, RPCIF_SMWDR0, data[0]); } else { regmap_write(rpc->regmap, RPCIF_SMWDR0, data[0] << 16); data[0]); } regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr + pos); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); regmap_write(rpc->regmap, RPCIF_SMCR, smcr); ret = wait_msg_xfer_end(rpc); if (ret) Loading Loading @@ -448,14 +492,16 @@ int rpcif_manual_xfer(struct rpcif *rpc) break; } while (pos < rpc->xferlen) { u32 nbytes = rpc->xferlen - pos; u32 data[2]; u32 bytes_left = rpc->xferlen - pos; u32 nbytes, data[2]; if (nbytes > max) nbytes = max; /* nbytes may only be 1, 2, 4, or 8 */ nbytes = bytes_left >= max ? max : (1 << ilog2(bytes_left)); regmap_write(rpc->regmap, RPCIF_SMADR, rpc->smadr + pos); smenr &= ~RPCIF_SMENR_SPIDE(0xF); smenr |= RPCIF_SMENR_SPIDE(rpcif_bits_set(rpc, nbytes)); regmap_write(rpc->regmap, RPCIF_SMENR, smenr); regmap_write(rpc->regmap, RPCIF_SMCR, rpc->smcr | RPCIF_SMCR_SPIE); Loading @@ -463,18 +509,14 @@ int rpcif_manual_xfer(struct rpcif *rpc) if (ret) goto err_out; if (nbytes > 4) { if (nbytes == 8) { regmap_read(rpc->regmap, RPCIF_SMRDR1, &data[0]); regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[1]); } else if (nbytes > 2) { regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[0]); } else { regmap_read(rpc->regmap, RPCIF_SMRDR0, &data[0]); data[0] >>= 16; } memcpy(rpc->buffer + pos, data, nbytes); Loading Loading @@ -502,6 +544,48 @@ int rpcif_manual_xfer(struct rpcif *rpc) } EXPORT_SYMBOL(rpcif_manual_xfer); static void memcpy_fromio_readw(void *to, const void __iomem *from, size_t count) { const int maxw = (IS_ENABLED(CONFIG_64BIT)) ? 8 : 4; u8 buf[2]; if (count && ((unsigned long)from & 1)) { *(u16 *)buf = __raw_readw((void __iomem *)((unsigned long)from & ~1)); *(u8 *)to = buf[1]; from++; to++; count--; } while (count >= 2 && !IS_ALIGNED((unsigned long)from, maxw)) { *(u16 *)to = __raw_readw(from); from += 2; to += 2; count -= 2; } while (count >= maxw) { #ifdef CONFIG_64BIT *(u64 *)to = __raw_readq(from); #else *(u32 *)to = __raw_readl(from); #endif from += maxw; to += maxw; count -= maxw; } while (count >= 2) { *(u16 *)to = __raw_readw(from); from += 2; to += 2; count -= 2; } if (count) { *(u16 *)buf = __raw_readw(from); *(u8 *)to = buf[0]; } } ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf) { loff_t from = offs & (RPCIF_DIRMAP_SIZE - 1); Loading @@ -523,6 +607,9 @@ ssize_t rpcif_dirmap_read(struct rpcif *rpc, u64 offs, size_t len, void *buf) regmap_write(rpc->regmap, RPCIF_DRDMCR, rpc->dummy); regmap_write(rpc->regmap, RPCIF_DRDRENR, rpc->ddr); if (rpc->bus_size == 2) memcpy_fromio_readw(buf, rpc->dirmap + from, len); else memcpy_fromio(buf, rpc->dirmap + from, len); pm_runtime_put(rpc->dev); Loading
include/memory/renesas-rpc-if.h +1 −0 Original line number Diff line number Diff line Loading @@ -59,6 +59,7 @@ struct rpcif_op { struct rpcif { struct device *dev; void __iomem *base; void __iomem *dirmap; struct regmap *regmap; struct reset_control *rstc; Loading