xref: /openbmc/linux/drivers/usb/typec/retimer.c (revision 10a03c36)
1ddaf8d96SPrashant Malani // SPDX-License-Identifier: GPL-2.0
2ddaf8d96SPrashant Malani /*
3ddaf8d96SPrashant Malani  * Copyright 2022 Google LLC
4ddaf8d96SPrashant Malani  *
5ddaf8d96SPrashant Malani  * USB Type-C Retimer support.
6ddaf8d96SPrashant Malani  * Author: Prashant Malani <pmalani@chromium.org>
7ddaf8d96SPrashant Malani  *
8ddaf8d96SPrashant Malani  */
9ddaf8d96SPrashant Malani 
10ddaf8d96SPrashant Malani #include <linux/device.h>
11ddaf8d96SPrashant Malani #include <linux/list.h>
12ddaf8d96SPrashant Malani #include <linux/module.h>
13ddaf8d96SPrashant Malani #include <linux/mutex.h>
14ddaf8d96SPrashant Malani #include <linux/property.h>
15ddaf8d96SPrashant Malani #include <linux/slab.h>
16ddaf8d96SPrashant Malani 
17ddaf8d96SPrashant Malani #include "class.h"
18ddaf8d96SPrashant Malani #include "retimer.h"
19ddaf8d96SPrashant Malani 
retimer_fwnode_match(struct device * dev,const void * fwnode)20ddaf8d96SPrashant Malani static int retimer_fwnode_match(struct device *dev, const void *fwnode)
21ddaf8d96SPrashant Malani {
22a84436a9SHeikki Krogerus 	return is_typec_retimer(dev) && device_match_fwnode(dev, fwnode);
23ddaf8d96SPrashant Malani }
24ddaf8d96SPrashant Malani 
typec_retimer_match(const struct fwnode_handle * fwnode,const char * id,void * data)25*23ead33bSAndy Shevchenko static void *typec_retimer_match(const struct fwnode_handle *fwnode, const char *id, void *data)
26ddaf8d96SPrashant Malani {
270c340438SNícolas F. R. A. Prado 	struct device *dev;
280c340438SNícolas F. R. A. Prado 
290c340438SNícolas F. R. A. Prado 	if (id && !fwnode_property_present(fwnode, id))
300c340438SNícolas F. R. A. Prado 		return NULL;
310c340438SNícolas F. R. A. Prado 
320c340438SNícolas F. R. A. Prado 	dev = class_find_device(&retimer_class, NULL, fwnode,
33ddaf8d96SPrashant Malani 				retimer_fwnode_match);
34ddaf8d96SPrashant Malani 
35ddaf8d96SPrashant Malani 	return dev ? to_typec_retimer(dev) : ERR_PTR(-EPROBE_DEFER);
36ddaf8d96SPrashant Malani }
37ddaf8d96SPrashant Malani 
38ddaf8d96SPrashant Malani /**
39ddaf8d96SPrashant Malani  * fwnode_typec_retimer_get - Find USB Type-C retimer.
40ddaf8d96SPrashant Malani  * @fwnode: The caller device node.
41ddaf8d96SPrashant Malani  *
42ddaf8d96SPrashant Malani  * Finds a retimer linked to the caller. This function is primarily meant for the
43ddaf8d96SPrashant Malani  * Type-C drivers. Returns a reference to the retimer on success, NULL if no
44ddaf8d96SPrashant Malani  * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
45ddaf8d96SPrashant Malani  * was found but the retimer has not been enumerated yet.
46ddaf8d96SPrashant Malani  */
fwnode_typec_retimer_get(struct fwnode_handle * fwnode)47ddaf8d96SPrashant Malani struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode)
48ddaf8d96SPrashant Malani {
49ddaf8d96SPrashant Malani 	struct typec_retimer *retimer;
50ddaf8d96SPrashant Malani 
51ddaf8d96SPrashant Malani 	retimer = fwnode_connection_find_match(fwnode, "retimer-switch", NULL, typec_retimer_match);
52ddaf8d96SPrashant Malani 	if (!IS_ERR_OR_NULL(retimer))
53ddaf8d96SPrashant Malani 		WARN_ON(!try_module_get(retimer->dev.parent->driver->owner));
54ddaf8d96SPrashant Malani 
55ddaf8d96SPrashant Malani 	return retimer;
56ddaf8d96SPrashant Malani }
57ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(fwnode_typec_retimer_get);
58ddaf8d96SPrashant Malani 
59ddaf8d96SPrashant Malani /**
60ddaf8d96SPrashant Malani  * typec_retimer_put - Release handle to a retimer.
61ddaf8d96SPrashant Malani  * @retimer: USB Type-C Connector Retimer.
62ddaf8d96SPrashant Malani  *
63ddaf8d96SPrashant Malani  * Decrements reference count for @retimer.
64ddaf8d96SPrashant Malani  */
typec_retimer_put(struct typec_retimer * retimer)65ddaf8d96SPrashant Malani void typec_retimer_put(struct typec_retimer *retimer)
66ddaf8d96SPrashant Malani {
67ddaf8d96SPrashant Malani 	if (!IS_ERR_OR_NULL(retimer)) {
68ddaf8d96SPrashant Malani 		module_put(retimer->dev.parent->driver->owner);
69ddaf8d96SPrashant Malani 		put_device(&retimer->dev);
70ddaf8d96SPrashant Malani 	}
71ddaf8d96SPrashant Malani }
72ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(typec_retimer_put);
73ddaf8d96SPrashant Malani 
typec_retimer_set(struct typec_retimer * retimer,struct typec_retimer_state * state)74ddaf8d96SPrashant Malani int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state)
75ddaf8d96SPrashant Malani {
76ddaf8d96SPrashant Malani 	if (IS_ERR_OR_NULL(retimer))
77ddaf8d96SPrashant Malani 		return 0;
78ddaf8d96SPrashant Malani 
79ddaf8d96SPrashant Malani 	return retimer->set(retimer, state);
80ddaf8d96SPrashant Malani }
81ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(typec_retimer_set);
82ddaf8d96SPrashant Malani 
typec_retimer_release(struct device * dev)83ddaf8d96SPrashant Malani static void typec_retimer_release(struct device *dev)
84ddaf8d96SPrashant Malani {
85ddaf8d96SPrashant Malani 	kfree(to_typec_retimer(dev));
86ddaf8d96SPrashant Malani }
87ddaf8d96SPrashant Malani 
88a84436a9SHeikki Krogerus const struct device_type typec_retimer_dev_type = {
89ddaf8d96SPrashant Malani 	.name = "typec_retimer",
90ddaf8d96SPrashant Malani 	.release = typec_retimer_release,
91ddaf8d96SPrashant Malani };
92ddaf8d96SPrashant Malani 
93ddaf8d96SPrashant Malani /**
94ddaf8d96SPrashant Malani  * typec_retimer_register - Register a retimer device.
95ddaf8d96SPrashant Malani  * @parent: Parent device.
96ddaf8d96SPrashant Malani  * @desc: Retimer description.
97ddaf8d96SPrashant Malani  *
98ddaf8d96SPrashant Malani  * Some USB Type-C connectors have their physical lines routed through retimers before they
99ddaf8d96SPrashant Malani  * reach muxes or host controllers. In some cases (for example: using alternate modes)
100ddaf8d96SPrashant Malani  * these retimers need to be reconfigured appropriately. This function registers retimer
101ddaf8d96SPrashant Malani  * switches which route and potentially modify the signals on the Type C physical lines
102ddaf8d96SPrashant Malani  * enroute to the host controllers.
103ddaf8d96SPrashant Malani  */
104ddaf8d96SPrashant Malani struct typec_retimer *
typec_retimer_register(struct device * parent,const struct typec_retimer_desc * desc)105ddaf8d96SPrashant Malani typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc)
106ddaf8d96SPrashant Malani {
107ddaf8d96SPrashant Malani 	struct typec_retimer *retimer;
108ddaf8d96SPrashant Malani 	int ret;
109ddaf8d96SPrashant Malani 
110ddaf8d96SPrashant Malani 	if (!desc || !desc->set)
111ddaf8d96SPrashant Malani 		return ERR_PTR(-EINVAL);
112ddaf8d96SPrashant Malani 
113ddaf8d96SPrashant Malani 	retimer = kzalloc(sizeof(*retimer), GFP_KERNEL);
114ddaf8d96SPrashant Malani 	if (!retimer)
115ddaf8d96SPrashant Malani 		return ERR_PTR(-ENOMEM);
116ddaf8d96SPrashant Malani 
117ddaf8d96SPrashant Malani 	retimer->set = desc->set;
118ddaf8d96SPrashant Malani 
119ddaf8d96SPrashant Malani 	device_initialize(&retimer->dev);
120ddaf8d96SPrashant Malani 	retimer->dev.parent = parent;
121ddaf8d96SPrashant Malani 	retimer->dev.fwnode = desc->fwnode;
122ddaf8d96SPrashant Malani 	retimer->dev.class = &retimer_class;
123ddaf8d96SPrashant Malani 	retimer->dev.type = &typec_retimer_dev_type;
124ddaf8d96SPrashant Malani 	retimer->dev.driver_data = desc->drvdata;
125ddaf8d96SPrashant Malani 	dev_set_name(&retimer->dev, "%s-retimer",
126ddaf8d96SPrashant Malani 		     desc->name ? desc->name : dev_name(parent));
127ddaf8d96SPrashant Malani 
128ddaf8d96SPrashant Malani 	ret = device_add(&retimer->dev);
129ddaf8d96SPrashant Malani 	if (ret) {
130ddaf8d96SPrashant Malani 		dev_err(parent, "failed to register retimer (%d)\n", ret);
131ddaf8d96SPrashant Malani 		put_device(&retimer->dev);
132ddaf8d96SPrashant Malani 		return ERR_PTR(ret);
133ddaf8d96SPrashant Malani 	}
134ddaf8d96SPrashant Malani 
135ddaf8d96SPrashant Malani 	return retimer;
136ddaf8d96SPrashant Malani }
137ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(typec_retimer_register);
138ddaf8d96SPrashant Malani 
139ddaf8d96SPrashant Malani /**
140ddaf8d96SPrashant Malani  * typec_retimer_unregister - Unregister retimer device.
141ddaf8d96SPrashant Malani  * @retimer: USB Type-C Connector retimer.
142ddaf8d96SPrashant Malani  *
143ddaf8d96SPrashant Malani  * Unregister retimer that was registered with typec_retimer_register().
144ddaf8d96SPrashant Malani  */
typec_retimer_unregister(struct typec_retimer * retimer)145ddaf8d96SPrashant Malani void typec_retimer_unregister(struct typec_retimer *retimer)
146ddaf8d96SPrashant Malani {
147ddaf8d96SPrashant Malani 	if (!IS_ERR_OR_NULL(retimer))
148ddaf8d96SPrashant Malani 		device_unregister(&retimer->dev);
149ddaf8d96SPrashant Malani }
150ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(typec_retimer_unregister);
151ddaf8d96SPrashant Malani 
typec_retimer_get_drvdata(struct typec_retimer * retimer)152ddaf8d96SPrashant Malani void *typec_retimer_get_drvdata(struct typec_retimer *retimer)
153ddaf8d96SPrashant Malani {
154ddaf8d96SPrashant Malani 	return dev_get_drvdata(&retimer->dev);
155ddaf8d96SPrashant Malani }
156ddaf8d96SPrashant Malani EXPORT_SYMBOL_GPL(typec_retimer_get_drvdata);
157ddaf8d96SPrashant Malani 
158ddaf8d96SPrashant Malani struct class retimer_class = {
159ddaf8d96SPrashant Malani 	.name = "retimer",
160ddaf8d96SPrashant Malani };
161