diff options
Diffstat (limited to 'drivers/memory/renesas-rpc-if.c')
| -rw-r--r-- | drivers/memory/renesas-rpc-if.c | 159 | 
1 files changed, 123 insertions, 36 deletions
diff --git a/drivers/memory/renesas-rpc-if.c b/drivers/memory/renesas-rpc-if.c index 45eed659b0c6..7435baad0007 100644 --- a/drivers/memory/renesas-rpc-if.c +++ b/drivers/memory/renesas-rpc-if.c @@ -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 @@ err_out:  }  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);  |