2017-04-28 15:43:39

by Tim Sander

[permalink] [raw]
Subject: RFC: i2c designware gpio recovery

Hi

I have tried to add a gpio recovery gpio controller to the designware i2c driver. The attempt is
attached below. I have a Intel(Altera) Cyclone V SOC Platform attached to a buggy power
supply which gives a lockup on the i2c controller as a external device gives to much noise
on the signal and destroys a clock signal on its way to a i2c device.
I don't care to much about this buggy power supply but as the cable to one i2c-slave is
rather long i fear that power surge conformance tests might give also some problems.
So i would like to be safe than sorry and recover from this problem.

I have created two gpio ports in fpga and have routed the designware pins through the fpga.
I can now read SDA input status and control SCL via these gpios. The recovery gets triggered
and after that i get lots of:
i2c_designware ffc05000.i2c: controller timed out
so i guess that my i2c_dw_unprepare_recovery does not enought to get the controller back.

I have also noticed that there does not seem do be a reset controller in the standard configuration.
so reset_control_(de)assert(i_dev->rst) seems to do nothing.

I have verified that the recovery of the bus works and if i do a warm reboot the i2c-bus is working
again. Which it doesn't without recovery. So i am pretty sure that the recovery works as far as the
i2c-slave is not pulling down SDA and that my gpio pins are in the correct state that they would not
interfere with the i2c-operation of the controller.

Any ideas what i can do to get the controller back up running with some special treatment in
i2c_dw_(un)prepare_recovery without having to resort to a warm reboot?

Best regards
Tim
---
drivers/i2c/busses/i2c-designware-core.c | 15 ++++++--
drivers/i2c/busses/i2c-designware-core.h | 1 +
drivers/i2c/busses/i2c-designware-platdrv.c | 60 ++++++++++++++++++++++++++++-
drivers/i2c/i2c-core.c | 10 ++++-
4 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
index 7a3faa551cf8..b98fab40ce9a 100644
--- a/drivers/i2c/busses/i2c-designware-core.c
+++ b/drivers/i2c/busses/i2c-designware-core.c
@@ -317,6 +317,7 @@ static void i2c_dw_release_lock(struct dw_i2c_dev *dev)
dev->release_lock(dev);
}

+
/**
* i2c_dw_init() - initialize the designware i2c master hardware
* @dev: device private data
@@ -463,7 +464,11 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
if (timeout <= 0) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
- return -ETIMEDOUT;
+ i2c_recover_bus(&dev->adapter);
+
+ if (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY)
+ return -EIO;
+ else return 0;
}
timeout--;
usleep_range(1000, 1100);
@@ -719,9 +724,10 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);

- if (abort_source & DW_IC_TX_ARB_LOST)
+ if (abort_source & DW_IC_TX_ARB_LOST) {
+ i2c_recover_bus(&dev->adapter);
return -EAGAIN;
- else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
+ } else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
return -EINVAL; /* wrong msgs[] data */
else
return -EIO;
@@ -766,6 +772,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
dev_err(dev->dev, "controller timed out\n");
/* i2c_dw_init implicitly disables the adapter */
+ //i2c_recover_bus(&dev->adapter);
i2c_dw_init(dev);
ret = -ETIMEDOUT;
goto done;
@@ -825,7 +832,7 @@ static const struct i2c_algorithm i2c_dw_algo = {
.functionality = i2c_dw_func,
};

-static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
+u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
{
u32 stat;

diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index d9aaf1790e0e..8bdf51e19f21 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -126,6 +126,7 @@ struct dw_i2c_dev {
int (*acquire_lock)(struct dw_i2c_dev *dev);
void (*release_lock)(struct dw_i2c_dev *dev);
bool pm_runtime_disabled;
+ struct i2c_bus_recovery_info rinfo;
};

#define ACCESS_SWAP 0x00000001
diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
index 79c4b4ea0539..28ed9e886983 100644
--- a/drivers/i2c/busses/i2c-designware-platdrv.c
+++ b/drivers/i2c/busses/i2c-designware-platdrv.c
@@ -41,6 +41,7 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/acpi.h>
+#include <linux/of_gpio.h>
#include <linux/platform_data/i2c-designware.h>
#include "i2c-designware-core.h"

@@ -174,6 +175,55 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
}
}

+/*
+ * This routine does i2c bus recovery by using i2c_generic_gpio_recovery
+ * which is provided by I2C Bus recovery infrastructure.
+ */
+static void i2c_dw_prepare_recovery(struct i2c_adapter *adap)
+{
+ struct platform_device *pdev = to_platform_device(&adap->dev);
+ struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
+
+ i2c_dw_disable(i_dev);
+ reset_control_assert(i_dev->rst);
+ i2c_dw_plat_prepare_clk(i_dev, false);
+}
+
+void i2c_dw_unprepare_recovery(struct i2c_adapter *adap)
+{
+ struct platform_device *pdev = to_platform_device(&adap->dev);
+ struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
+
+ i2c_dw_plat_prepare_clk(i_dev, true);
+ reset_control_deassert(i_dev->rst);
+ i2c_dw_init(i_dev);
+}
+
+static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev, struct i2c_adapter *adap)
+{
+ struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
+
+ rinfo->recover_bus = i2c_generic_gpio_recovery;
+ rinfo->prepare_recovery = i2c_dw_prepare_recovery;
+ rinfo->unprepare_recovery = i2c_dw_unprepare_recovery;
+
+ dev->rinfo.scl_gpio = of_get_named_gpio(dev->dev->of_node, "scl-gpios", 0);
+ if ( dev->rinfo.scl_gpio == -EPROBE_DEFER ) {
+ return -EPROBE_DEFER;
+ }
+ dev->rinfo.sda_gpio = of_get_named_gpio(dev->dev->of_node, "sda-gpios", 0);
+ if ( dev->rinfo.sda_gpio == -EPROBE_DEFER ) {
+ return -EPROBE_DEFER;
+ }
+ if ( !gpio_is_valid(dev->rinfo.scl_gpio) || !gpio_is_valid(dev->rinfo.sda_gpio) )
+ return -ENODEV;
+
+ dev_info(dev->dev,"adapter: %s running with gpio recovery mode! scl:%i sda:%i\n",adap->name,dev->rinfo.scl_gpio,dev->rinfo.sda_gpio);
+ adap->bus_recovery_info = &dev->rinfo;
+
+ return 0;
+};
+
static int dw_i2c_plat_probe(struct platform_device *pdev)
{
struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
@@ -285,6 +335,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
adap->class = I2C_CLASS_DEPRECATED;
ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
adap->dev.of_node = pdev->dev.of_node;
+ snprintf(adap->name, sizeof(adap->name), "Designware i2c adapter");

if (dev->pm_runtime_disabled) {
pm_runtime_forbid(&pdev->dev);
@@ -295,11 +346,16 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
}

+ r = i2c_dw_init_recovery_info(dev,adap);
+ if (r == -EPROBE_DEFER)
+ goto exit_probe ;
+
+
r = i2c_dw_probe(dev);
if (r)
- goto exit_probe;
+ goto exit_probe;

- return r;
+ return 0 ;

exit_probe:
if (!dev->pm_runtime_disabled)
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index d2402bbf6729..e1fc1d2a9548 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -739,6 +739,7 @@ static int get_scl_gpio_value(struct i2c_adapter *adap)

static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
{
+ dev_err(&adap->dev,"set scl:%i value: %i\n",adap->bus_recovery_info->scl_gpio, val);
gpio_set_value(adap->bus_recovery_info->scl_gpio, val);
}

@@ -753,7 +754,7 @@ static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
struct device *dev = &adap->dev;
int ret = 0;

- ret = gpio_request_one(bri->scl_gpio, GPIOF_OPEN_DRAIN |
+ ret = gpio_request_one(bri->scl_gpio, //GPIOF_OPEN_DRAIN |
GPIOF_OUT_INIT_HIGH, "i2c-scl");
if (ret) {
dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
@@ -807,8 +808,10 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
while (i++ < RECOVERY_CLK_CNT * 2) {
if (val) {
/* Break if SDA is high */
- if (bri->get_sda && bri->get_sda(adap))
+ if (bri->get_sda && bri->get_sda(adap)) {
+ dev_err(&adap->dev,"sda is high done\n");
break;
+ }
/* SCL shouldn't be low here */
if (!bri->get_scl(adap)) {
dev_err(&adap->dev,
@@ -823,6 +826,7 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
ndelay(RECOVERY_NDELAY);
}

+ dev_err(&adap->dev,"recovery cycle\n");
if (bri->unprepare_recovery)
bri->unprepare_recovery(adap);

@@ -839,10 +843,12 @@ int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
{
int ret;

+
ret = i2c_get_gpios_for_recovery(adap);
if (ret)
return ret;

+ dev_err(&adap->dev,"i2c_generic_gpio_recovery have gpios\n");
ret = i2c_generic_recovery(adap);
i2c_put_gpios_for_recovery(adap);

--
2.7.4


2017-04-28 16:14:53

by Tim Sander

[permalink] [raw]
Subject: Re: RFC: i2c designware gpio recovery

Hi

After sending this mail i just found out how i could reset the i2c-1 controller manually with
devmem 0xffd05014 32 0x2000
devmem 0xffd05014 32 0

So i took a look into the device tree file socfpga.dtsi and found that the reset lines
where not defined (although available in the corresponding reset manager). Is there a
reason for this? Other components are connected.

However with the patch below my previously sent patch works!

If there is interest in would cleanup the patch and send it in for mainlining.
I think the most unacceptable part would be this line:
+ ret = gpio_request_one(bri->scl_gpio, //GPIOF_OPEN_DRAIN |
My gpio drivers refuse to work as output as they have no open drain mode.
So i wonder how to get this solved in a clean manner.

Best regards
Tim
---
arch/arm/boot/dts/socfpga.dtsi | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index 2c43c4d85dee..5f28632bc88c 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -643,6 +643,7 @@
#size-cells = <0>;
compatible = "snps,designware-i2c";
reg = <0xffc04000 0x1000>;
+ resets = <&rst I2C0_RESET>;
clocks = <&l4_sp_clk>;
interrupts = <0 158 0x4>;
status = "disabled";
@@ -653,6 +654,7 @@
#size-cells = <0>;
compatible = "snps,designware-i2c";
reg = <0xffc05000 0x1000>;
+ resets = <&rst I2C1_RESET>;
clocks = <&l4_sp_clk>;
interrupts = <0 159 0x4>;
status = "disabled";
@@ -663,6 +665,7 @@
#size-cells = <0>;
compatible = "snps,designware-i2c";
reg = <0xffc06000 0x1000>;
+ resets = <&rst I2C2_RESET>;
clocks = <&l4_sp_clk>;
interrupts = <0 160 0x4>;
status = "disabled";
@@ -673,6 +676,7 @@
#size-cells = <0>;
compatible = "snps,designware-i2c";
reg = <0xffc07000 0x1000>;
+ resets = <&rst I2C3_RESET>;
clocks = <&l4_sp_clk>;
interrupts = <0 161 0x4>;
status = "disabled";
--
2.7.4

2017-05-01 02:04:30

by Phil Reid

[permalink] [raw]
Subject: Re: RFC: i2c designware gpio recovery

G'day Tim,


On 29/04/2017 00:14, Tim Sander wrote:
> Hi
>
> After sending this mail i just found out how i could reset the i2c-1 controller manually with
> devmem 0xffd05014 32 0x2000
> devmem 0xffd05014 32 0
>
> So i took a look into the device tree file socfpga.dtsi and found that the reset lines
> where not defined (although available in the corresponding reset manager). Is there a
> reason for this? Other components are connected.
There's a few thing like that where the bootloader has been expected to setup the resets etc.


>
> However with the patch below my previously sent patch works!
>
> If there is interest in would cleanup the patch and send it in for mainlining.
> I think the most unacceptable part would be this line:
> + ret = gpio_request_one(bri->scl_gpio, //GPIOF_OPEN_DRAIN |
> My gpio drivers refuse to work as output as they have no open drain mode.
> So i wonder how to get this solved in a clean manner.
I thought the gpio system would emulate open drain by switching the pin between an
input and output driven low in this case. How are you configuring the GPIO's in the
FPGA?



>
> Best regards
> Tim
> ---
> arch/arm/boot/dts/socfpga.dtsi | 4 ++++
> 1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
> index 2c43c4d85dee..5f28632bc88c 100644
> --- a/arch/arm/boot/dts/socfpga.dtsi
> +++ b/arch/arm/boot/dts/socfpga.dtsi
> @@ -643,6 +643,7 @@
> #size-cells = <0>;
> compatible = "snps,designware-i2c";
> reg = <0xffc04000 0x1000>;
> + resets = <&rst I2C0_RESET>;
> clocks = <&l4_sp_clk>;
> interrupts = <0 158 0x4>;
> status = "disabled";
> @@ -653,6 +654,7 @@
> #size-cells = <0>;
> compatible = "snps,designware-i2c";
> reg = <0xffc05000 0x1000>;
> + resets = <&rst I2C1_RESET>;
> clocks = <&l4_sp_clk>;
> interrupts = <0 159 0x4>;
> status = "disabled";
> @@ -663,6 +665,7 @@
> #size-cells = <0>;
> compatible = "snps,designware-i2c";
> reg = <0xffc06000 0x1000>;
> + resets = <&rst I2C2_RESET>;
> clocks = <&l4_sp_clk>;
> interrupts = <0 160 0x4>;
> status = "disabled";
> @@ -673,6 +676,7 @@
> #size-cells = <0>;
> compatible = "snps,designware-i2c";
> reg = <0xffc07000 0x1000>;
> + resets = <&rst I2C3_RESET>;
> clocks = <&l4_sp_clk>;
> interrupts = <0 161 0x4>;
> status = "disabled";
>


--
Regards
Phil Reid

ElectroMagnetic Imaging Technology Pty Ltd
Development of Geophysical Instrumentation & Software
http://www.electromag.com.au

3 The Avenue, Midland WA 6056, AUSTRALIA
Ph: +61 8 9250 8100
Fax: +61 8 9250 7100
Email: [email protected]

2017-05-01 02:16:07

by Phil Reid

[permalink] [raw]
Subject: Re: RFC: i2c designware gpio recovery

On 28/04/2017 23:43, Tim Sander wrote:
> Hi
G'day Tim,

Given a couple of days I can test this on some flack i2c hardware I have with a Cyclone-V SOC.
I'm interested in the functionality as well.

For i2c that are connected to the dedicated HPS pins it should be possible to reconfigure
the pin mux controller (see system manager) in the HPS to avoid the need to go thru the fpga
to get direct control. The docs say this is "unsupport" but I've done some test and it does
seem to work. I'm guess the no support is in a similar vain to the emac ptp FPGA interface
couldn't be used when the HPS pin where used. But that got changed when the user's proved otherwise.
There's just no pin ctrl driver yet to manage it.

>
> I have tried to add a gpio recovery gpio controller to the designware i2c driver. The attempt is
> attached below. I have a Intel(Altera) Cyclone V SOC Platform attached to a buggy power
> supply which gives a lockup on the i2c controller as a external device gives to much noise
> on the signal and destroys a clock signal on its way to a i2c device.
> I don't care to much about this buggy power supply but as the cable to one i2c-slave is
> rather long i fear that power surge conformance tests might give also some problems.
> So i would like to be safe than sorry and recover from this problem.
>
> I have created two gpio ports in fpga and have routed the designware pins through the fpga.
> I can now read SDA input status and control SCL via these gpios. The recovery gets triggered
> and after that i get lots of:
> i2c_designware ffc05000.i2c: controller timed out
> so i guess that my i2c_dw_unprepare_recovery does not enought to get the controller back.
>
> I have also noticed that there does not seem do be a reset controller in the standard configuration.
> so reset_control_(de)assert(i_dev->rst) seems to do nothing.
>
> I have verified that the recovery of the bus works and if i do a warm reboot the i2c-bus is working
> again. Which it doesn't without recovery. So i am pretty sure that the recovery works as far as the
> i2c-slave is not pulling down SDA and that my gpio pins are in the correct state that they would not
> interfere with the i2c-operation of the controller.
>
> Any ideas what i can do to get the controller back up running with some special treatment in
> i2c_dw_(un)prepare_recovery without having to resort to a warm reboot?
>
> Best regards
> Tim
> ---
> drivers/i2c/busses/i2c-designware-core.c | 15 ++++++--
> drivers/i2c/busses/i2c-designware-core.h | 1 +
> drivers/i2c/busses/i2c-designware-platdrv.c | 60 ++++++++++++++++++++++++++++-
> drivers/i2c/i2c-core.c | 10 ++++-
> 4 files changed, 78 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c
> index 7a3faa551cf8..b98fab40ce9a 100644
> --- a/drivers/i2c/busses/i2c-designware-core.c
> +++ b/drivers/i2c/busses/i2c-designware-core.c
> @@ -317,6 +317,7 @@ static void i2c_dw_release_lock(struct dw_i2c_dev *dev)
> dev->release_lock(dev);
> }
>
> +
> /**
> * i2c_dw_init() - initialize the designware i2c master hardware
> * @dev: device private data
> @@ -463,7 +464,11 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev)
> while (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) {
> if (timeout <= 0) {
> dev_warn(dev->dev, "timeout waiting for bus ready\n");
> - return -ETIMEDOUT;
> + i2c_recover_bus(&dev->adapter);
> +
> + if (dw_readl(dev, DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY)
> + return -EIO;
> + else return 0;
> }
> timeout--;
> usleep_range(1000, 1100);
> @@ -719,9 +724,10 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
> for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
> dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
>
> - if (abort_source & DW_IC_TX_ARB_LOST)
> + if (abort_source & DW_IC_TX_ARB_LOST) {
> + i2c_recover_bus(&dev->adapter);
> return -EAGAIN;
> - else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
> + } else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
> return -EINVAL; /* wrong msgs[] data */
> else
> return -EIO;
> @@ -766,6 +772,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
> if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) {
> dev_err(dev->dev, "controller timed out\n");
> /* i2c_dw_init implicitly disables the adapter */
> + //i2c_recover_bus(&dev->adapter);
> i2c_dw_init(dev);
> ret = -ETIMEDOUT;
> goto done;
> @@ -825,7 +832,7 @@ static const struct i2c_algorithm i2c_dw_algo = {
> .functionality = i2c_dw_func,
> };
>
> -static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
> +u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
> {
> u32 stat;
>
> diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
> index d9aaf1790e0e..8bdf51e19f21 100644
> --- a/drivers/i2c/busses/i2c-designware-core.h
> +++ b/drivers/i2c/busses/i2c-designware-core.h
> @@ -126,6 +126,7 @@ struct dw_i2c_dev {
> int (*acquire_lock)(struct dw_i2c_dev *dev);
> void (*release_lock)(struct dw_i2c_dev *dev);
> bool pm_runtime_disabled;
> + struct i2c_bus_recovery_info rinfo;
> };
>
> #define ACCESS_SWAP 0x00000001
> diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c
> index 79c4b4ea0539..28ed9e886983 100644
> --- a/drivers/i2c/busses/i2c-designware-platdrv.c
> +++ b/drivers/i2c/busses/i2c-designware-platdrv.c
> @@ -41,6 +41,7 @@
> #include <linux/reset.h>
> #include <linux/slab.h>
> #include <linux/acpi.h>
> +#include <linux/of_gpio.h>
> #include <linux/platform_data/i2c-designware.h>
> #include "i2c-designware-core.h"
>
> @@ -174,6 +175,55 @@ static void dw_i2c_set_fifo_size(struct dw_i2c_dev *dev, int id)
> }
> }
>
> +/*
> + * This routine does i2c bus recovery by using i2c_generic_gpio_recovery
> + * which is provided by I2C Bus recovery infrastructure.
> + */
> +static void i2c_dw_prepare_recovery(struct i2c_adapter *adap)
> +{
> + struct platform_device *pdev = to_platform_device(&adap->dev);
> + struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
> +
> + i2c_dw_disable(i_dev);
> + reset_control_assert(i_dev->rst);
> + i2c_dw_plat_prepare_clk(i_dev, false);
> +}
> +
> +void i2c_dw_unprepare_recovery(struct i2c_adapter *adap)
> +{
> + struct platform_device *pdev = to_platform_device(&adap->dev);
> + struct dw_i2c_dev *i_dev = platform_get_drvdata(pdev);
> +
> + i2c_dw_plat_prepare_clk(i_dev, true);
> + reset_control_deassert(i_dev->rst);
> + i2c_dw_init(i_dev);
> +}
> +
> +static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev, struct i2c_adapter *adap)
> +{
> + struct i2c_bus_recovery_info *rinfo = &dev->rinfo;
> +
> + rinfo->recover_bus = i2c_generic_gpio_recovery;
> + rinfo->prepare_recovery = i2c_dw_prepare_recovery;
> + rinfo->unprepare_recovery = i2c_dw_unprepare_recovery;
> +
> + dev->rinfo.scl_gpio = of_get_named_gpio(dev->dev->of_node, "scl-gpios", 0);
> + if ( dev->rinfo.scl_gpio == -EPROBE_DEFER ) {
> + return -EPROBE_DEFER;
> + }
> + dev->rinfo.sda_gpio = of_get_named_gpio(dev->dev->of_node, "sda-gpios", 0);
> + if ( dev->rinfo.sda_gpio == -EPROBE_DEFER ) {
> + return -EPROBE_DEFER;
> + }
> + if ( !gpio_is_valid(dev->rinfo.scl_gpio) || !gpio_is_valid(dev->rinfo.sda_gpio) )
> + return -ENODEV;
> +
> + dev_info(dev->dev,"adapter: %s running with gpio recovery mode! scl:%i sda:%i\n",adap->name,dev->rinfo.scl_gpio,dev->rinfo.sda_gpio);
> + adap->bus_recovery_info = &dev->rinfo;
> +
> + return 0;
> +};
> +
> static int dw_i2c_plat_probe(struct platform_device *pdev)
> {
> struct dw_i2c_platform_data *pdata = dev_get_platdata(&pdev->dev);
> @@ -285,6 +335,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
> adap->class = I2C_CLASS_DEPRECATED;
> ACPI_COMPANION_SET(&adap->dev, ACPI_COMPANION(&pdev->dev));
> adap->dev.of_node = pdev->dev.of_node;
> + snprintf(adap->name, sizeof(adap->name), "Designware i2c adapter");
>
> if (dev->pm_runtime_disabled) {
> pm_runtime_forbid(&pdev->dev);
> @@ -295,11 +346,16 @@ static int dw_i2c_plat_probe(struct platform_device *pdev)
> pm_runtime_enable(&pdev->dev);
> }
>
> + r = i2c_dw_init_recovery_info(dev,adap);
> + if (r == -EPROBE_DEFER)
> + goto exit_probe ;
> +
> +
> r = i2c_dw_probe(dev);
> if (r)
> - goto exit_probe;
> + goto exit_probe;
>
> - return r;
> + return 0 ;
>
> exit_probe:
> if (!dev->pm_runtime_disabled)
> diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
> index d2402bbf6729..e1fc1d2a9548 100644
> --- a/drivers/i2c/i2c-core.c
> +++ b/drivers/i2c/i2c-core.c
> @@ -739,6 +739,7 @@ static int get_scl_gpio_value(struct i2c_adapter *adap)
>
> static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
> {
> + dev_err(&adap->dev,"set scl:%i value: %i\n",adap->bus_recovery_info->scl_gpio, val);
> gpio_set_value(adap->bus_recovery_info->scl_gpio, val);
> }
>
> @@ -753,7 +754,7 @@ static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
> struct device *dev = &adap->dev;
> int ret = 0;
>
> - ret = gpio_request_one(bri->scl_gpio, GPIOF_OPEN_DRAIN |
> + ret = gpio_request_one(bri->scl_gpio, //GPIOF_OPEN_DRAIN |
> GPIOF_OUT_INIT_HIGH, "i2c-scl");
> if (ret) {
> dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
> @@ -807,8 +808,10 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
> while (i++ < RECOVERY_CLK_CNT * 2) {
> if (val) {
> /* Break if SDA is high */
> - if (bri->get_sda && bri->get_sda(adap))
> + if (bri->get_sda && bri->get_sda(adap)) {
> + dev_err(&adap->dev,"sda is high done\n");
> break;
> + }
> /* SCL shouldn't be low here */
> if (!bri->get_scl(adap)) {
> dev_err(&adap->dev,
> @@ -823,6 +826,7 @@ static int i2c_generic_recovery(struct i2c_adapter *adap)
> ndelay(RECOVERY_NDELAY);
> }
>
> + dev_err(&adap->dev,"recovery cycle\n");
> if (bri->unprepare_recovery)
> bri->unprepare_recovery(adap);
>
> @@ -839,10 +843,12 @@ int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
> {
> int ret;
>
> +
> ret = i2c_get_gpios_for_recovery(adap);
> if (ret)
> return ret;
>
> + dev_err(&adap->dev,"i2c_generic_gpio_recovery have gpios\n");
> ret = i2c_generic_recovery(adap);
> i2c_put_gpios_for_recovery(adap);
>
>


--
Regards
Phil Reid