Commit ae58d1e406986f31d1e88b32f5ac601506c196d8
Committed by
Wolfram Sang
1 parent
f8f5701bda
Exists in
master
and in
20 other branches
i2c: Add generic I2C multiplexer using pinctrl API
This is useful for SoCs whose I2C module's signals can be routed to different sets of pins at run-time, using the pinctrl API. +-----+ +-----+ | dev | | dev | +------------------------+ +-----+ +-----+ | SoC | | | | /----|------+--------+ | +---+ +------+ | child bus A, on first set of pins | |I2C|---|Pinmux| | | +---+ +------+ | child bus B, on second set of pins | \----|------+--------+--------+ | | | | | +------------------------+ +-----+ +-----+ +-----+ | dev | | dev | | dev | +-----+ +-----+ +-----+ Signed-off-by: Stephen Warren <swarren@nvidia.com> Acked-by: Linus Walleij <linus.walleij@linaro.org> Acked-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Showing 5 changed files with 426 additions and 0 deletions Side-by-side Diff
Documentation/devicetree/bindings/i2c/i2c-mux-pinctrl.txt
1 | +Pinctrl-based I2C Bus Mux | |
2 | + | |
3 | +This binding describes an I2C bus multiplexer that uses pin multiplexing to | |
4 | +route the I2C signals, and represents the pin multiplexing configuration | |
5 | +using the pinctrl device tree bindings. | |
6 | + | |
7 | + +-----+ +-----+ | |
8 | + | dev | | dev | | |
9 | + +------------------------+ +-----+ +-----+ | |
10 | + | SoC | | | | |
11 | + | /----|------+--------+ | |
12 | + | +---+ +------+ | child bus A, on first set of pins | |
13 | + | |I2C|---|Pinmux| | | |
14 | + | +---+ +------+ | child bus B, on second set of pins | |
15 | + | \----|------+--------+--------+ | |
16 | + | | | | | | |
17 | + +------------------------+ +-----+ +-----+ +-----+ | |
18 | + | dev | | dev | | dev | | |
19 | + +-----+ +-----+ +-----+ | |
20 | + | |
21 | +Required properties: | |
22 | +- compatible: i2c-mux-pinctrl | |
23 | +- i2c-parent: The phandle of the I2C bus that this multiplexer's master-side | |
24 | + port is connected to. | |
25 | + | |
26 | +Also required are: | |
27 | + | |
28 | +* Standard pinctrl properties that specify the pin mux state for each child | |
29 | + bus. See ../pinctrl/pinctrl-bindings.txt. | |
30 | + | |
31 | +* Standard I2C mux properties. See mux.txt in this directory. | |
32 | + | |
33 | +* I2C child bus nodes. See mux.txt in this directory. | |
34 | + | |
35 | +For each named state defined in the pinctrl-names property, an I2C child bus | |
36 | +will be created. I2C child bus numbers are assigned based on the index into | |
37 | +the pinctrl-names property. | |
38 | + | |
39 | +The only exception is that no bus will be created for a state named "idle". If | |
40 | +such a state is defined, it must be the last entry in pinctrl-names. For | |
41 | +example: | |
42 | + | |
43 | + pinctrl-names = "ddc", "pta", "idle" -> ddc = bus 0, pta = bus 1 | |
44 | + pinctrl-names = "ddc", "idle", "pta" -> Invalid ("idle" not last) | |
45 | + pinctrl-names = "idle", "ddc", "pta" -> Invalid ("idle" not last) | |
46 | + | |
47 | +Whenever an access is made to a device on a child bus, the relevant pinctrl | |
48 | +state will be programmed into hardware. | |
49 | + | |
50 | +If an idle state is defined, whenever an access is not being made to a device | |
51 | +on a child bus, the idle pinctrl state will be programmed into hardware. | |
52 | + | |
53 | +If an idle state is not defined, the most recently used pinctrl state will be | |
54 | +left programmed into hardware whenever no access is being made of a device on | |
55 | +a child bus. | |
56 | + | |
57 | +Example: | |
58 | + | |
59 | + i2cmux { | |
60 | + compatible = "i2c-mux-pinctrl"; | |
61 | + #address-cells = <1>; | |
62 | + #size-cells = <0>; | |
63 | + | |
64 | + i2c-parent = <&i2c1>; | |
65 | + | |
66 | + pinctrl-names = "ddc", "pta", "idle"; | |
67 | + pinctrl-0 = <&state_i2cmux_ddc>; | |
68 | + pinctrl-1 = <&state_i2cmux_pta>; | |
69 | + pinctrl-2 = <&state_i2cmux_idle>; | |
70 | + | |
71 | + i2c@0 { | |
72 | + reg = <0>; | |
73 | + #address-cells = <1>; | |
74 | + #size-cells = <0>; | |
75 | + | |
76 | + eeprom { | |
77 | + compatible = "eeprom"; | |
78 | + reg = <0x50>; | |
79 | + }; | |
80 | + }; | |
81 | + | |
82 | + i2c@1 { | |
83 | + reg = <1>; | |
84 | + #address-cells = <1>; | |
85 | + #size-cells = <0>; | |
86 | + | |
87 | + eeprom { | |
88 | + compatible = "eeprom"; | |
89 | + reg = <0x50>; | |
90 | + }; | |
91 | + }; | |
92 | + }; |
drivers/i2c/muxes/Kconfig
... | ... | @@ -37,5 +37,17 @@ |
37 | 37 | This driver can also be built as a module. If so, the module |
38 | 38 | will be called i2c-mux-pca954x. |
39 | 39 | |
40 | +config I2C_MUX_PINCTRL | |
41 | + tristate "pinctrl-based I2C multiplexer" | |
42 | + depends on PINCTRL | |
43 | + help | |
44 | + If you say yes to this option, support will be included for an I2C | |
45 | + multiplexer that uses the pinctrl subsystem, i.e. pin multiplexing. | |
46 | + This is useful for SoCs whose I2C module's signals can be routed to | |
47 | + different sets of pins at run-time. | |
48 | + | |
49 | + This driver can also be built as a module. If so, the module will be | |
50 | + called pinctrl-i2cmux. | |
51 | + | |
40 | 52 | endmenu |
drivers/i2c/muxes/Makefile
drivers/i2c/muxes/i2c-mux-pinctrl.c
1 | +/* | |
2 | + * I2C multiplexer using pinctrl API | |
3 | + * | |
4 | + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify it | |
7 | + * under the terms and conditions of the GNU General Public License, | |
8 | + * version 2, as published by the Free Software Foundation. | |
9 | + * | |
10 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | + * more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU General Public License | |
16 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | + */ | |
18 | + | |
19 | +#include <linux/i2c.h> | |
20 | +#include <linux/i2c-mux.h> | |
21 | +#include <linux/init.h> | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/of_i2c.h> | |
24 | +#include <linux/pinctrl/consumer.h> | |
25 | +#include <linux/i2c-mux-pinctrl.h> | |
26 | +#include <linux/platform_device.h> | |
27 | +#include <linux/slab.h> | |
28 | + | |
29 | +struct i2c_mux_pinctrl { | |
30 | + struct device *dev; | |
31 | + struct i2c_mux_pinctrl_platform_data *pdata; | |
32 | + struct pinctrl *pinctrl; | |
33 | + struct pinctrl_state **states; | |
34 | + struct pinctrl_state *state_idle; | |
35 | + struct i2c_adapter *parent; | |
36 | + struct i2c_adapter **busses; | |
37 | +}; | |
38 | + | |
39 | +static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data, | |
40 | + u32 chan) | |
41 | +{ | |
42 | + struct i2c_mux_pinctrl *mux = data; | |
43 | + | |
44 | + return pinctrl_select_state(mux->pinctrl, mux->states[chan]); | |
45 | +} | |
46 | + | |
47 | +static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data, | |
48 | + u32 chan) | |
49 | +{ | |
50 | + struct i2c_mux_pinctrl *mux = data; | |
51 | + | |
52 | + return pinctrl_select_state(mux->pinctrl, mux->state_idle); | |
53 | +} | |
54 | + | |
55 | +#ifdef CONFIG_OF | |
56 | +static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, | |
57 | + struct platform_device *pdev) | |
58 | +{ | |
59 | + struct device_node *np = pdev->dev.of_node; | |
60 | + int num_names, i, ret; | |
61 | + struct device_node *adapter_np; | |
62 | + struct i2c_adapter *adapter; | |
63 | + | |
64 | + if (!np) | |
65 | + return 0; | |
66 | + | |
67 | + mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); | |
68 | + if (!mux->pdata) { | |
69 | + dev_err(mux->dev, | |
70 | + "Cannot allocate i2c_mux_pinctrl_platform_data\n"); | |
71 | + return -ENOMEM; | |
72 | + } | |
73 | + | |
74 | + num_names = of_property_count_strings(np, "pinctrl-names"); | |
75 | + if (num_names < 0) { | |
76 | + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", | |
77 | + num_names); | |
78 | + return num_names; | |
79 | + } | |
80 | + | |
81 | + mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, | |
82 | + sizeof(*mux->pdata->pinctrl_states) * num_names, | |
83 | + GFP_KERNEL); | |
84 | + if (!mux->pdata->pinctrl_states) { | |
85 | + dev_err(mux->dev, "Cannot allocate pinctrl_states\n"); | |
86 | + return -ENOMEM; | |
87 | + } | |
88 | + | |
89 | + for (i = 0; i < num_names; i++) { | |
90 | + ret = of_property_read_string_index(np, "pinctrl-names", i, | |
91 | + &mux->pdata->pinctrl_states[mux->pdata->bus_count]); | |
92 | + if (ret < 0) { | |
93 | + dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n", | |
94 | + ret); | |
95 | + return ret; | |
96 | + } | |
97 | + if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], | |
98 | + "idle")) { | |
99 | + if (i != num_names - 1) { | |
100 | + dev_err(mux->dev, "idle state must be last\n"); | |
101 | + return -EINVAL; | |
102 | + } | |
103 | + mux->pdata->pinctrl_state_idle = "idle"; | |
104 | + } else { | |
105 | + mux->pdata->bus_count++; | |
106 | + } | |
107 | + } | |
108 | + | |
109 | + adapter_np = of_parse_phandle(np, "i2c-parent", 0); | |
110 | + if (!adapter_np) { | |
111 | + dev_err(mux->dev, "Cannot parse i2c-parent\n"); | |
112 | + return -ENODEV; | |
113 | + } | |
114 | + adapter = of_find_i2c_adapter_by_node(adapter_np); | |
115 | + if (!adapter) { | |
116 | + dev_err(mux->dev, "Cannot find parent bus\n"); | |
117 | + return -ENODEV; | |
118 | + } | |
119 | + mux->pdata->parent_bus_num = i2c_adapter_id(adapter); | |
120 | + put_device(&adapter->dev); | |
121 | + | |
122 | + return 0; | |
123 | +} | |
124 | +#else | |
125 | +static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, | |
126 | + struct platform_device *pdev) | |
127 | +{ | |
128 | + return 0; | |
129 | +} | |
130 | +#endif | |
131 | + | |
132 | +static int __devinit i2c_mux_pinctrl_probe(struct platform_device *pdev) | |
133 | +{ | |
134 | + struct i2c_mux_pinctrl *mux; | |
135 | + int (*deselect)(struct i2c_adapter *, void *, u32); | |
136 | + int i, ret; | |
137 | + | |
138 | + mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); | |
139 | + if (!mux) { | |
140 | + dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n"); | |
141 | + ret = -ENOMEM; | |
142 | + goto err; | |
143 | + } | |
144 | + platform_set_drvdata(pdev, mux); | |
145 | + | |
146 | + mux->dev = &pdev->dev; | |
147 | + | |
148 | + mux->pdata = pdev->dev.platform_data; | |
149 | + if (!mux->pdata) { | |
150 | + ret = i2c_mux_pinctrl_parse_dt(mux, pdev); | |
151 | + if (ret < 0) | |
152 | + goto err; | |
153 | + } | |
154 | + if (!mux->pdata) { | |
155 | + dev_err(&pdev->dev, "Missing platform data\n"); | |
156 | + ret = -ENODEV; | |
157 | + goto err; | |
158 | + } | |
159 | + | |
160 | + mux->states = devm_kzalloc(&pdev->dev, | |
161 | + sizeof(*mux->states) * mux->pdata->bus_count, | |
162 | + GFP_KERNEL); | |
163 | + if (!mux->states) { | |
164 | + dev_err(&pdev->dev, "Cannot allocate states\n"); | |
165 | + ret = -ENOMEM; | |
166 | + goto err; | |
167 | + } | |
168 | + | |
169 | + mux->busses = devm_kzalloc(&pdev->dev, | |
170 | + sizeof(mux->busses) * mux->pdata->bus_count, | |
171 | + GFP_KERNEL); | |
172 | + if (!mux->states) { | |
173 | + dev_err(&pdev->dev, "Cannot allocate busses\n"); | |
174 | + ret = -ENOMEM; | |
175 | + goto err; | |
176 | + } | |
177 | + | |
178 | + mux->pinctrl = devm_pinctrl_get(&pdev->dev); | |
179 | + if (IS_ERR(mux->pinctrl)) { | |
180 | + ret = PTR_ERR(mux->pinctrl); | |
181 | + dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); | |
182 | + goto err; | |
183 | + } | |
184 | + for (i = 0; i < mux->pdata->bus_count; i++) { | |
185 | + mux->states[i] = pinctrl_lookup_state(mux->pinctrl, | |
186 | + mux->pdata->pinctrl_states[i]); | |
187 | + if (IS_ERR(mux->states[i])) { | |
188 | + ret = PTR_ERR(mux->states[i]); | |
189 | + dev_err(&pdev->dev, | |
190 | + "Cannot look up pinctrl state %s: %d\n", | |
191 | + mux->pdata->pinctrl_states[i], ret); | |
192 | + goto err; | |
193 | + } | |
194 | + } | |
195 | + if (mux->pdata->pinctrl_state_idle) { | |
196 | + mux->state_idle = pinctrl_lookup_state(mux->pinctrl, | |
197 | + mux->pdata->pinctrl_state_idle); | |
198 | + if (IS_ERR(mux->state_idle)) { | |
199 | + ret = PTR_ERR(mux->state_idle); | |
200 | + dev_err(&pdev->dev, | |
201 | + "Cannot look up pinctrl state %s: %d\n", | |
202 | + mux->pdata->pinctrl_state_idle, ret); | |
203 | + goto err; | |
204 | + } | |
205 | + | |
206 | + deselect = i2c_mux_pinctrl_deselect; | |
207 | + } else { | |
208 | + deselect = NULL; | |
209 | + } | |
210 | + | |
211 | + mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num); | |
212 | + if (!mux->parent) { | |
213 | + dev_err(&pdev->dev, "Parent adapter (%d) not found\n", | |
214 | + mux->pdata->parent_bus_num); | |
215 | + ret = -ENODEV; | |
216 | + goto err; | |
217 | + } | |
218 | + | |
219 | + for (i = 0; i < mux->pdata->bus_count; i++) { | |
220 | + u32 bus = mux->pdata->base_bus_num ? | |
221 | + (mux->pdata->base_bus_num + i) : 0; | |
222 | + | |
223 | + mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, | |
224 | + mux, bus, i, | |
225 | + i2c_mux_pinctrl_select, | |
226 | + deselect); | |
227 | + if (!mux->busses[i]) { | |
228 | + ret = -ENODEV; | |
229 | + dev_err(&pdev->dev, "Failed to add adapter %d\n", i); | |
230 | + goto err_del_adapter; | |
231 | + } | |
232 | + } | |
233 | + | |
234 | + return 0; | |
235 | + | |
236 | +err_del_adapter: | |
237 | + for (; i > 0; i--) | |
238 | + i2c_del_mux_adapter(mux->busses[i - 1]); | |
239 | + i2c_put_adapter(mux->parent); | |
240 | +err: | |
241 | + return ret; | |
242 | +} | |
243 | + | |
244 | +static int __devexit i2c_mux_pinctrl_remove(struct platform_device *pdev) | |
245 | +{ | |
246 | + struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev); | |
247 | + int i; | |
248 | + | |
249 | + for (i = 0; i < mux->pdata->bus_count; i++) | |
250 | + i2c_del_mux_adapter(mux->busses[i]); | |
251 | + | |
252 | + i2c_put_adapter(mux->parent); | |
253 | + | |
254 | + return 0; | |
255 | +} | |
256 | + | |
257 | +#ifdef CONFIG_OF | |
258 | +static const struct of_device_id i2c_mux_pinctrl_of_match[] __devinitconst = { | |
259 | + { .compatible = "i2c-mux-pinctrl", }, | |
260 | + {}, | |
261 | +}; | |
262 | +MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); | |
263 | +#endif | |
264 | + | |
265 | +static struct platform_driver i2c_mux_pinctrl_driver = { | |
266 | + .driver = { | |
267 | + .name = "i2c-mux-pinctrl", | |
268 | + .owner = THIS_MODULE, | |
269 | + .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), | |
270 | + }, | |
271 | + .probe = i2c_mux_pinctrl_probe, | |
272 | + .remove = __devexit_p(i2c_mux_pinctrl_remove), | |
273 | +}; | |
274 | +module_platform_driver(i2c_mux_pinctrl_driver); | |
275 | + | |
276 | +MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); | |
277 | +MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | |
278 | +MODULE_LICENSE("GPL v2"); | |
279 | +MODULE_ALIAS("platform:i2c-mux-pinctrl"); |
include/linux/i2c-mux-pinctrl.h
1 | +/* | |
2 | + * i2c-mux-pinctrl platform data | |
3 | + * | |
4 | + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify it | |
7 | + * under the terms and conditions of the GNU General Public License, | |
8 | + * version 2, as published by the Free Software Foundation. | |
9 | + * | |
10 | + * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
13 | + * more details. | |
14 | + * | |
15 | + * You should have received a copy of the GNU General Public License | |
16 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
17 | + */ | |
18 | + | |
19 | +#ifndef _LINUX_I2C_MUX_PINCTRL_H | |
20 | +#define _LINUX_I2C_MUX_PINCTRL_H | |
21 | + | |
22 | +/** | |
23 | + * struct i2c_mux_pinctrl_platform_data - Platform data for i2c-mux-pinctrl | |
24 | + * @parent_bus_num: Parent I2C bus number | |
25 | + * @base_bus_num: Base I2C bus number for the child busses. 0 for dynamic. | |
26 | + * @bus_count: Number of child busses. Also the number of elements in | |
27 | + * @pinctrl_states | |
28 | + * @pinctrl_states: The names of the pinctrl state to select for each child bus | |
29 | + * @pinctrl_state_idle: The pinctrl state to select when no child bus is being | |
30 | + * accessed. If NULL, the most recently used pinctrl state will be left | |
31 | + * selected. | |
32 | + */ | |
33 | +struct i2c_mux_pinctrl_platform_data { | |
34 | + int parent_bus_num; | |
35 | + int base_bus_num; | |
36 | + int bus_count; | |
37 | + const char **pinctrl_states; | |
38 | + const char *pinctrl_state_idle; | |
39 | +}; | |
40 | + | |
41 | +#endif |