1c3788cd9SHeikki Krogerus // SPDX-License-Identifier: GPL-2.0
2c3788cd9SHeikki Krogerus /*
3c3788cd9SHeikki Krogerus * USB Role Switch Support
4c3788cd9SHeikki Krogerus *
5c3788cd9SHeikki Krogerus * Copyright (C) 2018 Intel Corporation
6c3788cd9SHeikki Krogerus * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
7c3788cd9SHeikki Krogerus * Hans de Goede <hdegoede@redhat.com>
8c3788cd9SHeikki Krogerus */
9c3788cd9SHeikki Krogerus
10c3788cd9SHeikki Krogerus #include <linux/usb/role.h>
11ec69e953SHeikki Krogerus #include <linux/property.h>
12c3788cd9SHeikki Krogerus #include <linux/device.h>
13c3788cd9SHeikki Krogerus #include <linux/module.h>
14c3788cd9SHeikki Krogerus #include <linux/mutex.h>
15c3788cd9SHeikki Krogerus #include <linux/slab.h>
16c3788cd9SHeikki Krogerus
17044a6115SIvan Orlov static const struct class role_class = {
18044a6115SIvan Orlov .name = "usb_role",
19044a6115SIvan Orlov };
20c3788cd9SHeikki Krogerus
21c3788cd9SHeikki Krogerus struct usb_role_switch {
22c3788cd9SHeikki Krogerus struct device dev;
23c3788cd9SHeikki Krogerus struct mutex lock; /* device lock*/
244b458294SXu Yang struct module *module; /* the module this device depends on */
25c3788cd9SHeikki Krogerus enum usb_role role;
266aba8cf6SXu Yang bool registered;
27c3788cd9SHeikki Krogerus
28c3788cd9SHeikki Krogerus /* From descriptor */
29c3788cd9SHeikki Krogerus struct device *usb2_port;
30c3788cd9SHeikki Krogerus struct device *usb3_port;
31c3788cd9SHeikki Krogerus struct device *udc;
32c3788cd9SHeikki Krogerus usb_role_switch_set_t set;
33c3788cd9SHeikki Krogerus usb_role_switch_get_t get;
34c3788cd9SHeikki Krogerus bool allow_userspace_control;
35c3788cd9SHeikki Krogerus };
36c3788cd9SHeikki Krogerus
37c3788cd9SHeikki Krogerus #define to_role_switch(d) container_of(d, struct usb_role_switch, dev)
38c3788cd9SHeikki Krogerus
39c3788cd9SHeikki Krogerus /**
40c3788cd9SHeikki Krogerus * usb_role_switch_set_role - Set USB role for a switch
41c3788cd9SHeikki Krogerus * @sw: USB role switch
42c3788cd9SHeikki Krogerus * @role: USB role to be switched to
43c3788cd9SHeikki Krogerus *
44c3788cd9SHeikki Krogerus * Set USB role @role for @sw.
45c3788cd9SHeikki Krogerus */
usb_role_switch_set_role(struct usb_role_switch * sw,enum usb_role role)46c3788cd9SHeikki Krogerus int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role)
47c3788cd9SHeikki Krogerus {
48c3788cd9SHeikki Krogerus int ret;
49c3788cd9SHeikki Krogerus
50c3788cd9SHeikki Krogerus if (IS_ERR_OR_NULL(sw))
51c3788cd9SHeikki Krogerus return 0;
52c3788cd9SHeikki Krogerus
536aba8cf6SXu Yang if (!sw->registered)
546aba8cf6SXu Yang return -EOPNOTSUPP;
556aba8cf6SXu Yang
56c3788cd9SHeikki Krogerus mutex_lock(&sw->lock);
57c3788cd9SHeikki Krogerus
58bce3052fSHeikki Krogerus ret = sw->set(sw, role);
593e63cff3SBryan O'Donoghue if (!ret) {
60c3788cd9SHeikki Krogerus sw->role = role;
613e63cff3SBryan O'Donoghue kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
623e63cff3SBryan O'Donoghue }
63c3788cd9SHeikki Krogerus
64c3788cd9SHeikki Krogerus mutex_unlock(&sw->lock);
65c3788cd9SHeikki Krogerus
66c3788cd9SHeikki Krogerus return ret;
67c3788cd9SHeikki Krogerus }
68c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_set_role);
69c3788cd9SHeikki Krogerus
70c3788cd9SHeikki Krogerus /**
71c3788cd9SHeikki Krogerus * usb_role_switch_get_role - Get the USB role for a switch
72c3788cd9SHeikki Krogerus * @sw: USB role switch
73c3788cd9SHeikki Krogerus *
74c3788cd9SHeikki Krogerus * Depending on the role-switch-driver this function returns either a cached
75c3788cd9SHeikki Krogerus * value of the last set role, or reads back the actual value from the hardware.
76c3788cd9SHeikki Krogerus */
usb_role_switch_get_role(struct usb_role_switch * sw)77c3788cd9SHeikki Krogerus enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
78c3788cd9SHeikki Krogerus {
79c3788cd9SHeikki Krogerus enum usb_role role;
80c3788cd9SHeikki Krogerus
816aba8cf6SXu Yang if (IS_ERR_OR_NULL(sw) || !sw->registered)
82c3788cd9SHeikki Krogerus return USB_ROLE_NONE;
83c3788cd9SHeikki Krogerus
84c3788cd9SHeikki Krogerus mutex_lock(&sw->lock);
85c3788cd9SHeikki Krogerus
86c3788cd9SHeikki Krogerus if (sw->get)
87bce3052fSHeikki Krogerus role = sw->get(sw);
88c3788cd9SHeikki Krogerus else
89c3788cd9SHeikki Krogerus role = sw->role;
90c3788cd9SHeikki Krogerus
91c3788cd9SHeikki Krogerus mutex_unlock(&sw->lock);
92c3788cd9SHeikki Krogerus
93c3788cd9SHeikki Krogerus return role;
94c3788cd9SHeikki Krogerus }
95c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_get_role);
96c3788cd9SHeikki Krogerus
usb_role_switch_match(const struct fwnode_handle * fwnode,const char * id,void * data)9723ead33bSAndy Shevchenko static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const char *id,
98c3788cd9SHeikki Krogerus void *data)
99c3788cd9SHeikki Krogerus {
100c3788cd9SHeikki Krogerus struct device *dev;
101c3788cd9SHeikki Krogerus
102f5514c91SHeikki Krogerus if (id && !fwnode_property_present(fwnode, id))
103ec69e953SHeikki Krogerus return NULL;
104ec69e953SHeikki Krogerus
105044a6115SIvan Orlov dev = class_find_device_by_fwnode(&role_class, fwnode);
106c3788cd9SHeikki Krogerus
107c3788cd9SHeikki Krogerus return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
108c3788cd9SHeikki Krogerus }
109c3788cd9SHeikki Krogerus
1106fadd729SChunfeng Yun static struct usb_role_switch *
usb_role_switch_is_parent(struct fwnode_handle * fwnode)1116fadd729SChunfeng Yun usb_role_switch_is_parent(struct fwnode_handle *fwnode)
1126fadd729SChunfeng Yun {
1136fadd729SChunfeng Yun struct fwnode_handle *parent = fwnode_get_parent(fwnode);
1146fadd729SChunfeng Yun struct device *dev;
1156fadd729SChunfeng Yun
1161ab30c61SYang Yingliang if (!fwnode_property_present(parent, "usb-role-switch")) {
1171ab30c61SYang Yingliang fwnode_handle_put(parent);
1186fadd729SChunfeng Yun return NULL;
1191ab30c61SYang Yingliang }
1206fadd729SChunfeng Yun
121044a6115SIvan Orlov dev = class_find_device_by_fwnode(&role_class, parent);
1221ab30c61SYang Yingliang fwnode_handle_put(parent);
1236fadd729SChunfeng Yun return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
1246fadd729SChunfeng Yun }
1256fadd729SChunfeng Yun
126c3788cd9SHeikki Krogerus /**
127c3788cd9SHeikki Krogerus * usb_role_switch_get - Find USB role switch linked with the caller
128c3788cd9SHeikki Krogerus * @dev: The caller device
129c3788cd9SHeikki Krogerus *
130c3788cd9SHeikki Krogerus * Finds and returns role switch linked with @dev. The reference count for the
131c3788cd9SHeikki Krogerus * found switch is incremented.
132c3788cd9SHeikki Krogerus */
usb_role_switch_get(struct device * dev)133c3788cd9SHeikki Krogerus struct usb_role_switch *usb_role_switch_get(struct device *dev)
134c3788cd9SHeikki Krogerus {
135c3788cd9SHeikki Krogerus struct usb_role_switch *sw;
136c3788cd9SHeikki Krogerus
1376fadd729SChunfeng Yun sw = usb_role_switch_is_parent(dev_fwnode(dev));
1386fadd729SChunfeng Yun if (!sw)
139c3788cd9SHeikki Krogerus sw = device_connection_find_match(dev, "usb-role-switch", NULL,
140c3788cd9SHeikki Krogerus usb_role_switch_match);
141c3788cd9SHeikki Krogerus
142c3788cd9SHeikki Krogerus if (!IS_ERR_OR_NULL(sw))
1434b458294SXu Yang WARN_ON(!try_module_get(sw->module));
144c3788cd9SHeikki Krogerus
145c3788cd9SHeikki Krogerus return sw;
146c3788cd9SHeikki Krogerus }
147c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_get);
148c3788cd9SHeikki Krogerus
149c3788cd9SHeikki Krogerus /**
150a31f0177SHeikki Krogerus * fwnode_usb_role_switch_get - Find USB role switch linked with the caller
151a31f0177SHeikki Krogerus * @fwnode: The caller device node
152a31f0177SHeikki Krogerus *
153a31f0177SHeikki Krogerus * This is similar to the usb_role_switch_get() function above, but it searches
154a31f0177SHeikki Krogerus * the switch using fwnode instead of device entry.
155a31f0177SHeikki Krogerus */
fwnode_usb_role_switch_get(struct fwnode_handle * fwnode)156a31f0177SHeikki Krogerus struct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
157a31f0177SHeikki Krogerus {
158a31f0177SHeikki Krogerus struct usb_role_switch *sw;
159a31f0177SHeikki Krogerus
1606fadd729SChunfeng Yun sw = usb_role_switch_is_parent(fwnode);
1616fadd729SChunfeng Yun if (!sw)
1626fadd729SChunfeng Yun sw = fwnode_connection_find_match(fwnode, "usb-role-switch",
1636fadd729SChunfeng Yun NULL, usb_role_switch_match);
164a31f0177SHeikki Krogerus if (!IS_ERR_OR_NULL(sw))
1654b458294SXu Yang WARN_ON(!try_module_get(sw->module));
166a31f0177SHeikki Krogerus
167a31f0177SHeikki Krogerus return sw;
168a31f0177SHeikki Krogerus }
169a31f0177SHeikki Krogerus EXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get);
170a31f0177SHeikki Krogerus
171a31f0177SHeikki Krogerus /**
172c3788cd9SHeikki Krogerus * usb_role_switch_put - Release handle to a switch
173c3788cd9SHeikki Krogerus * @sw: USB Role Switch
174c3788cd9SHeikki Krogerus *
175c3788cd9SHeikki Krogerus * Decrement reference count for @sw.
176c3788cd9SHeikki Krogerus */
usb_role_switch_put(struct usb_role_switch * sw)177c3788cd9SHeikki Krogerus void usb_role_switch_put(struct usb_role_switch *sw)
178c3788cd9SHeikki Krogerus {
179c3788cd9SHeikki Krogerus if (!IS_ERR_OR_NULL(sw)) {
1804b458294SXu Yang module_put(sw->module);
1811848a543SWen Yang put_device(&sw->dev);
182c3788cd9SHeikki Krogerus }
183c3788cd9SHeikki Krogerus }
184c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_put);
185c3788cd9SHeikki Krogerus
186c6919d5eSHeikki Krogerus /**
187c6919d5eSHeikki Krogerus * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode
188c6919d5eSHeikki Krogerus * @fwnode: fwnode of the USB Role Switch
189c6919d5eSHeikki Krogerus *
190c6919d5eSHeikki Krogerus * Finds and returns role switch with @fwnode. The reference count for the
191c6919d5eSHeikki Krogerus * found switch is incremented.
192c6919d5eSHeikki Krogerus */
193c6919d5eSHeikki Krogerus struct usb_role_switch *
usb_role_switch_find_by_fwnode(const struct fwnode_handle * fwnode)194c6919d5eSHeikki Krogerus usb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode)
195c6919d5eSHeikki Krogerus {
196c6919d5eSHeikki Krogerus struct device *dev;
1974b458294SXu Yang struct usb_role_switch *sw = NULL;
198c6919d5eSHeikki Krogerus
199c6919d5eSHeikki Krogerus if (!fwnode)
200c6919d5eSHeikki Krogerus return NULL;
201c6919d5eSHeikki Krogerus
202044a6115SIvan Orlov dev = class_find_device_by_fwnode(&role_class, fwnode);
2034b458294SXu Yang if (dev) {
2044b458294SXu Yang sw = to_role_switch(dev);
2054b458294SXu Yang WARN_ON(!try_module_get(sw->module));
2064b458294SXu Yang }
207c6919d5eSHeikki Krogerus
2084b458294SXu Yang return sw;
209c6919d5eSHeikki Krogerus }
210c6919d5eSHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode);
211c6919d5eSHeikki Krogerus
212c3788cd9SHeikki Krogerus static umode_t
usb_role_switch_is_visible(struct kobject * kobj,struct attribute * attr,int n)213c3788cd9SHeikki Krogerus usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n)
214c3788cd9SHeikki Krogerus {
2150616ca73Schenqiwu struct device *dev = kobj_to_dev(kobj);
216c3788cd9SHeikki Krogerus struct usb_role_switch *sw = to_role_switch(dev);
217c3788cd9SHeikki Krogerus
218c3788cd9SHeikki Krogerus if (sw->allow_userspace_control)
219c3788cd9SHeikki Krogerus return attr->mode;
220c3788cd9SHeikki Krogerus
221c3788cd9SHeikki Krogerus return 0;
222c3788cd9SHeikki Krogerus }
223c3788cd9SHeikki Krogerus
224c3788cd9SHeikki Krogerus static const char * const usb_roles[] = {
225c3788cd9SHeikki Krogerus [USB_ROLE_NONE] = "none",
226c3788cd9SHeikki Krogerus [USB_ROLE_HOST] = "host",
227c3788cd9SHeikki Krogerus [USB_ROLE_DEVICE] = "device",
228c3788cd9SHeikki Krogerus };
229c3788cd9SHeikki Krogerus
usb_role_string(enum usb_role role)23073e33008SChunfeng Yun const char *usb_role_string(enum usb_role role)
23173e33008SChunfeng Yun {
23273e33008SChunfeng Yun if (role < 0 || role >= ARRAY_SIZE(usb_roles))
23373e33008SChunfeng Yun return "unknown";
23473e33008SChunfeng Yun
23573e33008SChunfeng Yun return usb_roles[role];
23673e33008SChunfeng Yun }
23773e33008SChunfeng Yun EXPORT_SYMBOL_GPL(usb_role_string);
23873e33008SChunfeng Yun
239c3788cd9SHeikki Krogerus static ssize_t
role_show(struct device * dev,struct device_attribute * attr,char * buf)240c3788cd9SHeikki Krogerus role_show(struct device *dev, struct device_attribute *attr, char *buf)
241c3788cd9SHeikki Krogerus {
242c3788cd9SHeikki Krogerus struct usb_role_switch *sw = to_role_switch(dev);
243c3788cd9SHeikki Krogerus enum usb_role role = usb_role_switch_get_role(sw);
244c3788cd9SHeikki Krogerus
245c3788cd9SHeikki Krogerus return sprintf(buf, "%s\n", usb_roles[role]);
246c3788cd9SHeikki Krogerus }
247c3788cd9SHeikki Krogerus
role_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)248c3788cd9SHeikki Krogerus static ssize_t role_store(struct device *dev, struct device_attribute *attr,
249c3788cd9SHeikki Krogerus const char *buf, size_t size)
250c3788cd9SHeikki Krogerus {
251c3788cd9SHeikki Krogerus struct usb_role_switch *sw = to_role_switch(dev);
252c3788cd9SHeikki Krogerus int ret;
253c3788cd9SHeikki Krogerus
254c3788cd9SHeikki Krogerus ret = sysfs_match_string(usb_roles, buf);
255c3788cd9SHeikki Krogerus if (ret < 0) {
256c3788cd9SHeikki Krogerus bool res;
257c3788cd9SHeikki Krogerus
258c3788cd9SHeikki Krogerus /* Extra check if the user wants to disable the switch */
259c3788cd9SHeikki Krogerus ret = kstrtobool(buf, &res);
260c3788cd9SHeikki Krogerus if (ret || res)
261c3788cd9SHeikki Krogerus return -EINVAL;
262c3788cd9SHeikki Krogerus }
263c3788cd9SHeikki Krogerus
264c3788cd9SHeikki Krogerus ret = usb_role_switch_set_role(sw, ret);
265c3788cd9SHeikki Krogerus if (ret)
266c3788cd9SHeikki Krogerus return ret;
267c3788cd9SHeikki Krogerus
268c3788cd9SHeikki Krogerus return size;
269c3788cd9SHeikki Krogerus }
270c3788cd9SHeikki Krogerus static DEVICE_ATTR_RW(role);
271c3788cd9SHeikki Krogerus
272c3788cd9SHeikki Krogerus static struct attribute *usb_role_switch_attrs[] = {
273c3788cd9SHeikki Krogerus &dev_attr_role.attr,
274c3788cd9SHeikki Krogerus NULL,
275c3788cd9SHeikki Krogerus };
276c3788cd9SHeikki Krogerus
277c3788cd9SHeikki Krogerus static const struct attribute_group usb_role_switch_group = {
278c3788cd9SHeikki Krogerus .is_visible = usb_role_switch_is_visible,
279c3788cd9SHeikki Krogerus .attrs = usb_role_switch_attrs,
280c3788cd9SHeikki Krogerus };
281c3788cd9SHeikki Krogerus
282c3788cd9SHeikki Krogerus static const struct attribute_group *usb_role_switch_groups[] = {
283c3788cd9SHeikki Krogerus &usb_role_switch_group,
284c3788cd9SHeikki Krogerus NULL,
285c3788cd9SHeikki Krogerus };
286c3788cd9SHeikki Krogerus
usb_role_switch_uevent(const struct device * dev,struct kobj_uevent_env * env)287162736b0SGreg Kroah-Hartman static int usb_role_switch_uevent(const struct device *dev, struct kobj_uevent_env *env)
288c3788cd9SHeikki Krogerus {
289c3788cd9SHeikki Krogerus int ret;
290c3788cd9SHeikki Krogerus
291c3788cd9SHeikki Krogerus ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev));
292c3788cd9SHeikki Krogerus if (ret)
293c3788cd9SHeikki Krogerus dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n");
294c3788cd9SHeikki Krogerus
295c3788cd9SHeikki Krogerus return ret;
296c3788cd9SHeikki Krogerus }
297c3788cd9SHeikki Krogerus
usb_role_switch_release(struct device * dev)298c3788cd9SHeikki Krogerus static void usb_role_switch_release(struct device *dev)
299c3788cd9SHeikki Krogerus {
300c3788cd9SHeikki Krogerus struct usb_role_switch *sw = to_role_switch(dev);
301c3788cd9SHeikki Krogerus
302c3788cd9SHeikki Krogerus kfree(sw);
303c3788cd9SHeikki Krogerus }
304c3788cd9SHeikki Krogerus
305c3788cd9SHeikki Krogerus static const struct device_type usb_role_dev_type = {
306c3788cd9SHeikki Krogerus .name = "usb_role_switch",
307c3788cd9SHeikki Krogerus .groups = usb_role_switch_groups,
308c3788cd9SHeikki Krogerus .uevent = usb_role_switch_uevent,
309c3788cd9SHeikki Krogerus .release = usb_role_switch_release,
310c3788cd9SHeikki Krogerus };
311c3788cd9SHeikki Krogerus
312c3788cd9SHeikki Krogerus /**
313c3788cd9SHeikki Krogerus * usb_role_switch_register - Register USB Role Switch
314c3788cd9SHeikki Krogerus * @parent: Parent device for the switch
315c3788cd9SHeikki Krogerus * @desc: Description of the switch
316c3788cd9SHeikki Krogerus *
317c3788cd9SHeikki Krogerus * USB Role Switch is a device capable or choosing the role for USB connector.
318c3788cd9SHeikki Krogerus * On platforms where the USB controller is dual-role capable, the controller
319c3788cd9SHeikki Krogerus * driver will need to register the switch. On platforms where the USB host and
320c3788cd9SHeikki Krogerus * USB device controllers behind the connector are separate, there will be a
321c3788cd9SHeikki Krogerus * mux, and the driver for that mux will need to register the switch.
322c3788cd9SHeikki Krogerus *
323c3788cd9SHeikki Krogerus * Returns handle to a new role switch or ERR_PTR. The content of @desc is
324c3788cd9SHeikki Krogerus * copied.
325c3788cd9SHeikki Krogerus */
326c3788cd9SHeikki Krogerus struct usb_role_switch *
usb_role_switch_register(struct device * parent,const struct usb_role_switch_desc * desc)327c3788cd9SHeikki Krogerus usb_role_switch_register(struct device *parent,
328c3788cd9SHeikki Krogerus const struct usb_role_switch_desc *desc)
329c3788cd9SHeikki Krogerus {
330c3788cd9SHeikki Krogerus struct usb_role_switch *sw;
331c3788cd9SHeikki Krogerus int ret;
332c3788cd9SHeikki Krogerus
333c3788cd9SHeikki Krogerus if (!desc || !desc->set)
334c3788cd9SHeikki Krogerus return ERR_PTR(-EINVAL);
335c3788cd9SHeikki Krogerus
336c3788cd9SHeikki Krogerus sw = kzalloc(sizeof(*sw), GFP_KERNEL);
337c3788cd9SHeikki Krogerus if (!sw)
338c3788cd9SHeikki Krogerus return ERR_PTR(-ENOMEM);
339c3788cd9SHeikki Krogerus
340c3788cd9SHeikki Krogerus mutex_init(&sw->lock);
341c3788cd9SHeikki Krogerus
342c3788cd9SHeikki Krogerus sw->allow_userspace_control = desc->allow_userspace_control;
343c3788cd9SHeikki Krogerus sw->usb2_port = desc->usb2_port;
344c3788cd9SHeikki Krogerus sw->usb3_port = desc->usb3_port;
345c3788cd9SHeikki Krogerus sw->udc = desc->udc;
346c3788cd9SHeikki Krogerus sw->set = desc->set;
347c3788cd9SHeikki Krogerus sw->get = desc->get;
348c3788cd9SHeikki Krogerus
3494b458294SXu Yang sw->module = parent->driver->owner;
350c3788cd9SHeikki Krogerus sw->dev.parent = parent;
351ec69e953SHeikki Krogerus sw->dev.fwnode = desc->fwnode;
352044a6115SIvan Orlov sw->dev.class = &role_class;
353c3788cd9SHeikki Krogerus sw->dev.type = &usb_role_dev_type;
35469af044aSHeikki Krogerus dev_set_drvdata(&sw->dev, desc->driver_data);
355e5256194SHeikki Krogerus dev_set_name(&sw->dev, "%s-role-switch",
356e5256194SHeikki Krogerus desc->name ? desc->name : dev_name(parent));
357c3788cd9SHeikki Krogerus
358*5d6749bbSElson Roy Serrao sw->registered = true;
359*5d6749bbSElson Roy Serrao
360c3788cd9SHeikki Krogerus ret = device_register(&sw->dev);
361c3788cd9SHeikki Krogerus if (ret) {
362*5d6749bbSElson Roy Serrao sw->registered = false;
363c3788cd9SHeikki Krogerus put_device(&sw->dev);
364c3788cd9SHeikki Krogerus return ERR_PTR(ret);
365c3788cd9SHeikki Krogerus }
366c3788cd9SHeikki Krogerus
367c3788cd9SHeikki Krogerus /* TODO: Symlinks for the host port and the device controller. */
368c3788cd9SHeikki Krogerus
369c3788cd9SHeikki Krogerus return sw;
370c3788cd9SHeikki Krogerus }
371c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_register);
372c3788cd9SHeikki Krogerus
373c3788cd9SHeikki Krogerus /**
374c3788cd9SHeikki Krogerus * usb_role_switch_unregister - Unregsiter USB Role Switch
375c3788cd9SHeikki Krogerus * @sw: USB Role Switch
376c3788cd9SHeikki Krogerus *
377c3788cd9SHeikki Krogerus * Unregister switch that was registered with usb_role_switch_register().
378c3788cd9SHeikki Krogerus */
usb_role_switch_unregister(struct usb_role_switch * sw)379c3788cd9SHeikki Krogerus void usb_role_switch_unregister(struct usb_role_switch *sw)
380c3788cd9SHeikki Krogerus {
3816aba8cf6SXu Yang if (!IS_ERR_OR_NULL(sw)) {
3826aba8cf6SXu Yang sw->registered = false;
383c3788cd9SHeikki Krogerus device_unregister(&sw->dev);
384c3788cd9SHeikki Krogerus }
3856aba8cf6SXu Yang }
386c3788cd9SHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_unregister);
387c3788cd9SHeikki Krogerus
38869af044aSHeikki Krogerus /**
38969af044aSHeikki Krogerus * usb_role_switch_set_drvdata - Assign private data pointer to a switch
39069af044aSHeikki Krogerus * @sw: USB Role Switch
39169af044aSHeikki Krogerus * @data: Private data pointer
39269af044aSHeikki Krogerus */
usb_role_switch_set_drvdata(struct usb_role_switch * sw,void * data)39369af044aSHeikki Krogerus void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data)
39469af044aSHeikki Krogerus {
39569af044aSHeikki Krogerus dev_set_drvdata(&sw->dev, data);
39669af044aSHeikki Krogerus }
39769af044aSHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata);
39869af044aSHeikki Krogerus
39969af044aSHeikki Krogerus /**
40069af044aSHeikki Krogerus * usb_role_switch_get_drvdata - Get the private data pointer of a switch
40169af044aSHeikki Krogerus * @sw: USB Role Switch
40269af044aSHeikki Krogerus */
usb_role_switch_get_drvdata(struct usb_role_switch * sw)40369af044aSHeikki Krogerus void *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
40469af044aSHeikki Krogerus {
40569af044aSHeikki Krogerus return dev_get_drvdata(&sw->dev);
40669af044aSHeikki Krogerus }
40769af044aSHeikki Krogerus EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata);
40869af044aSHeikki Krogerus
usb_roles_init(void)409c3788cd9SHeikki Krogerus static int __init usb_roles_init(void)
410c3788cd9SHeikki Krogerus {
411044a6115SIvan Orlov return class_register(&role_class);
412c3788cd9SHeikki Krogerus }
413c3788cd9SHeikki Krogerus subsys_initcall(usb_roles_init);
414c3788cd9SHeikki Krogerus
usb_roles_exit(void)415c3788cd9SHeikki Krogerus static void __exit usb_roles_exit(void)
416c3788cd9SHeikki Krogerus {
417044a6115SIvan Orlov class_unregister(&role_class);
418c3788cd9SHeikki Krogerus }
419c3788cd9SHeikki Krogerus module_exit(usb_roles_exit);
420c3788cd9SHeikki Krogerus
421c3788cd9SHeikki Krogerus MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
422c3788cd9SHeikki Krogerus MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
423c3788cd9SHeikki Krogerus MODULE_LICENSE("GPL v2");
424c3788cd9SHeikki Krogerus MODULE_DESCRIPTION("USB Role Class");
425