1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * I2C multiplexer using GPIO API |
4 | * |
5 | * Peter Korsgaard <peter.korsgaard@barco.com> |
6 | */ |
7 | |
8 | #include <linux/i2c.h> |
9 | #include <linux/i2c-mux.h> |
10 | #include <linux/overflow.h> |
11 | #include <linux/platform_data/i2c-mux-gpio.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/module.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/bits.h> |
16 | #include <linux/gpio/consumer.h> |
17 | #include <linux/gpio/driver.h> |
18 | |
19 | struct gpiomux { |
20 | struct i2c_mux_gpio_platform_data data; |
21 | int ngpios; |
22 | struct gpio_desc **gpios; |
23 | }; |
24 | |
25 | static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned int val) |
26 | { |
27 | DECLARE_BITMAP(values, BITS_PER_TYPE(val)); |
28 | |
29 | values[0] = val; |
30 | |
31 | gpiod_set_array_value_cansleep(array_size: mux->ngpios, desc_array: mux->gpios, NULL, value_bitmap: values); |
32 | } |
33 | |
34 | static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan) |
35 | { |
36 | struct gpiomux *mux = i2c_mux_priv(muxc); |
37 | |
38 | i2c_mux_gpio_set(mux, val: chan); |
39 | |
40 | return 0; |
41 | } |
42 | |
43 | static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan) |
44 | { |
45 | struct gpiomux *mux = i2c_mux_priv(muxc); |
46 | |
47 | i2c_mux_gpio_set(mux, val: mux->data.idle); |
48 | |
49 | return 0; |
50 | } |
51 | |
52 | static int i2c_mux_gpio_probe_fw(struct gpiomux *mux, |
53 | struct platform_device *pdev) |
54 | { |
55 | struct device *dev = &pdev->dev; |
56 | struct fwnode_handle *fwnode = dev_fwnode(dev); |
57 | struct device_node *np = dev->of_node; |
58 | struct device_node *adapter_np; |
59 | struct i2c_adapter *adapter = NULL; |
60 | struct fwnode_handle *child; |
61 | unsigned int *values; |
62 | int rc, i = 0; |
63 | |
64 | if (is_of_node(fwnode)) { |
65 | if (!np) |
66 | return -ENODEV; |
67 | |
68 | adapter_np = of_parse_phandle(np, phandle_name: "i2c-parent" , index: 0); |
69 | if (!adapter_np) { |
70 | dev_err(&pdev->dev, "Cannot parse i2c-parent\n" ); |
71 | return -ENODEV; |
72 | } |
73 | adapter = of_find_i2c_adapter_by_node(node: adapter_np); |
74 | of_node_put(node: adapter_np); |
75 | |
76 | } else if (is_acpi_node(fwnode)) { |
77 | /* |
78 | * In ACPI land the mux should be a direct child of the i2c |
79 | * bus it muxes. |
80 | */ |
81 | acpi_handle dev_handle = ACPI_HANDLE(dev->parent); |
82 | |
83 | adapter = i2c_acpi_find_adapter_by_handle(handle: dev_handle); |
84 | } |
85 | |
86 | if (!adapter) |
87 | return -EPROBE_DEFER; |
88 | |
89 | mux->data.parent = i2c_adapter_id(adap: adapter); |
90 | put_device(dev: &adapter->dev); |
91 | |
92 | mux->data.n_values = device_get_child_node_count(dev); |
93 | values = devm_kcalloc(dev, |
94 | n: mux->data.n_values, size: sizeof(*mux->data.values), |
95 | GFP_KERNEL); |
96 | if (!values) { |
97 | dev_err(dev, "Cannot allocate values array" ); |
98 | return -ENOMEM; |
99 | } |
100 | |
101 | device_for_each_child_node(dev, child) { |
102 | if (is_of_node(fwnode: child)) { |
103 | fwnode_property_read_u32(fwnode: child, propname: "reg" , val: values + i); |
104 | } else if (is_acpi_node(fwnode: child)) { |
105 | rc = acpi_get_local_address(ACPI_HANDLE_FWNODE(child), addr: values + i); |
106 | if (rc) { |
107 | fwnode_handle_put(fwnode: child); |
108 | return dev_err_probe(dev, err: rc, fmt: "Cannot get address\n" ); |
109 | } |
110 | } |
111 | |
112 | i++; |
113 | } |
114 | mux->data.values = values; |
115 | |
116 | if (device_property_read_u32(dev, propname: "idle-state" , val: &mux->data.idle)) |
117 | mux->data.idle = I2C_MUX_GPIO_NO_IDLE; |
118 | |
119 | return 0; |
120 | } |
121 | |
122 | static int i2c_mux_gpio_probe(struct platform_device *pdev) |
123 | { |
124 | struct i2c_mux_core *muxc; |
125 | struct gpiomux *mux; |
126 | struct i2c_adapter *parent; |
127 | struct i2c_adapter *root; |
128 | unsigned int initial_state; |
129 | int i, ngpios, ret; |
130 | |
131 | mux = devm_kzalloc(dev: &pdev->dev, size: sizeof(*mux), GFP_KERNEL); |
132 | if (!mux) |
133 | return -ENOMEM; |
134 | |
135 | if (!dev_get_platdata(dev: &pdev->dev)) { |
136 | ret = i2c_mux_gpio_probe_fw(mux, pdev); |
137 | if (ret < 0) |
138 | return ret; |
139 | } else { |
140 | memcpy(&mux->data, dev_get_platdata(&pdev->dev), |
141 | sizeof(mux->data)); |
142 | } |
143 | |
144 | ngpios = gpiod_count(dev: &pdev->dev, con_id: "mux" ); |
145 | if (ngpios <= 0) { |
146 | dev_err(&pdev->dev, "no valid gpios provided\n" ); |
147 | return ngpios ?: -EINVAL; |
148 | } |
149 | mux->ngpios = ngpios; |
150 | |
151 | parent = i2c_get_adapter(nr: mux->data.parent); |
152 | if (!parent) |
153 | return -EPROBE_DEFER; |
154 | |
155 | muxc = i2c_mux_alloc(parent, dev: &pdev->dev, max_adapters: mux->data.n_values, |
156 | array_size(ngpios, sizeof(*mux->gpios)), flags: 0, |
157 | select: i2c_mux_gpio_select, NULL); |
158 | if (!muxc) { |
159 | ret = -ENOMEM; |
160 | goto alloc_failed; |
161 | } |
162 | mux->gpios = muxc->priv; |
163 | muxc->priv = mux; |
164 | |
165 | platform_set_drvdata(pdev, data: muxc); |
166 | |
167 | root = i2c_root_adapter(dev: &parent->dev); |
168 | |
169 | muxc->mux_locked = true; |
170 | |
171 | if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) { |
172 | initial_state = mux->data.idle; |
173 | muxc->deselect = i2c_mux_gpio_deselect; |
174 | } else { |
175 | initial_state = mux->data.values[0]; |
176 | } |
177 | |
178 | for (i = 0; i < ngpios; i++) { |
179 | struct gpio_device *gdev; |
180 | struct device *dev; |
181 | struct gpio_desc *gpiod; |
182 | enum gpiod_flags flag; |
183 | |
184 | if (initial_state & BIT(i)) |
185 | flag = GPIOD_OUT_HIGH; |
186 | else |
187 | flag = GPIOD_OUT_LOW; |
188 | gpiod = devm_gpiod_get_index(dev: &pdev->dev, con_id: "mux" , idx: i, flags: flag); |
189 | if (IS_ERR(ptr: gpiod)) { |
190 | ret = PTR_ERR(ptr: gpiod); |
191 | goto alloc_failed; |
192 | } |
193 | |
194 | mux->gpios[i] = gpiod; |
195 | |
196 | if (!muxc->mux_locked) |
197 | continue; |
198 | |
199 | gdev = gpiod_to_gpio_device(desc: gpiod); |
200 | dev = gpio_device_to_device(gdev); |
201 | muxc->mux_locked = i2c_root_adapter(dev) == root; |
202 | } |
203 | |
204 | if (muxc->mux_locked) |
205 | dev_info(&pdev->dev, "mux-locked i2c mux\n" ); |
206 | |
207 | for (i = 0; i < mux->data.n_values; i++) { |
208 | u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0; |
209 | unsigned int class = mux->data.classes ? mux->data.classes[i] : 0; |
210 | |
211 | ret = i2c_mux_add_adapter(muxc, force_nr: nr, chan_id: mux->data.values[i], class); |
212 | if (ret) |
213 | goto add_adapter_failed; |
214 | } |
215 | |
216 | dev_info(&pdev->dev, "%d port mux on %s adapter\n" , |
217 | mux->data.n_values, parent->name); |
218 | |
219 | return 0; |
220 | |
221 | add_adapter_failed: |
222 | i2c_mux_del_adapters(muxc); |
223 | alloc_failed: |
224 | i2c_put_adapter(adap: parent); |
225 | |
226 | return ret; |
227 | } |
228 | |
229 | static void i2c_mux_gpio_remove(struct platform_device *pdev) |
230 | { |
231 | struct i2c_mux_core *muxc = platform_get_drvdata(pdev); |
232 | |
233 | i2c_mux_del_adapters(muxc); |
234 | i2c_put_adapter(adap: muxc->parent); |
235 | } |
236 | |
237 | static const struct of_device_id i2c_mux_gpio_of_match[] = { |
238 | { .compatible = "i2c-mux-gpio" , }, |
239 | {}, |
240 | }; |
241 | MODULE_DEVICE_TABLE(of, i2c_mux_gpio_of_match); |
242 | |
243 | static struct platform_driver i2c_mux_gpio_driver = { |
244 | .probe = i2c_mux_gpio_probe, |
245 | .remove_new = i2c_mux_gpio_remove, |
246 | .driver = { |
247 | .name = "i2c-mux-gpio" , |
248 | .of_match_table = i2c_mux_gpio_of_match, |
249 | }, |
250 | }; |
251 | |
252 | module_platform_driver(i2c_mux_gpio_driver); |
253 | |
254 | MODULE_DESCRIPTION("GPIO-based I2C multiplexer driver" ); |
255 | MODULE_AUTHOR("Peter Korsgaard <peter.korsgaard@barco.com>" ); |
256 | MODULE_LICENSE("GPL" ); |
257 | MODULE_ALIAS("platform:i2c-mux-gpio" ); |
258 | |