diff options
Diffstat (limited to 'kernel/drivers/i2c/busses/i2c-imx.c')
-rw-r--r-- | kernel/drivers/i2c/busses/i2c-imx.c | 76 |
1 files changed, 74 insertions, 2 deletions
diff --git a/kernel/drivers/i2c/busses/i2c-imx.c b/kernel/drivers/i2c/busses/i2c-imx.c index a53a7dd66..d4d853680 100644 --- a/kernel/drivers/i2c/busses/i2c-imx.c +++ b/kernel/drivers/i2c/busses/i2c-imx.c @@ -49,6 +49,8 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/of_dma.h> +#include <linux/of_gpio.h> +#include <linux/pinctrl/consumer.h> #include <linux/platform_data/i2c-imx.h> #include <linux/platform_device.h> #include <linux/sched.h> @@ -207,6 +209,11 @@ struct imx_i2c_struct { unsigned int cur_clk; unsigned int bitrate; 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; }; @@ -241,7 +248,7 @@ static struct imx_i2c_hwdata vf610_i2c_hwdata = { }; -static struct platform_device_id imx_i2c_devtype[] = { +static const struct platform_device_id imx_i2c_devtype[] = { { .name = "imx1-i2c", .driver_data = (kernel_ulong_t)&imx1_i2c_hwdata, @@ -461,7 +468,7 @@ static int i2c_imx_acked(struct imx_i2c_struct *i2c_imx) { if (imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR) & I2SR_RXAK) { dev_dbg(&i2c_imx->adapter.dev, "<%s> No ACK\n", __func__); - return -EIO; /* No ACK */ + return -ENXIO; /* No ACK */ } dev_dbg(&i2c_imx->adapter.dev, "<%s> ACK received\n", __func__); @@ -896,6 +903,13 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, /* Start I2C transfer */ 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) goto fail0; @@ -956,6 +970,55 @@ fail0: 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) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL @@ -1023,6 +1086,13 @@ static int i2c_imx_probe(struct platform_device *pdev) dev_err(&pdev->dev, "can't enable I2C clock\n"); 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 */ ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0, pdev->name, i2c_imx); @@ -1049,6 +1119,8 @@ static int i2c_imx_probe(struct platform_device *pdev) i2c_imx, IMX_I2C_I2CR); imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR); + i2c_imx_init_recovery_info(i2c_imx, pdev); + /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); if (ret < 0) { |