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