Re: I2C writes with interrupts disabled

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

 



On 02/08/18 10:15, Wolfram Sang wrote:

Dealing with the new API sounds pretty cumbersome, as this would mean that
we need to change everything from the MFD / regmap level down to the i2c
platform drivers (the poweroff handler uses regmap to write to the PMIC.) I

Yes, that's the problem. That's where letting the I2C core decide will
save some hazzle. Some kind of whitelist for such transfer would be
nice, though, to still find buggy drivers. I haven't looked into that
yet if there is some information we can use (instead of passing yet
another flag around).

guess we could use a shortcut from the poweroff handler to write directly to
the i2c bus though.

It is not all about poweroff / reset. IIRC someone needed the irqless
transfer very early, too.

My personal use case would be "reset", too. There are some R-Car Gen2
boards which we need to reset via PMIC because of HW issues.


Grygorii proposed to use shutdown handler for omap I2C to fix the issue, and so it does. I added a simple shutdown handler to the driver which switches all following I2C traffic to use polling mode. Inlined patch below, what do you think of this approach?

==================================>

Subject: [RFCv2] i2c: omap: use fallback polling mechanism during shutdown

During shutdown, the irq subsystem is going down rendering omap I2C
driver un-usable, as the driver uses interrupts for data transmission.
This prevents shutting down the system by I2C writes to PMIC. To fix
this, we add a shutdown handler to the driver, which switches the
operating mode to polling based. This works properly during shutdown
phase also, and can power off the system properly.

Signed-off-by: Tero Kristo <t-kristo@xxxxxx>
---
drivers/i2c/busses/i2c-omap.c | 65 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 56 insertions(+), 9 deletions(-)

diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index b9172f0..748970b 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -213,6 +213,7 @@ struct omap_i2c_dev {
 						 * the I2C bus state
 						 */
 	unsigned		receiver:1;	/* true when we're in receiver mode */
+	unsigned		shutdown:1;	/* true if sys is shutting down */
 	u16			iestate;	/* Saved interrupt register */
 	u16			pscstate;
 	u16			scllstate;
@@ -269,6 +270,8 @@ struct omap_i2c_dev {
 	[OMAP_I2C_IP_V2_IRQENABLE_CLR] = 0x30,
 };

+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap);
+
 static inline void omap_i2c_write_reg(struct omap_i2c_dev *omap,
 				      int reg, u16 val)
 {
@@ -648,6 +651,18 @@ static void omap_i2c_resize_fifo(struct omap_i2c_dev *omap, u8 size, bool is_rx)
 			(1000 * omap->speed / 8);
 }

+static void omap_i2c_wait(struct omap_i2c_dev *omap)
+{
+	u16 stat;
+	u16 mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
+	int count = 0;
+
+	do {
+		stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
+		count++;
+	} while (!(stat & mask) && count < 5);
+}
+
 /*
  * Low level master read/write transaction.
  */
@@ -657,6 +672,7 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	struct omap_i2c_dev *omap = i2c_get_adapdata(adap);
 	unsigned long timeout;
 	u16 w;
+	int ret;

 	dev_dbg(omap->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
 		msg->addr, msg->len, msg->flags, stop);
@@ -683,7 +699,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	w |= OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR;
 	omap_i2c_write_reg(omap, OMAP_I2C_BUF_REG, w);

-	reinit_completion(&omap->cmd_complete);
+	if (!omap->shutdown)
+		reinit_completion(&omap->cmd_complete);
 	omap->cmd_err = 0;

 	w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
@@ -735,8 +752,21 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
 	 * REVISIT: We should abort the transfer on signals, but the bus goes
 	 * into arbitration and we're currently unable to recover from it.
 	 */
-	timeout = wait_for_completion_timeout(&omap->cmd_complete,
-						OMAP_I2C_TIMEOUT);
+	if (!omap->shutdown) {
+		timeout = wait_for_completion_timeout(&omap->cmd_complete,
+						      OMAP_I2C_TIMEOUT);
+	} else {
+		do {
+			omap_i2c_wait(omap);
+			ret = omap_i2c_xfer_data(omap);
+		} while (ret == -EAGAIN);
+
+		if (!ret)
+			timeout = 1;
+		else
+			timeout = 0;
+	}
+
 	if (timeout == 0) {
 		dev_err(omap->dev, "controller timed out\n");
 		omap_i2c_reset(omap);
@@ -1038,10 +1068,8 @@ static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes,
 	return ret;
 }

-static irqreturn_t
-omap_i2c_isr_thread(int this_irq, void *dev_id)
+static int omap_i2c_xfer_data(struct omap_i2c_dev *omap)
 {
-	struct omap_i2c_dev *omap = dev_id;
 	u16 bits;
 	u16 stat;
 	int err = 0, count = 0;
@@ -1059,7 +1087,8 @@ static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes,

 		if (!stat) {
 			/* my work here is done */
-			goto out;
+			err = -EAGAIN;
+			break;
 		}

 		dev_dbg(omap->dev, "IRQ (ISR = 0x%04x)\n", stat);
@@ -1168,9 +1197,19 @@ static int omap_i2c_transmit_data(struct omap_i2c_dev *omap, u8 num_bytes,
 		}
 	} while (stat);

-	omap_i2c_complete_cmd(omap, err);
+	return err;
+}
+
+static irqreturn_t
+omap_i2c_isr_thread(int this_irq, void *dev_id)
+{
+	int ret;
+	struct omap_i2c_dev *omap = dev_id;
+
+	ret = omap_i2c_xfer_data(omap);
+	if (ret != -EAGAIN)
+		omap_i2c_complete_cmd(omap, ret);

-out:
 	return IRQ_HANDLED;
 }

@@ -1539,6 +1578,13 @@ static int omap_i2c_runtime_resume(struct device *dev)
 	return 0;
 }

+static void omap_i2c_shutdown(struct platform_device *pdev)
+{
+	struct omap_i2c_dev     *omap = platform_get_drvdata(pdev);
+
+	omap->shutdown = true;
+}
+
 static const struct dev_pm_ops omap_i2c_pm_ops = {
 	SET_RUNTIME_PM_OPS(omap_i2c_runtime_suspend,
 			   omap_i2c_runtime_resume, NULL)
@@ -1551,6 +1597,7 @@ static int omap_i2c_runtime_resume(struct device *dev)
 static struct platform_driver omap_i2c_driver = {
 	.probe		= omap_i2c_probe,
 	.remove		= omap_i2c_remove,
+	.shutdown	= omap_i2c_shutdown,
 	.driver		= {
 		.name	= "omap_i2c",
 		.pm	= OMAP_I2C_PM_OPS,
--
1.9.1

--
Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki



[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