RE: [PATCH v7] i2c: riic: Implement bus recovery

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

 



> From: Prabhakar <prabhakar.csengg@xxxxxxxxx>
> Sent: 03 February 2025 14:35
> Subject: [PATCH v7] i2c: riic: Implement bus recovery
> 
> From: Lad Prabhakar <prabhakar.mahadev-lad.rj@xxxxxxxxxxxxxx>
> 
> 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@xxxxxxxxxxxxxx>
> Tested-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>
> Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@xxxxxxxxxxxxxx>

Reviewed-by: Fabrizio Castro <fabrizio.castro.jz@xxxxxxxxxxx>

> ---
> Resending this patch which was part of v6 [0] series.
> 
> [0] https://lore.kernel.org/lkml/Z4ZCJYPgvS0Ke39g@shikoro/T/
> 
> Hi Wolfram,
> 
> Ive replied to your comments on v2 here [1] as to why the generic
> recovery algorithm was not used.
> 
> [1] https://lore.kernel.org/all/CA+V-a8s4-g9vxyfYMgnKMK=Oej9kDBwWsWehWLYTkxw-06w-2g@xxxxxxxxxxxxxx/
> 
> Cheers,
> Prabhakar
> 
> v6->v7
> - None
> 
> 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.43.0






[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