From 88731dc0e8b6ba244e097b412b302acf3b9cc889 Mon Sep 17 00:00:00 2001 From: Max Krummenacher Date: Thu, 2 Apr 2015 12:42:51 +0200 Subject: i2c-imx: add gpio recovery functionality If the I2C is disturbed by other signals sometimes the i2c-imx bus gets stuck with SDA being low. This adds a recovery function to the bus driver. When e.g. a device driver detects a stuck bus int i2c_recover_bus(struct i2c_adapter *adap) can be called to try to recover the bus. --- Documentation/devicetree/bindings/i2c/i2c-imx.txt | 23 +++++++ arch/arm/boot/dts/imx6qdl-apalis.dtsi | 12 +++- drivers/i2c/busses/i2c-imx.c | 73 +++++++++++++++++++++++ drivers/i2c/i2c-core.c | 1 + 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-imx.txt b/Documentation/devicetree/bindings/i2c/i2c-imx.txt index 3614242e7732..9f3924fb503d 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-imx.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-imx.txt @@ -23,3 +23,26 @@ i2c@70038000 { /* HS-I2C on i.MX51 */ interrupts = <64>; clock-frequency = <400000>; }; + +The driver can provide recovery functionality for cases were the bus gets +stack with SDA pulled low. +In order to enable this one has to specify an additional pinctrl property with +the name 'recovery' to mux GPIO functionality on SDA/SCA and a gpios +property with the GPIOs for SDA/SCL. +The lack of the gpios property disables the functionality. + +Examples: +&i2c3 { + clock-frequency = <100000>; + pinctrl-names = "default", "recovery"; + pinctrl-0 = <&pinctrl_i2c3_1>; + pinctrl-1 = <&pinctrl_i2c3_recovery_1>; + gpios = <&gpio3 18 0 /* sda */ + &gpio3 17 0 /* scl */ + >; + status = "disabled"; +}; + +When e.g. a device driver detects a stuck bus +int i2c_recover_bus(struct i2c_adapter *adap) +can be called to try to recover the bus. diff --git a/arch/arm/boot/dts/imx6qdl-apalis.dtsi b/arch/arm/boot/dts/imx6qdl-apalis.dtsi index f57b0750a5f1..545e23a048db 100644 --- a/arch/arm/boot/dts/imx6qdl-apalis.dtsi +++ b/arch/arm/boot/dts/imx6qdl-apalis.dtsi @@ -409,8 +409,12 @@ */ &i2c3 { clock-frequency = <100000>; - pinctrl-names = "default"; + pinctrl-names = "default", "recovery"; pinctrl-0 = <&pinctrl_i2c3_1>; + pinctrl-1 = <&pinctrl_i2c3_recovery_1>; + gpios = <&gpio3 18 0 /* sda */ + &gpio3 17 0 /* scl */ + >; status = "disabled"; }; @@ -540,6 +544,12 @@ MX6QDL_PAD_EIM_D16__GPIO3_IO16 PAD_CTRL_HYS_PU /* DDC bitbang */ >; }; + pinctrl_i2c3_recovery_1: i2c2_recovery { + fsl,pins = < + MX6QDL_PAD_EIM_D17__GPIO3_IO17 PAD_CTRL_HYS_PU /* DDC bitbang */ + MX6QDL_PAD_EIM_D18__GPIO3_IO18 PAD_CTRL_HYS_PU /* DDC bitbang */ + >; + }; pinctrl_mmc_cd: gpio_mmc_cd { fsl,pins = < MX6QDL_PAD_DI0_PIN4__GPIO4_IO20 PAD_CTRL_NO /* MMC1 CD */ diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 9782527a8668..54a4b349f76e 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -495,16 +496,71 @@ static struct i2c_algorithm i2c_imx_algo = { .functionality = i2c_imx_func, }; +static int of_i2c_imx_get_pins(struct device_node *np, + unsigned int *sda_pin, unsigned int *scl_pin) +{ + if (of_gpio_count(np) < 2) + return -ENODEV; + + *sda_pin = of_get_gpio(np, 0); + *scl_pin = of_get_gpio(np, 1); + if (!gpio_is_valid(*sda_pin) || !gpio_is_valid(*scl_pin)) { + pr_err("%s: invalid GPIO pins, sda=%d/scl=%d\n", + np->full_name, *sda_pin, *scl_pin); + return -ENODEV; + } + + return 0; +} + +/* i2c bus recovery routines */ +static int get_scl_gpio_value(struct i2c_adapter *adap) +{ + return gpio_get_value(adap->bus_recovery_info->scl_gpio); +} + +static void set_scl_gpio_value(struct i2c_adapter *adap, int val) +{ + gpio_set_value(adap->bus_recovery_info->scl_gpio, val); +} + +static int get_sda_gpio_value(struct i2c_adapter *adap) +{ + return gpio_get_value(adap->bus_recovery_info->sda_gpio); +} + +int i2c_imx_recover_bus(struct i2c_adapter *adap) +{ + /* i2c_imx_(un)prepare_recovery can not be used for pinmuxing, as we + * need the data from struct i2c_adapter */ + int ret; + + if(!devm_pinctrl_get_select(adap->dev.parent, "recovery")) { + dev_err(adap->dev.parent, "%s> switching to 'recover' pinmux failed\n", __func__); + } + + ret = i2c_generic_gpio_recovery(adap); + + if(!devm_pinctrl_get_select_default(adap->dev.parent)) { + dev_err(adap->dev.parent, "<%s> switching from 'recover' to 'default' pinmux failed\n", __func__); + } + + return ret; +} + static int __init i2c_imx_probe(struct platform_device *pdev) { const struct of_device_id *of_id = of_match_device(i2c_imx_dt_ids, &pdev->dev); struct imx_i2c_struct *i2c_imx; + struct i2c_bus_recovery_info *bri; struct resource *res; struct imxi2c_platform_data *pdata = pdev->dev.platform_data; struct pinctrl *pinctrl; void __iomem *base; int irq, ret; + int sda_gpio = -1; + int scl_gpio = -1; dev_dbg(&pdev->dev, "<%s>\n", __func__); @@ -570,6 +626,23 @@ static int __init i2c_imx_probe(struct platform_device *pdev) /* Set up adapter data */ i2c_set_adapdata(&i2c_imx->adapter, i2c_imx); + /* Set up adapter recovery info */ + if (of_i2c_imx_get_pins(pdev->dev.of_node, &sda_gpio, &scl_gpio) >= 0) { + bri = devm_kzalloc(&pdev->dev, + sizeof(struct i2c_bus_recovery_info), GFP_KERNEL); + if (!bri) + dev_info(&pdev->dev, "can't allocate i2c recovery struct, recovery will not work\n"); + else { + bri->recover_bus = i2c_imx_recover_bus; + bri->get_sda = get_sda_gpio_value; + bri->get_scl = get_scl_gpio_value; + bri->set_scl = set_scl_gpio_value; + bri->sda_gpio = sda_gpio; + bri->scl_gpio = scl_gpio; + i2c_imx->adapter.bus_recovery_info = bri; + } + } + /* Set up clock divider */ i2c_imx->bitrate = IMX_I2C_BIT_RATE; ret = of_property_read_u32(pdev->dev.of_node, diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 48e31ed69dbf..49e278ef7587 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -229,6 +229,7 @@ int i2c_recover_bus(struct i2c_adapter *adap) dev_dbg(&adap->dev, "Trying i2c bus recovery\n"); return adap->bus_recovery_info->recover_bus(adap); } +EXPORT_SYMBOL_GPL(i2c_recover_bus); static int i2c_device_probe(struct device *dev) { -- cgit v1.2.3