This attribute file, named "usb_mode", will show the supported USB modes, which are USB 2.0, USB 3.2 and USB4. These modes are defined in the latest USB Type-C (R2.0) and USB Power Delivery (R3.0 V2.0) Specifications. Signed-off-by: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> --- Documentation/ABI/testing/sysfs-class-typec | 12 ++++ drivers/usb/typec/class.c | 61 +++++++++++++++++++++ include/linux/usb/typec.h | 9 +++ 3 files changed, 82 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 0c2eb26fdc06..8df6f599c967 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -108,6 +108,18 @@ Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> Description: Revision number of the supported USB Type-C specification. +What: /sys/class/typec/<port>/usb_mode +Date: February 2020 +Contact: Heikki Krogerus <heikki.krogerus@xxxxxxxxxxxxxxx> +Description: The supported USB Modes with the active one, that is to be used + next time with the Enter_USB message, in brackets. The active + mode can be changed by writing to the file when the connector + interface supports it. + + Valid values: + - usb2 (USB 2.0) + - usb3 (USB 3.2) + - usb4 (USB4) USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 70f3c5e9eb0c..07e4913f04c6 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -47,6 +47,7 @@ struct typec_port { enum typec_pwr_opmode pwr_opmode; enum typec_port_type port_type; struct mutex port_type_lock; + enum usb_mode usb_mode; enum typec_orientation orientation; struct typec_switch *sw; @@ -146,6 +147,54 @@ static void typec_report_identity(struct device *dev) sysfs_notify(&dev->kobj, "identity", "product"); } +static const char * const usb_modes[] = { + [USB_MODE_NONE] = "none", + [USB_MODE_USB2] = "usb2", + [USB_MODE_USB3] = "usb3", + [USB_MODE_USB4] = "usb4" +}; + +static ssize_t +usb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + enum usb_mode mode = to_typec_port(dev)->usb_mode; + u8 cap = to_typec_port(dev)->cap->usb; + int len = 0; + int i; + + for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) { + if (!(BIT(i - 1) & cap)) + continue; + + if (i == mode) + len += sprintf(buf + len, "[%s] ", usb_modes[i]); + else + len += sprintf(buf + len, "%s ", usb_modes[i]); + } + + buf[len - 1] = '\n'; + return len; +} + +static ssize_t usb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret = 0; + int mode; + + mode = sysfs_match_string(usb_modes, buf); + if (mode < 0) + return mode; + + ret = port->ops->usb_mode_set(port, mode); + if (ret) + return ret; + + return size; +} +static DEVICE_ATTR_RW(usb_mode); + /* ------------------------------------------------------------------------- */ /* Alternate Modes */ @@ -597,6 +646,7 @@ static DEVICE_ATTR_RO(supports_usb_power_delivery); static struct attribute *typec_partner_attrs[] = { &dev_attr_accessory_mode.attr, &dev_attr_supports_usb_power_delivery.attr, + &dev_attr_usb_mode.attr, NULL }; ATTRIBUTE_GROUPS(typec_partner); @@ -1273,6 +1323,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_usb_typec_revision.attr, &dev_attr_vconn_source.attr, &dev_attr_port_type.attr, + &dev_attr_usb_mode.attr, NULL, }; @@ -1302,6 +1353,9 @@ static umode_t typec_attr_is_visible(struct kobject *kobj, return 0; if (port->cap->type != TYPEC_PORT_DRP) return 0444; + } else if (attr == &dev_attr_usb_mode.attr) { + if (!port->ops || !port->ops->usb_mode_set) + return 0444; } return attr->mode; @@ -1656,6 +1710,13 @@ struct typec_port *typec_register_port(struct device *parent, port->port_type = cap->type; port->prefer_role = cap->prefer_role; + if (cap->usb & USB_CAPABILITY_USB4) + port->usb_mode = USB_MODE_USB4; + else if (cap->usb & USB_CAPABILITY_USB3) + port->usb_mode = USB_MODE_USB3; + else if (cap->usb & USB_CAPABILITY_USB2) + port->usb_mode = USB_MODE_USB2; + device_initialize(&port->dev); port->dev.class = typec_class; port->dev.parent = parent; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 0acfbcd8bf04..1128c3b58618 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -72,6 +72,13 @@ enum typec_orientation { TYPEC_ORIENTATION_REVERSE, }; +enum usb_mode { + USB_MODE_NONE, + USB_MODE_USB2, + USB_MODE_USB3, + USB_MODE_USB4 +}; + #define USB_CAPABILITY_USB2 BIT(0) #define USB_CAPABILITY_USB3 BIT(1) #define USB_CAPABILITY_USB4 BIT(2) @@ -184,6 +191,7 @@ struct typec_partner_desc { * @pr_set: Set Power Role * @vconn_set: Source VCONN * @port_type_set: Set port type + * @usb_mode_set: Set the USB Mode to be used with Enter_USB message */ struct typec_operations { int (*try_role)(struct typec_port *port, int role); @@ -192,6 +200,7 @@ struct typec_operations { int (*vconn_set)(struct typec_port *port, enum typec_role role); int (*port_type_set)(struct typec_port *port, enum typec_port_type type); + int (*usb_mode_set)(struct typec_port *port, enum usb_mode mode); }; /* -- 2.24.1