i2c: muxes: add i2c gpio multiplexer driver

Add an i2c mux driver providing access to i2c bus segments using a
hardware MUX sitting on a master bus and controlled through gpio pins.

E.G. something like:

         ----------              ----------  Bus segment 1   - - - - -
        |          | SCL/SDA    |          |-------------- |           |
        |          |------------|          |
        |          |            |          | Bus segment 2 |           |
        |  Linux   | GPIO 1..N  |   MUX    |---------------   Devices
        |          |------------|          |               |           |
        |          |            |          | Bus segment M
        |          |            |          |---------------|           |
         ----------              ----------                  - - - - -

SCL/SDA of the master I2C bus is multiplexed to bus segment 1..M
according to the settings of the GPIO pins 1..N.

Note commit log from kernel
commit 92ed1a76("i2c: Add generic I2C multiplexer using GPIO API")

Signed-off-by: Peng Fan <peng.fan@nxp.com>
Tested-by: Peng Fan <peng.fan@nxp.com> (i.MX6QP-Sabreauto)
Cc: Heiko Schocher <hs@denx.de>
Cc: Stefano Babic <sbabic@denx.de>
Cc: Simon Glass <sjg@chromium.org>
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 48900ed..156380c 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -34,3 +34,12 @@
 	  paritioning I2C bus and connect multiple devices with the same address
 	  to the same I2C controller where driver handles proper routing to
 	  target i2c device. PCA9544 and PCA9548 are supported.
+
+config I2C_MUX_GPIO
+        tristate "GPIO-based I2C multiplexer"
+	depends on I2C_MUX && DM_GPIO
+	help
+	  If you say yes to this option, support will be included for
+	  a GPIO based I2C multiplexer. This driver provides access to
+	  I2C busses connected through a MUX, which is controlled
+	  through GPIO pins.
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 0811add..3831f4e 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -6,3 +6,4 @@
 obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
 obj-$(CONFIG_$(SPL_)I2C_MUX) += i2c-mux-uclass.o
 obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
+obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o
diff --git a/drivers/i2c/muxes/i2c-mux-gpio.c b/drivers/i2c/muxes/i2c-mux-gpio.c
new file mode 100644
index 0000000..0269b3a
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-gpio.c
@@ -0,0 +1,138 @@
+/*
+ * I2C multiplexer using GPIO API
+ *
+ * Copyright 2017 NXP
+ *
+ * Peng Fan <peng.fan@nxp.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <asm/io.h>
+#include <asm-generic/gpio.h>
+#include <common.h>
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <linux/errno.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * struct i2c_mux_gpio_priv - private data for i2c mux gpio
+ *
+ * @values: the reg value of each child node
+ * @n_values: num of regs
+ * @gpios: the mux-gpios array
+ * @n_gpios: num of gpios in mux-gpios
+ * @idle: the value of idle-state
+ */
+struct i2c_mux_gpio_priv {
+	u32 *values;
+	int n_values;
+	struct gpio_desc *gpios;
+	int n_gpios;
+	u32 idle;
+};
+
+
+static int i2c_mux_gpio_select(struct udevice *dev, struct udevice *bus,
+			       uint channel)
+{
+	struct i2c_mux_gpio_priv *priv = dev_get_priv(dev);
+	int i, ret;
+
+	for (i = 0; i < priv->n_gpios; i++) {
+		ret = dm_gpio_set_value(&priv->gpios[i], (channel >> i) & 1);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int i2c_mux_gpio_deselect(struct udevice *dev, struct udevice *bus,
+				 uint channel)
+{
+	struct i2c_mux_gpio_priv *priv = dev_get_priv(dev);
+	int i, ret;
+
+	for (i = 0; i < priv->n_gpios; i++) {
+		ret = dm_gpio_set_value(&priv->gpios[i], (priv->idle >> i) & 1);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int i2c_mux_gpio_probe(struct udevice *dev)
+{
+	const void *fdt = gd->fdt_blob;
+	int node = dev_of_offset(dev);
+	struct i2c_mux_gpio_priv *mux = dev_get_priv(dev);
+	struct gpio_desc *gpios;
+	u32 *values;
+	int i = 0, subnode, ret;
+
+	mux->n_values = fdtdec_get_child_count(fdt, node);
+	values = devm_kzalloc(dev, sizeof(*mux->values) * mux->n_values,
+			      GFP_KERNEL);
+	if (!values) {
+		dev_err(dev, "Cannot alloc values array");
+		return -ENOMEM;
+	}
+
+	fdt_for_each_subnode(subnode, fdt, node) {
+		*(values + i) = fdtdec_get_uint(fdt, subnode, "reg", -1);
+		i++;
+	}
+
+	mux->values = values;
+
+	mux->idle = fdtdec_get_uint(fdt, node, "idle-state", -1);
+
+	mux->n_gpios = gpio_get_list_count(dev, "mux-gpios");
+	if (mux->n_gpios < 0) {
+		dev_err(dev, "Missing mux-gpios property\n");
+		return -EINVAL;
+	}
+
+	gpios = devm_kzalloc(dev, sizeof(struct gpio_desc) * mux->n_gpios,
+			     GFP_KERNEL);
+	if (!gpios) {
+		dev_err(dev, "Cannot allocate gpios array\n");
+		return -ENOMEM;
+	}
+
+	ret = gpio_request_list_by_name(dev, "mux-gpios", gpios, mux->n_gpios,
+					GPIOD_IS_OUT_ACTIVE);
+	if (ret <= 0) {
+		dev_err(dev, "Failed to request mux-gpios\n");
+		return ret;
+	}
+
+	mux->gpios = gpios;
+
+	return 0;
+}
+
+static const struct i2c_mux_ops i2c_mux_gpio_ops = {
+	.select = i2c_mux_gpio_select,
+	.deselect = i2c_mux_gpio_deselect,
+};
+
+static const struct udevice_id i2c_mux_gpio_ids[] = {
+	{ .compatible = "i2c-mux-gpio", },
+	{}
+};
+
+U_BOOT_DRIVER(i2c_mux_gpio) = {
+	.name = "i2c_mux_gpio",
+	.id = UCLASS_I2C_MUX,
+	.of_match = i2c_mux_gpio_ids,
+	.ops = &i2c_mux_gpio_ops,
+	.probe = i2c_mux_gpio_probe,
+	.priv_auto_alloc_size = sizeof(struct i2c_mux_gpio_priv),
+};