xref: /openbmc/linux/drivers/usb/roles/class.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
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