xref: /openbmc/linux/drivers/i2c/muxes/i2c-mux-pinctrl.c (revision 6ef91fcca8a8ba3df9810a4cc6cd6a9d3f21bf45)
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/module.h>
22ae58d1e4SStephen Warren #include <linux/pinctrl/consumer.h>
23ae58d1e4SStephen Warren #include <linux/i2c-mux-pinctrl.h>
24ae58d1e4SStephen Warren #include <linux/platform_device.h>
25ae58d1e4SStephen Warren #include <linux/slab.h>
264edd65e6SSachin Kamat #include <linux/of.h>
27*6ef91fccSPeter Rosin #include "../../pinctrl/core.h"
28ae58d1e4SStephen Warren 
29ae58d1e4SStephen Warren struct i2c_mux_pinctrl {
30ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl_platform_data *pdata;
31ae58d1e4SStephen Warren 	struct pinctrl *pinctrl;
32ae58d1e4SStephen Warren 	struct pinctrl_state **states;
33ae58d1e4SStephen Warren 	struct pinctrl_state *state_idle;
34ae58d1e4SStephen Warren };
35ae58d1e4SStephen Warren 
364bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
37ae58d1e4SStephen Warren {
384bbe7fb0SPeter Rosin 	struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
39ae58d1e4SStephen Warren 
40ae58d1e4SStephen Warren 	return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
41ae58d1e4SStephen Warren }
42ae58d1e4SStephen Warren 
434bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
44ae58d1e4SStephen Warren {
454bbe7fb0SPeter Rosin 	struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
46ae58d1e4SStephen Warren 
47ae58d1e4SStephen Warren 	return pinctrl_select_state(mux->pinctrl, mux->state_idle);
48ae58d1e4SStephen Warren }
49ae58d1e4SStephen Warren 
50ae58d1e4SStephen Warren #ifdef CONFIG_OF
51ae58d1e4SStephen Warren static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
52ae58d1e4SStephen Warren 				    struct platform_device *pdev)
53ae58d1e4SStephen Warren {
54ae58d1e4SStephen Warren 	struct device_node *np = pdev->dev.of_node;
55ae58d1e4SStephen Warren 	int num_names, i, ret;
56ae58d1e4SStephen Warren 	struct device_node *adapter_np;
57ae58d1e4SStephen Warren 	struct i2c_adapter *adapter;
58ae58d1e4SStephen Warren 
59ae58d1e4SStephen Warren 	if (!np)
60ae58d1e4SStephen Warren 		return 0;
61ae58d1e4SStephen Warren 
62ae58d1e4SStephen Warren 	mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
634bbe7fb0SPeter Rosin 	if (!mux->pdata)
64ae58d1e4SStephen Warren 		return -ENOMEM;
65ae58d1e4SStephen Warren 
66ae58d1e4SStephen Warren 	num_names = of_property_count_strings(np, "pinctrl-names");
67ae58d1e4SStephen Warren 	if (num_names < 0) {
684bbe7fb0SPeter Rosin 		dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
69ae58d1e4SStephen Warren 			num_names);
70ae58d1e4SStephen Warren 		return num_names;
71ae58d1e4SStephen Warren 	}
72ae58d1e4SStephen Warren 
73ae58d1e4SStephen Warren 	mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
74ae58d1e4SStephen Warren 		sizeof(*mux->pdata->pinctrl_states) * num_names,
75ae58d1e4SStephen Warren 		GFP_KERNEL);
764bbe7fb0SPeter Rosin 	if (!mux->pdata->pinctrl_states)
77ae58d1e4SStephen Warren 		return -ENOMEM;
78ae58d1e4SStephen Warren 
79ae58d1e4SStephen Warren 	for (i = 0; i < num_names; i++) {
80ae58d1e4SStephen Warren 		ret = of_property_read_string_index(np, "pinctrl-names", i,
81ae58d1e4SStephen Warren 			&mux->pdata->pinctrl_states[mux->pdata->bus_count]);
82ae58d1e4SStephen Warren 		if (ret < 0) {
834bbe7fb0SPeter Rosin 			dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
84ae58d1e4SStephen Warren 				ret);
85ae58d1e4SStephen Warren 			return ret;
86ae58d1e4SStephen Warren 		}
87ae58d1e4SStephen Warren 		if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
88ae58d1e4SStephen Warren 			    "idle")) {
89ae58d1e4SStephen Warren 			if (i != num_names - 1) {
904bbe7fb0SPeter Rosin 				dev_err(&pdev->dev,
914bbe7fb0SPeter Rosin 					"idle state must be last\n");
92ae58d1e4SStephen Warren 				return -EINVAL;
93ae58d1e4SStephen Warren 			}
94ae58d1e4SStephen Warren 			mux->pdata->pinctrl_state_idle = "idle";
95ae58d1e4SStephen Warren 		} else {
96ae58d1e4SStephen Warren 			mux->pdata->bus_count++;
97ae58d1e4SStephen Warren 		}
98ae58d1e4SStephen Warren 	}
99ae58d1e4SStephen Warren 
100ae58d1e4SStephen Warren 	adapter_np = of_parse_phandle(np, "i2c-parent", 0);
101ae58d1e4SStephen Warren 	if (!adapter_np) {
1024bbe7fb0SPeter Rosin 		dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
103ae58d1e4SStephen Warren 		return -ENODEV;
104ae58d1e4SStephen Warren 	}
105ae58d1e4SStephen Warren 	adapter = of_find_i2c_adapter_by_node(adapter_np);
106bdbf4a29SVladimir Zapolskiy 	of_node_put(adapter_np);
107ae58d1e4SStephen Warren 	if (!adapter) {
1084bbe7fb0SPeter Rosin 		dev_err(&pdev->dev, "Cannot find parent bus\n");
1092737de46SWolfram Sang 		return -EPROBE_DEFER;
110ae58d1e4SStephen Warren 	}
111ae58d1e4SStephen Warren 	mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
112ae58d1e4SStephen Warren 	put_device(&adapter->dev);
113ae58d1e4SStephen Warren 
114ae58d1e4SStephen Warren 	return 0;
115ae58d1e4SStephen Warren }
116ae58d1e4SStephen Warren #else
117ae58d1e4SStephen Warren static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
118ae58d1e4SStephen Warren 					   struct platform_device *pdev)
119ae58d1e4SStephen Warren {
120ae58d1e4SStephen Warren 	return 0;
121ae58d1e4SStephen Warren }
122ae58d1e4SStephen Warren #endif
123ae58d1e4SStephen Warren 
124*6ef91fccSPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
125*6ef91fccSPeter Rosin 	struct pinctrl_state *state)
126*6ef91fccSPeter Rosin {
127*6ef91fccSPeter Rosin 	struct i2c_adapter *root = NULL;
128*6ef91fccSPeter Rosin 	struct pinctrl_setting *setting;
129*6ef91fccSPeter Rosin 	struct i2c_adapter *pin_root;
130*6ef91fccSPeter Rosin 
131*6ef91fccSPeter Rosin 	list_for_each_entry(setting, &state->settings, node) {
132*6ef91fccSPeter Rosin 		pin_root = i2c_root_adapter(setting->pctldev->dev);
133*6ef91fccSPeter Rosin 		if (!pin_root)
134*6ef91fccSPeter Rosin 			return NULL;
135*6ef91fccSPeter Rosin 		if (!root)
136*6ef91fccSPeter Rosin 			root = pin_root;
137*6ef91fccSPeter Rosin 		else if (root != pin_root)
138*6ef91fccSPeter Rosin 			return NULL;
139*6ef91fccSPeter Rosin 	}
140*6ef91fccSPeter Rosin 
141*6ef91fccSPeter Rosin 	return root;
142*6ef91fccSPeter Rosin }
143*6ef91fccSPeter Rosin 
1440b255e92SBill Pemberton static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
145ae58d1e4SStephen Warren {
1464bbe7fb0SPeter Rosin 	struct i2c_mux_core *muxc;
147ae58d1e4SStephen Warren 	struct i2c_mux_pinctrl *mux;
148*6ef91fccSPeter Rosin 	struct i2c_adapter *root;
149ae58d1e4SStephen Warren 	int i, ret;
150ae58d1e4SStephen Warren 
151ae58d1e4SStephen Warren 	mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
152ae58d1e4SStephen Warren 	if (!mux) {
153ae58d1e4SStephen Warren 		ret = -ENOMEM;
154ae58d1e4SStephen Warren 		goto err;
155ae58d1e4SStephen Warren 	}
156ae58d1e4SStephen Warren 
1576d4028c6SJingoo Han 	mux->pdata = dev_get_platdata(&pdev->dev);
158ae58d1e4SStephen Warren 	if (!mux->pdata) {
159ae58d1e4SStephen Warren 		ret = i2c_mux_pinctrl_parse_dt(mux, pdev);
160ae58d1e4SStephen Warren 		if (ret < 0)
161ae58d1e4SStephen Warren 			goto err;
162ae58d1e4SStephen Warren 	}
163ae58d1e4SStephen Warren 	if (!mux->pdata) {
164ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Missing platform data\n");
165ae58d1e4SStephen Warren 		ret = -ENODEV;
166ae58d1e4SStephen Warren 		goto err;
167ae58d1e4SStephen Warren 	}
168ae58d1e4SStephen Warren 
169ae58d1e4SStephen Warren 	mux->states = devm_kzalloc(&pdev->dev,
170ae58d1e4SStephen Warren 				   sizeof(*mux->states) * mux->pdata->bus_count,
171ae58d1e4SStephen Warren 				   GFP_KERNEL);
172ae58d1e4SStephen Warren 	if (!mux->states) {
173ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot allocate states\n");
174ae58d1e4SStephen Warren 		ret = -ENOMEM;
175ae58d1e4SStephen Warren 		goto err;
176ae58d1e4SStephen Warren 	}
177ae58d1e4SStephen Warren 
1784bbe7fb0SPeter Rosin 	muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0,
1794bbe7fb0SPeter Rosin 			     i2c_mux_pinctrl_select, NULL);
1804bbe7fb0SPeter Rosin 	if (!muxc) {
181ae58d1e4SStephen Warren 		ret = -ENOMEM;
182ae58d1e4SStephen Warren 		goto err;
183ae58d1e4SStephen Warren 	}
1844bbe7fb0SPeter Rosin 	muxc->priv = mux;
1854bbe7fb0SPeter Rosin 
1864bbe7fb0SPeter Rosin 	platform_set_drvdata(pdev, muxc);
187ae58d1e4SStephen Warren 
188ae58d1e4SStephen Warren 	mux->pinctrl = devm_pinctrl_get(&pdev->dev);
189ae58d1e4SStephen Warren 	if (IS_ERR(mux->pinctrl)) {
190ae58d1e4SStephen Warren 		ret = PTR_ERR(mux->pinctrl);
191ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Cannot get pinctrl: %d\n", ret);
192ae58d1e4SStephen Warren 		goto err;
193ae58d1e4SStephen Warren 	}
194ae58d1e4SStephen Warren 	for (i = 0; i < mux->pdata->bus_count; i++) {
195ae58d1e4SStephen Warren 		mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
196ae58d1e4SStephen Warren 						mux->pdata->pinctrl_states[i]);
197ae58d1e4SStephen Warren 		if (IS_ERR(mux->states[i])) {
198ae58d1e4SStephen Warren 			ret = PTR_ERR(mux->states[i]);
199ae58d1e4SStephen Warren 			dev_err(&pdev->dev,
200ae58d1e4SStephen Warren 				"Cannot look up pinctrl state %s: %d\n",
201ae58d1e4SStephen Warren 				mux->pdata->pinctrl_states[i], ret);
202ae58d1e4SStephen Warren 			goto err;
203ae58d1e4SStephen Warren 		}
204ae58d1e4SStephen Warren 	}
205ae58d1e4SStephen Warren 	if (mux->pdata->pinctrl_state_idle) {
206ae58d1e4SStephen Warren 		mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
207ae58d1e4SStephen Warren 						mux->pdata->pinctrl_state_idle);
208ae58d1e4SStephen Warren 		if (IS_ERR(mux->state_idle)) {
209ae58d1e4SStephen Warren 			ret = PTR_ERR(mux->state_idle);
210ae58d1e4SStephen Warren 			dev_err(&pdev->dev,
211ae58d1e4SStephen Warren 				"Cannot look up pinctrl state %s: %d\n",
212ae58d1e4SStephen Warren 				mux->pdata->pinctrl_state_idle, ret);
213ae58d1e4SStephen Warren 			goto err;
214ae58d1e4SStephen Warren 		}
215ae58d1e4SStephen Warren 
2164bbe7fb0SPeter Rosin 		muxc->deselect = i2c_mux_pinctrl_deselect;
217ae58d1e4SStephen Warren 	}
218ae58d1e4SStephen Warren 
2194bbe7fb0SPeter Rosin 	muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
2204bbe7fb0SPeter Rosin 	if (!muxc->parent) {
221ae58d1e4SStephen Warren 		dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
222ae58d1e4SStephen Warren 			mux->pdata->parent_bus_num);
2232737de46SWolfram Sang 		ret = -EPROBE_DEFER;
224ae58d1e4SStephen Warren 		goto err;
225ae58d1e4SStephen Warren 	}
226ae58d1e4SStephen Warren 
227*6ef91fccSPeter Rosin 	root = i2c_root_adapter(&muxc->parent->dev);
228*6ef91fccSPeter Rosin 
229*6ef91fccSPeter Rosin 	muxc->mux_locked = true;
230*6ef91fccSPeter Rosin 	for (i = 0; i < mux->pdata->bus_count; i++) {
231*6ef91fccSPeter Rosin 		if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
232*6ef91fccSPeter Rosin 			muxc->mux_locked = false;
233*6ef91fccSPeter Rosin 			break;
234*6ef91fccSPeter Rosin 		}
235*6ef91fccSPeter Rosin 	}
236*6ef91fccSPeter Rosin 	if (muxc->mux_locked && mux->pdata->pinctrl_state_idle &&
237*6ef91fccSPeter Rosin 	    root != i2c_mux_pinctrl_root_adapter(mux->state_idle))
238*6ef91fccSPeter Rosin 		muxc->mux_locked = false;
239*6ef91fccSPeter Rosin 
240*6ef91fccSPeter Rosin 	if (muxc->mux_locked)
241*6ef91fccSPeter Rosin 		dev_info(&pdev->dev, "mux-locked i2c mux\n");
242*6ef91fccSPeter Rosin 
243ae58d1e4SStephen Warren 	for (i = 0; i < mux->pdata->bus_count; i++) {
244ae58d1e4SStephen Warren 		u32 bus = mux->pdata->base_bus_num ?
245ae58d1e4SStephen Warren 				(mux->pdata->base_bus_num + i) : 0;
246ae58d1e4SStephen Warren 
2474bbe7fb0SPeter Rosin 		ret = i2c_mux_add_adapter(muxc, bus, i, 0);
2484bbe7fb0SPeter Rosin 		if (ret) {
249ae58d1e4SStephen Warren 			dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
250ae58d1e4SStephen Warren 			goto err_del_adapter;
251ae58d1e4SStephen Warren 		}
252ae58d1e4SStephen Warren 	}
253ae58d1e4SStephen Warren 
254ae58d1e4SStephen Warren 	return 0;
255ae58d1e4SStephen Warren 
256ae58d1e4SStephen Warren err_del_adapter:
2574bbe7fb0SPeter Rosin 	i2c_mux_del_adapters(muxc);
2584bbe7fb0SPeter Rosin 	i2c_put_adapter(muxc->parent);
259ae58d1e4SStephen Warren err:
260ae58d1e4SStephen Warren 	return ret;
261ae58d1e4SStephen Warren }
262ae58d1e4SStephen Warren 
2630b255e92SBill Pemberton static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
264ae58d1e4SStephen Warren {
2654bbe7fb0SPeter Rosin 	struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
266ae58d1e4SStephen Warren 
2674bbe7fb0SPeter Rosin 	i2c_mux_del_adapters(muxc);
2684bbe7fb0SPeter Rosin 	i2c_put_adapter(muxc->parent);
269ae58d1e4SStephen Warren 	return 0;
270ae58d1e4SStephen Warren }
271ae58d1e4SStephen Warren 
272ae58d1e4SStephen Warren #ifdef CONFIG_OF
2730b255e92SBill Pemberton static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
274ae58d1e4SStephen Warren 	{ .compatible = "i2c-mux-pinctrl", },
275ae58d1e4SStephen Warren 	{},
276ae58d1e4SStephen Warren };
277ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
278ae58d1e4SStephen Warren #endif
279ae58d1e4SStephen Warren 
280ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = {
281ae58d1e4SStephen Warren 	.driver	= {
282ae58d1e4SStephen Warren 		.name	= "i2c-mux-pinctrl",
283ae58d1e4SStephen Warren 		.of_match_table = of_match_ptr(i2c_mux_pinctrl_of_match),
284ae58d1e4SStephen Warren 	},
285ae58d1e4SStephen Warren 	.probe	= i2c_mux_pinctrl_probe,
2860b255e92SBill Pemberton 	.remove	= i2c_mux_pinctrl_remove,
287ae58d1e4SStephen Warren };
288ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver);
289ae58d1e4SStephen Warren 
290ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
291ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
292ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2");
293ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl");
294