Commit | Line | Data |
---|---|---|
ae58d1e4 SW |
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> | |
ae58d1e4 | 21 | #include <linux/module.h> |
ae58d1e4 SW |
22 | #include <linux/pinctrl/consumer.h> |
23 | #include <linux/i2c-mux-pinctrl.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/slab.h> | |
4edd65e6 | 26 | #include <linux/of.h> |
6ef91fcc | 27 | #include "../../pinctrl/core.h" |
ae58d1e4 SW |
28 | |
29 | struct i2c_mux_pinctrl { | |
ae58d1e4 SW |
30 | struct i2c_mux_pinctrl_platform_data *pdata; |
31 | struct pinctrl *pinctrl; | |
32 | struct pinctrl_state **states; | |
33 | struct pinctrl_state *state_idle; | |
ae58d1e4 SW |
34 | }; |
35 | ||
4bbe7fb0 | 36 | static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) |
ae58d1e4 | 37 | { |
4bbe7fb0 | 38 | struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); |
ae58d1e4 SW |
39 | |
40 | return pinctrl_select_state(mux->pinctrl, mux->states[chan]); | |
41 | } | |
42 | ||
4bbe7fb0 | 43 | static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) |
ae58d1e4 | 44 | { |
4bbe7fb0 | 45 | struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); |
ae58d1e4 SW |
46 | |
47 | return pinctrl_select_state(mux->pinctrl, mux->state_idle); | |
48 | } | |
49 | ||
50 | #ifdef CONFIG_OF | |
51 | static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, | |
4bbe7fb0 | 52 | struct platform_device *pdev) |
ae58d1e4 SW |
53 | { |
54 | struct device_node *np = pdev->dev.of_node; | |
55 | int num_names, i, ret; | |
56 | struct device_node *adapter_np; | |
57 | struct i2c_adapter *adapter; | |
58 | ||
59 | if (!np) | |
60 | return 0; | |
61 | ||
62 | mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL); | |
4bbe7fb0 | 63 | if (!mux->pdata) |
ae58d1e4 | 64 | return -ENOMEM; |
ae58d1e4 SW |
65 | |
66 | num_names = of_property_count_strings(np, "pinctrl-names"); | |
67 | if (num_names < 0) { | |
4bbe7fb0 | 68 | dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", |
ae58d1e4 SW |
69 | num_names); |
70 | return num_names; | |
71 | } | |
72 | ||
73 | mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev, | |
74 | sizeof(*mux->pdata->pinctrl_states) * num_names, | |
75 | GFP_KERNEL); | |
4bbe7fb0 | 76 | if (!mux->pdata->pinctrl_states) |
ae58d1e4 | 77 | return -ENOMEM; |
ae58d1e4 SW |
78 | |
79 | for (i = 0; i < num_names; i++) { | |
80 | ret = of_property_read_string_index(np, "pinctrl-names", i, | |
81 | &mux->pdata->pinctrl_states[mux->pdata->bus_count]); | |
82 | if (ret < 0) { | |
4bbe7fb0 | 83 | dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n", |
ae58d1e4 SW |
84 | ret); |
85 | return ret; | |
86 | } | |
87 | if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count], | |
88 | "idle")) { | |
89 | if (i != num_names - 1) { | |
4bbe7fb0 PR |
90 | dev_err(&pdev->dev, |
91 | "idle state must be last\n"); | |
ae58d1e4 SW |
92 | return -EINVAL; |
93 | } | |
94 | mux->pdata->pinctrl_state_idle = "idle"; | |
95 | } else { | |
96 | mux->pdata->bus_count++; | |
97 | } | |
98 | } | |
99 | ||
100 | adapter_np = of_parse_phandle(np, "i2c-parent", 0); | |
101 | if (!adapter_np) { | |
4bbe7fb0 | 102 | dev_err(&pdev->dev, "Cannot parse i2c-parent\n"); |
ae58d1e4 SW |
103 | return -ENODEV; |
104 | } | |
105 | adapter = of_find_i2c_adapter_by_node(adapter_np); | |
bdbf4a29 | 106 | of_node_put(adapter_np); |
ae58d1e4 | 107 | if (!adapter) { |
4bbe7fb0 | 108 | dev_err(&pdev->dev, "Cannot find parent bus\n"); |
2737de46 | 109 | return -EPROBE_DEFER; |
ae58d1e4 SW |
110 | } |
111 | mux->pdata->parent_bus_num = i2c_adapter_id(adapter); | |
112 | put_device(&adapter->dev); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | #else | |
117 | static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux, | |
118 | struct platform_device *pdev) | |
119 | { | |
120 | return 0; | |
121 | } | |
122 | #endif | |
123 | ||
6ef91fcc PR |
124 | static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( |
125 | struct pinctrl_state *state) | |
126 | { | |
127 | struct i2c_adapter *root = NULL; | |
128 | struct pinctrl_setting *setting; | |
129 | struct i2c_adapter *pin_root; | |
130 | ||
131 | list_for_each_entry(setting, &state->settings, node) { | |
132 | pin_root = i2c_root_adapter(setting->pctldev->dev); | |
133 | if (!pin_root) | |
134 | return NULL; | |
135 | if (!root) | |
136 | root = pin_root; | |
137 | else if (root != pin_root) | |
138 | return NULL; | |
139 | } | |
140 | ||
141 | return root; | |
142 | } | |
143 | ||
0b255e92 | 144 | static int i2c_mux_pinctrl_probe(struct platform_device *pdev) |
ae58d1e4 | 145 | { |
4bbe7fb0 | 146 | struct i2c_mux_core *muxc; |
ae58d1e4 | 147 | struct i2c_mux_pinctrl *mux; |
6ef91fcc | 148 | struct i2c_adapter *root; |
ae58d1e4 SW |
149 | int i, ret; |
150 | ||
151 | mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL); | |
152 | if (!mux) { | |
ae58d1e4 SW |
153 | ret = -ENOMEM; |
154 | goto err; | |
155 | } | |
ae58d1e4 | 156 | |
6d4028c6 | 157 | mux->pdata = dev_get_platdata(&pdev->dev); |
ae58d1e4 SW |
158 | if (!mux->pdata) { |
159 | ret = i2c_mux_pinctrl_parse_dt(mux, pdev); | |
160 | if (ret < 0) | |
161 | goto err; | |
162 | } | |
163 | if (!mux->pdata) { | |
164 | dev_err(&pdev->dev, "Missing platform data\n"); | |
165 | ret = -ENODEV; | |
166 | goto err; | |
167 | } | |
168 | ||
169 | mux->states = devm_kzalloc(&pdev->dev, | |
170 | sizeof(*mux->states) * mux->pdata->bus_count, | |
171 | GFP_KERNEL); | |
172 | if (!mux->states) { | |
173 | dev_err(&pdev->dev, "Cannot allocate states\n"); | |
174 | ret = -ENOMEM; | |
175 | goto err; | |
176 | } | |
177 | ||
4bbe7fb0 PR |
178 | muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0, |
179 | i2c_mux_pinctrl_select, NULL); | |
180 | if (!muxc) { | |
ae58d1e4 SW |
181 | ret = -ENOMEM; |
182 | goto err; | |
183 | } | |
4bbe7fb0 PR |
184 | muxc->priv = mux; |
185 | ||
186 | platform_set_drvdata(pdev, muxc); | |
ae58d1e4 SW |
187 | |
188 | mux->pinctrl = devm_pinctrl_get(&pdev->dev); | |
189 | if (IS_ERR(mux->pinctrl)) { | |
190 | ret = PTR_ERR(mux->pinctrl); | |
191 | dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret); | |
192 | goto err; | |
193 | } | |
194 | for (i = 0; i < mux->pdata->bus_count; i++) { | |
195 | mux->states[i] = pinctrl_lookup_state(mux->pinctrl, | |
196 | mux->pdata->pinctrl_states[i]); | |
2c89e5d8 WS |
197 | if (IS_ERR(mux->states[i])) { |
198 | ret = PTR_ERR(mux->states[i]); | |
199 | dev_err(&pdev->dev, | |
200 | "Cannot look up pinctrl state %s: %d\n", | |
201 | mux->pdata->pinctrl_states[i], ret); | |
202 | goto err; | |
203 | } | |
ae58d1e4 SW |
204 | } |
205 | if (mux->pdata->pinctrl_state_idle) { | |
206 | mux->state_idle = pinctrl_lookup_state(mux->pinctrl, | |
207 | mux->pdata->pinctrl_state_idle); | |
208 | if (IS_ERR(mux->state_idle)) { | |
209 | ret = PTR_ERR(mux->state_idle); | |
210 | dev_err(&pdev->dev, | |
211 | "Cannot look up pinctrl state %s: %d\n", | |
212 | mux->pdata->pinctrl_state_idle, ret); | |
213 | goto err; | |
214 | } | |
215 | ||
4bbe7fb0 | 216 | muxc->deselect = i2c_mux_pinctrl_deselect; |
ae58d1e4 SW |
217 | } |
218 | ||
4bbe7fb0 PR |
219 | muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num); |
220 | if (!muxc->parent) { | |
ae58d1e4 SW |
221 | dev_err(&pdev->dev, "Parent adapter (%d) not found\n", |
222 | mux->pdata->parent_bus_num); | |
2737de46 | 223 | ret = -EPROBE_DEFER; |
ae58d1e4 SW |
224 | goto err; |
225 | } | |
226 | ||
6ef91fcc PR |
227 | root = i2c_root_adapter(&muxc->parent->dev); |
228 | ||
229 | muxc->mux_locked = true; | |
230 | for (i = 0; i < mux->pdata->bus_count; i++) { | |
231 | if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) { | |
232 | muxc->mux_locked = false; | |
233 | break; | |
234 | } | |
235 | } | |
236 | if (muxc->mux_locked && mux->pdata->pinctrl_state_idle && | |
237 | root != i2c_mux_pinctrl_root_adapter(mux->state_idle)) | |
238 | muxc->mux_locked = false; | |
239 | ||
240 | if (muxc->mux_locked) | |
241 | dev_info(&pdev->dev, "mux-locked i2c mux\n"); | |
242 | ||
ae58d1e4 SW |
243 | for (i = 0; i < mux->pdata->bus_count; i++) { |
244 | u32 bus = mux->pdata->base_bus_num ? | |
245 | (mux->pdata->base_bus_num + i) : 0; | |
246 | ||
4bbe7fb0 PR |
247 | ret = i2c_mux_add_adapter(muxc, bus, i, 0); |
248 | if (ret) { | |
ae58d1e4 SW |
249 | dev_err(&pdev->dev, "Failed to add adapter %d\n", i); |
250 | goto err_del_adapter; | |
251 | } | |
252 | } | |
253 | ||
254 | return 0; | |
255 | ||
256 | err_del_adapter: | |
4bbe7fb0 PR |
257 | i2c_mux_del_adapters(muxc); |
258 | i2c_put_adapter(muxc->parent); | |
ae58d1e4 SW |
259 | err: |
260 | return ret; | |
261 | } | |
262 | ||
0b255e92 | 263 | static int i2c_mux_pinctrl_remove(struct platform_device *pdev) |
ae58d1e4 | 264 | { |
4bbe7fb0 | 265 | struct i2c_mux_core *muxc = platform_get_drvdata(pdev); |
ae58d1e4 | 266 | |
4bbe7fb0 PR |
267 | i2c_mux_del_adapters(muxc); |
268 | i2c_put_adapter(muxc->parent); | |
ae58d1e4 SW |
269 | return 0; |
270 | } | |
271 | ||
272 | #ifdef CONFIG_OF | |
0b255e92 | 273 | static const struct of_device_id i2c_mux_pinctrl_of_match[] = { |
ae58d1e4 SW |
274 | { .compatible = "i2c-mux-pinctrl", }, |
275 | {}, | |
276 | }; | |
277 | MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); | |
278 | #endif | |
279 | ||
280 | static struct platform_driver i2c_mux_pinctrl_driver = { | |
281 | .driver = { | |
282 | .name = "i2c-mux-pinctrl", | |
ae58d1e4 SW |
283 | .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match), |
284 | }, | |
285 | .probe = i2c_mux_pinctrl_probe, | |
0b255e92 | 286 | .remove = i2c_mux_pinctrl_remove, |
ae58d1e4 SW |
287 | }; |
288 | module_platform_driver(i2c_mux_pinctrl_driver); | |
289 | ||
290 | MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver"); | |
291 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); | |
292 | MODULE_LICENSE("GPL v2"); | |
293 | MODULE_ALIAS("platform:i2c-mux-pinctrl"); |