From: Peter Korsgaard <peter.korsgaard@xxxxxxxxx> Add an i2c mux driver providing virtual i2c busses using a hardware MUX sitting on a master bus and controlled through gpio pins. E.G. something like: ---------- ---------- Virtual bus 1 - - - - - | | SCL/SDA | |-------------- | | | |------------| | | | | | Virtual bus 2 | | | Linux | GPIO 1..N | MUX |--------------- Devices | |------------| | | | | | | | Virtual bus M | | | |---------------| | ---------- ---------- - - - - - SCL/SDA of the master I2C bus is multiplexed to virtual bus 1..M according to the settings of the GPIO pins 1..N. Signed-off-by: Peter Korsgaard <peter.korsgaard@xxxxxxxxx> --- Changes since v1: - Use i2c-mux infrastructure - Move to drivers/i2c/muxes Documentation/i2c/busses/i2c-gpiomux | 65 +++++++++++++ MAINTAINERS | 7 ++ drivers/i2c/muxes/Kconfig | 12 +++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/i2c-gpiomux.c | 172 ++++++++++++++++++++++++++++++++++ include/linux/i2c-gpiomux.h | 35 +++++++ 6 files changed, 292 insertions(+), 0 deletions(-) create mode 100644 Documentation/i2c/busses/i2c-gpiomux create mode 100644 drivers/i2c/muxes/i2c-gpiomux.c create mode 100644 include/linux/i2c-gpiomux.h diff --git a/Documentation/i2c/busses/i2c-gpiomux b/Documentation/i2c/busses/i2c-gpiomux new file mode 100644 index 0000000..b0a7746 --- /dev/null +++ b/Documentation/i2c/busses/i2c-gpiomux @@ -0,0 +1,65 @@ +Kernel driver i2c-gpiomux + +Author: Peter Korsgaard <peter.korsgaard@xxxxxxxxx> + +Description +----------- + +i2c-gpiomux is an i2c bus driver providing virtual I2C busses from a +master I2C bus and a hardware MUX controlled through GPIO pins. + +E.G.: + + ---------- ---------- Virtual bus 1 - - - - - + | | SCL/SDA | |-------------- | | + | |------------| | + | | | | Virtual bus 2 | | + | Linux | GPIO 1..N | MUX |--------------- Devices + | |------------| | | | + | | | | Virtual bus M + | | | |---------------| | + ---------- ---------- - - - - - + +SCL/SDA of the master I2C bus is multiplexed to virtual bus 1..M +according to the settings of the GPIO pins 1..N. + +Usage +----- + +i2c-gpiomux uses the platform bus, so you need to provide a struct +platform_device with the platform_data pointing to a struct +gpiomux_i2c_platform_data with the I2C adapter number of the master +bus, the number of virtual busses to create and the GPIO pins used +to control it. See include/linux/i2c-gpiomux.h for details. + +E.G. something like this for a MUX providing 4 virtual busses +controlled through 3 GPIO pins: + +#include <linux/i2c-gpiomux.h> +#include <linux/platform_device.h> + +static unsigned myboard_gpiomux_pins[] = { + AT91_PIN_PC26, AT91_PIN_PC25, AT91_PIN_PC24 +}; + +static unsigned myboard_gpiomux_values[] = { + 0, 1, 2, 3 +}; + +static struct gpiomux_i2c_platform_data myboard_i2cmux_data = { + .parent = 1, + .base_nr = 2, + .busses = ARRAY_SIZE(myboard_gpiomux_values), + .gpios = ARRAY_SIZE(myboard_gpiomux_pins), + .gpio = myboard_gpiomux_pins, + .values = myboard_gpiomux_values, + .idle = 4, +}; + +static struct platform_device myboard_i2cmux = { + .name = "i2c-gpiomux", + .id = 0, + .dev = { + .platform_data = &myboard_i2cmux_data, + }, +}; diff --git a/MAINTAINERS b/MAINTAINERS index 7679bf3..f63d518 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2550,6 +2550,13 @@ S: Supported F: drivers/i2c/busses/i2c-gpio.c F: include/linux/i2c-gpio.h +GENERIC GPIO I2C MULTIPLEXER DRIVER +M: Peter Korsgaard <peter.korsgaard@xxxxxxxxx> +L: linux-i2c@xxxxxxxxxxxxxxx +S: Supported +F: drivers/i2c/muxes/i2c-gpiomux.c +F: include/linux/i2c-gpiomux.h + GENERIC HDLC (WAN) DRIVERS M: Krzysztof Halasa <khc@xxxxxxxxx> W: http://www.kernel.org/pub/linux/utils/net/hdlc/ diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 4c9a99c..d9e987d 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -15,4 +15,16 @@ config I2C_MUX_PCA954x This driver can also be built as a module. If so, the module will be called pca954x. + config I2C_GPIOMUX + tristate "GPIO-based I2C multiplexer" + depends on GENERIC_GPIO + help + If you say yes to this option, support will be included for a + GPIO based I2C multiplexer. This driver provides virtual I2C + busses from a master bus through a MUX controlled through + the generic GPIO interface. + + This driver can also be built as a module. If so, the module + will be called i2c-gpiomux. + endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index bd83b52..3cd2118 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -2,6 +2,7 @@ # Makefile for multiplexer I2C chip drivers. obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o +obj-$(CONFIG_I2C_GPIOMUX) += i2c-gpiomux.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/i2c/muxes/i2c-gpiomux.c b/drivers/i2c/muxes/i2c-gpiomux.c new file mode 100644 index 0000000..6e19ef4 --- /dev/null +++ b/drivers/i2c/muxes/i2c-gpiomux.c @@ -0,0 +1,172 @@ +/* + * I2C multiplexer using GPIO API + * + * Peter Korsgaard <peter.korsgaard@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/i2c.h> +#include <linux/i2c-mux.h> +#include <linux/i2c-gpiomux.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/gpio.h> + +struct gpiomux_i2c { + struct i2c_adapter *parent; + struct i2c_adapter **adap; /* child busses */ + struct gpiomux_i2c_platform_data data; +}; + +static void gpiomux_set(struct gpiomux_i2c *i2c, unsigned val) +{ + int i; + + for (i = 0; i < i2c->data.gpios; i++) + gpio_set_value(i2c->data.gpio[i], val & (1<<i)); +} + +static int gpiomux_select(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux_i2c *i2c = data; + + gpiomux_set(i2c, i2c->data.values[chan]); + + return 0; +} + +static int gpiomux_deselect(struct i2c_adapter *adap, void *data, u32 chan) +{ + struct gpiomux_i2c *i2c = data; + + gpiomux_set(i2c, i2c->data.idle); + + return 0; +} + +static int __devinit gpiomux_probe(struct platform_device *pdev) +{ + struct gpiomux_i2c *i2c; + struct gpiomux_i2c_platform_data *pdata; + struct i2c_adapter *parent; + int i, ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENODEV; + } + + parent = i2c_get_adapter(pdata->parent); + if (!parent) { + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", + pdata->parent); + return -ENODEV; + } + + i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto alloc_failed; + } + + i2c->parent = parent; + i2c->data = *pdata; + i2c->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->busses, + GFP_KERNEL); + if (!i2c->adap) { + ret = -ENOMEM; + goto alloc_failed2; + } + + for (i = 0; i < pdata->gpios; i++) { + ret = gpio_request(pdata->gpio[i], "i2c-gpiomux"); + if (ret) + goto err_request_gpio; + gpio_direction_output(pdata->gpio[i], pdata->idle & (1<<i)); + } + + for (i = 0; i < pdata->busses; i++) { + i2c->adap[i] = + i2c_add_mux_adapter(parent, i2c, pdata->base_nr + i, i, + gpiomux_select, gpiomux_deselect); + if (!i2c->adap[i]) { + ret = -ENODEV; + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); + goto add_adapter_failed; + } + } + + dev_info(&pdev->dev, "%d port mux on %s adapter\n", + pdata->busses, parent->name); + + platform_set_drvdata(pdev, i2c); + + return 0; + +add_adapter_failed: + for (; i > 0; i--) + i2c_del_mux_adapter(i2c->adap[i - 1]); + i = pdata->gpios; +err_request_gpio: + for (; i > 0; i--) + gpio_free(pdata->gpio[i - 1]); + kfree(i2c->adap); +alloc_failed2: + kfree(i2c); +alloc_failed: + i2c_put_adapter(parent); + + return ret; +} + +static int __devexit gpiomux_remove(struct platform_device *pdev) +{ + struct gpiomux_i2c *i2c = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < i2c->data.busses; i++) + i2c_del_mux_adapter(i2c->adap[i]); + + for (i = 0; i < i2c->data.gpios; i++) + gpio_free(i2c->data.gpio[i]); + + platform_set_drvdata(pdev, NULL); + i2c_put_adapter(i2c->parent); + kfree(i2c->adap); + kfree(i2c); + + return 0; +} + +static struct platform_driver gpiomux_driver = { + .probe = gpiomux_probe, + .remove = __devexit_p(gpiomux_remove), + .driver = { + .owner = THIS_MODULE, + .name = "i2c-gpiomux", + }, +}; + +static int __init gpiomux_init(void) +{ + return platform_driver_register(&gpiomux_driver); +} + +static void __exit gpiomux_exit(void) +{ + platform_driver_unregister(&gpiomux_driver); +} + +module_init(gpiomux_init); +module_exit(gpiomux_exit); + +MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); +MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@xxxxxxxxx>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:i2c-gpiomux"); diff --git a/include/linux/i2c-gpiomux.h b/include/linux/i2c-gpiomux.h new file mode 100644 index 0000000..2388ff2 --- /dev/null +++ b/include/linux/i2c-gpiomux.h @@ -0,0 +1,35 @@ +/* + * i2c-gpiomux interface to platform code + * + * Peter Korsgaard <peter.korsgaard@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _LINUX_I2C_GPIOMUX_H +#define _LINUX_I2C_GPIOMUX_H + +/** + * struct gpiomux_i2c_platform_data - Platform-dependent data for i2c-gpiomux + * @parent: Parent I2C bus adapter number + * @base_nr: Base bus number value to number adapters from + * @busses: Number of multiplexer positions (busses to instantiate) + * @gpios: Number of gpios used to control MUX + * @gpio: Array of @gpios gpio numbers used to control MUX + * @values: Array of @bussed bitmasks of gpio settings (low/high) for each + * position + * @idle: Bitmask to write to MUX when idle + */ +struct gpiomux_i2c_platform_data { + int parent; + int base_nr; + int busses; + int gpios; + unsigned *gpio; + unsigned *values; + unsigned idle; +}; + +#endif /* _LINUX_I2C_GPIOMUX_H */ -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html