]>
Commit | Line | Data |
---|---|---|
fde0aa6c HK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * USB Role 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/usb/role.h> | |
ec69e953 | 11 | #include <linux/property.h> |
fde0aa6c HK |
12 | #include <linux/device.h> |
13 | #include <linux/module.h> | |
14 | #include <linux/mutex.h> | |
15 | #include <linux/slab.h> | |
16 | ||
17 | static struct class *role_class; | |
18 | ||
19 | struct usb_role_switch { | |
20 | struct device dev; | |
21 | struct mutex lock; /* device lock*/ | |
22 | enum usb_role role; | |
23 | ||
24 | /* From descriptor */ | |
25 | struct device *usb2_port; | |
26 | struct device *usb3_port; | |
27 | struct device *udc; | |
28 | usb_role_switch_set_t set; | |
29 | usb_role_switch_get_t get; | |
30 | bool allow_userspace_control; | |
31 | }; | |
32 | ||
33 | #define to_role_switch(d) container_of(d, struct usb_role_switch, dev) | |
34 | ||
35 | /** | |
36 | * usb_role_switch_set_role - Set USB role for a switch | |
37 | * @sw: USB role switch | |
38 | * @role: USB role to be switched to | |
39 | * | |
40 | * Set USB role @role for @sw. | |
41 | */ | |
42 | int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role) | |
43 | { | |
44 | int ret; | |
45 | ||
46 | if (IS_ERR_OR_NULL(sw)) | |
47 | return 0; | |
48 | ||
49 | mutex_lock(&sw->lock); | |
50 | ||
51 | ret = sw->set(sw->dev.parent, role); | |
52 | if (!ret) | |
53 | sw->role = role; | |
54 | ||
55 | mutex_unlock(&sw->lock); | |
56 | ||
57 | return ret; | |
58 | } | |
59 | EXPORT_SYMBOL_GPL(usb_role_switch_set_role); | |
60 | ||
61 | /** | |
62 | * usb_role_switch_get_role - Get the USB role for a switch | |
63 | * @sw: USB role switch | |
64 | * | |
65 | * Depending on the role-switch-driver this function returns either a cached | |
66 | * value of the last set role, or reads back the actual value from the hardware. | |
67 | */ | |
68 | enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw) | |
69 | { | |
70 | enum usb_role role; | |
71 | ||
72 | if (IS_ERR_OR_NULL(sw)) | |
73 | return USB_ROLE_NONE; | |
74 | ||
75 | mutex_lock(&sw->lock); | |
76 | ||
77 | if (sw->get) | |
78 | role = sw->get(sw->dev.parent); | |
79 | else | |
80 | role = sw->role; | |
81 | ||
82 | mutex_unlock(&sw->lock); | |
83 | ||
84 | return role; | |
85 | } | |
86 | EXPORT_SYMBOL_GPL(usb_role_switch_get_role); | |
87 | ||
fde0aa6c HK |
88 | static void *usb_role_switch_match(struct device_connection *con, int ep, |
89 | void *data) | |
90 | { | |
91 | struct device *dev; | |
92 | ||
ec69e953 | 93 | if (con->fwnode) { |
fde77779 | 94 | if (con->id && !fwnode_property_present(con->fwnode, con->id)) |
ec69e953 HK |
95 | return NULL; |
96 | ||
67843bba | 97 | dev = class_find_device_by_fwnode(role_class, con->fwnode); |
ec69e953 | 98 | } else { |
6cda08a2 | 99 | dev = class_find_device_by_name(role_class, con->endpoint[ep]); |
ec69e953 | 100 | } |
fde0aa6c HK |
101 | |
102 | return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); | |
103 | } | |
104 | ||
6fadd729 CY |
105 | static struct usb_role_switch * |
106 | usb_role_switch_is_parent(struct fwnode_handle *fwnode) | |
107 | { | |
108 | struct fwnode_handle *parent = fwnode_get_parent(fwnode); | |
109 | struct device *dev; | |
110 | ||
111 | if (!parent || !fwnode_property_present(parent, "usb-role-switch")) | |
112 | return NULL; | |
113 | ||
114 | dev = class_find_device_by_fwnode(role_class, parent); | |
115 | return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); | |
116 | } | |
117 | ||
fde0aa6c HK |
118 | /** |
119 | * usb_role_switch_get - Find USB role switch linked with the caller | |
120 | * @dev: The caller device | |
121 | * | |
122 | * Finds and returns role switch linked with @dev. The reference count for the | |
123 | * found switch is incremented. | |
124 | */ | |
125 | struct usb_role_switch *usb_role_switch_get(struct device *dev) | |
126 | { | |
5c54fcac HK |
127 | struct usb_role_switch *sw; |
128 | ||
6fadd729 CY |
129 | sw = usb_role_switch_is_parent(dev_fwnode(dev)); |
130 | if (!sw) | |
131 | sw = device_connection_find_match(dev, "usb-role-switch", NULL, | |
132 | usb_role_switch_match); | |
5c54fcac HK |
133 | |
134 | if (!IS_ERR_OR_NULL(sw)) | |
135 | WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); | |
136 | ||
137 | return sw; | |
fde0aa6c HK |
138 | } |
139 | EXPORT_SYMBOL_GPL(usb_role_switch_get); | |
140 | ||
a31f0177 HK |
141 | /** |
142 | * fwnode_usb_role_switch_get - Find USB role switch linked with the caller | |
143 | * @fwnode: The caller device node | |
144 | * | |
145 | * This is similar to the usb_role_switch_get() function above, but it searches | |
146 | * the switch using fwnode instead of device entry. | |
147 | */ | |
148 | struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode) | |
149 | { | |
150 | struct usb_role_switch *sw; | |
151 | ||
6fadd729 CY |
152 | sw = usb_role_switch_is_parent(fwnode); |
153 | if (!sw) | |
154 | sw = fwnode_connection_find_match(fwnode, "usb-role-switch", | |
155 | NULL, usb_role_switch_match); | |
a31f0177 HK |
156 | if (!IS_ERR_OR_NULL(sw)) |
157 | WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); | |
158 | ||
159 | return sw; | |
160 | } | |
161 | EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get); | |
162 | ||
fde0aa6c HK |
163 | /** |
164 | * usb_role_switch_put - Release handle to a switch | |
165 | * @sw: USB Role Switch | |
166 | * | |
167 | * Decrement reference count for @sw. | |
168 | */ | |
169 | void usb_role_switch_put(struct usb_role_switch *sw) | |
170 | { | |
5c54fcac | 171 | if (!IS_ERR_OR_NULL(sw)) { |
5c54fcac | 172 | module_put(sw->dev.parent->driver->owner); |
1848a543 | 173 | put_device(&sw->dev); |
5c54fcac | 174 | } |
fde0aa6c HK |
175 | } |
176 | EXPORT_SYMBOL_GPL(usb_role_switch_put); | |
177 | ||
c6919d5e HK |
178 | /** |
179 | * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode | |
180 | * @fwnode: fwnode of the USB Role Switch | |
181 | * | |
182 | * Finds and returns role switch with @fwnode. The reference count for the | |
183 | * found switch is incremented. | |
184 | */ | |
185 | struct usb_role_switch * | |
186 | usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) | |
187 | { | |
188 | struct device *dev; | |
189 | ||
190 | if (!fwnode) | |
191 | return NULL; | |
192 | ||
193 | dev = class_find_device_by_fwnode(role_class, fwnode); | |
194 | ||
195 | return dev ? to_role_switch(dev) : NULL; | |
196 | } | |
197 | EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode); | |
198 | ||
fde0aa6c HK |
199 | static umode_t |
200 | usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) | |
201 | { | |
202 | struct device *dev = container_of(kobj, typeof(*dev), kobj); | |
203 | struct usb_role_switch *sw = to_role_switch(dev); | |
204 | ||
205 | if (sw->allow_userspace_control) | |
206 | return attr->mode; | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static const char * const usb_roles[] = { | |
212 | [USB_ROLE_NONE] = "none", | |
213 | [USB_ROLE_HOST] = "host", | |
214 | [USB_ROLE_DEVICE] = "device", | |
215 | }; | |
216 | ||
217 | static ssize_t | |
218 | role_show(struct device *dev, struct device_attribute *attr, char *buf) | |
219 | { | |
220 | struct usb_role_switch *sw = to_role_switch(dev); | |
221 | enum usb_role role = usb_role_switch_get_role(sw); | |
222 | ||
223 | return sprintf(buf, "%s\n", usb_roles[role]); | |
224 | } | |
225 | ||
226 | static ssize_t role_store(struct device *dev, struct device_attribute *attr, | |
227 | const char *buf, size_t size) | |
228 | { | |
229 | struct usb_role_switch *sw = to_role_switch(dev); | |
230 | int ret; | |
231 | ||
232 | ret = sysfs_match_string(usb_roles, buf); | |
233 | if (ret < 0) { | |
234 | bool res; | |
235 | ||
236 | /* Extra check if the user wants to disable the switch */ | |
237 | ret = kstrtobool(buf, &res); | |
238 | if (ret || res) | |
239 | return -EINVAL; | |
240 | } | |
241 | ||
242 | ret = usb_role_switch_set_role(sw, ret); | |
243 | if (ret) | |
244 | return ret; | |
245 | ||
246 | return size; | |
247 | } | |
248 | static DEVICE_ATTR_RW(role); | |
249 | ||
250 | static struct attribute *usb_role_switch_attrs[] = { | |
251 | &dev_attr_role.attr, | |
252 | NULL, | |
253 | }; | |
254 | ||
255 | static const struct attribute_group usb_role_switch_group = { | |
256 | .is_visible = usb_role_switch_is_visible, | |
257 | .attrs = usb_role_switch_attrs, | |
258 | }; | |
259 | ||
260 | static const struct attribute_group *usb_role_switch_groups[] = { | |
261 | &usb_role_switch_group, | |
262 | NULL, | |
263 | }; | |
264 | ||
265 | static int | |
266 | usb_role_switch_uevent(struct device *dev, struct kobj_uevent_env *env) | |
267 | { | |
268 | int ret; | |
269 | ||
270 | ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev)); | |
271 | if (ret) | |
272 | dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n"); | |
273 | ||
274 | return ret; | |
275 | } | |
276 | ||
277 | static void usb_role_switch_release(struct device *dev) | |
278 | { | |
279 | struct usb_role_switch *sw = to_role_switch(dev); | |
280 | ||
281 | kfree(sw); | |
282 | } | |
283 | ||
284 | static const struct device_type usb_role_dev_type = { | |
285 | .name = "usb_role_switch", | |
286 | .groups = usb_role_switch_groups, | |
287 | .uevent = usb_role_switch_uevent, | |
288 | .release = usb_role_switch_release, | |
289 | }; | |
290 | ||
291 | /** | |
292 | * usb_role_switch_register - Register USB Role Switch | |
293 | * @parent: Parent device for the switch | |
294 | * @desc: Description of the switch | |
295 | * | |
296 | * USB Role Switch is a device capable or choosing the role for USB connector. | |
297 | * On platforms where the USB controller is dual-role capable, the controller | |
298 | * driver will need to register the switch. On platforms where the USB host and | |
299 | * USB device controllers behind the connector are separate, there will be a | |
300 | * mux, and the driver for that mux will need to register the switch. | |
301 | * | |
302 | * Returns handle to a new role switch or ERR_PTR. The content of @desc is | |
303 | * copied. | |
304 | */ | |
305 | struct usb_role_switch * | |
306 | usb_role_switch_register(struct device *parent, | |
307 | const struct usb_role_switch_desc *desc) | |
308 | { | |
309 | struct usb_role_switch *sw; | |
310 | int ret; | |
311 | ||
312 | if (!desc || !desc->set) | |
313 | return ERR_PTR(-EINVAL); | |
314 | ||
315 | sw = kzalloc(sizeof(*sw), GFP_KERNEL); | |
316 | if (!sw) | |
317 | return ERR_PTR(-ENOMEM); | |
318 | ||
319 | mutex_init(&sw->lock); | |
320 | ||
321 | sw->allow_userspace_control = desc->allow_userspace_control; | |
322 | sw->usb2_port = desc->usb2_port; | |
323 | sw->usb3_port = desc->usb3_port; | |
324 | sw->udc = desc->udc; | |
325 | sw->set = desc->set; | |
326 | sw->get = desc->get; | |
327 | ||
328 | sw->dev.parent = parent; | |
ec69e953 | 329 | sw->dev.fwnode = desc->fwnode; |
fde0aa6c HK |
330 | sw->dev.class = role_class; |
331 | sw->dev.type = &usb_role_dev_type; | |
332 | dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); | |
333 | ||
334 | ret = device_register(&sw->dev); | |
335 | if (ret) { | |
336 | put_device(&sw->dev); | |
337 | return ERR_PTR(ret); | |
338 | } | |
339 | ||
340 | /* TODO: Symlinks for the host port and the device controller. */ | |
341 | ||
342 | return sw; | |
343 | } | |
344 | EXPORT_SYMBOL_GPL(usb_role_switch_register); | |
345 | ||
346 | /** | |
347 | * usb_role_switch_unregister - Unregsiter USB Role Switch | |
348 | * @sw: USB Role Switch | |
349 | * | |
350 | * Unregister switch that was registered with usb_role_switch_register(). | |
351 | */ | |
352 | void usb_role_switch_unregister(struct usb_role_switch *sw) | |
353 | { | |
354 | if (!IS_ERR_OR_NULL(sw)) | |
355 | device_unregister(&sw->dev); | |
356 | } | |
357 | EXPORT_SYMBOL_GPL(usb_role_switch_unregister); | |
358 | ||
359 | static int __init usb_roles_init(void) | |
360 | { | |
361 | role_class = class_create(THIS_MODULE, "usb_role"); | |
362 | return PTR_ERR_OR_ZERO(role_class); | |
363 | } | |
364 | subsys_initcall(usb_roles_init); | |
365 | ||
366 | static void __exit usb_roles_exit(void) | |
367 | { | |
368 | class_destroy(role_class); | |
369 | } | |
370 | module_exit(usb_roles_exit); | |
371 | ||
372 | MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>"); | |
373 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); | |
374 | MODULE_LICENSE("GPL v2"); | |
375 | MODULE_DESCRIPTION("USB Role Class"); |