1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * I2C multiplexer using pinctrl API |
4 | * |
5 | * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. |
6 | */ |
7 | |
8 | #include <linux/i2c.h> |
9 | #include <linux/i2c-mux.h> |
10 | #include <linux/module.h> |
11 | #include <linux/pinctrl/consumer.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/of.h> |
15 | #include "../../pinctrl/core.h" |
16 | |
17 | struct i2c_mux_pinctrl { |
18 | struct pinctrl *pinctrl; |
19 | struct pinctrl_state *states[]; |
20 | }; |
21 | |
22 | static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan) |
23 | { |
24 | struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc); |
25 | |
26 | return pinctrl_select_state(p: mux->pinctrl, s: mux->states[chan]); |
27 | } |
28 | |
29 | static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan) |
30 | { |
31 | return i2c_mux_pinctrl_select(muxc, chan: muxc->num_adapters); |
32 | } |
33 | |
34 | static struct i2c_adapter *i2c_mux_pinctrl_root_adapter( |
35 | struct pinctrl_state *state) |
36 | { |
37 | struct i2c_adapter *root = NULL; |
38 | struct pinctrl_setting *setting; |
39 | struct i2c_adapter *pin_root; |
40 | |
41 | list_for_each_entry(setting, &state->settings, node) { |
42 | pin_root = i2c_root_adapter(dev: setting->pctldev->dev); |
43 | if (!pin_root) |
44 | return NULL; |
45 | if (!root) |
46 | root = pin_root; |
47 | else if (root != pin_root) |
48 | return NULL; |
49 | } |
50 | |
51 | return root; |
52 | } |
53 | |
54 | static struct i2c_adapter *i2c_mux_pinctrl_parent_adapter(struct device *dev) |
55 | { |
56 | struct device_node *np = dev->of_node; |
57 | struct device_node *parent_np; |
58 | struct i2c_adapter *parent; |
59 | |
60 | parent_np = of_parse_phandle(np, phandle_name: "i2c-parent" , index: 0); |
61 | if (!parent_np) { |
62 | dev_err(dev, "Cannot parse i2c-parent\n" ); |
63 | return ERR_PTR(error: -ENODEV); |
64 | } |
65 | parent = of_get_i2c_adapter_by_node(node: parent_np); |
66 | of_node_put(node: parent_np); |
67 | if (!parent) |
68 | return ERR_PTR(error: -EPROBE_DEFER); |
69 | |
70 | return parent; |
71 | } |
72 | |
73 | static int i2c_mux_pinctrl_probe(struct platform_device *pdev) |
74 | { |
75 | struct device *dev = &pdev->dev; |
76 | struct device_node *np = dev->of_node; |
77 | struct i2c_mux_core *muxc; |
78 | struct i2c_mux_pinctrl *mux; |
79 | struct i2c_adapter *parent; |
80 | struct i2c_adapter *root; |
81 | int num_names, i, ret; |
82 | const char *name; |
83 | |
84 | num_names = of_property_count_strings(np, propname: "pinctrl-names" ); |
85 | if (num_names < 0) { |
86 | dev_err(dev, "Cannot parse pinctrl-names: %d\n" , |
87 | num_names); |
88 | return num_names; |
89 | } |
90 | |
91 | parent = i2c_mux_pinctrl_parent_adapter(dev); |
92 | if (IS_ERR(ptr: parent)) |
93 | return PTR_ERR(ptr: parent); |
94 | |
95 | muxc = i2c_mux_alloc(parent, dev, max_adapters: num_names, |
96 | struct_size(mux, states, num_names), |
97 | flags: 0, select: i2c_mux_pinctrl_select, NULL); |
98 | if (!muxc) { |
99 | ret = -ENOMEM; |
100 | goto err_put_parent; |
101 | } |
102 | mux = i2c_mux_priv(muxc); |
103 | |
104 | platform_set_drvdata(pdev, data: muxc); |
105 | |
106 | mux->pinctrl = devm_pinctrl_get(dev); |
107 | if (IS_ERR(ptr: mux->pinctrl)) { |
108 | ret = PTR_ERR(ptr: mux->pinctrl); |
109 | dev_err(dev, "Cannot get pinctrl: %d\n" , ret); |
110 | goto err_put_parent; |
111 | } |
112 | |
113 | for (i = 0; i < num_names; i++) { |
114 | ret = of_property_read_string_index(np, propname: "pinctrl-names" , index: i, |
115 | output: &name); |
116 | if (ret < 0) { |
117 | dev_err(dev, "Cannot parse pinctrl-names: %d\n" , ret); |
118 | goto err_put_parent; |
119 | } |
120 | |
121 | mux->states[i] = pinctrl_lookup_state(p: mux->pinctrl, name); |
122 | if (IS_ERR(ptr: mux->states[i])) { |
123 | ret = PTR_ERR(ptr: mux->states[i]); |
124 | dev_err(dev, "Cannot look up pinctrl state %s: %d\n" , |
125 | name, ret); |
126 | goto err_put_parent; |
127 | } |
128 | |
129 | if (strcmp(name, "idle" )) |
130 | continue; |
131 | |
132 | if (i != num_names - 1) { |
133 | dev_err(dev, "idle state must be last\n" ); |
134 | ret = -EINVAL; |
135 | goto err_put_parent; |
136 | } |
137 | muxc->deselect = i2c_mux_pinctrl_deselect; |
138 | } |
139 | |
140 | root = i2c_root_adapter(dev: &muxc->parent->dev); |
141 | |
142 | muxc->mux_locked = true; |
143 | for (i = 0; i < num_names; i++) { |
144 | if (root != i2c_mux_pinctrl_root_adapter(state: mux->states[i])) { |
145 | muxc->mux_locked = false; |
146 | break; |
147 | } |
148 | } |
149 | if (muxc->mux_locked) |
150 | dev_info(dev, "mux-locked i2c mux\n" ); |
151 | |
152 | /* Do not add any adapter for the idle state (if it's there at all). */ |
153 | for (i = 0; i < num_names - !!muxc->deselect; i++) { |
154 | ret = i2c_mux_add_adapter(muxc, force_nr: 0, chan_id: i, class: 0); |
155 | if (ret) |
156 | goto err_del_adapter; |
157 | } |
158 | |
159 | return 0; |
160 | |
161 | err_del_adapter: |
162 | i2c_mux_del_adapters(muxc); |
163 | err_put_parent: |
164 | i2c_put_adapter(adap: parent); |
165 | |
166 | return ret; |
167 | } |
168 | |
169 | static void i2c_mux_pinctrl_remove(struct platform_device *pdev) |
170 | { |
171 | struct i2c_mux_core *muxc = platform_get_drvdata(pdev); |
172 | |
173 | i2c_mux_del_adapters(muxc); |
174 | i2c_put_adapter(adap: muxc->parent); |
175 | } |
176 | |
177 | static const struct of_device_id i2c_mux_pinctrl_of_match[] = { |
178 | { .compatible = "i2c-mux-pinctrl" , }, |
179 | {}, |
180 | }; |
181 | MODULE_DEVICE_TABLE(of, i2c_mux_pinctrl_of_match); |
182 | |
183 | static struct platform_driver i2c_mux_pinctrl_driver = { |
184 | .driver = { |
185 | .name = "i2c-mux-pinctrl" , |
186 | .of_match_table = i2c_mux_pinctrl_of_match, |
187 | }, |
188 | .probe = i2c_mux_pinctrl_probe, |
189 | .remove_new = i2c_mux_pinctrl_remove, |
190 | }; |
191 | module_platform_driver(i2c_mux_pinctrl_driver); |
192 | |
193 | MODULE_DESCRIPTION("pinctrl-based I2C multiplexer driver" ); |
194 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>" ); |
195 | MODULE_LICENSE("GPL v2" ); |
196 | MODULE_ALIAS("platform:i2c-mux-pinctrl" ); |
197 | |