Unverified Commit 16667625 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

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.com


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents f47794f5 d611d7ea
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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:
+3 −2
Original line number Diff line number Diff line
@@ -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
+6 −7
Original line number Diff line number Diff line
@@ -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);
+123 −36
Original line number Diff line number Diff line
@@ -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) {
				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)
@@ -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 {
				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,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);
+7 −6
Original line number Diff line number Diff line
@@ -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
Loading