Commit d611d7ea authored by Krzysztof Kozlowski's avatar Krzysztof Kozlowski
Browse files

Merge branch 'for-v5.16/renesas-rpc' into mem-ctrl-next

parents 4ed2f354 4a26df8e
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:
+1 −0
Original line number Diff line number Diff line
@@ -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
+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);
+1 −0
Original line number Diff line number Diff line
@@ -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;