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