[PATCH v8] i2c: riic: Implement bus recovery

Prabhakar posted 1 patch 10 months, 1 week ago
There is a newer version of this series
drivers/i2c/busses/i2c-riic.c | 100 ++++++++++++++++++++++++++++++----
1 file changed, 90 insertions(+), 10 deletions(-)
[PATCH v8] i2c: riic: Implement bus recovery
Posted by Prabhakar 10 months, 1 week ago
From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>

Implement bus recovery by reinitializing the hardware to reset the bus
state and generating 9 clock cycles (and a stop condition) to release
the SDA line.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Reviewed-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
Reviewed-by: Andy Shevchenko <andy@kernel.org>
---
v7->v8
- Included Acks from Andy and Fabrizio.

v6->v7
- https://lore.kernel.org/all/20250203143511.629140-1-prabhakar.mahadev-lad.rj@bp.renesas.com/

v2->v6
- Included RB and TB from Claudiu.

v1->v2
- Used single register read to check SDA/SCL lines
---
 drivers/i2c/busses/i2c-riic.c | 100 ++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 10 deletions(-)

diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index d7dddd6c296a..888825423d94 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -51,6 +51,7 @@
 
 #define ICCR1_ICE	BIT(7)
 #define ICCR1_IICRST	BIT(6)
+#define ICCR1_CLO	BIT(5)
 #define ICCR1_SOWP	BIT(4)
 #define ICCR1_SCLI	BIT(1)
 #define ICCR1_SDAI	BIT(0)
@@ -69,6 +70,7 @@
 #define ICMR3_ACKBT	BIT(3)
 
 #define ICFER_FMPE	BIT(7)
+#define ICFER_MALE	BIT(1)
 
 #define ICIER_TIE	BIT(7)
 #define ICIER_TEIE	BIT(6)
@@ -82,6 +84,8 @@
 
 #define RIIC_INIT_MSG	-1
 
+#define RIIC_RECOVERY_CLK_CNT	9
+
 enum riic_reg_list {
 	RIIC_ICCR1 = 0,
 	RIIC_ICCR2,
@@ -151,13 +155,16 @@ static int riic_bus_barrier(struct riic_dev *riic)
 	ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR2], val,
 				 !(val & ICCR2_BBSY), 10, riic->adapter.timeout);
 	if (ret)
-		return ret;
+		goto i2c_recover;
 
 	if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
 	     (ICCR1_SDAI | ICCR1_SCLI))
-		return -EBUSY;
+		goto i2c_recover;
 
 	return 0;
+
+i2c_recover:
+	return i2c_recover_bus(&riic->adapter);
 }
 
 static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
@@ -332,7 +339,7 @@ static const struct i2c_algorithm riic_algo = {
 	.functionality = riic_func,
 };
 
-static int riic_init_hw(struct riic_dev *riic)
+static int riic_init_hw(struct riic_dev *riic, bool recover)
 {
 	int ret;
 	unsigned long rate;
@@ -414,9 +421,11 @@ static int riic_init_hw(struct riic_dev *riic)
 		 rate / total_ticks, ((brl + 3) * 100) / (brl + brh + 6),
 		 t->scl_fall_ns / ns_per_tick, t->scl_rise_ns / ns_per_tick, cks, brl, brh);
 
-	ret = pm_runtime_resume_and_get(dev);
-	if (ret)
-		return ret;
+	if (!recover) {
+		ret = pm_runtime_resume_and_get(dev);
+		if (ret)
+			return ret;
+	}
 
 	/* Changing the order of accessing IICRST and ICE may break things! */
 	riic_writeb(riic, ICCR1_IICRST | ICCR1_SOWP, RIIC_ICCR1);
@@ -434,8 +443,74 @@ static int riic_init_hw(struct riic_dev *riic)
 
 	riic_clear_set_bit(riic, ICCR1_IICRST, 0, RIIC_ICCR1);
 
-	pm_runtime_mark_last_busy(dev);
-	pm_runtime_put_autosuspend(dev);
+	if (!recover) {
+		pm_runtime_mark_last_busy(dev);
+		pm_runtime_put_autosuspend(dev);
+	}
+	return 0;
+}
+
+static int riic_recover_bus(struct i2c_adapter *adap)
+{
+	struct riic_dev *riic = i2c_get_adapdata(adap);
+	struct device *dev = riic->adapter.dev.parent;
+	int ret;
+	u8 val;
+
+	ret = riic_init_hw(riic, true);
+	if (ret)
+		return ret;
+
+	/* output extra SCL clock cycles with master arbitration-lost detection disabled */
+	riic_clear_set_bit(riic, ICFER_MALE, 0, RIIC_ICFER);
+
+	for (unsigned int i = 0; i < RIIC_RECOVERY_CLK_CNT; i++) {
+		riic_clear_set_bit(riic, 0, ICCR1_CLO, RIIC_ICCR1);
+		ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR1], val,
+					 !(val & ICCR1_CLO), 0, 100);
+		if (ret) {
+			dev_err(dev, "SCL clock cycle timeout\n");
+			return ret;
+		}
+	}
+
+	/*
+	 * The last clock cycle may have driven the SDA line high, so add a
+	 * short delay to allow the line to stabilize before checking the status.
+	 */
+	udelay(5);
+
+	/*
+	 * If an incomplete byte write occurs, the SDA line may remain low
+	 * even after 9 clock pulses, indicating the bus is not released.
+	 * To resolve this, send an additional clock pulse to simulate a STOP
+	 * condition and ensure proper bus release.
+	 */
+	if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
+	    (ICCR1_SDAI | ICCR1_SCLI)) {
+		riic_clear_set_bit(riic, 0, ICCR1_CLO, RIIC_ICCR1);
+		ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR1], val,
+					 !(val & ICCR1_CLO), 0, 100);
+		if (ret) {
+			dev_err(dev, "SCL clock cycle timeout occurred while issuing the STOP condition\n");
+			return ret;
+		}
+		/* delay to make sure SDA line goes back HIGH again */
+		udelay(5);
+	}
+
+	/* clear any flags set */
+	riic_writeb(riic, 0, RIIC_ICSR2);
+	/* read back register to confirm writes */
+	riic_readb(riic, RIIC_ICSR2);
+
+	/* restore back ICFER_MALE */
+	riic_clear_set_bit(riic, 0, ICFER_MALE, RIIC_ICFER);
+
+	if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
+	    (ICCR1_SDAI | ICCR1_SCLI))
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -447,6 +522,10 @@ static const struct riic_irq_desc riic_irqs[] = {
 	{ .res_num = 5, .isr = riic_tend_isr, .name = "riic-nack" },
 };
 
+static struct i2c_bus_recovery_info riic_bri = {
+	.recover_bus = riic_recover_bus,
+};
+
 static int riic_i2c_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -493,6 +572,7 @@ static int riic_i2c_probe(struct platform_device *pdev)
 	strscpy(adap->name, "Renesas RIIC adapter", sizeof(adap->name));
 	adap->owner = THIS_MODULE;
 	adap->algo = &riic_algo;
+	adap->bus_recovery_info = &riic_bri;
 	adap->dev.parent = dev;
 	adap->dev.of_node = dev->of_node;
 
@@ -505,7 +585,7 @@ static int riic_i2c_probe(struct platform_device *pdev)
 	pm_runtime_use_autosuspend(dev);
 	pm_runtime_enable(dev);
 
-	ret = riic_init_hw(riic);
+	ret = riic_init_hw(riic, false);
 	if (ret)
 		goto out;
 
@@ -613,7 +693,7 @@ static int riic_i2c_resume(struct device *dev)
 	if (ret)
 		return ret;
 
-	ret = riic_init_hw(riic);
+	ret = riic_init_hw(riic, false);
 	if (ret) {
 		/*
 		 * In case this happens there is no way to recover from this
-- 
2.49.0
Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Wolfram Sang 9 months, 4 weeks ago
On Mon, Apr 07, 2025 at 01:18:59PM +0100, Prabhakar wrote:
> From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> 
> Implement bus recovery by reinitializing the hardware to reset the bus
> state and generating 9 clock cycles (and a stop condition) to release
> the SDA line.
> 
> Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> Reviewed-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> Reviewed-by: Andy Shevchenko <andy@kernel.org>

As I wrote in the other thread: I took your generic_scl approach and
replaced bus_free() with get_sda(). Works fine here with my G3S:

# echo 0x1a > /sys/kernel/debug/i2c/i2c-2/incomplete_address_phase ; i2cget -f -y 0 0x1a 3
0x0c

And I see 9 pulses in the scope. Can you try this with your setup
please?

Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Lad, Prabhakar 9 months, 3 weeks ago
Hi Wolfram,

Thank you for the review.

Finally I got my setup out for this.

On Thu, Apr 17, 2025 at 9:32 AM Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
>
> On Mon, Apr 07, 2025 at 01:18:59PM +0100, Prabhakar wrote:
> > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Implement bus recovery by reinitializing the hardware to reset the bus
> > state and generating 9 clock cycles (and a stop condition) to release
> > the SDA line.
> >
> > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> > Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> > Reviewed-by: Fabrizio Castro <fabrizio.castro.jz@renesas.com>
> > Reviewed-by: Andy Shevchenko <andy@kernel.org>
>
> As I wrote in the other thread: I took your generic_scl approach and
> replaced bus_free() with get_sda(). Works fine here with my G3S:
>
As suggested I have the below now, (are there any changes Ive missed?)

+static int riic_get_sda(struct i2c_adapter *adap)
+{
+       struct riic_dev *riic = i2c_get_adapdata(adap);
+
+       /* Check if the bus is busy or SDA is not high */
+       if ((riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) ||
+           !(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI))
+               return -EBUSY;
+
+       return 1;
+}
+
 static const struct riic_irq_desc riic_irqs[] = {
        { .res_num = 0, .isr = riic_tend_isr, .name = "riic-tend" },
        { .res_num = 1, .isr = riic_rdrf_isr, .name = "riic-rdrf" },
@@ -523,7 +568,11 @@ static const struct riic_irq_desc riic_irqs[] = {
 };

 static struct i2c_bus_recovery_info riic_bri = {
-       .recover_bus = riic_recover_bus,
+       .get_scl = riic_get_scl,
+       .set_scl = riic_set_scl,
+       .set_sda = riic_set_sda,
+       .get_sda = riic_get_sda,
+       .recover_bus = i2c_generic_scl_recovery,
 }

> # echo 0x1a > /sys/kernel/debug/i2c/i2c-2/incomplete_address_phase ; i2cget -f -y 0 0x1a 3
> 0x0c
>
> And I see 9 pulses in the scope. Can you try this with your setup
> please?
>
This seems to be unreliable on my side below are my test results.

root@smarc-rzg2l:~/i2c# cat ./incomplete_address_phase.sh
cd /sys/kernel/debug/i2c/i2c-4/
for i in {1..1}; do
        echo 0x68 > incomplete_address_phase;
        val=$(i2cget -y -f 3 0x68 8)
        if [ "$?" != "0" ] || [ "${val}" != "0x83" ]; then
                echo "I2C Read error (ret:$?) ${val}!!"
                exit 1
        fi
        echo "Read val:${val}"
done

root@smarc-rzg2l:~/i2c#
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Read val:0x83
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Read val:0x83
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c#
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Read val:0x83
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c#
root@smarc-rzg2l:~/i2c#

Cheers,
Prabhakar
Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Wolfram Sang 9 months, 3 weeks ago
> As suggested I have the below now, (are there any changes Ive missed?)

Well, get_sda should really only get SDA :)
> 
> +static int riic_get_sda(struct i2c_adapter *adap)
> +{
> +       struct riic_dev *riic = i2c_get_adapdata(adap);
> +
> +       /* Check if the bus is busy or SDA is not high */
> +       if ((riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) ||
> +           !(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI))
> +               return -EBUSY;
> +
> +       return 1;
> +}

I have

+static int riic_get_sda(struct i2c_adapter *adap)
+{
+       struct riic_dev *riic = i2c_get_adapdata(adap);
+
+       return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI);
+}

I believe the BBSY handling could be why it does not work.

Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Lad, Prabhakar 9 months, 3 weeks ago
Hi Wolfram,

On Thu, Apr 17, 2025 at 9:12 PM Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
>
>
> > As suggested I have the below now, (are there any changes Ive missed?)
>
> Well, get_sda should really only get SDA :)
> >
> > +static int riic_get_sda(struct i2c_adapter *adap)
> > +{
> > +       struct riic_dev *riic = i2c_get_adapdata(adap);
> > +
> > +       /* Check if the bus is busy or SDA is not high */
> > +       if ((riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) ||
> > +           !(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI))
> > +               return -EBUSY;
> > +
> > +       return 1;
> > +}
>
> I have
>
> +static int riic_get_sda(struct i2c_adapter *adap)
> +{
> +       struct riic_dev *riic = i2c_get_adapdata(adap);
> +
> +       return !!(riic_readb(riic, RIIC_ICCR1) & ICCR1_SDAI);
> +}
>
> I believe the BBSY handling could be why it does not work.
>
Thanks, that did the trick. The incomplete_write_byte test case is
passing for me. Now moving onto the incomplete_address_phase case this
seems to be failing on my side. Did you test this on your side?

root@smarc-rzg2l:~/i2c# cat incomplete_address_phase.sh
cd /sys/kernel/debug/i2c/i2c-4/
for i in {1..1}; do
        echo 0x68 > incomplete_address_phase;
        val=$(i2cget -y -f 3 0x68 8)
        if [ "$?" != "0" ] || [ "${val}" != "0x83" ]; then
                echo "I2C Read error (ret:$?) ${val}!!"
                exit 1
        fi
        echo "Read val:${val}"
done

root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Read val:0x83
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Read val:0x83
root@smarc-rzg2l:~/i2c# ./incomplete_address_phase.sh
Error: Read failed
I2C Read error (ret:0) !!
root@smarc-rzg2l:~/i2c#
root@smarc-rzg2l:~/i2c#

Cheers,
Prabhakar
Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Wolfram Sang 9 months, 3 weeks ago
> Thanks, that did the trick. The incomplete_write_byte test case is
> passing for me. Now moving onto the incomplete_address_phase case this
> seems to be failing on my side. Did you test this on your side?

Well, frankly, this is the only test I tried and, yes, it did work for
me. Will check 'incomplete_write_byte' later today. I will also check if
I need to run the tests more often. I did not do an endless loop.

Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Wolfram Sang 9 months, 3 weeks ago
> Well, frankly, this is the only test I tried and, yes, it did work for
> me. Will check 'incomplete_write_byte' later today. I will also check if
> I need to run the tests more often. I did not do an endless loop.

Both tests work for me, even in an endless loop. I also get the desired
signal outputs checking with the logic analyzer.

Here is the branch I used:

git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/g3s/riic-recovery-experimental

Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Lad, Prabhakar 9 months, 3 weeks ago
Hi Wolfram,

On Wed, Apr 23, 2025 at 9:26 AM Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
>
>
> > Well, frankly, this is the only test I tried and, yes, it did work for
> > me. Will check 'incomplete_write_byte' later today. I will also check if
> > I need to run the tests more often. I did not do an endless loop.
>
> Both tests work for me, even in an endless loop. I also get the desired
> signal outputs checking with the logic analyzer.
>
> Here is the branch I used:
>
> git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/g3s/riic-recovery-experimental
>
I prepared a setup on SMARC2 RZ/G3S and I can confirm it is working
(but this is failing on SMARC RZ/G2L I'll look further into this)

For the SMARC2 RZ/G3S to make sure the I2C GPIO pins behave as
opendrain I have the below patch for pinctrl

 drivers/pinctrl/renesas/pinctrl-rzg2l.c | 24 +++++++++++++++++++++++-
 1 file changed, 23 insertions(+), 1 deletion(-)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index 78fa08ff0faa..04bbc7499ce9 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -167,6 +167,7 @@
 #define NOD_MASK        0x01
 #define SMT_MASK        0x01

+#define PM_HI_Z         0x0
 #define PM_INPUT        0x1
 #define PM_OUTPUT        0x2

@@ -176,6 +177,9 @@
 #define RZG2L_TINT_MAX_INTERRUPT    32
 #define RZG2L_PACK_HWIRQ(t, i)        (((t) << 16) | (i))

+#define GPIO8_P8_2        66
+#define GPIO8_P8_3        67
+
 /* Custom pinconf parameters */
 #define RENESAS_RZV2H_PIN_CONFIG_OUTPUT_IMPEDANCE    (PIN_CONFIG_END + 1)

@@ -1703,6 +1707,10 @@ static int rzg2l_gpio_request(struct gpio_chip
*chip, unsigned int offset)
     pctrl->data->pmc_writeb(pctrl, reg8, PMC(off));

     spin_unlock_irqrestore(&pctrl->lock, flags);
+    if (offset == GPIO8_P8_2 || offset == GPIO8_P8_3) {
+        rzg2l_rmw_pin_config(pctrl, PUPD(off), bit, PUPD_MASK, 1);
+        rzg2l_rmw_pin_config(pctrl, IOLH(off), bit, IOLH_MASK, 0x3);
+    }

     return 0;
 }
@@ -1722,7 +1730,10 @@ static void rzg2l_gpio_set_direction(struct
rzg2l_pinctrl *pctrl, u32 offset,
     reg16 = readw(pctrl->base + PM(off));
     reg16 &= ~(PM_MASK << (bit * 2));

-    reg16 |= (output ? PM_OUTPUT : PM_INPUT) << (bit * 2);
+    if (offset == GPIO8_P8_2 || offset == GPIO8_P8_3)
+        reg16 |= (output ? PM_OUTPUT : PM_HI_Z) << (bit * 2);
+    else
+        reg16 |= (output ? PM_OUTPUT : PM_INPUT) << (bit * 2);
     writew(reg16, pctrl->base + PM(off));

     spin_unlock_irqrestore(&pctrl->lock, flags);
@@ -1769,6 +1780,11 @@ static void rzg2l_gpio_set(struct gpio_chip
*chip, unsigned int offset,
     unsigned long flags;
     u8 reg8;

+       if (offset == GPIO8_P8_2 || offset == GPIO8_P8_3) {
+               rzg2l_gpio_set_direction(pctrl, offset, value ? 0 : 1);
+               return;
+       }
+
     spin_lock_irqsave(&pctrl->lock, flags);

     reg8 = readb(pctrl->base + P(off));
@@ -1804,6 +1820,12 @@ static int rzg2l_gpio_get(struct gpio_chip
*chip, unsigned int offset)
     reg16 = readw(pctrl->base + PM(off));
     reg16 = (reg16 >> (bit * 2)) & PM_MASK;

+       if (offset == GPIO8_P8_2 || offset == GPIO8_P8_3) {
+               if (reg16 == PM_HI_Z)
+                       return 1;
+               return 0;
+       }
+
     if (reg16 == PM_INPUT)
         return !!(readb(pctrl->base + PIN(off)) & BIT(bit));
     else if (reg16 == PM_OUTPUT)

--
2.49.0

Below is my loop tests for SMARC2 RZ/G3S which worked without any issues:
root@smarc-rzg3s:~/i2c# cat ./i2c-inject-error.sh
DEFAULT_LOOP_COUNT=500

incomplete_address_phase() {
        cd /sys/kernel/debug/i2c/i2c-2/
        for i in {1..1}; do
                echo 0x1a > incomplete_address_phase;
                val=$(i2cget -y -f 0 0x1a 3)
                if [ "$?" != "0" ] || [ "${val}" != "0x0c" ]; then
                        echo "I2C Read error (ret:$?) ${val}!!"
                        exit 1
                fi
                echo "Read val:${val}"
                i2cget -y -f 0 0x1a 3;
                if [ "$?" != "0" ]; then
                        echo "I2C Read error when no error injection!!"
                        exit 1
                fi
        done
}

incomplete_write_byte() {
        cd /sys/kernel/debug/i2c/i2c-2/
        for i in {1..1}; do
                val1=$(printf "0x%02x" $((RANDOM & 0xff)))
                val2=$(printf "0x%02x" $((RANDOM & 0xff)))
                i2cset -y -f 0 0x57 0 ${val1}
                i2cset -y -f 0 0x57 8 ${val2}
                r1=$(i2cget -y -f 0 0x57 0x0)
                if [ "$?" != "0" ] || [ "${r1}" != "${val1}" ]; then
                        echo "1: I2C Read error (ret:$?) ${r1}!!"
                        exit 1
                fi
                echo 0x57 > incomplete_write_byte
                r1=$(i2cget -y -f 0 0x57 0x8)
                if [ "$?" != "0" ] || [ "${r1}" != "${val2}" ]; then
                        echo "2: I2C Read error (ret:$?) ${r1}!!"
                        exit 1
                fi
                r1=$(i2cget -y -f 0 0x57 0x0)
                if [ "$?" != "0" ] || [ "${r1}" != "${val1}" ]; then
                        echo "3: I2C Read error (ret:$?) ${r1}!!"
                        exit 1
                fi
        done
}


if [ ! -z "${1}" ];then
        DEFAULT_LOOP_COUNT=$1
fi

for i in $(seq 0 $DEFAULT_LOOP_COUNT);do
        echo "Loop count: "$i;
        incomplete_address_phase
        incomplete_write_byte
done

root@smarc-rzg3s:~/i2c#

Ive attached the analyzer for SMARC RZ/G2L where it is failing for
`incomplete_address_phase`.

Cheers,
Prabhakar
Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Wolfram Sang 9 months, 2 weeks ago
Hi Prabhakar,

> I prepared a setup on SMARC2 RZ/G3S and I can confirm it is working

This is good news!

> (but this is failing on SMARC RZ/G2L I'll look further into this)

This not so much. Hmmm, are you verifying against the same I2C device
on the carrier board? If you compare against different I2C devices on
the module board, the culprit might be the I2C device.

> For the SMARC2 RZ/G3S to make sure the I2C GPIO pins behave as
> opendrain I have the below patch for pinctrl

Do you really need this patch? The GPIO lines are wired to SCL/SDA which
are already pulled up. Search for R118 in the module schematics.

All the best,

   Wolfram

Re: [PATCH v8] i2c: riic: Implement bus recovery
Posted by Lad, Prabhakar 9 months, 2 weeks ago
Hi Wolfram,

On Fri, Apr 25, 2025 at 10:14 AM Wolfram Sang
<wsa+renesas@sang-engineering.com> wrote:
>
> Hi Prabhakar,
>
> > I prepared a setup on SMARC2 RZ/G3S and I can confirm it is working
>
> This is good news!
>
> > (but this is failing on SMARC RZ/G2L I'll look further into this)
>
> This not so much. Hmmm, are you verifying against the same I2C device
> on the carrier board? If you compare against different I2C devices on
> the module board, the culprit might be the I2C device.
>
You were right, it was the slave indeed. After recovery from the
generic algorithm during the transfer I am seeing arbitration lost due
to which there was a timeout during transmission. So for now we can
conclude the generic I2C recovery algo works for RIIC. I have tested
with two slaves:

- PMOD RTC on SMARC RZ/G2L
- PMOD RTC + Audio codec on SMARC RZ/G3S

After tidying up, I'll resping a new version of the patch.

> > For the SMARC2 RZ/G3S to make sure the I2C GPIO pins behave as
> > opendrain I have the below patch for pinctrl
>
> Do you really need this patch? The GPIO lines are wired to SCL/SDA which
> are already pulled up. Search for R118 in the module schematics.
>
Agreed the pullup isn't needed. This was needed on SMARC RZ/G2L as
there were spikes.

Cheers,
Prabhakar