xref: /openbmc/linux/drivers/i2c/muxes/i2c-mux-pinctrl.c (revision ae58d1e406986f31d1e88b32f5ac601506c196d8)
1*ae58d1e4SStephen Warren /*
2*ae58d1e4SStephen Warren  * I2C multiplexer using pinctrl API
3*ae58d1e4SStephen Warren  *
4*ae58d1e4SStephen Warren  * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
5*ae58d1e4SStephen Warren  *
6*ae58d1e4SStephen Warren  * This program is free software; you can redistribute it and/or modify it
7*ae58d1e4SStephen Warren  * under the terms and conditions of the GNU General Public License,
8*ae58d1e4SStephen Warren  * version 2, as published by the Free Software Foundation.
9*ae58d1e4SStephen Warren  *
10*ae58d1e4SStephen Warren  * This program is distributed in the hope it will be useful, but WITHOUT
11*ae58d1e4SStephen Warren  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12*ae58d1e4SStephen Warren  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13*ae58d1e4SStephen Warren  * more details.
14*ae58d1e4SStephen Warren  *
15*ae58d1e4SStephen Warren  * You should have received a copy of the GNU General Public License
16*ae58d1e4SStephen Warren  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17*ae58d1e4SStephen Warren  */
18*ae58d1e4SStephen Warren 
19*ae58d1e4SStephen Warren #include <linux/i2c.h>
20*ae58d1e4SStephen Warren #include <linux/i2c-mux.h>
21*ae58d1e4SStephen Warren #include <linux/init.h>
22*ae58d1e4SStephen Warren #include <linux/module.h>
23*ae58d1e4SStephen Warren #include <linux/of_i2c.h>
24*ae58d1e4SStephen Warren #include <linux/pinctrl/consumer.h>
25*ae58d1e4SStephen Warren #include <linux/i2c-mux-pinctrl.h>
26*ae58d1e4SStephen Warren #include <linux/platform_device.h>
27*ae58d1e4SStephen Warren #include <linux/slab.h>
28*ae58d1e4SStephen Warren 
29*ae58d1e4SStephen Warren struct i2c_mux_pinctrl {
30*ae58d1e4SStephen Warren 	struct device *dev;
31*ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl_platform_data *pdata;
32*ae58d1e4SStephen Warren 	struct pinctrl *pinctrl;
33*ae58d1e4SStephen Warren 	struct pinctrl_state **states;
34*ae58d1e4SStephen Warren 	struct pinctrl_state *state_idle;
35*ae58d1e4SStephen Warren 	struct i2c_adapter *parent;
36*ae58d1e4SStephen Warren 	struct i2c_adapter **busses;
37*ae58d1e4SStephen Warren };
38*ae58d1e4SStephen Warren 
39*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data,
40*ae58d1e4SStephen Warren 				  u32 chan)
41*ae58d1e4SStephen Warren {
42*ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl *mux = data;
43*ae58d1e4SStephen Warren 
44*ae58d1e4SStephen Warren 	return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
45*ae58d1e4SStephen Warren }
46*ae58d1e4SStephen Warren 
47*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data,
48*ae58d1e4SStephen Warren 				    u32 chan)
49*ae58d1e4SStephen Warren {
50*ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl *mux = data;
51*ae58d1e4SStephen Warren 
52*ae58d1e4SStephen Warren 	return pinctrl_select_state(mux->pinctrl, mux->state_idle);
53*ae58d1e4SStephen Warren }
54*ae58d1e4SStephen Warren 
55*ae58d1e4SStephen Warren #ifdef CONFIG_OF
56*ae58d1e4SStephen Warren static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
57*ae58d1e4SStephen Warren 				struct platform_device *pdev)
58*ae58d1e4SStephen Warren {
59*ae58d1e4SStephen Warren 	struct device_node *np = pdev->dev.of_node;
60*ae58d1e4SStephen Warren 	int num_names, i, ret;
61*ae58d1e4SStephen Warren 	struct device_node *adapter_np;
62*ae58d1e4SStephen Warren 	struct i2c_adapter *adapter;
63*ae58d1e4SStephen Warren 
64*ae58d1e4SStephen Warren 	if (!np)
65*ae58d1e4SStephen Warren 		return 0;
66*ae58d1e4SStephen Warren 
67*ae58d1e4SStephen Warren 	mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
68*ae58d1e4SStephen Warren 	if (!mux->pdata) {
69*ae58d1e4SStephen Warren 		dev_err(mux->dev,
70*ae58d1e4SStephen Warren 			"Cannot allocate i2c_mux_pinctrl_platform_data\n");
71*ae58d1e4SStephen Warren 		return -ENOMEM;
72*ae58d1e4SStephen Warren 	}
73*ae58d1e4SStephen Warren 
74*ae58d1e4SStephen Warren 	num_names = of_property_count_strings(np, "pinctrl-names");
75*ae58d1e4SStephen Warren 	if (num_names < 0) {
76*ae58d1e4SStephen Warren 		dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
77*ae58d1e4SStephen Warren 			num_names);
78*ae58d1e4SStephen Warren 		return num_names;
79*ae58d1e4SStephen Warren 	}
80*ae58d1e4SStephen Warren 
81*ae58d1e4SStephen Warren 	mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
82*ae58d1e4SStephen Warren 		sizeof(*mux->pdata->pinctrl_states) * num_names,
83*ae58d1e4SStephen Warren 		GFP_KERNEL);
84*ae58d1e4SStephen Warren 	if (!mux->pdata->pinctrl_states) {
85*ae58d1e4SStephen Warren 		dev_err(mux->dev, "Cannot allocate pinctrl_states\n");
86*ae58d1e4SStephen Warren 		return -ENOMEM;
87*ae58d1e4SStephen Warren 	}
88*ae58d1e4SStephen Warren 
89*ae58d1e4SStephen Warren 	for (i = 0; i < num_names; i++) {
90*ae58d1e4SStephen Warren 		ret = of_property_read_string_index(np, "pinctrl-names", i,
91*ae58d1e4SStephen Warren 			&mux->pdata->pinctrl_states[mux->pdata->bus_count]);
92*ae58d1e4SStephen Warren 		if (ret < 0) {
93*ae58d1e4SStephen Warren 			dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
94*ae58d1e4SStephen Warren 				ret);
95*ae58d1e4SStephen Warren 			return ret;
96*ae58d1e4SStephen Warren 		}
97*ae58d1e4SStephen Warren 		if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
98*ae58d1e4SStephen Warren 			    "idle")) {
99*ae58d1e4SStephen Warren 			if (i != num_names - 1) {
100*ae58d1e4SStephen Warren 				dev_err(mux->dev, "idle state must be last\n");
101*ae58d1e4SStephen Warren 				return -EINVAL;
102*ae58d1e4SStephen Warren 			}
103*ae58d1e4SStephen Warren 			mux->pdata->pinctrl_state_idle = "idle";
104*ae58d1e4SStephen Warren 		} else {
105*ae58d1e4SStephen Warren 			mux->pdata->bus_count++;
106*ae58d1e4SStephen Warren 		}
107*ae58d1e4SStephen Warren 	}
108*ae58d1e4SStephen Warren 
109*ae58d1e4SStephen Warren 	adapter_np = of_parse_phandle(np, "i2c-parent", 0);
110*ae58d1e4SStephen Warren 	if (!adapter_np) {
111*ae58d1e4SStephen Warren 		dev_err(mux->dev, "Cannot parse i2c-parent\n");
112*ae58d1e4SStephen Warren 		return -ENODEV;
113*ae58d1e4SStephen Warren 	}
114*ae58d1e4SStephen Warren 	adapter = of_find_i2c_adapter_by_node(adapter_np);
115*ae58d1e4SStephen Warren 	if (!adapter) {
116*ae58d1e4SStephen Warren 		dev_err(mux->dev, "Cannot find parent bus\n");
117*ae58d1e4SStephen Warren 		return -ENODEV;
118*ae58d1e4SStephen Warren 	}
119*ae58d1e4SStephen Warren 	mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
120*ae58d1e4SStephen Warren 	put_device(&adapter->dev);
121*ae58d1e4SStephen Warren 
122*ae58d1e4SStephen Warren 	return 0;
123*ae58d1e4SStephen Warren }
124*ae58d1e4SStephen Warren #else
125*ae58d1e4SStephen Warren static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
126*ae58d1e4SStephen Warren 					   struct platform_device *pdev)
127*ae58d1e4SStephen Warren {
128*ae58d1e4SStephen Warren 	return 0;
129*ae58d1e4SStephen Warren }
130*ae58d1e4SStephen Warren #endif
131*ae58d1e4SStephen Warren 
132*ae58d1e4SStephen Warren static int __devinit i2c_mux_pinctrl_probe(struct platform_device *pdev)
133*ae58d1e4SStephen Warren {
134*ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl *mux;
135*ae58d1e4SStephen Warren 	int (*deselect)(struct i2c_adapter *, void *, u32);
136*ae58d1e4SStephen Warren 	int i, ret;
137*ae58d1e4SStephen Warren 
138*ae58d1e4SStephen Warren 	mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
139*ae58d1e4SStephen Warren 	if (!mux) {
140*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n");
141*ae58d1e4SStephen Warren 		ret = -ENOMEM;
142*ae58d1e4SStephen Warren 		goto err;
143*ae58d1e4SStephen Warren 	}
144*ae58d1e4SStephen Warren 	platform_set_drvdata(pdev, mux);
145*ae58d1e4SStephen Warren 
146*ae58d1e4SStephen Warren 	mux->dev = &pdev->dev;
147*ae58d1e4SStephen Warren 
148*ae58d1e4SStephen Warren 	mux->pdata = pdev->dev.platform_data;
149*ae58d1e4SStephen Warren 	if (!mux->pdata) {
150*ae58d1e4SStephen Warren 		ret = i2c_mux_pinctrl_parse_dt(mux, pdev);
151*ae58d1e4SStephen Warren 		if (ret < 0)
152*ae58d1e4SStephen Warren 			goto err;
153*ae58d1e4SStephen Warren 	}
154*ae58d1e4SStephen Warren 	if (!mux->pdata) {
155*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Missing platform data\n");
156*ae58d1e4SStephen Warren 		ret = -ENODEV;
157*ae58d1e4SStephen Warren 		goto err;
158*ae58d1e4SStephen Warren 	}
159*ae58d1e4SStephen Warren 
160*ae58d1e4SStephen Warren 	mux->states = devm_kzalloc(&pdev->dev,
161*ae58d1e4SStephen Warren 				   sizeof(*mux->states) * mux->pdata->bus_count,
162*ae58d1e4SStephen Warren 				   GFP_KERNEL);
163*ae58d1e4SStephen Warren 	if (!mux->states) {
164*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot allocate states\n");
165*ae58d1e4SStephen Warren 		ret = -ENOMEM;
166*ae58d1e4SStephen Warren 		goto err;
167*ae58d1e4SStephen Warren 	}
168*ae58d1e4SStephen Warren 
169*ae58d1e4SStephen Warren 	mux->busses = devm_kzalloc(&pdev->dev,
170*ae58d1e4SStephen Warren 				   sizeof(mux->busses) * mux->pdata->bus_count,
171*ae58d1e4SStephen Warren 				   GFP_KERNEL);
172*ae58d1e4SStephen Warren 	if (!mux->states) {
173*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot allocate busses\n");
174*ae58d1e4SStephen Warren 		ret = -ENOMEM;
175*ae58d1e4SStephen Warren 		goto err;
176*ae58d1e4SStephen Warren 	}
177*ae58d1e4SStephen Warren 
178*ae58d1e4SStephen Warren 	mux->pinctrl = devm_pinctrl_get(&pdev->dev);
179*ae58d1e4SStephen Warren 	if (IS_ERR(mux->pinctrl)) {
180*ae58d1e4SStephen Warren 		ret = PTR_ERR(mux->pinctrl);
181*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret);
182*ae58d1e4SStephen Warren 		goto err;
183*ae58d1e4SStephen Warren 	}
184*ae58d1e4SStephen Warren 	for (i = 0; i < mux->pdata->bus_count; i++) {
185*ae58d1e4SStephen Warren 		mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
186*ae58d1e4SStephen Warren 						mux->pdata->pinctrl_states[i]);
187*ae58d1e4SStephen Warren 			if (IS_ERR(mux->states[i])) {
188*ae58d1e4SStephen Warren 				ret = PTR_ERR(mux->states[i]);
189*ae58d1e4SStephen Warren 				dev_err(&pdev->dev,
190*ae58d1e4SStephen Warren 					"Cannot look up pinctrl state %s: %d\n",
191*ae58d1e4SStephen Warren 					mux->pdata->pinctrl_states[i], ret);
192*ae58d1e4SStephen Warren 				goto err;
193*ae58d1e4SStephen Warren 			}
194*ae58d1e4SStephen Warren 	}
195*ae58d1e4SStephen Warren 	if (mux->pdata->pinctrl_state_idle) {
196*ae58d1e4SStephen Warren 		mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
197*ae58d1e4SStephen Warren 						mux->pdata->pinctrl_state_idle);
198*ae58d1e4SStephen Warren 		if (IS_ERR(mux->state_idle)) {
199*ae58d1e4SStephen Warren 			ret = PTR_ERR(mux->state_idle);
200*ae58d1e4SStephen Warren 			dev_err(&pdev->dev,
201*ae58d1e4SStephen Warren 				"Cannot look up pinctrl state %s: %d\n",
202*ae58d1e4SStephen Warren 				mux->pdata->pinctrl_state_idle, ret);
203*ae58d1e4SStephen Warren 			goto err;
204*ae58d1e4SStephen Warren 		}
205*ae58d1e4SStephen Warren 
206*ae58d1e4SStephen Warren 		deselect = i2c_mux_pinctrl_deselect;
207*ae58d1e4SStephen Warren 	} else {
208*ae58d1e4SStephen Warren 		deselect = NULL;
209*ae58d1e4SStephen Warren 	}
210*ae58d1e4SStephen Warren 
211*ae58d1e4SStephen Warren 	mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
212*ae58d1e4SStephen Warren 	if (!mux->parent) {
213*ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
214*ae58d1e4SStephen Warren 			mux->pdata->parent_bus_num);
215*ae58d1e4SStephen Warren 		ret = -ENODEV;
216*ae58d1e4SStephen Warren 		goto err;
217*ae58d1e4SStephen Warren 	}
218*ae58d1e4SStephen Warren 
219*ae58d1e4SStephen Warren 	for (i = 0; i < mux->pdata->bus_count; i++) {
220*ae58d1e4SStephen Warren 		u32 bus = mux->pdata->base_bus_num ?
221*ae58d1e4SStephen Warren 				(mux->pdata->base_bus_num + i) : 0;
222*ae58d1e4SStephen Warren 
223*ae58d1e4SStephen Warren 		mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev,
224*ae58d1e4SStephen Warren 						     mux, bus, i,
225*ae58d1e4SStephen Warren 						     i2c_mux_pinctrl_select,
226*ae58d1e4SStephen Warren 						     deselect);
227*ae58d1e4SStephen Warren 		if (!mux->busses[i]) {
228*ae58d1e4SStephen Warren 			ret = -ENODEV;
229*ae58d1e4SStephen Warren 			dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
230*ae58d1e4SStephen Warren 			goto err_del_adapter;
231*ae58d1e4SStephen Warren 		}
232*ae58d1e4SStephen Warren 	}
233*ae58d1e4SStephen Warren 
234*ae58d1e4SStephen Warren 	return 0;
235*ae58d1e4SStephen Warren 
236*ae58d1e4SStephen Warren err_del_adapter:
237*ae58d1e4SStephen Warren 	for (; i > 0; i--)
238*ae58d1e4SStephen Warren 		i2c_del_mux_adapter(mux->busses[i - 1]);
239*ae58d1e4SStephen Warren 	i2c_put_adapter(mux->parent);
240*ae58d1e4SStephen Warren err:
241*ae58d1e4SStephen Warren 	return ret;
242*ae58d1e4SStephen Warren }
243*ae58d1e4SStephen Warren 
244*ae58d1e4SStephen Warren static int __devexit i2c_mux_pinctrl_remove(struct platform_device *pdev)
245*ae58d1e4SStephen Warren {
246*ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev);
247*ae58d1e4SStephen Warren 	int i;
248*ae58d1e4SStephen Warren 
249*ae58d1e4SStephen Warren 	for (i = 0; i < mux->pdata->bus_count; i++)
250*ae58d1e4SStephen Warren 		i2c_del_mux_adapter(mux->busses[i]);
251*ae58d1e4SStephen Warren 
252*ae58d1e4SStephen Warren 	i2c_put_adapter(mux->parent);
253*ae58d1e4SStephen Warren 
254*ae58d1e4SStephen Warren 	return 0;
255*ae58d1e4SStephen Warren }
256*ae58d1e4SStephen Warren 
257*ae58d1e4SStephen Warren #ifdef CONFIG_OF
258*ae58d1e4SStephen Warren static const struct of_device_id i2c_mux_pinctrl_of_match[] __devinitconst = {
259*ae58d1e4SStephen Warren 	{ .compatible = "i2c-mux-pinctrl", },
260*ae58d1e4SStephen Warren 	{},
261*ae58d1e4SStephen Warren };
262*ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
263*ae58d1e4SStephen Warren #endif
264*ae58d1e4SStephen Warren 
265*ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = {
266*ae58d1e4SStephen Warren 	.driver	= {
267*ae58d1e4SStephen Warren 		.name	= "i2c-mux-pinctrl",
268*ae58d1e4SStephen Warren 		.owner	= THIS_MODULE,
269*ae58d1e4SStephen Warren 		.of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
270*ae58d1e4SStephen Warren 	},
271*ae58d1e4SStephen Warren 	.probe	= i2c_mux_pinctrl_probe,
272*ae58d1e4SStephen Warren 	.remove	= __devexit_p(i2c_mux_pinctrl_remove),
273*ae58d1e4SStephen Warren };
274*ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver);
275*ae58d1e4SStephen Warren 
276*ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
277*ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
278*ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2");
279*ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl");
280