| .. | .. |
|---|
| 8 | 8 | */ |
|---|
| 9 | 9 | |
|---|
| 10 | 10 | #include <linux/usb/role.h> |
|---|
| 11 | +#include <linux/property.h> |
|---|
| 11 | 12 | #include <linux/device.h> |
|---|
| 12 | 13 | #include <linux/module.h> |
|---|
| 13 | 14 | #include <linux/mutex.h> |
|---|
| .. | .. |
|---|
| 47 | 48 | |
|---|
| 48 | 49 | mutex_lock(&sw->lock); |
|---|
| 49 | 50 | |
|---|
| 50 | | - ret = sw->set(sw->dev.parent, role); |
|---|
| 51 | | - if (!ret) |
|---|
| 51 | + ret = sw->set(sw, role); |
|---|
| 52 | + if (!ret) { |
|---|
| 52 | 53 | sw->role = role; |
|---|
| 54 | + kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE); |
|---|
| 55 | + } |
|---|
| 53 | 56 | |
|---|
| 54 | 57 | mutex_unlock(&sw->lock); |
|---|
| 55 | 58 | |
|---|
| .. | .. |
|---|
| 74 | 77 | mutex_lock(&sw->lock); |
|---|
| 75 | 78 | |
|---|
| 76 | 79 | if (sw->get) |
|---|
| 77 | | - role = sw->get(sw->dev.parent); |
|---|
| 80 | + role = sw->get(sw); |
|---|
| 78 | 81 | else |
|---|
| 79 | 82 | role = sw->role; |
|---|
| 80 | 83 | |
|---|
| .. | .. |
|---|
| 84 | 87 | } |
|---|
| 85 | 88 | EXPORT_SYMBOL_GPL(usb_role_switch_get_role); |
|---|
| 86 | 89 | |
|---|
| 87 | | -static int __switch_match(struct device *dev, const void *name) |
|---|
| 88 | | -{ |
|---|
| 89 | | - return !strcmp((const char *)name, dev_name(dev)); |
|---|
| 90 | | -} |
|---|
| 91 | | - |
|---|
| 92 | | -static void *usb_role_switch_match(struct device_connection *con, int ep, |
|---|
| 90 | +static void *usb_role_switch_match(struct fwnode_handle *fwnode, const char *id, |
|---|
| 93 | 91 | void *data) |
|---|
| 94 | 92 | { |
|---|
| 95 | 93 | struct device *dev; |
|---|
| 96 | 94 | |
|---|
| 97 | | - dev = class_find_device(role_class, NULL, con->endpoint[ep], |
|---|
| 98 | | - __switch_match); |
|---|
| 95 | + if (id && !fwnode_property_present(fwnode, id)) |
|---|
| 96 | + return NULL; |
|---|
| 99 | 97 | |
|---|
| 98 | + dev = class_find_device_by_fwnode(role_class, fwnode); |
|---|
| 99 | + |
|---|
| 100 | + return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); |
|---|
| 101 | +} |
|---|
| 102 | + |
|---|
| 103 | +static struct usb_role_switch * |
|---|
| 104 | +usb_role_switch_is_parent(struct fwnode_handle *fwnode) |
|---|
| 105 | +{ |
|---|
| 106 | + struct fwnode_handle *parent = fwnode_get_parent(fwnode); |
|---|
| 107 | + struct device *dev; |
|---|
| 108 | + |
|---|
| 109 | + if (!parent || !fwnode_property_present(parent, "usb-role-switch")) |
|---|
| 110 | + return NULL; |
|---|
| 111 | + |
|---|
| 112 | + dev = class_find_device_by_fwnode(role_class, parent); |
|---|
| 100 | 113 | return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER); |
|---|
| 101 | 114 | } |
|---|
| 102 | 115 | |
|---|
| .. | .. |
|---|
| 111 | 124 | { |
|---|
| 112 | 125 | struct usb_role_switch *sw; |
|---|
| 113 | 126 | |
|---|
| 114 | | - sw = device_connection_find_match(dev, "usb-role-switch", NULL, |
|---|
| 115 | | - usb_role_switch_match); |
|---|
| 127 | + sw = usb_role_switch_is_parent(dev_fwnode(dev)); |
|---|
| 128 | + if (!sw) |
|---|
| 129 | + sw = device_connection_find_match(dev, "usb-role-switch", NULL, |
|---|
| 130 | + usb_role_switch_match); |
|---|
| 116 | 131 | |
|---|
| 117 | 132 | if (!IS_ERR_OR_NULL(sw)) |
|---|
| 118 | 133 | WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); |
|---|
| .. | .. |
|---|
| 120 | 135 | return sw; |
|---|
| 121 | 136 | } |
|---|
| 122 | 137 | EXPORT_SYMBOL_GPL(usb_role_switch_get); |
|---|
| 138 | + |
|---|
| 139 | +/** |
|---|
| 140 | + * fwnode_usb_role_switch_get - Find USB role switch linked with the caller |
|---|
| 141 | + * @fwnode: The caller device node |
|---|
| 142 | + * |
|---|
| 143 | + * This is similar to the usb_role_switch_get() function above, but it searches |
|---|
| 144 | + * the switch using fwnode instead of device entry. |
|---|
| 145 | + */ |
|---|
| 146 | +struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode) |
|---|
| 147 | +{ |
|---|
| 148 | + struct usb_role_switch *sw; |
|---|
| 149 | + |
|---|
| 150 | + sw = usb_role_switch_is_parent(fwnode); |
|---|
| 151 | + if (!sw) |
|---|
| 152 | + sw = fwnode_connection_find_match(fwnode, "usb-role-switch", |
|---|
| 153 | + NULL, usb_role_switch_match); |
|---|
| 154 | + if (!IS_ERR_OR_NULL(sw)) |
|---|
| 155 | + WARN_ON(!try_module_get(sw->dev.parent->driver->owner)); |
|---|
| 156 | + |
|---|
| 157 | + return sw; |
|---|
| 158 | +} |
|---|
| 159 | +EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get); |
|---|
| 123 | 160 | |
|---|
| 124 | 161 | /** |
|---|
| 125 | 162 | * usb_role_switch_put - Release handle to a switch |
|---|
| .. | .. |
|---|
| 136 | 173 | } |
|---|
| 137 | 174 | EXPORT_SYMBOL_GPL(usb_role_switch_put); |
|---|
| 138 | 175 | |
|---|
| 176 | +/** |
|---|
| 177 | + * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode |
|---|
| 178 | + * @fwnode: fwnode of the USB Role Switch |
|---|
| 179 | + * |
|---|
| 180 | + * Finds and returns role switch with @fwnode. The reference count for the |
|---|
| 181 | + * found switch is incremented. |
|---|
| 182 | + */ |
|---|
| 183 | +struct usb_role_switch * |
|---|
| 184 | +usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode) |
|---|
| 185 | +{ |
|---|
| 186 | + struct device *dev; |
|---|
| 187 | + |
|---|
| 188 | + if (!fwnode) |
|---|
| 189 | + return NULL; |
|---|
| 190 | + |
|---|
| 191 | + dev = class_find_device_by_fwnode(role_class, fwnode); |
|---|
| 192 | + if (dev) |
|---|
| 193 | + WARN_ON(!try_module_get(dev->parent->driver->owner)); |
|---|
| 194 | + |
|---|
| 195 | + return dev ? to_role_switch(dev) : NULL; |
|---|
| 196 | +} |
|---|
| 197 | +EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode); |
|---|
| 198 | + |
|---|
| 139 | 199 | static umode_t |
|---|
| 140 | 200 | usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n) |
|---|
| 141 | 201 | { |
|---|
| 142 | | - struct device *dev = container_of(kobj, typeof(*dev), kobj); |
|---|
| 202 | + struct device *dev = kobj_to_dev(kobj); |
|---|
| 143 | 203 | struct usb_role_switch *sw = to_role_switch(dev); |
|---|
| 144 | 204 | |
|---|
| 145 | 205 | if (sw->allow_userspace_control) |
|---|
| .. | .. |
|---|
| 266 | 326 | sw->get = desc->get; |
|---|
| 267 | 327 | |
|---|
| 268 | 328 | sw->dev.parent = parent; |
|---|
| 329 | + sw->dev.fwnode = desc->fwnode; |
|---|
| 269 | 330 | sw->dev.class = role_class; |
|---|
| 270 | 331 | sw->dev.type = &usb_role_dev_type; |
|---|
| 271 | | - dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent)); |
|---|
| 332 | + dev_set_drvdata(&sw->dev, desc->driver_data); |
|---|
| 333 | + dev_set_name(&sw->dev, "%s-role-switch", |
|---|
| 334 | + desc->name ? desc->name : dev_name(parent)); |
|---|
| 272 | 335 | |
|---|
| 273 | 336 | ret = device_register(&sw->dev); |
|---|
| 274 | 337 | if (ret) { |
|---|
| .. | .. |
|---|
| 295 | 358 | } |
|---|
| 296 | 359 | EXPORT_SYMBOL_GPL(usb_role_switch_unregister); |
|---|
| 297 | 360 | |
|---|
| 361 | +/** |
|---|
| 362 | + * usb_role_switch_set_drvdata - Assign private data pointer to a switch |
|---|
| 363 | + * @sw: USB Role Switch |
|---|
| 364 | + * @data: Private data pointer |
|---|
| 365 | + */ |
|---|
| 366 | +void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data) |
|---|
| 367 | +{ |
|---|
| 368 | + dev_set_drvdata(&sw->dev, data); |
|---|
| 369 | +} |
|---|
| 370 | +EXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata); |
|---|
| 371 | + |
|---|
| 372 | +/** |
|---|
| 373 | + * usb_role_switch_get_drvdata - Get the private data pointer of a switch |
|---|
| 374 | + * @sw: USB Role Switch |
|---|
| 375 | + */ |
|---|
| 376 | +void *usb_role_switch_get_drvdata(struct usb_role_switch *sw) |
|---|
| 377 | +{ |
|---|
| 378 | + return dev_get_drvdata(&sw->dev); |
|---|
| 379 | +} |
|---|
| 380 | +EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata); |
|---|
| 381 | + |
|---|
| 298 | 382 | static int __init usb_roles_init(void) |
|---|
| 299 | 383 | { |
|---|
| 300 | 384 | role_class = class_create(THIS_MODULE, "usb_role"); |
|---|