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