@@ -160,10 +160,62 @@ 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 +225,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",
@@ -356,20 +406,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,
@@ -380,37 +426,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)
@@ -450,14 +495,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);
@@ -465,18 +512,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);
@@ -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;