diff options
author | Peter Korsgaard <peter.korsgaard@barco.com> | 2011-01-10 16:11:23 -0500 |
---|---|---|
committer | Jean Delvare <khali@endymion.delvare> | 2011-01-10 16:11:23 -0500 |
commit | 92ed1a76ca31774eb27de14b2215841367c68056 (patch) | |
tree | f9becefa5dbcdaa8cfd65100e1735044cd3910d6 | |
parent | b18a5c80eb2e7e9c72d23f1960b09d78ddf7e5b0 (diff) |
i2c: Add generic I2C multiplexer using GPIO API
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.
Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
-rw-r--r-- | Documentation/i2c/muxes/gpio-i2cmux | 65 | ||||
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | drivers/i2c/muxes/Kconfig | 12 | ||||
-rw-r--r-- | drivers/i2c/muxes/Makefile | 1 | ||||
-rw-r--r-- | drivers/i2c/muxes/gpio-i2cmux.c | 184 | ||||
-rw-r--r-- | include/linux/gpio-i2cmux.h | 38 |
6 files changed, 308 insertions, 0 deletions
diff --git a/Documentation/i2c/muxes/gpio-i2cmux b/Documentation/i2c/muxes/gpio-i2cmux new file mode 100644 index 000000000000..811cd78d4cdc --- /dev/null +++ b/Documentation/i2c/muxes/gpio-i2cmux | |||
@@ -0,0 +1,65 @@ | |||
1 | Kernel driver gpio-i2cmux | ||
2 | |||
3 | Author: Peter Korsgaard <peter.korsgaard@barco.com> | ||
4 | |||
5 | Description | ||
6 | ----------- | ||
7 | |||
8 | gpio-i2cmux is an i2c mux driver providing access to I2C bus segments | ||
9 | from a master I2C bus and a hardware MUX controlled through GPIO pins. | ||
10 | |||
11 | E.G.: | ||
12 | |||
13 | ---------- ---------- Bus segment 1 - - - - - | ||
14 | | | SCL/SDA | |-------------- | | | ||
15 | | |------------| | | ||
16 | | | | | Bus segment 2 | | | ||
17 | | Linux | GPIO 1..N | MUX |--------------- Devices | ||
18 | | |------------| | | | | ||
19 | | | | | Bus segment M | ||
20 | | | | |---------------| | | ||
21 | ---------- ---------- - - - - - | ||
22 | |||
23 | SCL/SDA of the master I2C bus is multiplexed to bus segment 1..M | ||
24 | according to the settings of the GPIO pins 1..N. | ||
25 | |||
26 | Usage | ||
27 | ----- | ||
28 | |||
29 | gpio-i2cmux uses the platform bus, so you need to provide a struct | ||
30 | platform_device with the platform_data pointing to a struct | ||
31 | gpio_i2cmux_platform_data with the I2C adapter number of the master | ||
32 | bus, the number of bus segments to create and the GPIO pins used | ||
33 | to control it. See include/linux/gpio-i2cmux.h for details. | ||
34 | |||
35 | E.G. something like this for a MUX providing 4 bus segments | ||
36 | controlled through 3 GPIO pins: | ||
37 | |||
38 | #include <linux/gpio-i2cmux.h> | ||
39 | #include <linux/platform_device.h> | ||
40 | |||
41 | static const unsigned myboard_gpiomux_gpios[] = { | ||
42 | AT91_PIN_PC26, AT91_PIN_PC25, AT91_PIN_PC24 | ||
43 | }; | ||
44 | |||
45 | static const unsigned myboard_gpiomux_values[] = { | ||
46 | 0, 1, 2, 3 | ||
47 | }; | ||
48 | |||
49 | static struct gpio_i2cmux_platform_data myboard_i2cmux_data = { | ||
50 | .parent = 1, | ||
51 | .base_nr = 2, /* optional */ | ||
52 | .values = myboard_gpiomux_values, | ||
53 | .n_values = ARRAY_SIZE(myboard_gpiomux_values), | ||
54 | .gpios = myboard_gpiomux_gpios, | ||
55 | .n_gpios = ARRAY_SIZE(myboard_gpiomux_gpios), | ||
56 | .idle = 4, /* optional */ | ||
57 | }; | ||
58 | |||
59 | static struct platform_device myboard_i2cmux = { | ||
60 | .name = "gpio-i2cmux", | ||
61 | .id = 0, | ||
62 | .dev = { | ||
63 | .platform_data = &myboard_i2cmux_data, | ||
64 | }, | ||
65 | }; | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 23d04363a195..23a4765ab8d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -2608,6 +2608,14 @@ S: Supported | |||
2608 | F: drivers/i2c/busses/i2c-gpio.c | 2608 | F: drivers/i2c/busses/i2c-gpio.c |
2609 | F: include/linux/i2c-gpio.h | 2609 | F: include/linux/i2c-gpio.h |
2610 | 2610 | ||
2611 | GENERIC GPIO I2C MULTIPLEXER DRIVER | ||
2612 | M: Peter Korsgaard <peter.korsgaard@barco.com> | ||
2613 | L: linux-i2c@vger.kernel.org | ||
2614 | S: Supported | ||
2615 | F: drivers/i2c/muxes/gpio-i2cmux.c | ||
2616 | F: include/linux/gpio-i2cmux.h | ||
2617 | F: Documentation/i2c/muxes/gpio-i2cmux | ||
2618 | |||
2611 | GENERIC HDLC (WAN) DRIVERS | 2619 | GENERIC HDLC (WAN) DRIVERS |
2612 | M: Krzysztof Halasa <khc@pm.waw.pl> | 2620 | M: Krzysztof Halasa <khc@pm.waw.pl> |
2613 | W: http://www.kernel.org/pub/linux/utils/net/hdlc/ | 2621 | W: http://www.kernel.org/pub/linux/utils/net/hdlc/ |
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index 4d91d80bfd23..90b7a0163899 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig | |||
@@ -5,6 +5,18 @@ | |||
5 | menu "Multiplexer I2C Chip support" | 5 | menu "Multiplexer I2C Chip support" |
6 | depends on I2C_MUX | 6 | depends on I2C_MUX |
7 | 7 | ||
8 | config I2C_MUX_GPIO | ||
9 | tristate "GPIO-based I2C multiplexer" | ||
10 | depends on GENERIC_GPIO | ||
11 | help | ||
12 | If you say yes to this option, support will be included for a | ||
13 | GPIO based I2C multiplexer. This driver provides access to | ||
14 | I2C busses connected through a MUX, which is controlled | ||
15 | through GPIO pins. | ||
16 | |||
17 | This driver can also be built as a module. If so, the module | ||
18 | will be called gpio-i2cmux. | ||
19 | |||
8 | config I2C_MUX_PCA9541 | 20 | config I2C_MUX_PCA9541 |
9 | tristate "NXP PCA9541 I2C Master Selector" | 21 | tristate "NXP PCA9541 I2C Master Selector" |
10 | depends on EXPERIMENTAL | 22 | depends on EXPERIMENTAL |
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index d743806d9b42..4640436ea61f 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | # | 1 | # |
2 | # Makefile for multiplexer I2C chip drivers. | 2 | # Makefile for multiplexer I2C chip drivers. |
3 | 3 | ||
4 | obj-$(CONFIG_I2C_MUX_GPIO) += gpio-i2cmux.o | ||
4 | obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o | 5 | obj-$(CONFIG_I2C_MUX_PCA9541) += pca9541.o |
5 | obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o | 6 | obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o |
6 | 7 | ||
diff --git a/drivers/i2c/muxes/gpio-i2cmux.c b/drivers/i2c/muxes/gpio-i2cmux.c new file mode 100644 index 000000000000..7b6ce624cd6e --- /dev/null +++ b/drivers/i2c/muxes/gpio-i2cmux.c | |||
@@ -0,0 +1,184 @@ | |||
1 | /* | ||
2 | * I2C multiplexer using GPIO API | ||
3 | * | ||
4 | * Peter Korsgaard <peter.korsgaard@barco.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/i2c.h> | ||
12 | #include <linux/i2c-mux.h> | ||
13 | #include <linux/gpio-i2cmux.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/init.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/gpio.h> | ||
19 | |||
20 | struct gpiomux { | ||
21 | struct i2c_adapter *parent; | ||
22 | struct i2c_adapter **adap; /* child busses */ | ||
23 | struct gpio_i2cmux_platform_data data; | ||
24 | }; | ||
25 | |||
26 | static void gpiomux_set(const struct gpiomux *mux, unsigned val) | ||
27 | { | ||
28 | int i; | ||
29 | |||
30 | for (i = 0; i < mux->data.n_gpios; i++) | ||
31 | gpio_set_value(mux->data.gpios[i], val & (1 << i)); | ||
32 | } | ||
33 | |||
34 | static int gpiomux_select(struct i2c_adapter *adap, void *data, u32 chan) | ||
35 | { | ||
36 | struct gpiomux *mux = data; | ||
37 | |||
38 | gpiomux_set(mux, mux->data.values[chan]); | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int gpiomux_deselect(struct i2c_adapter *adap, void *data, u32 chan) | ||
44 | { | ||
45 | struct gpiomux *mux = data; | ||
46 | |||
47 | gpiomux_set(mux, mux->data.idle); | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static int __devinit gpiomux_probe(struct platform_device *pdev) | ||
53 | { | ||
54 | struct gpiomux *mux; | ||
55 | struct gpio_i2cmux_platform_data *pdata; | ||
56 | struct i2c_adapter *parent; | ||
57 | int (*deselect) (struct i2c_adapter *, void *, u32); | ||
58 | unsigned initial_state; | ||
59 | int i, ret; | ||
60 | |||
61 | pdata = pdev->dev.platform_data; | ||
62 | if (!pdata) { | ||
63 | dev_err(&pdev->dev, "Missing platform data\n"); | ||
64 | return -ENODEV; | ||
65 | } | ||
66 | |||
67 | parent = i2c_get_adapter(pdata->parent); | ||
68 | if (!parent) { | ||
69 | dev_err(&pdev->dev, "Parent adapter (%d) not found\n", | ||
70 | pdata->parent); | ||
71 | return -ENODEV; | ||
72 | } | ||
73 | |||
74 | mux = kzalloc(sizeof(*mux), GFP_KERNEL); | ||
75 | if (!mux) { | ||
76 | ret = -ENOMEM; | ||
77 | goto alloc_failed; | ||
78 | } | ||
79 | |||
80 | mux->parent = parent; | ||
81 | mux->data = *pdata; | ||
82 | mux->adap = kzalloc(sizeof(struct i2c_adapter *) * pdata->n_values, | ||
83 | GFP_KERNEL); | ||
84 | if (!mux->adap) { | ||
85 | ret = -ENOMEM; | ||
86 | goto alloc_failed2; | ||
87 | } | ||
88 | |||
89 | if (pdata->idle != GPIO_I2CMUX_NO_IDLE) { | ||
90 | initial_state = pdata->idle; | ||
91 | deselect = gpiomux_deselect; | ||
92 | } else { | ||
93 | initial_state = pdata->values[0]; | ||
94 | deselect = NULL; | ||
95 | } | ||
96 | |||
97 | for (i = 0; i < pdata->n_gpios; i++) { | ||
98 | ret = gpio_request(pdata->gpios[i], "gpio-i2cmux"); | ||
99 | if (ret) | ||
100 | goto err_request_gpio; | ||
101 | gpio_direction_output(pdata->gpios[i], | ||
102 | initial_state & (1 << i)); | ||
103 | } | ||
104 | |||
105 | for (i = 0; i < pdata->n_values; i++) { | ||
106 | u32 nr = pdata->base_nr ? (pdata->base_nr + i) : 0; | ||
107 | |||
108 | mux->adap[i] = i2c_add_mux_adapter(parent, mux, nr, i, | ||
109 | gpiomux_select, deselect); | ||
110 | if (!mux->adap[i]) { | ||
111 | ret = -ENODEV; | ||
112 | dev_err(&pdev->dev, "Failed to add adapter %d\n", i); | ||
113 | goto add_adapter_failed; | ||
114 | } | ||
115 | } | ||
116 | |||
117 | dev_info(&pdev->dev, "%d port mux on %s adapter\n", | ||
118 | pdata->n_values, parent->name); | ||
119 | |||
120 | platform_set_drvdata(pdev, mux); | ||
121 | |||
122 | return 0; | ||
123 | |||
124 | add_adapter_failed: | ||
125 | for (; i > 0; i--) | ||
126 | i2c_del_mux_adapter(mux->adap[i - 1]); | ||
127 | i = pdata->n_gpios; | ||
128 | err_request_gpio: | ||
129 | for (; i > 0; i--) | ||
130 | gpio_free(pdata->gpios[i - 1]); | ||
131 | kfree(mux->adap); | ||
132 | alloc_failed2: | ||
133 | kfree(mux); | ||
134 | alloc_failed: | ||
135 | i2c_put_adapter(parent); | ||
136 | |||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | static int __devexit gpiomux_remove(struct platform_device *pdev) | ||
141 | { | ||
142 | struct gpiomux *mux = platform_get_drvdata(pdev); | ||
143 | int i; | ||
144 | |||
145 | for (i = 0; i < mux->data.n_values; i++) | ||
146 | i2c_del_mux_adapter(mux->adap[i]); | ||
147 | |||
148 | for (i = 0; i < mux->data.n_gpios; i++) | ||
149 | gpio_free(mux->data.gpios[i]); | ||
150 | |||
151 | platform_set_drvdata(pdev, NULL); | ||
152 | i2c_put_adapter(mux->parent); | ||
153 | kfree(mux->adap); | ||
154 | kfree(mux); | ||
155 | |||
156 | return 0; | ||
157 | } | ||
158 | |||
159 | static struct platform_driver gpiomux_driver = { | ||
160 | .probe = gpiomux_probe, | ||
161 | .remove = __devexit_p(gpiomux_remove), | ||
162 | .driver = { | ||
163 | .owner = THIS_MODULE, | ||
164 | .name = "gpio-i2cmux", | ||
165 | }, | ||
166 | }; | ||
167 | |||
168 | static int __init gpiomux_init(void) | ||
169 | { | ||
170 | return platform_driver_register(&gpiomux_driver); | ||
171 | } | ||
172 | |||
173 | static void __exit gpiomux_exit(void) | ||
174 | { | ||
175 | platform_driver_unregister(&gpiomux_driver); | ||
176 | } | ||
177 | |||
178 | module_init(gpiomux_init); | ||
179 | module_exit(gpiomux_exit); | ||
180 | |||
181 | MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver"); | ||
182 | MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>"); | ||
183 | MODULE_LICENSE("GPL"); | ||
184 | MODULE_ALIAS("platform:gpio-i2cmux"); | ||
diff --git a/include/linux/gpio-i2cmux.h b/include/linux/gpio-i2cmux.h new file mode 100644 index 000000000000..4a333bb0bd0d --- /dev/null +++ b/include/linux/gpio-i2cmux.h | |||
@@ -0,0 +1,38 @@ | |||
1 | /* | ||
2 | * gpio-i2cmux interface to platform code | ||
3 | * | ||
4 | * Peter Korsgaard <peter.korsgaard@barco.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #ifndef _LINUX_GPIO_I2CMUX_H | ||
12 | #define _LINUX_GPIO_I2CMUX_H | ||
13 | |||
14 | /* MUX has no specific idle mode */ | ||
15 | #define GPIO_I2CMUX_NO_IDLE ((unsigned)-1) | ||
16 | |||
17 | /** | ||
18 | * struct gpio_i2cmux_platform_data - Platform-dependent data for gpio-i2cmux | ||
19 | * @parent: Parent I2C bus adapter number | ||
20 | * @base_nr: Base I2C bus number to number adapters from or zero for dynamic | ||
21 | * @values: Array of bitmasks of GPIO settings (low/high) for each | ||
22 | * position | ||
23 | * @n_values: Number of multiplexer positions (busses to instantiate) | ||
24 | * @gpios: Array of GPIO numbers used to control MUX | ||
25 | * @n_gpios: Number of GPIOs used to control MUX | ||
26 | * @idle: Bitmask to write to MUX when idle or GPIO_I2CMUX_NO_IDLE if not used | ||
27 | */ | ||
28 | struct gpio_i2cmux_platform_data { | ||
29 | int parent; | ||
30 | int base_nr; | ||
31 | const unsigned *values; | ||
32 | int n_values; | ||
33 | const unsigned *gpios; | ||
34 | int n_gpios; | ||
35 | unsigned idle; | ||
36 | }; | ||
37 | |||
38 | #endif /* _LINUX_GPIO_I2CMUX_H */ | ||