Due to inconsistent/broken HW, SW may need to set the appropriate endianess of the grlib accessors (instead of defaulting to big endian). This patch adds such a capability. Signed-off-by: Mohammed Billoo <mab@xxxxxxxxxxxx> --- drivers/i2c/busses/i2c-ocores.c | 55 +++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index f5fc75b65a19..2ef735f8c71f 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -488,11 +488,12 @@ MODULE_DEVICE_TABLE(of, ocores_i2c_match); #ifdef CONFIG_OF /* - * Read and write functions for the GRLIB port of the controller. Registers are - * 32-bit big endian and the PRELOW and PREHIGH registers are merged into one + * Read and write functions for the GRLIB port of the controller. Unfortunately, + * do to some broken/inconsistent HW, SW may need to account for different + * endianess of GRLIB. PRELOW and PREHIGH registers are merged into one * register. The subsequent registers have their offsets decreased accordingly. */ -static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) +static u8 oc_getreg_grlib_be(struct ocores_i2c *i2c, int reg) { u32 rd; int rreg = reg; @@ -506,7 +507,21 @@ static u8 oc_getreg_grlib(struct ocores_i2c *i2c, int reg) return (u8)rd; } -static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) +static u8 oc_getreg_grlib_le(struct ocores_i2c *i2c, int reg) +{ + u32 rd; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + rd = ioread32(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PREHIGH) + return (u8)(rd >> 8); + else + return (u8)rd; +} + +static void oc_setreg_grlib_be(struct ocores_i2c *i2c, int reg, u8 value) { u32 curr, wr; int rreg = reg; @@ -525,6 +540,25 @@ static void oc_setreg_grlib(struct ocores_i2c *i2c, int reg, u8 value) iowrite32be(wr, i2c->base + (rreg << i2c->reg_shift)); } +static void oc_setreg_grlib_le(struct ocores_i2c *i2c, int reg, u8 value) +{ + u32 curr, wr; + int rreg = reg; + + if (reg != OCI2C_PRELOW) + rreg--; + if (reg == OCI2C_PRELOW || reg == OCI2C_PREHIGH) { + curr = ioread32(i2c->base + (rreg << i2c->reg_shift)); + if (reg == OCI2C_PRELOW) + wr = (curr & 0xff00) | value; + else + wr = (((u32)value) << 8) | (curr & 0xff); + } else { + wr = value; + } + iowrite32(wr, i2c->base + (rreg << i2c->reg_shift)); +} + static int ocores_i2c_of_probe(struct platform_device *pdev, struct ocores_i2c *i2c) { @@ -592,8 +626,17 @@ static int ocores_i2c_of_probe(struct platform_device *pdev, match = of_match_node(ocores_i2c_match, pdev->dev.of_node); if (match && (long)match->data == TYPE_GRLIB) { dev_dbg(&pdev->dev, "GRLIB variant of i2c-ocores\n"); - i2c->setreg = oc_setreg_grlib; - i2c->getreg = oc_getreg_grlib; + /* + * This is a workaround for inconsistent/broken HW, + * where SW has to set the appropriate endianess + */ + if (of_device_is_big_endian(pdev->dev.of_node)) { + i2c->setreg = oc_setreg_grlib_be; + i2c->getreg = oc_getreg_grlib_be; + } else { + i2c->setreg = oc_setreg_grlib_le; + i2c->getreg = oc_getreg_grlib_le; + } } return 0; -- 2.17.1