blob: 1b8dc711815ed36089862dad31f967eccad5a81d [file] [log] [blame]
Stephen Warrenae58d1e2012-05-18 09:29:34 -06001/*
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>
Stephen Warrenae58d1e2012-05-18 09:29:34 -060021#include <linux/module.h>
Stephen Warrenae58d1e2012-05-18 09:29:34 -060022#include <linux/pinctrl/consumer.h>
23#include <linux/i2c-mux-pinctrl.h>
24#include <linux/platform_device.h>
25#include <linux/slab.h>
Sachin Kamat4edd65e2013-10-16 15:26:33 +053026#include <linux/of.h>
Stephen Warrenae58d1e2012-05-18 09:29:34 -060027
28struct i2c_mux_pinctrl {
Stephen Warrenae58d1e2012-05-18 09:29:34 -060029 struct i2c_mux_pinctrl_platform_data *pdata;
30 struct pinctrl *pinctrl;
31 struct pinctrl_state **states;
32 struct pinctrl_state *state_idle;
Stephen Warrenae58d1e2012-05-18 09:29:34 -060033};
34
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020035static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
Stephen Warrenae58d1e2012-05-18 09:29:34 -060036{
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020037 struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
Stephen Warrenae58d1e2012-05-18 09:29:34 -060038
39 return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
40}
41
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020042static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
Stephen Warrenae58d1e2012-05-18 09:29:34 -060043{
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020044 struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
Stephen Warrenae58d1e2012-05-18 09:29:34 -060045
46 return pinctrl_select_state(mux->pinctrl, mux->state_idle);
47}
48
49#ifdef CONFIG_OF
50static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020051 struct platform_device *pdev)
Stephen Warrenae58d1e2012-05-18 09:29:34 -060052{
53 struct device_node *np = pdev->dev.of_node;
54 int num_names, i, ret;
55 struct device_node *adapter_np;
56 struct i2c_adapter *adapter;
57
58 if (!np)
59 return 0;
60
61 mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020062 if (!mux->pdata)
Stephen Warrenae58d1e2012-05-18 09:29:34 -060063 return -ENOMEM;
Stephen Warrenae58d1e2012-05-18 09:29:34 -060064
65 num_names = of_property_count_strings(np, "pinctrl-names");
66 if (num_names < 0) {
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020067 dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
Stephen Warrenae58d1e2012-05-18 09:29:34 -060068 num_names);
69 return num_names;
70 }
71
72 mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
73 sizeof(*mux->pdata->pinctrl_states) * num_names,
74 GFP_KERNEL);
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020075 if (!mux->pdata->pinctrl_states)
Stephen Warrenae58d1e2012-05-18 09:29:34 -060076 return -ENOMEM;
Stephen Warrenae58d1e2012-05-18 09:29:34 -060077
78 for (i = 0; i < num_names; i++) {
79 ret = of_property_read_string_index(np, "pinctrl-names", i,
80 &mux->pdata->pinctrl_states[mux->pdata->bus_count]);
81 if (ret < 0) {
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020082 dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
Stephen Warrenae58d1e2012-05-18 09:29:34 -060083 ret);
84 return ret;
85 }
86 if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
87 "idle")) {
88 if (i != num_names - 1) {
Peter Rosin4bbe7fb2016-04-20 08:39:31 +020089 dev_err(&pdev->dev,
90 "idle state must be last\n");
Stephen Warrenae58d1e2012-05-18 09:29:34 -060091 return -EINVAL;
92 }
93 mux->pdata->pinctrl_state_idle = "idle";
94 } else {
95 mux->pdata->bus_count++;
96 }
97 }
98
99 adapter_np = of_parse_phandle(np, "i2c-parent", 0);
100 if (!adapter_np) {
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200101 dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600102 return -ENODEV;
103 }
104 adapter = of_find_i2c_adapter_by_node(adapter_np);
Vladimir Zapolskiybdbf4a22015-08-26 23:59:33 +0300105 of_node_put(adapter_np);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600106 if (!adapter) {
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200107 dev_err(&pdev->dev, "Cannot find parent bus\n");
Wolfram Sang2737de42013-10-10 10:19:13 +0200108 return -EPROBE_DEFER;
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600109 }
110 mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
111 put_device(&adapter->dev);
112
113 return 0;
114}
115#else
116static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
117 struct platform_device *pdev)
118{
119 return 0;
120}
121#endif
122
Bill Pemberton0b255e92012-11-27 15:59:38 -0500123static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600124{
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200125 struct i2c_mux_core *muxc;
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600126 struct i2c_mux_pinctrl *mux;
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600127 int i, ret;
128
129 mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
130 if (!mux) {
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600131 ret = -ENOMEM;
132 goto err;
133 }
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600134
Jingoo Han6d4028c2013-07-30 16:59:33 +0900135 mux->pdata = dev_get_platdata(&pdev->dev);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600136 if (!mux->pdata) {
137 ret = i2c_mux_pinctrl_parse_dt(mux, pdev);
138 if (ret < 0)
139 goto err;
140 }
141 if (!mux->pdata) {
142 dev_err(&pdev->dev, "Missing platform data\n");
143 ret = -ENODEV;
144 goto err;
145 }
146
147 mux->states = devm_kzalloc(&pdev->dev,
148 sizeof(*mux->states) * mux->pdata->bus_count,
149 GFP_KERNEL);
150 if (!mux->states) {
151 dev_err(&pdev->dev, "Cannot allocate states\n");
152 ret = -ENOMEM;
153 goto err;
154 }
155
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200156 muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0,
157 i2c_mux_pinctrl_select, NULL);
158 if (!muxc) {
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600159 ret = -ENOMEM;
160 goto err;
161 }
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200162 muxc->priv = mux;
163
164 platform_set_drvdata(pdev, muxc);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600165
166 mux->pinctrl = devm_pinctrl_get(&pdev->dev);
167 if (IS_ERR(mux->pinctrl)) {
168 ret = PTR_ERR(mux->pinctrl);
169 dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret);
170 goto err;
171 }
172 for (i = 0; i < mux->pdata->bus_count; i++) {
173 mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
174 mux->pdata->pinctrl_states[i]);
175 if (IS_ERR(mux->states[i])) {
176 ret = PTR_ERR(mux->states[i]);
177 dev_err(&pdev->dev,
178 "Cannot look up pinctrl state %s: %d\n",
179 mux->pdata->pinctrl_states[i], ret);
180 goto err;
181 }
182 }
183 if (mux->pdata->pinctrl_state_idle) {
184 mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
185 mux->pdata->pinctrl_state_idle);
186 if (IS_ERR(mux->state_idle)) {
187 ret = PTR_ERR(mux->state_idle);
188 dev_err(&pdev->dev,
189 "Cannot look up pinctrl state %s: %d\n",
190 mux->pdata->pinctrl_state_idle, ret);
191 goto err;
192 }
193
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200194 muxc->deselect = i2c_mux_pinctrl_deselect;
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600195 }
196
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200197 muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
198 if (!muxc->parent) {
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600199 dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
200 mux->pdata->parent_bus_num);
Wolfram Sang2737de42013-10-10 10:19:13 +0200201 ret = -EPROBE_DEFER;
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600202 goto err;
203 }
204
205 for (i = 0; i < mux->pdata->bus_count; i++) {
206 u32 bus = mux->pdata->base_bus_num ?
207 (mux->pdata->base_bus_num + i) : 0;
208
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200209 ret = i2c_mux_add_adapter(muxc, bus, i, 0);
210 if (ret) {
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600211 dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
212 goto err_del_adapter;
213 }
214 }
215
216 return 0;
217
218err_del_adapter:
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200219 i2c_mux_del_adapters(muxc);
220 i2c_put_adapter(muxc->parent);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600221err:
222 return ret;
223}
224
Bill Pemberton0b255e92012-11-27 15:59:38 -0500225static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600226{
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200227 struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600228
Peter Rosin4bbe7fb2016-04-20 08:39:31 +0200229 i2c_mux_del_adapters(muxc);
230 i2c_put_adapter(muxc->parent);
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600231 return 0;
232}
233
234#ifdef CONFIG_OF
Bill Pemberton0b255e92012-11-27 15:59:38 -0500235static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600236 { .compatible = "i2c-mux-pinctrl", },
237 {},
238};
239MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
240#endif
241
242static struct platform_driver i2c_mux_pinctrl_driver = {
243 .driver = {
244 .name = "i2c-mux-pinctrl",
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600245 .of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
246 },
247 .probe = i2c_mux_pinctrl_probe,
Bill Pemberton0b255e92012-11-27 15:59:38 -0500248 .remove = i2c_mux_pinctrl_remove,
Stephen Warrenae58d1e2012-05-18 09:29:34 -0600249};
250module_platform_driver(i2c_mux_pinctrl_driver);
251
252MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
253MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
254MODULE_LICENSE("GPL v2");
255MODULE_ALIAS("platform:i2c-mux-pinctrl");