[PATCH] i2c: ocores: Allow endian-specific grlib accessors

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux GPIO]     [Linux SPI]     [Linux Hardward Monitoring]     [LM Sensors]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux