19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ae58d1e4SStephen Warren /*
3ae58d1e4SStephen Warren * I2C multiplexer using pinctrl API
4ae58d1e4SStephen Warren *
5ae58d1e4SStephen Warren * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
6ae58d1e4SStephen Warren */
7ae58d1e4SStephen Warren
8ae58d1e4SStephen Warren #include <linux/i2c.h>
9ae58d1e4SStephen Warren #include <linux/i2c-mux.h>
10ae58d1e4SStephen Warren #include <linux/module.h>
11ae58d1e4SStephen Warren #include <linux/pinctrl/consumer.h>
12ae58d1e4SStephen Warren #include <linux/platform_device.h>
13ae58d1e4SStephen Warren #include <linux/slab.h>
144edd65e6SSachin Kamat #include <linux/of.h>
156ef91fccSPeter Rosin #include "../../pinctrl/core.h"
16ae58d1e4SStephen Warren
17ae58d1e4SStephen Warren struct i2c_mux_pinctrl {
18ae58d1e4SStephen Warren struct pinctrl *pinctrl;
1990af2731SGustavo A. R. Silva struct pinctrl_state *states[];
20ae58d1e4SStephen Warren };
21ae58d1e4SStephen Warren
i2c_mux_pinctrl_select(struct i2c_mux_core * muxc,u32 chan)224bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
23ae58d1e4SStephen Warren {
244bbe7fb0SPeter Rosin struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
25ae58d1e4SStephen Warren
26ae58d1e4SStephen Warren return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
27ae58d1e4SStephen Warren }
28ae58d1e4SStephen Warren
i2c_mux_pinctrl_deselect(struct i2c_mux_core * muxc,u32 chan)294bbe7fb0SPeter Rosin static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
30ae58d1e4SStephen Warren {
31fc204671SPeter Rosin return i2c_mux_pinctrl_select(muxc, muxc->num_adapters);
32ae58d1e4SStephen Warren }
33ae58d1e4SStephen Warren
i2c_mux_pinctrl_root_adapter(struct pinctrl_state * state)346ef91fccSPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
356ef91fccSPeter Rosin struct pinctrl_state *state)
366ef91fccSPeter Rosin {
376ef91fccSPeter Rosin struct i2c_adapter *root = NULL;
386ef91fccSPeter Rosin struct pinctrl_setting *setting;
396ef91fccSPeter Rosin struct i2c_adapter *pin_root;
406ef91fccSPeter Rosin
416ef91fccSPeter Rosin list_for_each_entry(setting, &state->settings, node) {
426ef91fccSPeter Rosin pin_root = i2c_root_adapter(setting->pctldev->dev);
436ef91fccSPeter Rosin if (!pin_root)
446ef91fccSPeter Rosin return NULL;
456ef91fccSPeter Rosin if (!root)
466ef91fccSPeter Rosin root = pin_root;
476ef91fccSPeter Rosin else if (root != pin_root)
486ef91fccSPeter Rosin return NULL;
496ef91fccSPeter Rosin }
506ef91fccSPeter Rosin
516ef91fccSPeter Rosin return root;
526ef91fccSPeter Rosin }
536ef91fccSPeter Rosin
i2c_mux_pinctrl_parent_adapter(struct device * dev)54c4aee3e1SPeter Rosin static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev)
55c4aee3e1SPeter Rosin {
56c4aee3e1SPeter Rosin struct device_node *np = dev->of_node;
57c4aee3e1SPeter Rosin struct device_node *parent_np;
58c4aee3e1SPeter Rosin struct i2c_adapter *parent;
59c4aee3e1SPeter Rosin
60c4aee3e1SPeter Rosin parent_np = of_parse_phandle(np, "i2c-parent", 0);
61c4aee3e1SPeter Rosin if (!parent_np) {
62c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse i2c-parent\n");
63c4aee3e1SPeter Rosin return ERR_PTR(-ENODEV);
64c4aee3e1SPeter Rosin }
65*3171d37bSHerve Codina parent = of_get_i2c_adapter_by_node(parent_np);
66c4aee3e1SPeter Rosin of_node_put(parent_np);
67c4aee3e1SPeter Rosin if (!parent)
68c4aee3e1SPeter Rosin return ERR_PTR(-EPROBE_DEFER);
69c4aee3e1SPeter Rosin
70c4aee3e1SPeter Rosin return parent;
71c4aee3e1SPeter Rosin }
72c4aee3e1SPeter Rosin
i2c_mux_pinctrl_probe(struct platform_device * pdev)730b255e92SBill Pemberton static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
74ae58d1e4SStephen Warren {
75c4aee3e1SPeter Rosin struct device *dev = &pdev->dev;
76c4aee3e1SPeter Rosin struct device_node *np = dev->of_node;
774bbe7fb0SPeter Rosin struct i2c_mux_core *muxc;
78ae58d1e4SStephen Warren struct i2c_mux_pinctrl *mux;
79c4aee3e1SPeter Rosin struct i2c_adapter *parent;
806ef91fccSPeter Rosin struct i2c_adapter *root;
81c4aee3e1SPeter Rosin int num_names, i, ret;
82c4aee3e1SPeter Rosin const char *name;
83ae58d1e4SStephen Warren
84c4aee3e1SPeter Rosin num_names = of_property_count_strings(np, "pinctrl-names");
85c4aee3e1SPeter Rosin if (num_names < 0) {
86c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse pinctrl-names: %d\n",
87c4aee3e1SPeter Rosin num_names);
88c4aee3e1SPeter Rosin return num_names;
89ae58d1e4SStephen Warren }
90ae58d1e4SStephen Warren
91c4aee3e1SPeter Rosin parent = i2c_mux_pinctrl_parent_adapter(dev);
92c4aee3e1SPeter Rosin if (IS_ERR(parent))
93c4aee3e1SPeter Rosin return PTR_ERR(parent);
94ae58d1e4SStephen Warren
95c4aee3e1SPeter Rosin muxc = i2c_mux_alloc(parent, dev, num_names,
9690af2731SGustavo A. R. Silva struct_size(mux, states, num_names),
97c4aee3e1SPeter Rosin 0, i2c_mux_pinctrl_select, NULL);
984bbe7fb0SPeter Rosin if (!muxc) {
99ae58d1e4SStephen Warren ret = -ENOMEM;
100c4aee3e1SPeter Rosin goto err_put_parent;
101ae58d1e4SStephen Warren }
102c4aee3e1SPeter Rosin mux = i2c_mux_priv(muxc);
1034bbe7fb0SPeter Rosin
1044bbe7fb0SPeter Rosin platform_set_drvdata(pdev, muxc);
105ae58d1e4SStephen Warren
106c4aee3e1SPeter Rosin mux->pinctrl = devm_pinctrl_get(dev);
107ae58d1e4SStephen Warren if (IS_ERR(mux->pinctrl)) {
108ae58d1e4SStephen Warren ret = PTR_ERR(mux->pinctrl);
109c4aee3e1SPeter Rosin dev_err(dev, "Cannot get pinctrl: %d\n", ret);
110c4aee3e1SPeter Rosin goto err_put_parent;
111ae58d1e4SStephen Warren }
112c4aee3e1SPeter Rosin
113c4aee3e1SPeter Rosin for (i = 0; i < num_names; i++) {
114c4aee3e1SPeter Rosin ret = of_property_read_string_index(np, "pinctrl-names", i,
115c4aee3e1SPeter Rosin &name);
116c4aee3e1SPeter Rosin if (ret < 0) {
117c4aee3e1SPeter Rosin dev_err(dev, "Cannot parse pinctrl-names: %d\n", ret);
118c4aee3e1SPeter Rosin goto err_put_parent;
119c4aee3e1SPeter Rosin }
120c4aee3e1SPeter Rosin
121c4aee3e1SPeter Rosin mux->states[i] = pinctrl_lookup_state(mux->pinctrl, name);
122ae58d1e4SStephen Warren if (IS_ERR(mux->states[i])) {
123ae58d1e4SStephen Warren ret = PTR_ERR(mux->states[i]);
124c4aee3e1SPeter Rosin dev_err(dev, "Cannot look up pinctrl state %s: %d\n",
125c4aee3e1SPeter Rosin name, ret);
126c4aee3e1SPeter Rosin goto err_put_parent;
127ae58d1e4SStephen Warren }
128ae58d1e4SStephen Warren
129c4aee3e1SPeter Rosin if (strcmp(name, "idle"))
130c4aee3e1SPeter Rosin continue;
131c4aee3e1SPeter Rosin
132c4aee3e1SPeter Rosin if (i != num_names - 1) {
133c4aee3e1SPeter Rosin dev_err(dev, "idle state must be last\n");
134c4aee3e1SPeter Rosin ret = -EINVAL;
135c4aee3e1SPeter Rosin goto err_put_parent;
136c4aee3e1SPeter Rosin }
1374bbe7fb0SPeter Rosin muxc->deselect = i2c_mux_pinctrl_deselect;
138ae58d1e4SStephen Warren }
139ae58d1e4SStephen Warren
1406ef91fccSPeter Rosin root = i2c_root_adapter(&muxc->parent->dev);
1416ef91fccSPeter Rosin
1426ef91fccSPeter Rosin muxc->mux_locked = true;
143c4aee3e1SPeter Rosin for (i = 0; i < num_names; i++) {
1446ef91fccSPeter Rosin if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
1456ef91fccSPeter Rosin muxc->mux_locked = false;
1466ef91fccSPeter Rosin break;
1476ef91fccSPeter Rosin }
1486ef91fccSPeter Rosin }
1496ef91fccSPeter Rosin if (muxc->mux_locked)
150c4aee3e1SPeter Rosin dev_info(dev, "mux-locked i2c mux\n");
1516ef91fccSPeter Rosin
152c4aee3e1SPeter Rosin /* Do not add any adapter for the idle state (if it's there at all). */
153fc204671SPeter Rosin for (i = 0; i < num_names - !!muxc->deselect; i++) {
154c4aee3e1SPeter Rosin ret = i2c_mux_add_adapter(muxc, 0, i, 0);
155c99a23e5SPeter Rosin if (ret)
156ae58d1e4SStephen Warren goto err_del_adapter;
157ae58d1e4SStephen Warren }
158ae58d1e4SStephen Warren
159ae58d1e4SStephen Warren return 0;
160ae58d1e4SStephen Warren
161ae58d1e4SStephen Warren err_del_adapter:
1624bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc);
163c4aee3e1SPeter Rosin err_put_parent:
164be9bac02SDan Carpenter i2c_put_adapter(parent);
165c4aee3e1SPeter Rosin
166ae58d1e4SStephen Warren return ret;
167ae58d1e4SStephen Warren }
168ae58d1e4SStephen Warren
i2c_mux_pinctrl_remove(struct platform_device * pdev)169e190a0c3SUwe Kleine-König static void i2c_mux_pinctrl_remove(struct platform_device *pdev)
170ae58d1e4SStephen Warren {
1714bbe7fb0SPeter Rosin struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
172ae58d1e4SStephen Warren
1734bbe7fb0SPeter Rosin i2c_mux_del_adapters(muxc);
1744bbe7fb0SPeter Rosin i2c_put_adapter(muxc->parent);
175ae58d1e4SStephen Warren }
176ae58d1e4SStephen Warren
1770b255e92SBill Pemberton static const struct of_device_id i2c_mux_pinctrl_of_match[] = {
178ae58d1e4SStephen Warren { .compatible = "i2c-mux-pinctrl", },
179ae58d1e4SStephen Warren {},
180ae58d1e4SStephen Warren };
181ae58d1e4SStephen Warren MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match);
182ae58d1e4SStephen Warren
183ae58d1e4SStephen Warren static struct platform_driver i2c_mux_pinctrl_driver = {
184ae58d1e4SStephen Warren .driver = {
185ae58d1e4SStephen Warren .name = "i2c-mux-pinctrl",
186874765c9SJean Delvare .of_match_table = i2c_mux_pinctrl_of_match,
187ae58d1e4SStephen Warren },
188ae58d1e4SStephen Warren .probe = i2c_mux_pinctrl_probe,
189e190a0c3SUwe Kleine-König .remove_new = i2c_mux_pinctrl_remove,
190ae58d1e4SStephen Warren };
191ae58d1e4SStephen Warren module_platform_driver(i2c_mux_pinctrl_driver);
192ae58d1e4SStephen Warren
193ae58d1e4SStephen Warren MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver");
194ae58d1e4SStephen Warren MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
195ae58d1e4SStephen Warren MODULE_LICENSE("GPL v2");
196ae58d1e4SStephen Warren MODULE_ALIAS("platform:i2c-mux-pinctrl");
197