1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * USB Type-C Multiplexer/DeMultiplexer Switch support
4 *
5 * Copyright (C) 2018 Intel Corporation
6 * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7 * Hans de Goede <hdegoede@redhat.com>
8 */
9
10 #include <linux/device.h>
11 #include <linux/list.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/property.h>
15 #include <linux/slab.h>
16
17 #include "class.h"
18 #include "mux.h"
19
20 #define TYPEC_MUX_MAX_DEVS 3
21
22 struct typec_switch {
23 struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
24 unsigned int num_sw_devs;
25 };
26
switch_fwnode_match(struct device * dev,const void * fwnode)27 static int switch_fwnode_match(struct device *dev, const void *fwnode)
28 {
29 if (!is_typec_switch_dev(dev))
30 return 0;
31
32 return device_match_fwnode(dev, fwnode);
33 }
34
typec_switch_match(const struct fwnode_handle * fwnode,const char * id,void * data)35 static void *typec_switch_match(const struct fwnode_handle *fwnode,
36 const char *id, void *data)
37 {
38 struct typec_switch_dev **sw_devs = data;
39 struct device *dev;
40 int i;
41
42 /*
43 * Device graph (OF graph) does not give any means to identify the
44 * device type or the device class of the remote port parent that @fwnode
45 * represents, so in order to identify the type or the class of @fwnode
46 * an additional device property is needed. With typec switches the
47 * property is named "orientation-switch" (@id). The value of the device
48 * property is ignored.
49 */
50 if (id && !fwnode_property_present(fwnode, id))
51 return NULL;
52
53 /*
54 * At this point we are sure that @fwnode is a typec switch in all
55 * cases. If the switch hasn't yet been registered for some reason, the
56 * function "defers probe" for now.
57 */
58 dev = class_find_device(&typec_mux_class, NULL, fwnode,
59 switch_fwnode_match);
60
61 /* Skip duplicates */
62 for (i = 0; i < TYPEC_MUX_MAX_DEVS; i++)
63 if (to_typec_switch_dev(dev) == sw_devs[i]) {
64 put_device(dev);
65 return NULL;
66 }
67
68 return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
69 }
70
71 /**
72 * fwnode_typec_switch_get - Find USB Type-C orientation switch
73 * @fwnode: The caller device node
74 *
75 * Finds a switch linked with @dev. Returns a reference to the switch on
76 * success, NULL if no matching connection was found, or
77 * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
78 * has not been enumerated yet.
79 */
fwnode_typec_switch_get(struct fwnode_handle * fwnode)80 struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
81 {
82 struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
83 struct typec_switch *sw;
84 int count;
85 int err;
86 int i;
87
88 sw = kzalloc_obj(*sw);
89 if (!sw)
90 return ERR_PTR(-ENOMEM);
91
92 count = fwnode_connection_find_matches(fwnode, "orientation-switch",
93 (void **)sw_devs,
94 typec_switch_match,
95 (void **)sw_devs,
96 ARRAY_SIZE(sw_devs));
97 if (count <= 0) {
98 kfree(sw);
99 return NULL;
100 }
101
102 for (i = 0; i < count; i++) {
103 if (IS_ERR(sw_devs[i])) {
104 err = PTR_ERR(sw_devs[i]);
105 goto put_sw_devs;
106 }
107 }
108
109 for (i = 0; i < count; i++) {
110 WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner));
111 sw->sw_devs[i] = sw_devs[i];
112 }
113
114 sw->num_sw_devs = count;
115
116 return sw;
117
118 put_sw_devs:
119 for (i = 0; i < count; i++) {
120 if (!IS_ERR(sw_devs[i]))
121 put_device(&sw_devs[i]->dev);
122 }
123
124 kfree(sw);
125
126 return ERR_PTR(err);
127 }
128 EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
129
130 /**
131 * typec_switch_put - Release USB Type-C orientation switch
132 * @sw: USB Type-C orientation switch
133 *
134 * Decrement reference count for @sw.
135 */
typec_switch_put(struct typec_switch * sw)136 void typec_switch_put(struct typec_switch *sw)
137 {
138 struct typec_switch_dev *sw_dev;
139 unsigned int i;
140
141 if (IS_ERR_OR_NULL(sw))
142 return;
143
144 for (i = 0; i < sw->num_sw_devs; i++) {
145 sw_dev = sw->sw_devs[i];
146
147 module_put(sw_dev->dev.parent->driver->owner);
148 put_device(&sw_dev->dev);
149 }
150 kfree(sw);
151 }
152 EXPORT_SYMBOL_GPL(typec_switch_put);
153
typec_switch_release(struct device * dev)154 static void typec_switch_release(struct device *dev)
155 {
156 kfree(to_typec_switch_dev(dev));
157 }
158
159 const struct device_type typec_switch_dev_type = {
160 .name = "orientation_switch",
161 .release = typec_switch_release,
162 };
163
164 /**
165 * typec_switch_register - Register USB Type-C orientation switch
166 * @parent: Parent device
167 * @desc: Orientation switch description
168 *
169 * This function registers a switch that can be used for routing the correct
170 * data pairs depending on the cable plug orientation from the USB Type-C
171 * connector to the USB controllers. USB Type-C plugs can be inserted
172 * right-side-up or upside-down.
173 */
174 struct typec_switch_dev *
typec_switch_register(struct device * parent,const struct typec_switch_desc * desc)175 typec_switch_register(struct device *parent,
176 const struct typec_switch_desc *desc)
177 {
178 struct typec_switch_dev *sw_dev;
179 int ret;
180
181 if (!desc || !desc->set)
182 return ERR_PTR(-EINVAL);
183
184 sw_dev = kzalloc_obj(*sw_dev);
185 if (!sw_dev)
186 return ERR_PTR(-ENOMEM);
187
188 sw_dev->set = desc->set;
189
190 device_initialize(&sw_dev->dev);
191 sw_dev->dev.parent = parent;
192 sw_dev->dev.fwnode = desc->fwnode;
193 sw_dev->dev.class = &typec_mux_class;
194 sw_dev->dev.type = &typec_switch_dev_type;
195 sw_dev->dev.driver_data = desc->drvdata;
196 ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent));
197 if (ret) {
198 put_device(&sw_dev->dev);
199 return ERR_PTR(ret);
200 }
201
202 ret = device_add(&sw_dev->dev);
203 if (ret) {
204 dev_err(parent, "failed to register switch (%d)\n", ret);
205 put_device(&sw_dev->dev);
206 return ERR_PTR(ret);
207 }
208
209 return sw_dev;
210 }
211 EXPORT_SYMBOL_GPL(typec_switch_register);
212
typec_switch_set(struct typec_switch * sw,enum typec_orientation orientation)213 int typec_switch_set(struct typec_switch *sw,
214 enum typec_orientation orientation)
215 {
216 struct typec_switch_dev *sw_dev;
217 unsigned int i;
218 int ret;
219
220 if (IS_ERR_OR_NULL(sw))
221 return 0;
222
223 for (i = 0; i < sw->num_sw_devs; i++) {
224 sw_dev = sw->sw_devs[i];
225
226 ret = sw_dev->set(sw_dev, orientation);
227 if (ret && ret != -EOPNOTSUPP)
228 return ret;
229 }
230
231 return 0;
232 }
233 EXPORT_SYMBOL_GPL(typec_switch_set);
234
235 /**
236 * typec_switch_unregister - Unregister USB Type-C orientation switch
237 * @sw_dev: USB Type-C orientation switch
238 *
239 * Unregister switch that was registered with typec_switch_register().
240 */
typec_switch_unregister(struct typec_switch_dev * sw_dev)241 void typec_switch_unregister(struct typec_switch_dev *sw_dev)
242 {
243 if (!IS_ERR_OR_NULL(sw_dev))
244 device_unregister(&sw_dev->dev);
245 }
246 EXPORT_SYMBOL_GPL(typec_switch_unregister);
247
typec_switch_set_drvdata(struct typec_switch_dev * sw_dev,void * data)248 void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data)
249 {
250 dev_set_drvdata(&sw_dev->dev, data);
251 }
252 EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
253
typec_switch_get_drvdata(struct typec_switch_dev * sw_dev)254 void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev)
255 {
256 return dev_get_drvdata(&sw_dev->dev);
257 }
258 EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
259
260 /* ------------------------------------------------------------------------- */
261
262 struct typec_mux {
263 struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
264 unsigned int num_mux_devs;
265 };
266
mux_fwnode_match(struct device * dev,const void * fwnode)267 static int mux_fwnode_match(struct device *dev, const void *fwnode)
268 {
269 if (!is_typec_mux_dev(dev))
270 return 0;
271
272 return device_match_fwnode(dev, fwnode);
273 }
274
typec_mux_match(const struct fwnode_handle * fwnode,const char * id,void * data)275 static void *typec_mux_match(const struct fwnode_handle *fwnode,
276 const char *id, void *data)
277 {
278 struct device *dev;
279
280 /*
281 * Device graph (OF graph) does not give any means to identify the
282 * device type or the device class of the remote port parent that @fwnode
283 * represents, so in order to identify the type or the class of @fwnode
284 * an additional device property is needed. With typec muxes the
285 * property is named "mode-switch" (@id). The value of the device
286 * property is ignored.
287 */
288 if (id && !fwnode_property_present(fwnode, id))
289 return NULL;
290
291 dev = class_find_device(&typec_mux_class, NULL, fwnode,
292 mux_fwnode_match);
293
294 return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
295 }
296
297 /**
298 * fwnode_typec_mux_get - Find USB Type-C Multiplexer
299 * @fwnode: The caller device node
300 *
301 * Finds a mux linked to the caller. This function is primarily meant for the
302 * Type-C drivers. Returns a reference to the mux on success, NULL if no
303 * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
304 * was found but the mux has not been enumerated yet.
305 */
fwnode_typec_mux_get(struct fwnode_handle * fwnode)306 struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode)
307 {
308 struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
309 struct typec_mux *mux;
310 int count;
311 int err;
312 int i;
313
314 mux = kzalloc_obj(*mux);
315 if (!mux)
316 return ERR_PTR(-ENOMEM);
317
318 count = fwnode_connection_find_matches(fwnode, "mode-switch",
319 NULL, typec_mux_match,
320 (void **)mux_devs,
321 ARRAY_SIZE(mux_devs));
322 if (count <= 0) {
323 kfree(mux);
324 return NULL;
325 }
326
327 for (i = 0; i < count; i++) {
328 if (IS_ERR(mux_devs[i])) {
329 err = PTR_ERR(mux_devs[i]);
330 goto put_mux_devs;
331 }
332 }
333
334 for (i = 0; i < count; i++) {
335 WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner));
336 mux->mux_devs[i] = mux_devs[i];
337 }
338
339 mux->num_mux_devs = count;
340
341 return mux;
342
343 put_mux_devs:
344 for (i = 0; i < count; i++) {
345 if (!IS_ERR(mux_devs[i]))
346 put_device(&mux_devs[i]->dev);
347 }
348
349 kfree(mux);
350
351 return ERR_PTR(err);
352 }
353 EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
354
355 /**
356 * typec_mux_put - Release handle to a Multiplexer
357 * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
358 *
359 * Decrements reference count for @mux.
360 */
typec_mux_put(struct typec_mux * mux)361 void typec_mux_put(struct typec_mux *mux)
362 {
363 struct typec_mux_dev *mux_dev;
364 unsigned int i;
365
366 if (IS_ERR_OR_NULL(mux))
367 return;
368
369 for (i = 0; i < mux->num_mux_devs; i++) {
370 mux_dev = mux->mux_devs[i];
371 module_put(mux_dev->dev.parent->driver->owner);
372 put_device(&mux_dev->dev);
373 }
374 kfree(mux);
375 }
376 EXPORT_SYMBOL_GPL(typec_mux_put);
377
typec_mux_set(struct typec_mux * mux,struct typec_mux_state * state)378 int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
379 {
380 struct typec_mux_dev *mux_dev;
381 unsigned int i;
382 int ret;
383
384 if (IS_ERR_OR_NULL(mux))
385 return 0;
386
387 for (i = 0; i < mux->num_mux_devs; i++) {
388 mux_dev = mux->mux_devs[i];
389
390 ret = mux_dev->set(mux_dev, state);
391 if (ret && ret != -EOPNOTSUPP)
392 return ret;
393 }
394
395 return 0;
396 }
397 EXPORT_SYMBOL_GPL(typec_mux_set);
398
typec_mux_release(struct device * dev)399 static void typec_mux_release(struct device *dev)
400 {
401 kfree(to_typec_mux_dev(dev));
402 }
403
404 const struct device_type typec_mux_dev_type = {
405 .name = "mode_switch",
406 .release = typec_mux_release,
407 };
408
409 /**
410 * typec_mux_register - Register Multiplexer routing USB Type-C pins
411 * @parent: Parent device
412 * @desc: Multiplexer description
413 *
414 * USB Type-C connectors can be used for alternate modes of operation besides
415 * USB when Accessory/Alternate Modes are supported. With some of those modes,
416 * the pins on the connector need to be reconfigured. This function registers
417 * multiplexer switches routing the pins on the connector.
418 */
419 struct typec_mux_dev *
typec_mux_register(struct device * parent,const struct typec_mux_desc * desc)420 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
421 {
422 struct typec_mux_dev *mux_dev;
423 int ret;
424
425 if (!desc || !desc->set)
426 return ERR_PTR(-EINVAL);
427
428 mux_dev = kzalloc_obj(*mux_dev);
429 if (!mux_dev)
430 return ERR_PTR(-ENOMEM);
431
432 mux_dev->set = desc->set;
433
434 device_initialize(&mux_dev->dev);
435 mux_dev->dev.parent = parent;
436 mux_dev->dev.fwnode = desc->fwnode;
437 mux_dev->dev.class = &typec_mux_class;
438 mux_dev->dev.type = &typec_mux_dev_type;
439 mux_dev->dev.driver_data = desc->drvdata;
440 ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent));
441 if (ret) {
442 put_device(&mux_dev->dev);
443 return ERR_PTR(ret);
444 }
445
446 ret = device_add(&mux_dev->dev);
447 if (ret) {
448 dev_err(parent, "failed to register mux (%d)\n", ret);
449 put_device(&mux_dev->dev);
450 return ERR_PTR(ret);
451 }
452
453 return mux_dev;
454 }
455 EXPORT_SYMBOL_GPL(typec_mux_register);
456
457 /**
458 * typec_mux_unregister - Unregister Multiplexer Switch
459 * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer
460 *
461 * Unregister mux that was registered with typec_mux_register().
462 */
typec_mux_unregister(struct typec_mux_dev * mux_dev)463 void typec_mux_unregister(struct typec_mux_dev *mux_dev)
464 {
465 if (!IS_ERR_OR_NULL(mux_dev))
466 device_unregister(&mux_dev->dev);
467 }
468 EXPORT_SYMBOL_GPL(typec_mux_unregister);
469
typec_mux_set_drvdata(struct typec_mux_dev * mux_dev,void * data)470 void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data)
471 {
472 dev_set_drvdata(&mux_dev->dev, data);
473 }
474 EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
475
typec_mux_get_drvdata(struct typec_mux_dev * mux_dev)476 void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev)
477 {
478 return dev_get_drvdata(&mux_dev->dev);
479 }
480 EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
481
482 const struct class typec_mux_class = {
483 .name = "typec_mux",
484 };
485