Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: typec: Add type sysfs attribute file for partners

USB Power Delivery Specification defines a set of product
types for partners and cables. The product type can be read
from the ID Header VDO which is the first object in the
response to the Discover Identity command. This attribute
will display the product type of the partner. The cables
already have the attribute.

This sysfs attribute file is only created for the partners
and cables if the product type is really known in the
driver. Some interfaces do not give access to the Discover
Identity response from the partner or cable, but they may
still supply the product type separately in some cases.

When the product type of the partner or cable is detected,
uevent is also raised with PRODUCT_TYPE set to show the
actual product type (for example PRODUCT_TYPE=host).

Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20201126115735.50529-1-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
ab37fa85 52170e93

+165 -17
+61 -3
Documentation/ABI/testing/sysfs-class-typec
··· 147 147 during Power Delivery discovery. This file remains hidden until a value 148 148 greater than or equal to 0 is set by Type C port driver. 149 149 150 + What: /sys/class/typec/<port>-partner/type 151 + Date: December 2020 152 + Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 153 + Description: USB Power Delivery Specification defines a set of product types 154 + for the partner devices. This file will show the product type of 155 + the partner if it is known. Dual-role capable partners will have 156 + both UFP and DFP product types defined, but only one that 157 + matches the current role will be active at the time. If the 158 + product type of the partner is not visible to the device driver, 159 + this file will not exist. 160 + 161 + When the partner product type is detected, or changed with role 162 + swap, uvevent is also raised that contains PRODUCT_TYPE=<product 163 + type> (for example PRODUCT_TYPE=hub). 164 + 165 + Valid values: 166 + 167 + UFP / device role 168 + ====================== ========================== 169 + undefined - 170 + hub PDUSB Hub 171 + peripheral PDUSB Peripheral 172 + psd Power Bank 173 + ama Alternate Mode Adapter 174 + ====================== ========================== 175 + 176 + DFP / host role 177 + ====================== ========================== 178 + undefined - 179 + hub PDUSB Hub 180 + host PDUSB Host 181 + power_brick Power Brick 182 + amc Alternate Mode Controller 183 + ====================== ========================== 184 + 185 + What: /sys/class/typec/<port>-partner>/identity/ 186 + Date: April 2017 187 + Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 188 + Description: 189 + This directory appears only if the port device driver is capable 190 + of showing the result of Discover Identity USB power delivery 191 + command. That will not always be possible even when USB power 192 + delivery is supported, for example when USB power delivery 193 + communication for the port is mostly handled in firmware. If the 194 + directory exists, it will have an attribute file for every VDO 195 + in Discover Identity command result. 150 196 151 197 USB Type-C cable devices (eg. /sys/class/typec/port0-cable/) 152 198 ··· 205 159 What: /sys/class/typec/<port>-cable/type 206 160 Date: April 2017 207 161 Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 208 - Description: 209 - Shows if the cable is active. 210 - Valid values: active, passive 162 + Description: USB Power Delivery Specification defines a set of product types 163 + for the cables. This file will show the product type of the 164 + cable if it is known. If the product type of the cable is not 165 + visible to the device driver, this file will not exist. 166 + 167 + When the cable product type is detected, uvevent is also raised 168 + with PRODUCT_TYPE showing the product type of the cable. 169 + 170 + Valid values: 171 + 172 + ====================== ========================== 173 + undefined - 174 + active Active Cable 175 + passive Passive Cable 176 + ====================== ========================== 211 177 212 178 What: /sys/class/typec/<port>-cable/plug_type 213 179 Date: April 2017
+104 -14
drivers/usb/typec/class.c
··· 11 11 #include <linux/mutex.h> 12 12 #include <linux/property.h> 13 13 #include <linux/slab.h> 14 + #include <linux/usb/pd_vdo.h> 14 15 15 16 #include "bus.h" 16 17 ··· 84 83 [TYPEC_ACCESSORY_DEBUG] = "debug", 85 84 }; 86 85 86 + /* Product types defined in USB PD Specification R3.0 V2.0 */ 87 + static const char * const product_type_ufp[8] = { 88 + [IDH_PTYPE_UNDEF] = "undefined", 89 + [IDH_PTYPE_HUB] = "hub", 90 + [IDH_PTYPE_PERIPH] = "peripheral", 91 + [IDH_PTYPE_PSD] = "psd", 92 + [IDH_PTYPE_AMA] = "ama", 93 + }; 94 + 95 + static const char * const product_type_dfp[8] = { 96 + [IDH_PTYPE_DFP_UNDEF] = "undefined", 97 + [IDH_PTYPE_DFP_HUB] = "hub", 98 + [IDH_PTYPE_DFP_HOST] = "host", 99 + [IDH_PTYPE_DFP_PB] = "power_brick", 100 + [IDH_PTYPE_DFP_AMC] = "amc", 101 + }; 102 + 103 + static const char * const product_type_cable[8] = { 104 + [IDH_PTYPE_UNDEF] = "undefined", 105 + [IDH_PTYPE_PCABLE] = "passive", 106 + [IDH_PTYPE_ACABLE] = "active", 107 + }; 108 + 87 109 static struct usb_pd_identity *get_pd_identity(struct device *dev) 88 110 { 89 111 if (is_typec_partner(dev)) { ··· 119 95 return cable->identity; 120 96 } 121 97 return NULL; 98 + } 99 + 100 + static const char *get_pd_product_type(struct device *dev) 101 + { 102 + struct typec_port *port = to_typec_port(dev->parent); 103 + struct usb_pd_identity *id = get_pd_identity(dev); 104 + const char *ptype = NULL; 105 + 106 + if (is_typec_partner(dev)) { 107 + if (!id) 108 + return NULL; 109 + 110 + if (port->data_role == TYPEC_HOST) 111 + ptype = product_type_ufp[PD_IDH_PTYPE(id->id_header)]; 112 + else 113 + ptype = product_type_dfp[PD_IDH_DFP_PTYPE(id->id_header)]; 114 + } else if (is_typec_cable(dev)) { 115 + if (id) 116 + ptype = product_type_cable[PD_IDH_PTYPE(id->id_header)]; 117 + else 118 + ptype = to_typec_cable(dev)->active ? 119 + product_type_cable[IDH_PTYPE_ACABLE] : 120 + product_type_cable[IDH_PTYPE_PCABLE]; 121 + } 122 + 123 + return ptype; 122 124 } 123 125 124 126 static ssize_t id_header_show(struct device *dev, struct device_attribute *attr, ··· 221 171 NULL, 222 172 }; 223 173 174 + static void typec_product_type_notify(struct device *dev) 175 + { 176 + char *envp[2] = { }; 177 + const char *ptype; 178 + 179 + ptype = get_pd_product_type(dev); 180 + if (!ptype) 181 + return; 182 + 183 + sysfs_notify(&dev->kobj, NULL, "type"); 184 + 185 + envp[0] = kasprintf(GFP_KERNEL, "PRODUCT_TYPE=%s", ptype); 186 + if (!envp[0]) 187 + return; 188 + 189 + kobject_uevent_env(&dev->kobj, KOBJ_CHANGE, envp); 190 + kfree(envp[0]); 191 + } 192 + 224 193 static void typec_report_identity(struct device *dev) 225 194 { 226 195 sysfs_notify(&dev->kobj, "identity", "id_header"); ··· 248 179 sysfs_notify(&dev->kobj, "identity", "product_type_vdo1"); 249 180 sysfs_notify(&dev->kobj, "identity", "product_type_vdo2"); 250 181 sysfs_notify(&dev->kobj, "identity", "product_type_vdo3"); 182 + typec_product_type_notify(dev); 251 183 } 184 + 185 + static ssize_t 186 + type_show(struct device *dev, struct device_attribute *attr, char *buf) 187 + { 188 + const char *ptype; 189 + 190 + ptype = get_pd_product_type(dev); 191 + if (!ptype) 192 + return 0; 193 + 194 + return sysfs_emit(buf, "%s\n", ptype); 195 + } 196 + static DEVICE_ATTR_RO(type); 252 197 253 198 /* ------------------------------------------------------------------------- */ 254 199 /* Alternate Modes */ ··· 675 592 &dev_attr_accessory_mode.attr, 676 593 &dev_attr_supports_usb_power_delivery.attr, 677 594 &dev_attr_number_of_alternate_modes.attr, 595 + &dev_attr_type.attr, 678 596 NULL 679 597 }; 680 598 ··· 687 603 if (partner->num_altmodes < 0) 688 604 return 0; 689 605 } 606 + 607 + if (attr == &dev_attr_type.attr) 608 + if (!get_pd_product_type(kobj_to_dev(kobj))) 609 + return 0; 690 610 691 611 return attr->mode; 692 612 } ··· 1001 913 EXPORT_SYMBOL_GPL(typec_unregister_plug); 1002 914 1003 915 /* Type-C Cables */ 1004 - 1005 - static ssize_t 1006 - type_show(struct device *dev, struct device_attribute *attr, char *buf) 1007 - { 1008 - struct typec_cable *cable = to_typec_cable(dev); 1009 - 1010 - return sprintf(buf, "%s\n", cable->active ? "active" : "passive"); 1011 - } 1012 - static DEVICE_ATTR_RO(type); 1013 916 1014 917 static const char * const typec_plug_types[] = { 1015 918 [USB_PLUG_NONE] = "unknown", ··· 1601 1522 /* --------------------------------------- */ 1602 1523 /* Driver callbacks to report role updates */ 1603 1524 1525 + static int partner_match(struct device *dev, void *data) 1526 + { 1527 + return is_typec_partner(dev); 1528 + } 1529 + 1604 1530 /** 1605 1531 * typec_set_data_role - Report data role change 1606 1532 * @port: The USB Type-C Port where the role was changed ··· 1615 1531 */ 1616 1532 void typec_set_data_role(struct typec_port *port, enum typec_data_role role) 1617 1533 { 1534 + struct device *partner_dev; 1535 + 1618 1536 if (port->data_role == role) 1619 1537 return; 1620 1538 1621 1539 port->data_role = role; 1622 1540 sysfs_notify(&port->dev.kobj, NULL, "data_role"); 1623 1541 kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); 1542 + 1543 + partner_dev = device_find_child(&port->dev, NULL, partner_match); 1544 + if (!partner_dev) 1545 + return; 1546 + 1547 + if (to_typec_partner(partner_dev)->identity) 1548 + typec_product_type_notify(partner_dev); 1549 + 1550 + put_device(partner_dev); 1624 1551 } 1625 1552 EXPORT_SYMBOL_GPL(typec_set_data_role); 1626 1553 ··· 1671 1576 kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); 1672 1577 } 1673 1578 EXPORT_SYMBOL_GPL(typec_set_vconn_role); 1674 - 1675 - static int partner_match(struct device *dev, void *data) 1676 - { 1677 - return is_typec_partner(dev); 1678 - } 1679 1579 1680 1580 /** 1681 1581 * typec_set_pwr_opmode - Report changed power operation mode