usb: typec: Add attribute file showing the supported USB modes of the port
authorHeikki Krogerus <heikki.krogerus@linux.intel.com>
Wed, 16 Oct 2024 13:18:31 +0000 (16:18 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Oct 2024 06:41:45 +0000 (08:41 +0200)
This attribute file, named "usb_capability", will show the
supported USB modes, which are USB 2.0, USB 3.2 and USB4.
These modes are defined in the USB Type-C (R2.0) and USB
Power Delivery (R3.0 V2.0) Specifications.

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20241016131834.898599-2-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-class-typec
drivers/usb/typec/class.c
drivers/usb/typec/class.h
include/linux/usb/typec.h

index 281b995beb05ae62106d6a4037a235dcfc7087c9..3ee757208122e04a58ff927f3dff783852e63f40 100644 (file)
@@ -149,6 +149,19 @@ Description:
                advertise to the partner. The currently used capabilities are in
                brackets. Selection happens by writing to the file.
 
+What:          /sys/class/typec/<port>/usb_capability
+Date:          November 2024
+Contact:       Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:   Lists the supported USB Modes. The default USB mode that is used
+               next time with the Enter_USB Message is in brackets. The default
+               mode can be changed by writing to the file when supported by the
+               driver.
+
+               Valid values:
+               - usb2 (USB 2.0)
+               - usb3 (USB 3.2)
+               - usb4 (USB4)
+
 USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
 
 What:          /sys/class/typec/<port>-partner/accessory_mode
index 9262fcd4144f85ab3f089f820c2063fbe634b0d1..2f12269d1465b6575df01c486211c581e2a58169 100644 (file)
@@ -219,6 +219,13 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev,
                                                char *buf);
 static DEVICE_ATTR_RO(usb_power_delivery_revision);
 
+static const char * const usb_modes[] = {
+       [USB_MODE_NONE] = "none",
+       [USB_MODE_USB2] = "usb2",
+       [USB_MODE_USB3] = "usb3",
+       [USB_MODE_USB4] = "usb4"
+};
+
 /* ------------------------------------------------------------------------- */
 /* Alternate Modes */
 
@@ -1289,6 +1296,67 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable);
 /* ------------------------------------------------------------------------- */
 /* USB Type-C ports */
 
+/**
+ * typec_port_set_usb_mode - Set the operational USB mode for the port
+ * @port: USB Type-C port
+ * @mode: USB Mode (USB2, USB3 or USB4)
+ *
+ * @mode will be used with the next Enter_USB message. Existing connections are
+ * not affected.
+ */
+void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode)
+{
+       port->usb_mode = mode;
+}
+EXPORT_SYMBOL_GPL(typec_port_set_usb_mode);
+
+static ssize_t
+usb_capability_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct typec_port *port = to_typec_port(dev);
+       int len = 0;
+       int i;
+
+       for (i = USB_MODE_USB2; i < USB_MODE_USB4 + 1; i++) {
+               if (!(BIT(i - 1) & port->cap->usb_capability))
+                       continue;
+
+               if (i == port->usb_mode)
+                       len += sysfs_emit_at(buf, len, "[%s] ", usb_modes[i]);
+               else
+                       len += sysfs_emit_at(buf, len, "%s ", usb_modes[i]);
+       }
+
+       sysfs_emit_at(buf, len - 1, "\n");
+
+       return len;
+}
+
+static ssize_t
+usb_capability_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;
+
+       if (!port->ops || !port->ops->default_usb_mode_set)
+               return -EOPNOTSUPP;
+
+       mode = sysfs_match_string(usb_modes, buf);
+       if (mode < 0)
+               return mode;
+
+       ret = port->ops->default_usb_mode_set(port, mode);
+       if (ret)
+               return ret;
+
+       port->usb_mode = mode;
+
+       return size;
+}
+static DEVICE_ATTR_RW(usb_capability);
+
 /**
  * typec_port_set_usb_power_delivery - Assign USB PD for port.
  * @port: USB Type-C port.
@@ -1757,6 +1825,7 @@ static struct attribute *typec_attrs[] = {
        &dev_attr_vconn_source.attr,
        &dev_attr_port_type.attr,
        &dev_attr_orientation.attr,
+       &dev_attr_usb_capability.attr,
        NULL,
 };
 
@@ -1790,6 +1859,11 @@ static umode_t typec_attr_is_visible(struct kobject *kobj,
                if (port->cap->orientation_aware)
                        return 0444;
                return 0;
+       } else if (attr == &dev_attr_usb_capability.attr) {
+               if (!port->cap->usb_capability)
+                       return 0;
+               if (!port->ops || !port->ops->default_usb_mode_set)
+                       return 0444;
        }
 
        return attr->mode;
@@ -2428,6 +2502,13 @@ struct typec_port *typec_register_port(struct device *parent,
        port->con.attach = typec_partner_attach;
        port->con.deattach = typec_partner_deattach;
 
+       if (cap->usb_capability & USB_CAPABILITY_USB4)
+               port->usb_mode = USB_MODE_USB4;
+       else if (cap->usb_capability & USB_CAPABILITY_USB3)
+               port->usb_mode = USB_MODE_USB3;
+       else if (cap->usb_capability & USB_CAPABILITY_USB2)
+               port->usb_mode = USB_MODE_USB2;
+
        device_initialize(&port->dev);
        port->dev.class = &typec_class;
        port->dev.parent = parent;
index 7485cdb9dd2017ce1d55987de2ca6a3f63fe38ae..85bc50aa54f71e1d81d847b0dec024b5e9f675e7 100644 (file)
@@ -55,6 +55,7 @@ struct typec_port {
        enum typec_role                 vconn_role;
        enum typec_pwr_opmode           pwr_opmode;
        enum typec_port_type            port_type;
+       enum usb_mode                   usb_mode;
        struct mutex                    port_type_lock;
 
        enum typec_orientation          orientation;
index 549275f8ac1b385cf6ead37a06566cd2e6a4ba44..f7edced5b10be2c51f65ed78e3df7611c12a1a54 100644 (file)
@@ -87,6 +87,17 @@ 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)
+
 /*
  * struct enter_usb_data - Enter_USB Message details
  * @eudo: Enter_USB Data Object
@@ -240,6 +251,7 @@ struct typec_partner_desc {
  * @port_type_set: Set port type
  * @pd_get: Get available USB Power Delivery Capabilities.
  * @pd_set: Set USB Power Delivery Capabilities.
+ * @default_usb_mode_set: USB Mode to be used by default with Enter_USB Message
  */
 struct typec_operations {
        int (*try_role)(struct typec_port *port, int role);
@@ -250,6 +262,7 @@ struct typec_operations {
                             enum typec_port_type type);
        struct usb_power_delivery **(*pd_get)(struct typec_port *port);
        int (*pd_set)(struct typec_port *port, struct usb_power_delivery *pd);
+       int (*default_usb_mode_set)(struct typec_port *port, enum usb_mode mode);
 };
 
 enum usb_pd_svdm_ver {
@@ -267,6 +280,7 @@ enum usb_pd_svdm_ver {
  * @svdm_version: USB PD Structured VDM version if supported
  * @prefer_role: Initial role preference (DRP ports).
  * @accessory: Supported Accessory Modes
+ * @usb_capability: Supported USB Modes
  * @fwnode: Optional fwnode of the port
  * @driver_data: Private pointer for driver specific info
  * @pd: Optional USB Power Delivery Support
@@ -283,6 +297,7 @@ struct typec_capability {
        int                     prefer_role;
        enum typec_accessory    accessory[TYPEC_MAX_ACCESSORY];
        unsigned int            orientation_aware:1;
+       u8                      usb_capability;
 
        struct fwnode_handle    *fwnode;
        void                    *driver_data;
@@ -350,6 +365,8 @@ int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_
 int typec_partner_set_usb_power_delivery(struct typec_partner *partner,
                                         struct usb_power_delivery *pd);
 
+void typec_port_set_usb_mode(struct typec_port *port, enum usb_mode mode);
+
 /**
  * struct typec_connector - Representation of Type-C port for external drivers
  * @attach: notification about device removal