]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
i2c: riic: Add `riic_bus_barrier()` to check bus availability
authorLad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Mon, 13 Jan 2025 12:26:42 +0000 (12:26 +0000)
committerWolfram Sang <wsa+renesas@sang-engineering.com>
Tue, 14 Jan 2025 12:01:30 +0000 (13:01 +0100)
Introduce a new `riic_bus_barrier()` function to verify bus availability
before initiating an I2C transfer. This function enhances the bus
arbitration check by ensuring that the SDA and SCL lines are not held low,
in addition to checking the BBSY flag using `readb_poll_timeout()`.

Previously, only the BBSY flag was checked to determine bus availability.
However, it is possible for the SDA line to remain low even when BBSY = 0.
This new implementation performs an additional check on the SDA and SCL
lines to avoid potential bus contention issues.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reviewed-by: Andy Shevchenko <andy@kernel.org>
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
drivers/i2c/busses/i2c-riic.c

index aa3e4f430b113061b7f048569fbb136c1d4d1779..d7dddd6c296a2645c6cdb4146a10b4796003f30f 100644 (file)
@@ -41,6 +41,7 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -51,6 +52,8 @@
 #define ICCR1_ICE      BIT(7)
 #define ICCR1_IICRST   BIT(6)
 #define ICCR1_SOWP     BIT(4)
+#define ICCR1_SCLI     BIT(1)
+#define ICCR1_SDAI     BIT(0)
 
 #define ICCR2_BBSY     BIT(7)
 #define ICCR2_SP       BIT(3)
@@ -136,6 +139,27 @@ static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u
        riic_writeb(riic, (riic_readb(riic, reg) & ~clear) | set, reg);
 }
 
+static int riic_bus_barrier(struct riic_dev *riic)
+{
+       int ret;
+       u8 val;
+
+       /*
+        * The SDA line can still be low even when BBSY = 0. Therefore, after checking
+        * the BBSY flag, also verify that the SDA and SCL lines are not being held low.
+        */
+       ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR2], val,
+                                !(val & ICCR2_BBSY), 10, riic->adapter.timeout);
+       if (ret)
+               return ret;
+
+       if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
+            (ICCR1_SDAI | ICCR1_SCLI))
+               return -EBUSY;
+
+       return 0;
+}
+
 static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
 {
        struct riic_dev *riic = i2c_get_adapdata(adap);
@@ -148,13 +172,11 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
        if (ret)
                return ret;
 
-       if (riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) {
-               riic->err = -EBUSY;
+       riic->err = riic_bus_barrier(riic);
+       if (riic->err)
                goto out;
-       }
 
        reinit_completion(&riic->msg_done);
-       riic->err = 0;
 
        riic_writeb(riic, 0, RIIC_ICSR2);