From d166aec2c769ae0ebc6bf54366d794469cec5122 Mon Sep 17 00:00:00 2001
From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
Date: Wed, 13 Jun 2018 14:06:59 +0300
Subject: [PATCH 063/122] i2c: mix: pca954x: reset mux in case of error during
 bus (de)select

Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
---
 drivers/i2c/muxes/i2c-mux-pca954x.c | 36 ++++++++++++++++++++++++++++++------
 1 file changed, 30 insertions(+), 6 deletions(-)

diff --git a/drivers/i2c/muxes/i2c-mux-pca954x.c b/drivers/i2c/muxes/i2c-mux-pca954x.c
index 7b992db..0c8c965 100644
--- a/drivers/i2c/muxes/i2c-mux-pca954x.c
+++ b/drivers/i2c/muxes/i2c-mux-pca954x.c
@@ -35,6 +35,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
@@ -78,6 +79,7 @@ struct chip_desc {
 struct pca954x {
 	const struct chip_desc *chip;
 
+	struct gpio_desc *gpio;	/* reset gpio */
 	u8 last_chan;		/* last register value */
 	u8 deselect;
 	struct i2c_client *client;
@@ -207,6 +209,15 @@ static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
 	/* Only select the channel if its different from the last channel */
 	if (data->last_chan != regval) {
 		ret = pca954x_reg_write(muxc->parent, client, regval);
+		if (ret < 0) {
+			dev_err(&data->client->dev, "error selecting channed %d (was 0x%02x), reseting mux\n",
+				chan, data->last_chan);
+			gpiod_set_value(data->gpio, 1);
+			usleep_range(1, 10);
+			gpiod_set_value(data->gpio, 0);
+			/* retry */
+			ret = pca954x_reg_write(muxc->parent, client, regval);
+		}
 		data->last_chan = ret < 0 ? 0 : regval;
 	}
 
@@ -217,13 +228,23 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
 {
 	struct pca954x *data = i2c_mux_priv(muxc);
 	struct i2c_client *client = data->client;
+	int ret;
 
 	if (!(data->deselect & (1 << chan)))
 		return 0;
 
 	/* Deselect active channel */
 	data->last_chan = 0;
-	return pca954x_reg_write(muxc->parent, client, data->last_chan);
+	ret = pca954x_reg_write(muxc->parent, client, data->last_chan);
+	if (ret < 0) {
+		dev_err(&data->client->dev, "error deselecting channed %d, reseting mux\n", chan);
+		gpiod_set_value(data->gpio, 1);
+		usleep_range(1, 10);
+		gpiod_set_value(data->gpio, 0);
+		/* retry */
+		ret = pca954x_reg_write(muxc->parent, client, data->last_chan);
+	}
+	return ret;
 }
 
 static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
@@ -344,7 +365,6 @@ static int pca954x_probe(struct i2c_client *client,
 	struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
 	struct device_node *of_node = client->dev.of_node;
 	bool idle_disconnect_dt;
-	struct gpio_desc *gpio;
 	int num, force, class;
 	struct i2c_mux_core *muxc;
 	struct pca954x *data;
@@ -364,10 +384,14 @@ static int pca954x_probe(struct i2c_client *client,
 	i2c_set_clientdata(client, muxc);
 	data->client = client;
 
-	/* Get the mux out of reset if a reset GPIO is specified. */
-	gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
-	if (IS_ERR(gpio))
-		return PTR_ERR(gpio);
+	/* Perform reset if a reset GPIO is specified. */
+	data->gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
+	if (IS_ERR(data->gpio)) {
+		return PTR_ERR(data->gpio);
+	} else {
+		usleep_range(1, 10);
+		gpiod_set_value(data->gpio, 0);
+	}
 
 	/* Write the mux register at addr to verify
 	 * that the mux is in fact present. This also
-- 
2.7.4

