diff mbox series

[RESEND,v1,6/6] i2c: i2c-stm32f7: Implement I2C recovery mechanism

Message ID 1520852023-27083-7-git-send-email-pierre-yves.mordret@st.com
State Superseded
Headers show
Series Add different features for I2C | expand

Commit Message

Pierre Yves MORDRET March 12, 2018, 10:53 a.m. UTC
Feature prevents I2C lock-ups. Mechanism resets I2C state machine
and releases SCL/SDA signals but preserves I2C registers.

Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
---
  Version history:
    v1:
       * Initial
---
---
 drivers/i2c/busses/i2c-stm32f7.c | 32 +++++++++++++++++++++++++++++---
 1 file changed, 29 insertions(+), 3 deletions(-)

Comments

Wolfram Sang March 17, 2018, 8:47 p.m. UTC | #1
On Mon, Mar 12, 2018 at 11:53:43AM +0100, Pierre-Yves MORDRET wrote:
> Feature prevents I2C lock-ups. Mechanism resets I2C state machine
> and releases SCL/SDA signals but preserves I2C registers.

Does it release SDA when held down by the slave? Because that is what
the recovery mechanism is for.
Pierre Yves MORDRET March 19, 2018, 8:51 a.m. UTC | #2
Yes. Recovery mechanism is triggered whenever a busy line is detected.
This busy state can be checked by sw when either SDA or SCL is low: a bit is set
by Hw for this purpose.
On busy state I2C is reconfigured and lines are released the time of recovery.

Regards
On 03/17/2018 09:47 PM, Wolfram Sang wrote:
> On Mon, Mar 12, 2018 at 11:53:43AM +0100, Pierre-Yves MORDRET wrote:
>> Feature prevents I2C lock-ups. Mechanism resets I2C state machine
>> and releases SCL/SDA signals but preserves I2C registers.
> 
> Does it release SDA when held down by the slave? Because that is what
> the recovery mechanism is for.
>
Phil Reid March 19, 2018, 9:36 a.m. UTC | #3
On 12/03/2018 18:53, Pierre-Yves MORDRET wrote:
> Feature prevents I2C lock-ups. Mechanism resets I2C state machine
> and releases SCL/SDA signals but preserves I2C registers.
> 
> Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
> ---
>    Version history:
>      v1:
>         * Initial
> ---
> ---
>   drivers/i2c/busses/i2c-stm32f7.c | 32 +++++++++++++++++++++++++++++---
>   1 file changed, 29 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
> index 69a2e5e..3808bc9 100644
> --- a/drivers/i2c/busses/i2c-stm32f7.c
> +++ b/drivers/i2c/busses/i2c-stm32f7.c
> @@ -718,6 +718,20 @@ static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
>   	writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
>   }
>   
> +static int stm32f7_i2c_recover_bus(struct i2c_adapter *i2c_adap)
> +{
> +	struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
> +
> +	dev_info(i2c_dev->dev, "Trying to recover bus\n");
> +
> +	stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
> +			     STM32F7_I2C_CR1_PE);

This only "releases" the scl / sda on the stm32f7 end (outputs are hiz I guess).
It doesn't trigger the scl clocking needed to recover a stuck device  via i2c recovery
from what I can see in the data sheet.


> +
> +	stm32f7_i2c_hw_config(i2c_dev);

Nothing in here either I think.


> +
> +	return 0;
> +}
> +
>   static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
>   {
>   	u32 status;
> @@ -727,12 +741,18 @@ static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
>   					 status,
>   					 !(status & STM32F7_I2C_ISR_BUSY),
>   					 10, 1000);
> +	if (!ret)
> +		return 0;
> +
> +	dev_info(i2c_dev->dev, "bus busy\n");
> +
> +	ret = i2c_recover_bus(&i2c_dev->adap);
>   	if (ret) {
> -		dev_dbg(i2c_dev->dev, "bus busy\n");
> -		ret = -EBUSY;
> +		dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
> +		return ret;
>   	}
>   
> -	return ret;
> +	return -EBUSY;
>   }
>   
>   static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
> @@ -1476,6 +1496,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
>   	if (status & STM32F7_I2C_ISR_BERR) {
>   		dev_err(dev, "<%s>: Bus error\n", __func__);
>   		writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
> +		i2c_recover_bus(&i2c_dev->adap);
>   		f7_msg->result = -EIO;
>   	}
>   
> @@ -1760,6 +1781,10 @@ static struct i2c_algorithm stm32f7_i2c_algo = {
>   	.unreg_slave = stm32f7_i2c_unreg_slave,
>   };
>   
> +static struct i2c_bus_recovery_info stm32f7_i2c_recovery_info = {
> +	.recover_bus = stm32f7_i2c_recover_bus,
> +};
> +
>   static int stm32f7_i2c_probe(struct platform_device *pdev)
>   {
>   	struct device_node *np = pdev->dev.of_node;
> @@ -1879,6 +1904,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
>   	adap->algo = &stm32f7_i2c_algo;
>   	adap->dev.parent = &pdev->dev;
>   	adap->dev.of_node = pdev->dev.of_node;
> +	adap->bus_recovery_info = &stm32f7_i2c_recovery_info;
>   
>   	init_completion(&i2c_dev->complete);
>   
>
Pierre Yves MORDRET March 19, 2018, 4:25 p.m. UTC | #4
On 03/19/2018 10:36 AM, Phil Reid wrote:
> On 12/03/2018 18:53, Pierre-Yves MORDRET wrote:
>> Feature prevents I2C lock-ups. Mechanism resets I2C state machine
>> and releases SCL/SDA signals but preserves I2C registers.
>>
>> Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
>> ---
>>    Version history:
>>      v1:
>>         * Initial
>> ---
>> ---
>>   drivers/i2c/busses/i2c-stm32f7.c | 32 +++++++++++++++++++++++++++++---
>>   1 file changed, 29 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
>> index 69a2e5e..3808bc9 100644
>> --- a/drivers/i2c/busses/i2c-stm32f7.c
>> +++ b/drivers/i2c/busses/i2c-stm32f7.c
>> @@ -718,6 +718,20 @@ static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
>>   	writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
>>   }
>>   
>> +static int stm32f7_i2c_recover_bus(struct i2c_adapter *i2c_adap)
>> +{
>> +	struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
>> +
>> +	dev_info(i2c_dev->dev, "Trying to recover bus\n");
>> +
>> +	stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
>> +			     STM32F7_I2C_CR1_PE);
> 
> This only "releases" the scl / sda on the stm32f7 end (outputs are hiz I guess).
> It doesn't trigger the scl clocking needed to recover a stuck device  via i2c recovery
> from what I can see in the data sheet.
> 

Correct. This mechanism resets the core IP and not the slave.

> 
>> +
>> +	stm32f7_i2c_hw_config(i2c_dev);
> 
> Nothing in here either I think.
> 
> 
>> +
>> +	return 0;
>> +}
>> +
>>   static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
>>   {
>>   	u32 status;
>> @@ -727,12 +741,18 @@ static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
>>   					 status,
>>   					 !(status & STM32F7_I2C_ISR_BUSY),
>>   					 10, 1000);
>> +	if (!ret)
>> +		return 0;
>> +
>> +	dev_info(i2c_dev->dev, "bus busy\n");
>> +
>> +	ret = i2c_recover_bus(&i2c_dev->adap);
>>   	if (ret) {
>> -		dev_dbg(i2c_dev->dev, "bus busy\n");
>> -		ret = -EBUSY;
>> +		dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
>> +		return ret;
>>   	}
>>   
>> -	return ret;
>> +	return -EBUSY;
>>   }
>>   
>>   static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
>> @@ -1476,6 +1496,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
>>   	if (status & STM32F7_I2C_ISR_BERR) {
>>   		dev_err(dev, "<%s>: Bus error\n", __func__);
>>   		writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
>> +		i2c_recover_bus(&i2c_dev->adap);
>>   		f7_msg->result = -EIO;
>>   	}
>>   
>> @@ -1760,6 +1781,10 @@ static struct i2c_algorithm stm32f7_i2c_algo = {
>>   	.unreg_slave = stm32f7_i2c_unreg_slave,
>>   };
>>   
>> +static struct i2c_bus_recovery_info stm32f7_i2c_recovery_info = {
>> +	.recover_bus = stm32f7_i2c_recover_bus,
>> +};
>> +
>>   static int stm32f7_i2c_probe(struct platform_device *pdev)
>>   {
>>   	struct device_node *np = pdev->dev.of_node;
>> @@ -1879,6 +1904,7 @@ static int stm32f7_i2c_probe(struct platform_device *pdev)
>>   	adap->algo = &stm32f7_i2c_algo;
>>   	adap->dev.parent = &pdev->dev;
>>   	adap->dev.of_node = pdev->dev.of_node;
>> +	adap->bus_recovery_info = &stm32f7_i2c_recovery_info;
>>   
>>   	init_completion(&i2c_dev->complete);
>>   
>>
> 
>
Pierre Yves MORDRET March 20, 2018, 9:31 a.m. UTC | #5
Hi Wolfram,

I do believe there is a misunderstanding of this recovery mechanism at my end.
I intends to solve a problem where my I2C was stall but not the slave.
Thus if bus is busy I reset the IP.

Looking at the recovery API, this recovery is for slave and nothing else with my
case. Therefore I think I have to get move this reset out of recovery API.
Please correct me whether I'm wrong

Slave Recovery mechanism is another story to implement in our platform since we
have to deal with GPIOs.

Let me know
Regards

On 03/19/2018 09:51 AM, Pierre Yves MORDRET wrote:
> Yes. Recovery mechanism is triggered whenever a busy line is detected.
> This busy state can be checked by sw when either SDA or SCL is low: a bit is set
> by Hw for this purpose.
> On busy state I2C is reconfigured and lines are released the time of recovery.
> 
> Regards
> On 03/17/2018 09:47 PM, Wolfram Sang wrote:
>> On Mon, Mar 12, 2018 at 11:53:43AM +0100, Pierre-Yves MORDRET wrote:
>>> Feature prevents I2C lock-ups. Mechanism resets I2C state machine
>>> and releases SCL/SDA signals but preserves I2C registers.
>>
>> Does it release SDA when held down by the slave? Because that is what
>> the recovery mechanism is for.
>>
Wolfram Sang March 20, 2018, 9:42 a.m. UTC | #6
Hi,

> Looking at the recovery API, this recovery is for slave and nothing else with my
> case. Therefore I think I have to get move this reset out of recovery API.

Yes, you are correct. You need a custom function, this is totally driver
specific.

> Slave Recovery mechanism is another story to implement in our platform since we
> have to deal with GPIOs.

For that, the recovery infrastructure in the core has lots of helpers
for you.

Regards,

   Wolfram
Pierre Yves MORDRET March 20, 2018, 9:45 a.m. UTC | #7
Thanks for your quick answer

On 03/20/2018 10:42 AM, Wolfram Sang wrote:
> Hi,
> 
>> Looking at the recovery API, this recovery is for slave and nothing else with my
>> case. Therefore I think I have to get move this reset out of recovery API.
> 
> Yes, you are correct. You need a custom function, this is totally driver
> specific.

OK !
> 
>> Slave Recovery mechanism is another story to implement in our platform since we
>> have to deal with GPIOs.
> 
> For that, the recovery infrastructure in the core has lots of helpers
> for you.
> 
Yeah. This is what I saw nonetheless the main trouble is to setup SCL/SDA into
GPIO. Not that easy. but feasible of course.

BTW I will rework my driver.

Thanks.

> Regards,
> 
>    Wolfram
>
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index 69a2e5e..3808bc9 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -718,6 +718,20 @@  static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev)
 	writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
 }
 
+static int stm32f7_i2c_recover_bus(struct i2c_adapter *i2c_adap)
+{
+	struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
+
+	dev_info(i2c_dev->dev, "Trying to recover bus\n");
+
+	stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
+			     STM32F7_I2C_CR1_PE);
+
+	stm32f7_i2c_hw_config(i2c_dev);
+
+	return 0;
+}
+
 static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
 {
 	u32 status;
@@ -727,12 +741,18 @@  static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
 					 status,
 					 !(status & STM32F7_I2C_ISR_BUSY),
 					 10, 1000);
+	if (!ret)
+		return 0;
+
+	dev_info(i2c_dev->dev, "bus busy\n");
+
+	ret = i2c_recover_bus(&i2c_dev->adap);
 	if (ret) {
-		dev_dbg(i2c_dev->dev, "bus busy\n");
-		ret = -EBUSY;
+		dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
+		return ret;
 	}
 
-	return ret;
+	return -EBUSY;
 }
 
 static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
@@ -1476,6 +1496,7 @@  static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
 	if (status & STM32F7_I2C_ISR_BERR) {
 		dev_err(dev, "<%s>: Bus error\n", __func__);
 		writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
+		i2c_recover_bus(&i2c_dev->adap);
 		f7_msg->result = -EIO;
 	}
 
@@ -1760,6 +1781,10 @@  static struct i2c_algorithm stm32f7_i2c_algo = {
 	.unreg_slave = stm32f7_i2c_unreg_slave,
 };
 
+static struct i2c_bus_recovery_info stm32f7_i2c_recovery_info = {
+	.recover_bus = stm32f7_i2c_recover_bus,
+};
+
 static int stm32f7_i2c_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node;
@@ -1879,6 +1904,7 @@  static int stm32f7_i2c_probe(struct platform_device *pdev)
 	adap->algo = &stm32f7_i2c_algo;
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
+	adap->bus_recovery_info = &stm32f7_i2c_recovery_info;
 
 	init_completion(&i2c_dev->complete);