summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorMax Krummenacher <max.krummenacher@toradex.com>2015-04-02 12:42:51 +0200
committerMax Krummenacher <max.krummenacher@toradex.com>2015-12-26 14:35:56 +0100
commit478388b32ac83fc44a75d63ae819aadb220c83ec (patch)
treee25ec8de54e851680e29730c05b29cf2e6e75f4e /drivers
parent8437c2f0e4fafb9a31dd260704cc6b7a22bf33f8 (diff)
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. (cherry picked from commit 88731dc0e8b6ba244e097b412b302acf3b9cc889) Conflicts: arch/arm/boot/dts/imx6qdl-apalis.dtsi drivers/i2c/busses/i2c-imx.c
Diffstat (limited to 'drivers')
-rw-r--r--drivers/i2c/busses/i2c-imx.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 3ed8e47785fc..bae984eebe08 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -52,6 +52,7 @@
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/platform_data/i2c-imx.h>
/** Defines ********************************************************************
@@ -610,16 +611,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 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 = dev_get_platdata(&pdev->dev);
void __iomem *base;
int irq, ret;
u32 bitrate;
+ int sda_gpio = -1;
+ int scl_gpio = -1;
dev_dbg(&pdev->dev, "<%s>\n", __func__);
@@ -682,6 +738,23 @@ static int 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 */
bitrate = IMX_I2C_BIT_RATE;
ret = of_property_read_u32(pdev->dev.of_node,