xref: /linux/drivers/usb/typec/mux.c (revision fab183d632628381b466a41479489541ac0e29a0)
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