[3/3] i2c: ocores: Add support for polling interrupt status

Message ID 1544105543-18578-4-git-send-email-andrew@lunn.ch
State New
Headers show
Series
  • i2c-ocores: Add IO mapped polled support
Related show

Commit Message

Andrew Lunn Dec. 6, 2018, 2:12 p.m.
Not all implementations of the OCORES i2c bus master support
generating interrupts. Add support for polling the interrupt status
bit when no interrupt is defined.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/i2c/busses/i2c-ocores.c | 64 ++++++++++++++++++++++++++-------
 1 file changed, 51 insertions(+), 13 deletions(-)

Patch

diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index d2730efe6bec..9576e842e557 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -13,6 +13,7 @@ 
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -40,10 +41,14 @@  struct ocores_i2c {
 	struct clk *clk;
 	int ip_clock_khz;
 	int bus_clock_khz;
+	int flags;
 	void (*setreg)(struct ocores_i2c *i2c, int reg, u8 value);
 	u8 (*getreg)(struct ocores_i2c *i2c, int reg);
 };
 
+/* flags */
+#define I2C_FLAG_NOIRQ		BIT(0)
+
 /* registers */
 #define OCI2C_PRELOW		0
 #define OCI2C_PREHIGH		1
@@ -224,6 +229,27 @@  static irqreturn_t ocores_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void ocores_poll_irq(struct ocores_i2c *i2c)
+{
+	int timeout = 500;
+	u8 stat;
+
+	do {
+		/* delay 2-3 SCL (5-7usec for 400KHz) */
+		usleep_range(5, 7);
+		stat = oc_getreg(i2c, OCI2C_STATUS);
+	} while ((stat & OCI2C_STAT_TIP) && timeout--);
+
+
+	if (!(stat & OCI2C_STAT_TIP)) {
+		ocores_process(i2c);
+	} else {
+		i2c->state = STATE_ERROR;
+		dev_dbg(&i2c->adap.dev, "Timeout while polling %02x\n", stat);
+		oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_STOP);
+	}
+}
+
 static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 {
 	struct ocores_i2c *i2c = i2c_get_adapdata(adap);
@@ -236,11 +262,18 @@  static int ocores_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
 	oc_setreg(i2c, OCI2C_DATA, i2c_8bit_addr_from_msg(i2c->msg));
 	oc_setreg(i2c, OCI2C_CMD, OCI2C_CMD_START);
 
-	if (wait_event_timeout(i2c->wait, (i2c->state == STATE_ERROR) ||
-			       (i2c->state == STATE_DONE), HZ))
+	if (i2c->flags & I2C_FLAG_NOIRQ) {
+		while (!((i2c->state == STATE_DONE) ||
+			 (i2c->state == STATE_ERROR)))
+			ocores_poll_irq(i2c);
 		return (i2c->state == STATE_DONE) ? num : -EIO;
-	else
-		return -ETIMEDOUT;
+	} else {
+		if (wait_event_timeout(i2c->wait,
+				       (i2c->state == STATE_ERROR) ||
+				       (i2c->state == STATE_DONE), HZ))
+			return (i2c->state == STATE_DONE) ? num : -EIO;
+	}
+	return -ETIMEDOUT;
 }
 
 static int ocores_init(struct device *dev, struct ocores_i2c *i2c)
@@ -425,14 +458,17 @@  static int ocores_i2c_probe(struct platform_device *pdev)
 	int ret;
 	int i;
 
-	irq = platform_get_irq(pdev, 0);
-	if (irq < 0)
-		return irq;
-
 	i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);
 	if (!i2c)
 		return -ENOMEM;
 
+	irq = platform_get_irq(pdev, 0);
+	if (irq == -ENXIO)
+		i2c->flags |= I2C_FLAG_NOIRQ;
+	else
+		if (irq < 0)
+			return irq;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (res) {
 		i2c->base = devm_ioremap_resource(&pdev->dev, res);
@@ -504,11 +540,13 @@  static int ocores_i2c_probe(struct platform_device *pdev)
 		goto err_clk;
 
 	init_waitqueue_head(&i2c->wait);
-	ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
-			       pdev->name, i2c);
-	if (ret) {
-		dev_err(&pdev->dev, "Cannot claim IRQ\n");
-		goto err_clk;
+	if (!(i2c->flags & I2C_FLAG_NOIRQ)) {
+		ret = devm_request_irq(&pdev->dev, irq, ocores_isr, 0,
+				       pdev->name, i2c);
+		if (ret) {
+			dev_err(&pdev->dev, "Cannot claim IRQ\n");
+			goto err_clk;
+		}
 	}
 
 	/* hook up driver to tree */