mbox series

[V2,00/18] i2c: imx-lpi2c: New features and bug fixes

Message ID 20210406113306.2633595-1-xiaoning.wang@nxp.com
Headers show
Series i2c: imx-lpi2c: New features and bug fixes | expand

Message

Clark Wang April 6, 2021, 11:32 a.m. UTC
Hi,

According to V1's feedback, V2 has been modified.
For summary of the changes, please refer to the header of each patch file.

Added some dt-bindings and dts patches.
At the same time, a patch has been added to fix the problem that data larger
than 256 bytes cannot be sent in one frame in PIO mode.

Clark Wang (14):
  i2c: imx-lpi2c: add ipg clk for lpi2c driver
  ARM: dts: imx7ulp: add the missing lpi2c ipg clock
  ARM: dts: imx7ulp: add the missing lpi2c nodes
  ARM64: dts: imx8: add the missing lpi2c ipg clock
  ARM64: dts: imx8: change i2c irq number to non-combined
  i2c: imx-lpi2c: increase PM timeout to avoid operate clk frequently
  i2c: imx-lpi2c: add bus recovery feature
  dt-bindings: i2c: imx-lpi2c: Add bus recovery example
  i2c: imx-lpi2c: fix i2c timing issue
  i2c: imx-lpi2c: fix type char overflow issue when calculating the
    clock cycle
  i2c: imx-lpi2c: add edma mode support
  dt-bindings: i2c: imx-lpi2c: Add dma configuration example
  ARM: dts: imx7ulp: add dma configurations for lpi2c
  ARM: dts: imx7ulp: add the missing status property of lpi2c5
  i2c: imx-lpi2c: fix pio mode cannot send 256+ bytes in one frame

Fugang Duan (1):
  i2c: imx-lpi2c: manage irq resource request/release in runtime pm

Gao Pan (2):
  i2c: imx-lpi2c: directly retrun ISR when detect a NACK
  i2c: imx-lpi2c: add debug message when i2c peripheral clk doesn't work

 .../bindings/i2c/i2c-imx-lpi2c.yaml           |  26 +
 arch/arm/boot/dts/imx7ulp.dtsi                |  50 +-
 .../arm64/boot/dts/freescale/imx8-ss-dma.dtsi |  32 +-
 drivers/i2c/busses/i2c-imx-lpi2c.c            | 506 ++++++++++++++++--
 4 files changed, 548 insertions(+), 66 deletions(-)

Comments

Rob Herring April 9, 2021, 4:33 p.m. UTC | #1
On Tue, Apr 06, 2021 at 07:33:03PM +0800, Clark Wang wrote:
> Add i2c bus dma mode configuration example.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> ---
> V2 changes:
>  - New patch added in V2
> ---
>  .../devicetree/bindings/i2c/i2c-imx-lpi2c.yaml       | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml
> index 0a4b28827dcc..3868fec0cf27 100644
> --- a/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml
> +++ b/Documentation/devicetree/bindings/i2c/i2c-imx-lpi2c.yaml
> @@ -51,6 +51,16 @@ properties:
>    sda-gpios:
>      maxItems: 1
>  
> +  dmas:
> +    minItems: 2
> +    maxItems: 2
> +
> +  dma-names:
> +    minItems: 2
> +    maxItems: 2
> +    items:
> +      enum: [ "tx", "rx" ]

Please define the order:

items:
  - const: tx
  - const: rx

> +
>  required:
>    - compatible
>    - reg
> @@ -75,4 +85,6 @@ examples:
>          pinctrl-1 = <&pinctrl_i2c_recovery>;
>          scl-gpios = <&gpio5 14 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
>          sda-gpios = <&gpio5 15 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>;
> +        dmas = <&edma1 0 16>, <&edma1 0 15>;
> +        dma-names = "tx","rx";
>      };
> -- 
> 2.25.1
>
Shawn Guo May 11, 2021, 2:59 a.m. UTC | #2
On Tue, Apr 06, 2021 at 07:32:53PM +0800, Clark Wang wrote:
> The lpi2c driver has add the missing ipg clock.
> So add the ipg clock here for all lpi2c nodes.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>

Historically, we use 'arm64: dts: ...' as subject prefix for arm64 DTS
patch, and 'ARM: dts: ...' for arm.

Shawn

> ---
> V2 changes:
>  - New patch added in V2
> ---
>  .../arm64/boot/dts/freescale/imx8-ss-dma.dtsi | 24 ++++++++++++-------
>  1 file changed, 16 insertions(+), 8 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi
> index 960a802b8b6e..b5ed12a06538 100644
> --- a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi
> +++ b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi
> @@ -111,8 +111,10 @@ uart3_lpcg: clock-controller@5a490000 {
>  	i2c0: i2c@5a800000 {
>  		reg = <0x5a800000 0x4000>;
>  		interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;
> -		clocks = <&i2c0_lpcg IMX_LPCG_CLK_0>;
> -		clock-names = "per";
> +		interrupt-parent = <&gic>;
> +		clocks = <&i2c0_lpcg IMX_LPCG_CLK_0>,
> +			 <&i2c0_lpcg IMX_LPCG_CLK_4>;
> +		clock-names = "per", "ipg";
>  		assigned-clocks = <&clk IMX_SC_R_I2C_0 IMX_SC_PM_CLK_PER>;
>  		assigned-clock-rates = <24000000>;
>  		power-domains = <&pd IMX_SC_R_I2C_0>;
> @@ -122,8 +124,10 @@ i2c0: i2c@5a800000 {
>  	i2c1: i2c@5a810000 {
>  		reg = <0x5a810000 0x4000>;
>  		interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;
> -		clocks = <&i2c1_lpcg IMX_LPCG_CLK_0>;
> -		clock-names = "per";
> +		interrupt-parent = <&gic>;
> +		clocks = <&i2c1_lpcg IMX_LPCG_CLK_0>,
> +			 <&i2c1_lpcg IMX_LPCG_CLK_4>;
> +		clock-names = "per", "ipg";
>  		assigned-clocks = <&clk IMX_SC_R_I2C_1 IMX_SC_PM_CLK_PER>;
>  		assigned-clock-rates = <24000000>;
>  		power-domains = <&pd IMX_SC_R_I2C_1>;
> @@ -133,8 +137,10 @@ i2c1: i2c@5a810000 {
>  	i2c2: i2c@5a820000 {
>  		reg = <0x5a820000 0x4000>;
>  		interrupts = <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>;
> -		clocks = <&i2c2_lpcg IMX_LPCG_CLK_0>;
> -		clock-names = "per";
> +		interrupt-parent = <&gic>;
> +		clocks = <&i2c2_lpcg IMX_LPCG_CLK_0>,
> +			 <&i2c2_lpcg IMX_LPCG_CLK_4>;
> +		clock-names = "per", "ipg";
>  		assigned-clocks = <&clk IMX_SC_R_I2C_2 IMX_SC_PM_CLK_PER>;
>  		assigned-clock-rates = <24000000>;
>  		power-domains = <&pd IMX_SC_R_I2C_2>;
> @@ -144,8 +150,10 @@ i2c2: i2c@5a820000 {
>  	i2c3: i2c@5a830000 {
>  		reg = <0x5a830000 0x4000>;
>  		interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;
> -		clocks = <&i2c3_lpcg IMX_LPCG_CLK_0>;
> -		clock-names = "per";
> +		interrupt-parent = <&gic>;
> +		clocks = <&i2c3_lpcg IMX_LPCG_CLK_0>,
> +			 <&i2c3_lpcg IMX_LPCG_CLK_4>;
> +		clock-names = "per", "ipg";
>  		assigned-clocks = <&clk IMX_SC_R_I2C_3 IMX_SC_PM_CLK_PER>;
>  		assigned-clock-rates = <24000000>;
>  		power-domains = <&pd IMX_SC_R_I2C_3>;
> -- 
> 2.25.1
>
Aisheng Dong May 21, 2021, 6:17 a.m. UTC | #3
> From: Clark Wang <xiaoning.wang@nxp.com>

> Sent: Tuesday, April 6, 2021 7:33 PM

> 

> The lpi2c driver has add the missing ipg clock.


Pls drop this line as binding is not decided by driver 

> So add the ipg clock here for all lpi2c nodes.


Per clock?

Maybe better refined as add the missing per clock ....

> 

> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>

> ---

> V2 changes:

>  - New patch added in V2

> ---

>  arch/arm/boot/dts/imx7ulp.dtsi | 10 ++++++----

>  1 file changed, 6 insertions(+), 4 deletions(-)

> 

> diff --git a/arch/arm/boot/dts/imx7ulp.dtsi b/arch/arm/boot/dts/imx7ulp.dtsi

> index b7ea37ad4e55..eb0d4b8f624d 100644

> --- a/arch/arm/boot/dts/imx7ulp.dtsi

> +++ b/arch/arm/boot/dts/imx7ulp.dtsi

> @@ -328,8 +328,9 @@ lpi2c6: i2c@40a40000 {

>  			compatible = "fsl,imx7ulp-lpi2c";

>  			reg = <0x40a40000 0x10000>;

>  			interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;

> -			clocks = <&pcc3 IMX7ULP_CLK_LPI2C6>;

> -			clock-names = "ipg";

> +			clocks = <&pcc3 IMX7ULP_CLK_LPI2C6>,

> +				 <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>;

> +			clock-names = "per", "ipg";

>  			assigned-clocks = <&pcc3 IMX7ULP_CLK_LPI2C6>;

>  			assigned-clock-parents = <&scg1 IMX7ULP_CLK_FIRC>;

>  			assigned-clock-rates = <48000000>;

> @@ -340,8 +341,9 @@ lpi2c7: i2c@40a50000 {

>  			compatible = "fsl,imx7ulp-lpi2c";

>  			reg = <0x40a50000 0x10000>;

>  			interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;

> -			clocks = <&pcc3 IMX7ULP_CLK_LPI2C7>;

> -			clock-names = "ipg";

> +			clocks = <&pcc3 IMX7ULP_CLK_LPI2C7>,

> +				 <&scg1 IMX7ULP_CLK_NIC1_BUS_DIV>;

> +			clock-names = "per", "ipg";

>  			assigned-clocks = <&pcc3 IMX7ULP_CLK_LPI2C7>;

>  			assigned-clock-parents = <&scg1 IMX7ULP_CLK_FIRC>;

>  			assigned-clock-rates = <48000000>;

> --

> 2.25.1
Aisheng Dong May 21, 2021, 6:28 a.m. UTC | #4
> From: Clark Wang <xiaoning.wang@nxp.com>

> Sent: Tuesday, April 6, 2021 7:33 PM

> 

> The lpi2c driver has add the missing ipg clock.

> So add the ipg clock here for all lpi2c nodes.

> 

> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>

> ---

> V2 changes:

>  - New patch added in V2

> ---

>  .../arm64/boot/dts/freescale/imx8-ss-dma.dtsi | 24 ++++++++++++-------

>  1 file changed, 16 insertions(+), 8 deletions(-)

> 

> diff --git a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi

> b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi

> index 960a802b8b6e..b5ed12a06538 100644

> --- a/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi

> +++ b/arch/arm64/boot/dts/freescale/imx8-ss-dma.dtsi

> @@ -111,8 +111,10 @@ uart3_lpcg: clock-controller@5a490000 {

>  	i2c0: i2c@5a800000 {

>  		reg = <0x5a800000 0x4000>;

>  		interrupts = <GIC_SPI 220 IRQ_TYPE_LEVEL_HIGH>;

> -		clocks = <&i2c0_lpcg IMX_LPCG_CLK_0>;

> -		clock-names = "per";

> +		interrupt-parent = <&gic>;


Added by mistake?

> +		clocks = <&i2c0_lpcg IMX_LPCG_CLK_0>,

> +			 <&i2c0_lpcg IMX_LPCG_CLK_4>;

> +		clock-names = "per", "ipg";

>  		assigned-clocks = <&clk IMX_SC_R_I2C_0 IMX_SC_PM_CLK_PER>;

>  		assigned-clock-rates = <24000000>;

>  		power-domains = <&pd IMX_SC_R_I2C_0>; @@ -122,8 +124,10 @@

> i2c0: i2c@5a800000 {

>  	i2c1: i2c@5a810000 {

>  		reg = <0x5a810000 0x4000>;

>  		interrupts = <GIC_SPI 221 IRQ_TYPE_LEVEL_HIGH>;

> -		clocks = <&i2c1_lpcg IMX_LPCG_CLK_0>;

> -		clock-names = "per";

> +		interrupt-parent = <&gic>;


Ditto

> +		clocks = <&i2c1_lpcg IMX_LPCG_CLK_0>,

> +			 <&i2c1_lpcg IMX_LPCG_CLK_4>;

> +		clock-names = "per", "ipg";

>  		assigned-clocks = <&clk IMX_SC_R_I2C_1 IMX_SC_PM_CLK_PER>;

>  		assigned-clock-rates = <24000000>;

>  		power-domains = <&pd IMX_SC_R_I2C_1>; @@ -133,8 +137,10 @@

> i2c1: i2c@5a810000 {

>  	i2c2: i2c@5a820000 {

>  		reg = <0x5a820000 0x4000>;

>  		interrupts = <GIC_SPI 222 IRQ_TYPE_LEVEL_HIGH>;

> -		clocks = <&i2c2_lpcg IMX_LPCG_CLK_0>;

> -		clock-names = "per";

> +		interrupt-parent = <&gic>;

> +		clocks = <&i2c2_lpcg IMX_LPCG_CLK_0>,

> +			 <&i2c2_lpcg IMX_LPCG_CLK_4>;

> +		clock-names = "per", "ipg";

>  		assigned-clocks = <&clk IMX_SC_R_I2C_2 IMX_SC_PM_CLK_PER>;

>  		assigned-clock-rates = <24000000>;

>  		power-domains = <&pd IMX_SC_R_I2C_2>; @@ -144,8 +150,10 @@

> i2c2: i2c@5a820000 {

>  	i2c3: i2c@5a830000 {

>  		reg = <0x5a830000 0x4000>;

>  		interrupts = <GIC_SPI 223 IRQ_TYPE_LEVEL_HIGH>;

> -		clocks = <&i2c3_lpcg IMX_LPCG_CLK_0>;

> -		clock-names = "per";

> +		interrupt-parent = <&gic>;

> +		clocks = <&i2c3_lpcg IMX_LPCG_CLK_0>,

> +			 <&i2c3_lpcg IMX_LPCG_CLK_4>;

> +		clock-names = "per", "ipg";

>  		assigned-clocks = <&clk IMX_SC_R_I2C_3 IMX_SC_PM_CLK_PER>;

>  		assigned-clock-rates = <24000000>;

>  		power-domains = <&pd IMX_SC_R_I2C_3>;

> --

> 2.25.1
Aisheng Dong May 21, 2021, 6:38 a.m. UTC | #5
> From: Clark Wang <xiaoning.wang@nxp.com>

> Sent: Tuesday, April 6, 2021 7:33 PM

> 

> Manage irq resource request/release in runtime pm to save irq domain's

> power.

> 

> Signed-off-by: Frank Li <Frank.Li@nxp.com>

> Signed-off-by: Fugang Duan <fugang.duan@nxp.com>

> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>

> Reviewed-by: Frank Li <Frank.Li@nxp.com>


Reviewed-by: Dong Aisheng <aisheng.dong@nxp.com>


Regards
Aisheng

> ---

> V2 changes:

>  - Change to use request_irq/free_irq.

> ---

>  drivers/i2c/busses/i2c-imx-lpi2c.c | 30 ++++++++++++++++++------------

>  1 file changed, 18 insertions(+), 12 deletions(-)

> 

> diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c

> b/drivers/i2c/busses/i2c-imx-lpi2c.c

> index 89b7b0795f51..333209ba81c1 100644

> --- a/drivers/i2c/busses/i2c-imx-lpi2c.c

> +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c

> @@ -94,6 +94,7 @@ enum lpi2c_imx_pincfg {

> 

>  struct lpi2c_imx_struct {

>  	struct i2c_adapter	adapter;

> +	int			irq;

>  	struct clk		*clk_per;

>  	struct clk		*clk_ipg;

>  	void __iomem		*base;

> @@ -571,7 +572,7 @@ static int lpi2c_imx_probe(struct platform_device

> *pdev)  {

>  	struct lpi2c_imx_struct *lpi2c_imx;

>  	unsigned int temp;

> -	int irq, ret;

> +	int ret;

> 

>  	lpi2c_imx = devm_kzalloc(&pdev->dev, sizeof(*lpi2c_imx), GFP_KERNEL);

>  	if (!lpi2c_imx)

> @@ -581,9 +582,9 @@ static int lpi2c_imx_probe(struct platform_device

> *pdev)

>  	if (IS_ERR(lpi2c_imx->base))

>  		return PTR_ERR(lpi2c_imx->base);

> 

> -	irq = platform_get_irq(pdev, 0);

> -	if (irq < 0)

> -		return irq;

> +	lpi2c_imx->irq = platform_get_irq(pdev, 0);

> +	if (lpi2c_imx->irq < 0)

> +		return lpi2c_imx->irq;

> 

>  	lpi2c_imx->adapter.owner	= THIS_MODULE;

>  	lpi2c_imx->adapter.algo		= &lpi2c_imx_algo;

> @@ -609,13 +610,6 @@ static int lpi2c_imx_probe(struct platform_device

> *pdev)

>  	if (ret)

>  		lpi2c_imx->bitrate = I2C_MAX_STANDARD_MODE_FREQ;

> 

> -	ret = devm_request_irq(&pdev->dev, irq, lpi2c_imx_isr, 0,

> -			       pdev->name, lpi2c_imx);

> -	if (ret) {

> -		dev_err(&pdev->dev, "can't claim irq %d\n", irq);

> -		return ret;

> -	}

> -

>  	i2c_set_adapdata(&lpi2c_imx->adapter, lpi2c_imx);

>  	platform_set_drvdata(pdev, lpi2c_imx);

> 

> @@ -668,6 +662,7 @@ static int __maybe_unused

> lpi2c_runtime_suspend(struct device *dev)  {

>  	struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);

> 

> +	free_irq(lpi2c_imx->irq, lpi2c_imx);

>  	lpi2c_imx_clocks_unprepare(lpi2c_imx);

>  	pinctrl_pm_select_sleep_state(dev);

> 

> @@ -677,10 +672,21 @@ static int __maybe_unused

> lpi2c_runtime_suspend(struct device *dev)  static int __maybe_unused

> lpi2c_runtime_resume(struct device *dev)  {

>  	struct lpi2c_imx_struct *lpi2c_imx = dev_get_drvdata(dev);

> +	int ret = 0;

> 

>  	pinctrl_pm_select_default_state(dev);

> +	ret = lpi2c_imx_clocks_prepare(lpi2c_imx);

> +	if (ret)

> +		return ret;

> 

> -	return lpi2c_imx_clocks_prepare(lpi2c_imx);

> +	ret = request_irq(lpi2c_imx->irq, lpi2c_imx_isr, 0,

> +			       dev_name(dev), lpi2c_imx);

> +	if (ret) {

> +		dev_err(dev, "can't claim irq %d\n", lpi2c_imx->irq);

> +		return ret;

> +	}

> +

> +	return ret;

>  }

> 

>  static const struct dev_pm_ops lpi2c_pm_ops = {

> --

> 2.25.1
Aisheng Dong May 21, 2021, 6:44 a.m. UTC | #6
> From: Clark Wang <xiaoning.wang@nxp.com>

> Sent: Tuesday, April 6, 2021 7:33 PM

> 

> Add bus recovery feature for LPI2C.

> Need add gpio pinctrl, scl-gpios and sda-gpios configuration in dts.

> 

> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>

> ---

> V2 changes:

>  - No change. Add dt-bindings in the next patch.


Dt binding patch should be sent before driver change

> ---

>  drivers/i2c/busses/i2c-imx-lpi2c.c | 83 ++++++++++++++++++++++++++++++

>  1 file changed, 83 insertions(+)

> 

> diff --git a/drivers/i2c/busses/i2c-imx-lpi2c.c

> b/drivers/i2c/busses/i2c-imx-lpi2c.c

> index 77ceb743b282..77dd6ee5a4a8 100644

> --- a/drivers/i2c/busses/i2c-imx-lpi2c.c

> +++ b/drivers/i2c/busses/i2c-imx-lpi2c.c

> @@ -18,6 +18,7 @@

>  #include <linux/module.h>

>  #include <linux/of.h>

>  #include <linux/of_device.h>

> +#include <linux/of_gpio.h>

>  #include <linux/pinctrl/consumer.h>

>  #include <linux/platform_device.h>

>  #include <linux/pm_runtime.h>

> @@ -108,6 +109,11 @@ struct lpi2c_imx_struct {

>  	unsigned int		txfifosize;

>  	unsigned int		rxfifosize;

>  	enum lpi2c_imx_mode	mode;

> +

> +	struct i2c_bus_recovery_info rinfo;

> +	struct pinctrl *pinctrl;

> +	struct pinctrl_state *pinctrl_pins_default;

> +	struct pinctrl_state *pinctrl_pins_gpio;

>  };

> 

>  static void lpi2c_imx_intctrl(struct lpi2c_imx_struct *lpi2c_imx, @@ -135,6

> +141,8 @@ static int lpi2c_imx_bus_busy(struct lpi2c_imx_struct *lpi2c_imx)

> 

>  		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {

>  			dev_dbg(&lpi2c_imx->adapter.dev, "bus not work\n");

> +			if (lpi2c_imx->adapter.bus_recovery_info)

> +				i2c_recover_bus(&lpi2c_imx->adapter);

>  			return -ETIMEDOUT;

>  		}

>  		schedule();

> @@ -192,6 +200,8 @@ static void lpi2c_imx_stop(struct lpi2c_imx_struct

> *lpi2c_imx)

> 

>  		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {

>  			dev_dbg(&lpi2c_imx->adapter.dev, "stop timeout\n");

> +			if (lpi2c_imx->adapter.bus_recovery_info)

> +				i2c_recover_bus(&lpi2c_imx->adapter);

>  			break;

>  		}

>  		schedule();

> @@ -329,6 +339,8 @@ static int lpi2c_imx_txfifo_empty(struct

> lpi2c_imx_struct *lpi2c_imx)

> 

>  		if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {

>  			dev_dbg(&lpi2c_imx->adapter.dev, "txfifo empty timeout\n");

> +			if (lpi2c_imx->adapter.bus_recovery_info)

> +				i2c_recover_bus(&lpi2c_imx->adapter);

>  			return -ETIMEDOUT;

>  		}

>  		schedule();

> @@ -528,6 +540,71 @@ static irqreturn_t lpi2c_imx_isr(int irq, void *dev_id)

>  	return IRQ_HANDLED;

>  }

> 

> +static void lpi2c_imx_prepare_recovery(struct i2c_adapter *adap) {

> +	struct lpi2c_imx_struct *lpi2c_imx;

> +

> +	lpi2c_imx = container_of(adap, struct lpi2c_imx_struct, adapter);

> +

> +	pinctrl_select_state(lpi2c_imx->pinctrl,

> +lpi2c_imx->pinctrl_pins_gpio); }

> +

> +static void lpi2c_imx_unprepare_recovery(struct i2c_adapter *adap) {

> +	struct lpi2c_imx_struct *lpi2c_imx;

> +

> +	lpi2c_imx = container_of(adap, struct lpi2c_imx_struct, adapter);

> +

> +	pinctrl_select_state(lpi2c_imx->pinctrl,

> +lpi2c_imx->pinctrl_pins_default); }

> +

> +/*

> + * We switch SCL and SDA to their GPIO function and do some bitbanging

> + * for bus recovery. These alternative pinmux settings can be

> + * described in the device tree by a separate pinctrl state "gpio". If

> + * this is missing this is not a big problem, the only implication is

> + * that we can't do bus recovery.

> + */

> +static int lpi2c_imx_init_recovery_info(struct lpi2c_imx_struct *lpi2c_imx,

> +		struct platform_device *pdev)

> +{

> +	struct i2c_bus_recovery_info *rinfo = &lpi2c_imx->rinfo;

> +

> +	lpi2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);

> +	if (!lpi2c_imx->pinctrl || IS_ERR(lpi2c_imx->pinctrl)) {

> +		dev_info(&pdev->dev, "can't get pinctrl, bus recovery not

> supported\n");

> +		return PTR_ERR(lpi2c_imx->pinctrl);

> +	}

> +

> +	lpi2c_imx->pinctrl_pins_default = pinctrl_lookup_state(lpi2c_imx->pinctrl,

> +			PINCTRL_STATE_DEFAULT);

> +	lpi2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(lpi2c_imx->pinctrl,

> +			"gpio");

> +	rinfo->sda_gpiod = devm_gpiod_get(&pdev->dev, "sda", GPIOD_IN);

> +	rinfo->scl_gpiod = devm_gpiod_get(&pdev->dev, "scl",

> +GPIOD_OUT_HIGH_OPEN_DRAIN);

> +

> +	if (PTR_ERR(rinfo->sda_gpiod) == -EPROBE_DEFER ||

> +	    PTR_ERR(rinfo->scl_gpiod) == -EPROBE_DEFER) {

> +		return -EPROBE_DEFER;

> +	} else if (IS_ERR(rinfo->sda_gpiod) ||

> +		   IS_ERR(rinfo->scl_gpiod) ||

> +		   IS_ERR(lpi2c_imx->pinctrl_pins_default) ||

> +		   IS_ERR(lpi2c_imx->pinctrl_pins_gpio)) {

> +		dev_dbg(&pdev->dev, "recovery information incomplete\n");

> +		return 0;

> +	}

> +

> +	dev_info(&pdev->dev, "using scl%s for recovery\n",

> +		 rinfo->sda_gpiod ? ",sda" : "");

> +

> +	rinfo->prepare_recovery = lpi2c_imx_prepare_recovery;

> +	rinfo->unprepare_recovery = lpi2c_imx_unprepare_recovery;

> +	rinfo->recover_bus = i2c_generic_scl_recovery;

> +	lpi2c_imx->adapter.bus_recovery_info = rinfo;

> +

> +	return 0;

> +}

> +

>  static u32 lpi2c_imx_func(struct i2c_adapter *adapter)  {

>  	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | @@ -632,6 +709,12

> @@ static int lpi2c_imx_probe(struct platform_device *pdev)

>  	lpi2c_imx->txfifosize = 1 << (temp & 0x0f);

>  	lpi2c_imx->rxfifosize = 1 << ((temp >> 8) & 0x0f);

> 

> +	/* Init optional bus recovery function */

> +	ret = lpi2c_imx_init_recovery_info(lpi2c_imx, pdev);

> +	/* Give it another chance if pinctrl used is not ready yet */

> +	if (ret == -EPROBE_DEFER)

> +		goto rpm_disable;

> +

>  	ret = i2c_add_adapter(&lpi2c_imx->adapter);

>  	if (ret)

>  		goto rpm_disable;

> --

> 2.25.1