Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bab35480 authored by Badhri Jagan Sridharan's avatar Badhri Jagan Sridharan Committed by Greg Kroah-Hartman
Browse files

usb: typec: Add a sysfs node to manage port type



User space applications in some cases have the need to enforce a
specific port type(DFP/UFP/DRP). This change allows userspace to
attempt setting the desired port type. Low level drivers can
however reject the request if the specific port type is not supported.

Signed-off-by: default avatarBadhri Jagan Sridharan <Badhri@google.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7ee4ce6e
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -30,6 +30,21 @@ Description:

		Valid values: source, sink

What:           /sys/class/typec/<port>/port_type
Date:           May 2017
Contact:	Badhri Jagan Sridharan <Badhri@google.com>
Description:
		Indicates the type of the port. This attribute can be used for
		requesting a change in the port type. Port type change is
		supported as a synchronous operation, so write(2) to the
		attribute will not return until the operation has finished.

		Valid values:
		- source (The port will behave as source only DFP port)
		- sink (The port will behave as sink only UFP port)
		- dual (The port will behave as dual-role-data and
			dual-role-power port)

What:		/sys/class/typec/<port>/vconn_source
Date:		April 2017
Contact:	Heikki Krogerus <heikki.krogerus@linux.intel.com>
+96 −10
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@

#include <linux/device.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/usb/typec.h>

@@ -69,6 +70,8 @@ struct typec_port {
	enum typec_role			pwr_role;
	enum typec_role			vconn_role;
	enum typec_pwr_opmode		pwr_opmode;
	enum typec_port_type		port_type;
	struct mutex			port_type_lock;

	const struct typec_capability	*cap;
};
@@ -790,6 +793,18 @@ static const char * const typec_data_roles[] = {
	[TYPEC_HOST]	= "host",
};

static const char * const typec_port_types[] = {
	[TYPEC_PORT_DFP] = "source",
	[TYPEC_PORT_UFP] = "sink",
	[TYPEC_PORT_DRP] = "dual",
};

static const char * const typec_port_types_drp[] = {
	[TYPEC_PORT_DFP] = "dual [source] sink",
	[TYPEC_PORT_UFP] = "dual source [sink]",
	[TYPEC_PORT_DRP] = "[dual] source sink",
};

static ssize_t
preferred_role_store(struct device *dev, struct device_attribute *attr,
		     const char *buf, size_t size)
@@ -847,11 +862,6 @@ static ssize_t data_role_store(struct device *dev,
	struct typec_port *port = to_typec_port(dev);
	int ret;

	if (port->cap->type != TYPEC_PORT_DRP) {
		dev_dbg(dev, "data role swap only supported with DRP ports\n");
		return -EOPNOTSUPP;
	}

	if (!port->cap->dr_set) {
		dev_dbg(dev, "data role swapping not supported\n");
		return -EOPNOTSUPP;
@@ -861,11 +871,22 @@ static ssize_t data_role_store(struct device *dev,
	if (ret < 0)
		return ret;

	mutex_lock(&port->port_type_lock);
	if (port->port_type != TYPEC_PORT_DRP) {
		dev_dbg(dev, "port type fixed at \"%s\"",
			     typec_port_types[port->port_type]);
		ret = -EOPNOTSUPP;
		goto unlock_and_ret;
	}

	ret = port->cap->dr_set(port->cap, ret);
	if (ret)
		return ret;
		goto unlock_and_ret;

	return size;
	ret = size;
unlock_and_ret:
	mutex_unlock(&port->port_type_lock);
	return ret;
}

static ssize_t data_role_show(struct device *dev,
@@ -886,7 +907,7 @@ static ssize_t power_role_store(struct device *dev,
				const char *buf, size_t size)
{
	struct typec_port *port = to_typec_port(dev);
	int ret = size;
	int ret;

	if (!port->cap->pd_revision) {
		dev_dbg(dev, "USB Power Delivery not supported\n");
@@ -907,11 +928,22 @@ static ssize_t power_role_store(struct device *dev,
	if (ret < 0)
		return ret;

	mutex_lock(&port->port_type_lock);
	if (port->port_type != TYPEC_PORT_DRP) {
		dev_dbg(dev, "port type fixed at \"%s\"",
			     typec_port_types[port->port_type]);
		ret = -EOPNOTSUPP;
		goto unlock_and_ret;
	}

	ret = port->cap->pr_set(port->cap, ret);
	if (ret)
		return ret;
		goto unlock_and_ret;

	return size;
	ret = size;
unlock_and_ret:
	mutex_unlock(&port->port_type_lock);
	return ret;
}

static ssize_t power_role_show(struct device *dev,
@@ -927,6 +959,57 @@ static ssize_t power_role_show(struct device *dev,
}
static DEVICE_ATTR_RW(power_role);

static ssize_t
port_type_store(struct device *dev, struct device_attribute *attr,
			const char *buf, size_t size)
{
	struct typec_port *port = to_typec_port(dev);
	int ret;
	enum typec_port_type type;

	if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) {
		dev_dbg(dev, "changing port type not supported\n");
		return -EOPNOTSUPP;
	}

	ret = sysfs_match_string(typec_port_types, buf);
	if (ret < 0)
		return ret;

	type = ret;
	mutex_lock(&port->port_type_lock);

	if (port->port_type == type) {
		ret = size;
		goto unlock_and_ret;
	}

	ret = port->cap->port_type_set(port->cap, type);
	if (ret)
		goto unlock_and_ret;

	port->port_type = type;
	ret = size;

unlock_and_ret:
	mutex_unlock(&port->port_type_lock);
	return ret;
}

static ssize_t
port_type_show(struct device *dev, struct device_attribute *attr,
		char *buf)
{
	struct typec_port *port = to_typec_port(dev);

	if (port->cap->type == TYPEC_PORT_DRP)
		return sprintf(buf, "%s\n",
			       typec_port_types_drp[port->port_type]);

	return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]);
}
static DEVICE_ATTR_RW(port_type);

static const char * const typec_pwr_opmodes[] = {
	[TYPEC_PWR_MODE_USB]	= "default",
	[TYPEC_PWR_MODE_1_5A]	= "1.5A",
@@ -1036,6 +1119,7 @@ static struct attribute *typec_attrs[] = {
	&dev_attr_usb_power_delivery_revision.attr,
	&dev_attr_usb_typec_revision.attr,
	&dev_attr_vconn_source.attr,
	&dev_attr_port_type.attr,
	NULL,
};
ATTRIBUTE_GROUPS(typec);
@@ -1231,6 +1315,8 @@ struct typec_port *typec_register_port(struct device *parent,

	port->id = id;
	port->cap = cap;
	port->port_type = cap->type;
	mutex_init(&port->port_type_lock);
	port->prefer_role = cap->prefer_role;

	port->dev.class = typec_class;
+4 −0
Original line number Diff line number Diff line
@@ -190,6 +190,7 @@ struct typec_partner_desc {
 * @pr_set: Set Power Role
 * @vconn_set: Set VCONN Role
 * @activate_mode: Enter/exit given Alternate Mode
 * @port_type_set: Set port type
 *
 * Static capabilities of a single USB Type-C port.
 */
@@ -214,6 +215,9 @@ struct typec_capability {

	int		(*activate_mode)(const struct typec_capability *,
					 int mode, int activate);
	int		(*port_type_set)(const struct typec_capability *,
					enum typec_port_type);

};

/* Specific to try_role(). Indicates the user want's to clear the preference. */