Commit 1c4b6c3b authored by Gao Pan's avatar Gao Pan Committed by Wolfram Sang

i2c: imx: implement bus recovery

Implement bus recovery methods for i2c-imx so we can recover from
situations where SCL/SDA are stuck low.

Once i2c bus SCL/SDA are stuck low during transfer, config the i2c
pinctrl to gpio mode by calling pinctrl sleep set function, and then
use GPIO to emulate the i2c protocol to send nine dummy clock to recover
i2c device. After recovery, set i2c pinctrl to default group setting.
Signed-off-by: default avatarFugang Duan <B38611@freescale.com>
Signed-off-by: default avatarGao Pan <b54642@freescale.com>
Signed-off-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 3861841d
...@@ -14,6 +14,10 @@ Optional properties: ...@@ -14,6 +14,10 @@ Optional properties:
The absence of the propoerty indicates the default frequency 100 kHz. The absence of the propoerty indicates the default frequency 100 kHz.
- dmas: A list of two dma specifiers, one for each entry in dma-names. - dmas: A list of two dma specifiers, one for each entry in dma-names.
- dma-names: should contain "tx" and "rx". - dma-names: should contain "tx" and "rx".
- scl-gpios: specify the gpio related to SCL pin
- sda-gpios: specify the gpio related to SDA pin
- pinctrl: add extra pinctrl to configure i2c pins to gpio function for i2c
bus recovery, call it "gpio" state
Examples: Examples:
...@@ -37,4 +41,9 @@ i2c0: i2c@40066000 { /* i2c0 on vf610 */ ...@@ -37,4 +41,9 @@ i2c0: i2c@40066000 { /* i2c0 on vf610 */
dmas = <&edma0 0 50>, dmas = <&edma0 0 50>,
<&edma0 0 51>; <&edma0 0 51>;
dma-names = "rx","tx"; dma-names = "rx","tx";
pinctrl-names = "default", "gpio";
pinctrl-0 = <&pinctrl_i2c1>;
pinctrl-1 = <&pinctrl_i2c1_gpio>;
scl-gpios = <&gpio5 26 GPIO_ACTIVE_HIGH>;
sda-gpios = <&gpio5 27 GPIO_ACTIVE_HIGH>;
}; };
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_dma.h> #include <linux/of_dma.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/i2c-imx.h> #include <linux/platform_data/i2c-imx.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -207,6 +208,11 @@ struct imx_i2c_struct { ...@@ -207,6 +208,11 @@ struct imx_i2c_struct {
unsigned int cur_clk; unsigned int cur_clk;
unsigned int bitrate; unsigned int bitrate;
const struct imx_i2c_hwdata *hwdata; const struct imx_i2c_hwdata *hwdata;
struct i2c_bus_recovery_info rinfo;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_pins_default;
struct pinctrl_state *pinctrl_pins_gpio;
struct imx_i2c_dma *dma; struct imx_i2c_dma *dma;
}; };
...@@ -896,6 +902,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, ...@@ -896,6 +902,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
/* Start I2C transfer */ /* Start I2C transfer */
result = i2c_imx_start(i2c_imx); result = i2c_imx_start(i2c_imx);
if (result) {
if (i2c_imx->adapter.bus_recovery_info) {
i2c_recover_bus(&i2c_imx->adapter);
result = i2c_imx_start(i2c_imx);
}
}
if (result) if (result)
goto fail0; goto fail0;
...@@ -956,6 +969,55 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, ...@@ -956,6 +969,55 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
return (result < 0) ? result : num; return (result < 0) ? result : num;
} }
static void i2c_imx_prepare_recovery(struct i2c_adapter *adap)
{
struct imx_i2c_struct *i2c_imx;
i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_gpio);
}
static void i2c_imx_unprepare_recovery(struct i2c_adapter *adap)
{
struct imx_i2c_struct *i2c_imx;
i2c_imx = container_of(adap, struct imx_i2c_struct, adapter);
pinctrl_select_state(i2c_imx->pinctrl, i2c_imx->pinctrl_pins_default);
}
static void i2c_imx_init_recovery_info(struct imx_i2c_struct *i2c_imx,
struct platform_device *pdev)
{
struct i2c_bus_recovery_info *rinfo = &i2c_imx->rinfo;
i2c_imx->pinctrl_pins_default = pinctrl_lookup_state(i2c_imx->pinctrl,
PINCTRL_STATE_DEFAULT);
i2c_imx->pinctrl_pins_gpio = pinctrl_lookup_state(i2c_imx->pinctrl,
"gpio");
rinfo->sda_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
"sda-gpios", 0, NULL);
rinfo->scl_gpio = of_get_named_gpio_flags(pdev->dev.of_node,
"scl-gpios", 0, NULL);
if (!gpio_is_valid(rinfo->sda_gpio) ||
!gpio_is_valid(rinfo->scl_gpio) ||
IS_ERR(i2c_imx->pinctrl_pins_default) ||
IS_ERR(i2c_imx->pinctrl_pins_gpio)) {
dev_dbg(&pdev->dev, "recovery information incomplete\n");
return;
}
dev_dbg(&pdev->dev, "using scl-gpio %d and sda-gpio %d for recovery\n",
rinfo->sda_gpio, rinfo->scl_gpio);
rinfo->prepare_recovery = i2c_imx_prepare_recovery;
rinfo->unprepare_recovery = i2c_imx_unprepare_recovery;
rinfo->recover_bus = i2c_generic_gpio_recovery;
i2c_imx->adapter.bus_recovery_info = rinfo;
}
static u32 i2c_imx_func(struct i2c_adapter *adapter) static u32 i2c_imx_func(struct i2c_adapter *adapter)
{ {
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
...@@ -1023,6 +1085,13 @@ static int i2c_imx_probe(struct platform_device *pdev) ...@@ -1023,6 +1085,13 @@ static int i2c_imx_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "can't enable I2C clock\n"); dev_err(&pdev->dev, "can't enable I2C clock\n");
return ret; return ret;
} }
i2c_imx->pinctrl = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(i2c_imx->pinctrl)) {
ret = PTR_ERR(i2c_imx->pinctrl);
goto clk_disable;
}
/* Request IRQ */ /* Request IRQ */
ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
pdev->name, i2c_imx); pdev->name, i2c_imx);
...@@ -1056,6 +1125,8 @@ static int i2c_imx_probe(struct platform_device *pdev) ...@@ -1056,6 +1125,8 @@ static int i2c_imx_probe(struct platform_device *pdev)
goto clk_disable; goto clk_disable;
} }
i2c_imx_init_recovery_info(i2c_imx, pdev);
/* Set up platform driver data */ /* Set up platform driver data */
platform_set_drvdata(pdev, i2c_imx); platform_set_drvdata(pdev, i2c_imx);
clk_disable_unprepare(i2c_imx->clk); clk_disable_unprepare(i2c_imx->clk);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment