Commit 16667625 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'memory-controller-drv-5.16' of...

Merge tag 'memory-controller-drv-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl into arm/drivers

Memory controller drivers for v5.16

1. Renesas RPC: fix unaligned bus access and QSPI data transfers in
   manual modes.
2. Renesas RPC: select RESET_CONTROLLER as it is necessary for
   operation.
3. FSL IFC: fix error paths.
4. Broadcom: allow building as module.

* tag 'memory-controller-drv-5.16' of git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux-mem-ctrl:
  memory: fsl_ifc: fix leak of irq and nand_irq in fsl_ifc_ctrl_probe
  memory: renesas-rpc-if: RENESAS_RPCIF should select RESET_CONTROLLER
  memory: brcmstb_dpfe: Allow building Broadcom STB DPFE as module
  memory: samsung: describe drivers in KConfig
  memory: renesas-rpc-if: Avoid unaligned bus access for HyperFlash
  memory: renesas-rpc-if: Correct QSPI data transfer in Manual mode
  dt-bindings: rpc: renesas-rpc-if: Add support for the R8A779A0 RPC-IF

Link: https://lore.kernel.org/r/20211010175836.13302-1-krzysztof.kozlowski@canonical.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents f47794f5 d611d7ea
......@@ -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:
......
......@@ -55,8 +55,8 @@ config ATMEL_EBI
SRAMs, ATA devices, etc.
config BRCMSTB_DPFE
bool "Broadcom STB DPFE driver" if COMPILE_TEST
default y if ARCH_BRCMSTB
tristate "Broadcom STB DPFE driver"
default ARCH_BRCMSTB
depends on ARCH_BRCMSTB || COMPILE_TEST
help
This driver provides access to the DPFE interface of Broadcom
......@@ -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
......
......@@ -263,7 +263,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
ret = fsl_ifc_ctrl_init(fsl_ifc_ctrl_dev);
if (ret < 0)
goto err;
goto err_unmap_nandirq;
init_waitqueue_head(&fsl_ifc_ctrl_dev->nand_wait);
......@@ -272,7 +272,7 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->irq);
goto err_irq;
goto err_unmap_nandirq;
}
if (fsl_ifc_ctrl_dev->nand_irq) {
......@@ -281,17 +281,16 @@ static int fsl_ifc_ctrl_probe(struct platform_device *dev)
if (ret != 0) {
dev_err(&dev->dev, "failed to install irq (%d)\n",
fsl_ifc_ctrl_dev->nand_irq);
goto err_nandirq;
goto err_free_irq;
}
}
return 0;
err_nandirq:
free_irq(fsl_ifc_ctrl_dev->nand_irq, fsl_ifc_ctrl_dev);
irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
err_irq:
err_free_irq:
free_irq(fsl_ifc_ctrl_dev->irq, fsl_ifc_ctrl_dev);
err_unmap_nandirq:
irq_dispose_mapping(fsl_ifc_ctrl_dev->nand_irq);
irq_dispose_mapping(fsl_ifc_ctrl_dev->irq);
err:
iounmap(fsl_ifc_ctrl_dev->gregs);
......
......@@ -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,
......@@ -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",
......@@ -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,
......@@ -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) {
} else {
regmap_write(rpc->regmap, RPCIF_SMWDR0,
data[0]);
} else {
regmap_write(rpc->regmap, RPCIF_SMWDR0,
data[0] << 16);
}
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)
......@@ -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);
......@@ -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 {
} else {
regmap_read(rpc->regmap, RPCIF_SMRDR0,
&data[0]);
data[0] >>= 16;
}
memcpy(rpc->buffer + pos, data, nbytes);
......@@ -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);
......@@ -523,7 +607,10 @@ 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);
memcpy_fromio(buf, rpc->dirmap + from, len);
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);
......
......@@ -14,11 +14,12 @@ config EXYNOS5422_DMC
depends on DEVFREQ_GOV_SIMPLE_ONDEMAND
depends on (PM_DEVFREQ && PM_DEVFREQ_EVENT)
help
This adds driver for Exynos5422 DMC (Dynamic Memory Controller).
The driver provides support for Dynamic Voltage and Frequency Scaling in
DMC and DRAM. It also supports changing timings of DRAM running with
different frequency. The timings are calculated based on DT memory
information.
This adds driver for Samsung Exynos5422 SoC DMC (Dynamic Memory
Controller). The driver provides support for Dynamic Voltage and
Frequency Scaling in DMC and DRAM. It also supports changing timings
of DRAM running with different frequency. The timings are calculated
based on DT memory information.
If unsure, say Y on devices with Samsung Exynos SoCs.
config EXYNOS_SROM
bool "Exynos SROM controller driver" if COMPILE_TEST
......@@ -29,6 +30,6 @@ config EXYNOS_SROM
during suspend. If however appropriate device tree configuration
is provided, the driver enables support for external memory
or external devices.
If unsure, say Y on devices with Samsung Exynos SocS.
If unsure, say Y on devices with Samsung Exynos SoCs.
endif
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment