Re: [PATCH v7 2/5] Re: i2c: core: run atomic i2c xfer when !preemptible

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

 



Hi Michael,

On Tue, 2 Jan 2024 at 16:03, Michael Walle <mwalle@xxxxxxxxxx> wrote:
> With preemption disabled, this boils down to
>   return system_state > SYSTEM_RUNNING (&& !0)
>
> and will then generate a backtrace splash on each reboot on our
> board:
>
> # reboot -f
> [   12.687169] No atomic I2C transfer handler for 'i2c-0'
> ...
> [   12.806359] Call trace:
> [   12.808793]  i2c_smbus_xfer+0x100/0x118
> ...
>
> I'm not sure if this is now the expected behavior or not. There will be
> no backtraces, if I build a preemptible kernel, nor will there be
> backtraces if I revert this patch.


thanks for the report.

In your case, the warning comes from shutting down a regulator during
device_shutdown(), so nothing really problematic here. However, later in
the "restart sequence", IRQs are disabled before the restart handlers
are called. If the reboot handlers would rely on irq-based
("non-atomic") i2c transfer, they might not work properly.

> OTOH, the driver I'm using (drivers/i2c/busses/i2c-mt65xx.c) has no
> *_atomic(). So the warning is correct. There is also [1], which seems to
> be the same issue I'm facing.
>
> -michael
>
> [1] https://lore.kernel.org/linux-i2c/13271b9b-4132-46ef-abf8-2c311967bb46@xxxxxxxxxxx/


I tried to implement an atomic handler for the mt65xx, but I don't have
the respective hardware available to test it. I decided to use a similar
approach as done in drivers/i2c/busses/i2c-rk3x.c, which calls the IRQ
handler in a while loop if an atomic xfer is requested. IMHO, this
should work with IRQs enabled and disabled, but I am not sure if this is
the best approach...

diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index a8b5719c3372..3c18305e6059 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/iopoll.h>
+#include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/mm.h>
 #include <linux/module.h>
@@ -307,6 +308,8 @@ struct mtk_i2c {
     bool ignore_restart_irq;
     struct mtk_i2c_ac_timing ac_timing;
     const struct mtk_i2c_compatible *dev_comp;
+    bool atomic_xfer;
+    bool xfer_complete;
 };

 /**
@@ -994,6 +997,20 @@ static void i2c_dump_register(struct mtk_i2c *i2c)
         readl(i2c->pdmabase + OFFSET_RX_4G_MODE));
 }

+static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id);
+
+static int mtk_i2c_wait_xfer_atomic(struct mtk_i2c *i2c)
+{
+    unsigned long timeout = jiffies + i2c->adap.timeout;
+
+    do {
+        udelay(10);
+        mtk_i2c_irq(0, i2c);
+    } while (!i2c->xfer_complete && time_before(jiffies, timeout));
+
+    return i2c->xfer_complete;
+}
+
 static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
                   int num, int left_num)
 {
@@ -1015,7 +1032,10 @@ static int mtk_i2c_do_transfer(struct mtk_i2c
*i2c, struct i2c_msg *msgs,
     if (i2c->auto_restart)
         restart_flag = I2C_RS_TRANSFER;

-    reinit_completion(&i2c->msg_complete);
+    if (i2c->atomic_xfer)
+        i2c->xfer_complete = false;
+    else
+        reinit_completion(&i2c->msg_complete);

     if (i2c->dev_comp->apdma_sync &&
        i2c->op != I2C_MASTER_WRRD && num > 1) {
@@ -1195,8 +1215,12 @@ static int mtk_i2c_do_transfer(struct mtk_i2c
*i2c, struct i2c_msg *msgs,
     }
     mtk_i2c_writew(i2c, start_reg, OFFSET_START);

-    ret = wait_for_completion_timeout(&i2c->msg_complete,
-                     i2c->adap.timeout);
+    if (i2c->atomic_xfer)
+        /* We can't rely on wait_for_completion* calls in atomic mode. */
+        ret = mtk_i2c_wait_xfer_atomic(i2c);
+    else
+        ret = wait_for_completion_timeout(&i2c->msg_complete,
+                         i2c->adap.timeout);

     /* Clear interrupt mask */
     mtk_i2c_writew(i2c, ~(restart_flag | I2C_HS_NACKERR | I2C_ACKERR |
@@ -1238,8 +1262,8 @@ static int mtk_i2c_do_transfer(struct mtk_i2c
*i2c, struct i2c_msg *msgs,
     return 0;
 }

-static int mtk_i2c_transfer(struct i2c_adapter *adap,
-               struct i2c_msg msgs[], int num)
+static int mtk_i2c_transfer_common(struct i2c_adapter *adap,
+                  struct i2c_msg msgs[], int num, bool atomic)
 {
     int ret;
     int left_num = num;
@@ -1249,6 +1273,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
     if (ret)
         return ret;

+    i2c->atomic_xfer = atomic;
     i2c->auto_restart = i2c->dev_comp->auto_restart;

     /* checking if we can skip restart and optimize using WRRD mode */
@@ -1303,6 +1328,18 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
     return ret;
 }

+static int mtk_i2c_transfer(struct i2c_adapter *adap,
+               struct i2c_msg msgs[], int num)
+{
+    return mtk_i2c_transfer_common(adap, msgs, num, false);
+}
+
+static int mtk_i2c_transfer_atomic(struct i2c_adapter *adap,
+                  struct i2c_msg msgs[], int num)
+{
+    return mtk_i2c_transfer_common(adap, msgs, num, true);
+}
+
 static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
 {
     struct mtk_i2c *i2c = dev_id;
@@ -1328,8 +1365,12 @@ static irqreturn_t mtk_i2c_irq(int irqno, void *dev_id)
         mtk_i2c_writew(i2c, I2C_RS_MUL_CNFG | I2C_RS_MUL_TRIG |
                    I2C_TRANSAC_START, OFFSET_START);
     } else {
-        if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag))
-            complete(&i2c->msg_complete);
+        if (i2c->irq_stat & (I2C_TRANSAC_COMP | restart_flag)) {
+            if (i2c->atomic_xfer)
+                i2c->xfer_complete = true;
+            else
+                complete(&i2c->msg_complete);
+        }
     }

     return IRQ_HANDLED;
@@ -1346,6 +1387,7 @@ static u32 mtk_i2c_functionality(struct i2c_adapter *adap)

 static const struct i2c_algorithm mtk_i2c_algorithm = {
     .master_xfer = mtk_i2c_transfer,
+    .master_xfer_atomic = mtk_i2c_transfer_atomic,
     .functionality = mtk_i2c_functionality,
 };




[Index of Archives]     [ARM Kernel]     [Linux ARM]     [Linux ARM MSM]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux