162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * USB Role Switch Support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2018 Intel Corporation
662306a36Sopenharmony_ci * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
762306a36Sopenharmony_ci *         Hans de Goede <hdegoede@redhat.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/usb/role.h>
1162306a36Sopenharmony_ci#include <linux/property.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic const struct class role_class = {
1862306a36Sopenharmony_ci	.name = "usb_role",
1962306a36Sopenharmony_ci};
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistruct usb_role_switch {
2262306a36Sopenharmony_ci	struct device dev;
2362306a36Sopenharmony_ci	struct mutex lock; /* device lock*/
2462306a36Sopenharmony_ci	struct module *module; /* the module this device depends on */
2562306a36Sopenharmony_ci	enum usb_role role;
2662306a36Sopenharmony_ci	bool registered;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	/* From descriptor */
2962306a36Sopenharmony_ci	struct device *usb2_port;
3062306a36Sopenharmony_ci	struct device *usb3_port;
3162306a36Sopenharmony_ci	struct device *udc;
3262306a36Sopenharmony_ci	usb_role_switch_set_t set;
3362306a36Sopenharmony_ci	usb_role_switch_get_t get;
3462306a36Sopenharmony_ci	bool allow_userspace_control;
3562306a36Sopenharmony_ci};
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define to_role_switch(d)	container_of(d, struct usb_role_switch, dev)
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * usb_role_switch_set_role - Set USB role for a switch
4162306a36Sopenharmony_ci * @sw: USB role switch
4262306a36Sopenharmony_ci * @role: USB role to be switched to
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Set USB role @role for @sw.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_ciint usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	int ret;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(sw))
5162306a36Sopenharmony_ci		return 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (!sw->registered)
5462306a36Sopenharmony_ci		return -EOPNOTSUPP;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	mutex_lock(&sw->lock);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ret = sw->set(sw, role);
5962306a36Sopenharmony_ci	if (!ret) {
6062306a36Sopenharmony_ci		sw->role = role;
6162306a36Sopenharmony_ci		kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	mutex_unlock(&sw->lock);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return ret;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_set_role);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/**
7162306a36Sopenharmony_ci * usb_role_switch_get_role - Get the USB role for a switch
7262306a36Sopenharmony_ci * @sw: USB role switch
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * Depending on the role-switch-driver this function returns either a cached
7562306a36Sopenharmony_ci * value of the last set role, or reads back the actual value from the hardware.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_cienum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	enum usb_role role;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(sw) || !sw->registered)
8262306a36Sopenharmony_ci		return USB_ROLE_NONE;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	mutex_lock(&sw->lock);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (sw->get)
8762306a36Sopenharmony_ci		role = sw->get(sw);
8862306a36Sopenharmony_ci	else
8962306a36Sopenharmony_ci		role = sw->role;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	mutex_unlock(&sw->lock);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return role;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_get_role);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic void *usb_role_switch_match(const struct fwnode_handle *fwnode, const char *id,
9862306a36Sopenharmony_ci				   void *data)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	struct device *dev;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (id && !fwnode_property_present(fwnode, id))
10362306a36Sopenharmony_ci		return NULL;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	dev = class_find_device_by_fwnode(&role_class, fwnode);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic struct usb_role_switch *
11162306a36Sopenharmony_ciusb_role_switch_is_parent(struct fwnode_handle *fwnode)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct fwnode_handle *parent = fwnode_get_parent(fwnode);
11462306a36Sopenharmony_ci	struct device *dev;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!fwnode_property_present(parent, "usb-role-switch")) {
11762306a36Sopenharmony_ci		fwnode_handle_put(parent);
11862306a36Sopenharmony_ci		return NULL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	dev = class_find_device_by_fwnode(&role_class, parent);
12262306a36Sopenharmony_ci	fwnode_handle_put(parent);
12362306a36Sopenharmony_ci	return dev ? to_role_switch(dev) : ERR_PTR(-EPROBE_DEFER);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * usb_role_switch_get - Find USB role switch linked with the caller
12862306a36Sopenharmony_ci * @dev: The caller device
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * Finds and returns role switch linked with @dev. The reference count for the
13162306a36Sopenharmony_ci * found switch is incremented.
13262306a36Sopenharmony_ci */
13362306a36Sopenharmony_cistruct usb_role_switch *usb_role_switch_get(struct device *dev)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct usb_role_switch *sw;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	sw = usb_role_switch_is_parent(dev_fwnode(dev));
13862306a36Sopenharmony_ci	if (!sw)
13962306a36Sopenharmony_ci		sw = device_connection_find_match(dev, "usb-role-switch", NULL,
14062306a36Sopenharmony_ci						  usb_role_switch_match);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(sw))
14362306a36Sopenharmony_ci		WARN_ON(!try_module_get(sw->module));
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	return sw;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_get);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci/**
15062306a36Sopenharmony_ci * fwnode_usb_role_switch_get - Find USB role switch linked with the caller
15162306a36Sopenharmony_ci * @fwnode: The caller device node
15262306a36Sopenharmony_ci *
15362306a36Sopenharmony_ci * This is similar to the usb_role_switch_get() function above, but it searches
15462306a36Sopenharmony_ci * the switch using fwnode instead of device entry.
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistruct usb_role_switch *fwnode_usb_role_switch_get(struct fwnode_handle *fwnode)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	struct usb_role_switch *sw;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	sw = usb_role_switch_is_parent(fwnode);
16162306a36Sopenharmony_ci	if (!sw)
16262306a36Sopenharmony_ci		sw = fwnode_connection_find_match(fwnode, "usb-role-switch",
16362306a36Sopenharmony_ci						  NULL, usb_role_switch_match);
16462306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(sw))
16562306a36Sopenharmony_ci		WARN_ON(!try_module_get(sw->module));
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return sw;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(fwnode_usb_role_switch_get);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci/**
17262306a36Sopenharmony_ci * usb_role_switch_put - Release handle to a switch
17362306a36Sopenharmony_ci * @sw: USB Role Switch
17462306a36Sopenharmony_ci *
17562306a36Sopenharmony_ci * Decrement reference count for @sw.
17662306a36Sopenharmony_ci */
17762306a36Sopenharmony_civoid usb_role_switch_put(struct usb_role_switch *sw)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(sw)) {
18062306a36Sopenharmony_ci		module_put(sw->module);
18162306a36Sopenharmony_ci		put_device(&sw->dev);
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_put);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * usb_role_switch_find_by_fwnode - Find USB role switch with its fwnode
18862306a36Sopenharmony_ci * @fwnode: fwnode of the USB Role Switch
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * Finds and returns role switch with @fwnode. The reference count for the
19162306a36Sopenharmony_ci * found switch is incremented.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_cistruct usb_role_switch *
19462306a36Sopenharmony_ciusb_role_switch_find_by_fwnode(const struct fwnode_handle *fwnode)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct device *dev;
19762306a36Sopenharmony_ci	struct usb_role_switch *sw = NULL;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (!fwnode)
20062306a36Sopenharmony_ci		return NULL;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	dev = class_find_device_by_fwnode(&role_class, fwnode);
20362306a36Sopenharmony_ci	if (dev) {
20462306a36Sopenharmony_ci		sw = to_role_switch(dev);
20562306a36Sopenharmony_ci		WARN_ON(!try_module_get(sw->module));
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return sw;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic umode_t
21362306a36Sopenharmony_ciusb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct device *dev = kobj_to_dev(kobj);
21662306a36Sopenharmony_ci	struct usb_role_switch *sw = to_role_switch(dev);
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	if (sw->allow_userspace_control)
21962306a36Sopenharmony_ci		return attr->mode;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	return 0;
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic const char * const usb_roles[] = {
22562306a36Sopenharmony_ci	[USB_ROLE_NONE]		= "none",
22662306a36Sopenharmony_ci	[USB_ROLE_HOST]		= "host",
22762306a36Sopenharmony_ci	[USB_ROLE_DEVICE]	= "device",
22862306a36Sopenharmony_ci};
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ciconst char *usb_role_string(enum usb_role role)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	if (role < 0 || role >= ARRAY_SIZE(usb_roles))
23362306a36Sopenharmony_ci		return "unknown";
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return usb_roles[role];
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_string);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic ssize_t
24062306a36Sopenharmony_cirole_show(struct device *dev, struct device_attribute *attr, char *buf)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct usb_role_switch *sw = to_role_switch(dev);
24362306a36Sopenharmony_ci	enum usb_role role = usb_role_switch_get_role(sw);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return sprintf(buf, "%s\n", usb_roles[role]);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic ssize_t role_store(struct device *dev, struct device_attribute *attr,
24962306a36Sopenharmony_ci			  const char *buf, size_t size)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct usb_role_switch *sw = to_role_switch(dev);
25262306a36Sopenharmony_ci	int ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ret = sysfs_match_string(usb_roles, buf);
25562306a36Sopenharmony_ci	if (ret < 0) {
25662306a36Sopenharmony_ci		bool res;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci		/* Extra check if the user wants to disable the switch */
25962306a36Sopenharmony_ci		ret = kstrtobool(buf, &res);
26062306a36Sopenharmony_ci		if (ret || res)
26162306a36Sopenharmony_ci			return -EINVAL;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	ret = usb_role_switch_set_role(sw, ret);
26562306a36Sopenharmony_ci	if (ret)
26662306a36Sopenharmony_ci		return ret;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return size;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_cistatic DEVICE_ATTR_RW(role);
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic struct attribute *usb_role_switch_attrs[] = {
27362306a36Sopenharmony_ci	&dev_attr_role.attr,
27462306a36Sopenharmony_ci	NULL,
27562306a36Sopenharmony_ci};
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_cistatic const struct attribute_group usb_role_switch_group = {
27862306a36Sopenharmony_ci	.is_visible = usb_role_switch_is_visible,
27962306a36Sopenharmony_ci	.attrs = usb_role_switch_attrs,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct attribute_group *usb_role_switch_groups[] = {
28362306a36Sopenharmony_ci	&usb_role_switch_group,
28462306a36Sopenharmony_ci	NULL,
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int usb_role_switch_uevent(const struct device *dev, struct kobj_uevent_env *env)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	int ret;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	ret = add_uevent_var(env, "USB_ROLE_SWITCH=%s", dev_name(dev));
29262306a36Sopenharmony_ci	if (ret)
29362306a36Sopenharmony_ci		dev_err(dev, "failed to add uevent USB_ROLE_SWITCH\n");
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	return ret;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic void usb_role_switch_release(struct device *dev)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct usb_role_switch *sw = to_role_switch(dev);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	kfree(sw);
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct device_type usb_role_dev_type = {
30662306a36Sopenharmony_ci	.name = "usb_role_switch",
30762306a36Sopenharmony_ci	.groups = usb_role_switch_groups,
30862306a36Sopenharmony_ci	.uevent = usb_role_switch_uevent,
30962306a36Sopenharmony_ci	.release = usb_role_switch_release,
31062306a36Sopenharmony_ci};
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci/**
31362306a36Sopenharmony_ci * usb_role_switch_register - Register USB Role Switch
31462306a36Sopenharmony_ci * @parent: Parent device for the switch
31562306a36Sopenharmony_ci * @desc: Description of the switch
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * USB Role Switch is a device capable or choosing the role for USB connector.
31862306a36Sopenharmony_ci * On platforms where the USB controller is dual-role capable, the controller
31962306a36Sopenharmony_ci * driver will need to register the switch. On platforms where the USB host and
32062306a36Sopenharmony_ci * USB device controllers behind the connector are separate, there will be a
32162306a36Sopenharmony_ci * mux, and the driver for that mux will need to register the switch.
32262306a36Sopenharmony_ci *
32362306a36Sopenharmony_ci * Returns handle to a new role switch or ERR_PTR. The content of @desc is
32462306a36Sopenharmony_ci * copied.
32562306a36Sopenharmony_ci */
32662306a36Sopenharmony_cistruct usb_role_switch *
32762306a36Sopenharmony_ciusb_role_switch_register(struct device *parent,
32862306a36Sopenharmony_ci			 const struct usb_role_switch_desc *desc)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct usb_role_switch *sw;
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (!desc || !desc->set)
33462306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	sw = kzalloc(sizeof(*sw), GFP_KERNEL);
33762306a36Sopenharmony_ci	if (!sw)
33862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	mutex_init(&sw->lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	sw->allow_userspace_control = desc->allow_userspace_control;
34362306a36Sopenharmony_ci	sw->usb2_port = desc->usb2_port;
34462306a36Sopenharmony_ci	sw->usb3_port = desc->usb3_port;
34562306a36Sopenharmony_ci	sw->udc = desc->udc;
34662306a36Sopenharmony_ci	sw->set = desc->set;
34762306a36Sopenharmony_ci	sw->get = desc->get;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	sw->module = parent->driver->owner;
35062306a36Sopenharmony_ci	sw->dev.parent = parent;
35162306a36Sopenharmony_ci	sw->dev.fwnode = desc->fwnode;
35262306a36Sopenharmony_ci	sw->dev.class = &role_class;
35362306a36Sopenharmony_ci	sw->dev.type = &usb_role_dev_type;
35462306a36Sopenharmony_ci	dev_set_drvdata(&sw->dev, desc->driver_data);
35562306a36Sopenharmony_ci	dev_set_name(&sw->dev, "%s-role-switch",
35662306a36Sopenharmony_ci		     desc->name ? desc->name : dev_name(parent));
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	ret = device_register(&sw->dev);
35962306a36Sopenharmony_ci	if (ret) {
36062306a36Sopenharmony_ci		put_device(&sw->dev);
36162306a36Sopenharmony_ci		return ERR_PTR(ret);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	sw->registered = true;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* TODO: Symlinks for the host port and the device controller. */
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return sw;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_register);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci/**
37362306a36Sopenharmony_ci * usb_role_switch_unregister - Unregsiter USB Role Switch
37462306a36Sopenharmony_ci * @sw: USB Role Switch
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Unregister switch that was registered with usb_role_switch_register().
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_civoid usb_role_switch_unregister(struct usb_role_switch *sw)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(sw)) {
38162306a36Sopenharmony_ci		sw->registered = false;
38262306a36Sopenharmony_ci		device_unregister(&sw->dev);
38362306a36Sopenharmony_ci	}
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_unregister);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * usb_role_switch_set_drvdata - Assign private data pointer to a switch
38962306a36Sopenharmony_ci * @sw: USB Role Switch
39062306a36Sopenharmony_ci * @data: Private data pointer
39162306a36Sopenharmony_ci */
39262306a36Sopenharmony_civoid usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	dev_set_drvdata(&sw->dev, data);
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci/**
39962306a36Sopenharmony_ci * usb_role_switch_get_drvdata - Get the private data pointer of a switch
40062306a36Sopenharmony_ci * @sw: USB Role Switch
40162306a36Sopenharmony_ci */
40262306a36Sopenharmony_civoid *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	return dev_get_drvdata(&sw->dev);
40562306a36Sopenharmony_ci}
40662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistatic int __init usb_roles_init(void)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	return class_register(&role_class);
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_cisubsys_initcall(usb_roles_init);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_cistatic void __exit usb_roles_exit(void)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	class_unregister(&role_class);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_cimodule_exit(usb_roles_exit);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ciMODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
42162306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
42262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
42362306a36Sopenharmony_ciMODULE_DESCRIPTION("USB Role Class");
424