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

usb: typec: Link enumerated USB devices with Type-C partner

Adding functions that USB hub code can use to inform the
Type-C class about connected USB devices.

Once taken into use, it will allow the Type-C port drivers
to power off components that are not needed, for example if
USB2 device is enumerated, everything that is only relevant
for USB3 (retimers, etc.), can be powered off.

This will also create a symlink "typec" for the USB devices
pointing to the USB Type-C partner device.

Suggested-by: Benson Leung <bleung@chromium.org>
Tested-by: Benson Leung <bleung@chromium.org>
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://lore.kernel.org/r/20231011105825.320062-2-heikki.krogerus@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Heikki Krogerus and committed by
Greg Kroah-Hartman
59de2a56 17d6b82d

+169 -10
+9
Documentation/ABI/testing/sysfs-bus-usb
··· 313 313 Inter-Chip SSIC devices support asymmetric lanes up to 4 lanes per 314 314 direction. Devices before USB 3.2 are single lane (tx_lanes = 1) 315 315 316 + What: /sys/bus/usb/devices/.../typec 317 + Date: November 2023 318 + Contact: Heikki Krogerus <heikki.krogerus@linux.intel.com> 319 + Description: 320 + Symlink to the USB Type-C partner device. USB Type-C partner 321 + represents the component that communicates over the 322 + Configuration Channel (CC signal on USB Type-C connectors and 323 + cables) with the local port. 324 + 316 325 What: /sys/bus/usb/devices/usbX/bAlternateSetting 317 326 Description: 318 327 The current interface alternate setting number, in decimal.
+100 -8
drivers/usb/typec/class.c
··· 13 13 #include <linux/usb/pd_vdo.h> 14 14 #include <linux/usb/typec_mux.h> 15 15 #include <linux/usb/typec_retimer.h> 16 + #include <linux/usb.h> 16 17 17 18 #include "bus.h" 18 19 #include "class.h" ··· 682 681 .release = typec_partner_release, 683 682 }; 684 683 684 + static void typec_partner_link_device(struct typec_partner *partner, struct device *dev) 685 + { 686 + int ret; 687 + 688 + ret = sysfs_create_link(&dev->kobj, &partner->dev.kobj, "typec"); 689 + if (ret) 690 + return; 691 + 692 + ret = sysfs_create_link(&partner->dev.kobj, &dev->kobj, dev_name(dev)); 693 + if (ret) { 694 + sysfs_remove_link(&dev->kobj, "typec"); 695 + return; 696 + } 697 + 698 + if (partner->attach) 699 + partner->attach(partner, dev); 700 + } 701 + 702 + static void typec_partner_unlink_device(struct typec_partner *partner, struct device *dev) 703 + { 704 + sysfs_remove_link(&partner->dev.kobj, dev_name(dev)); 705 + sysfs_remove_link(&dev->kobj, "typec"); 706 + 707 + if (partner->deattach) 708 + partner->deattach(partner, dev); 709 + } 710 + 685 711 /** 686 712 * typec_partner_set_identity - Report result from Discover Identity command 687 713 * @partner: The partner updated identity values ··· 893 865 partner->num_altmodes = -1; 894 866 partner->pd_revision = desc->pd_revision; 895 867 partner->svdm_version = port->cap->svdm_version; 868 + partner->attach = desc->attach; 869 + partner->deattach = desc->deattach; 896 870 897 871 if (desc->identity) { 898 872 /* ··· 917 887 return ERR_PTR(ret); 918 888 } 919 889 890 + if (port->usb2_dev) 891 + typec_partner_link_device(partner, port->usb2_dev); 892 + if (port->usb3_dev) 893 + typec_partner_link_device(partner, port->usb3_dev); 894 + 920 895 return partner; 921 896 } 922 897 EXPORT_SYMBOL_GPL(typec_register_partner); ··· 934 899 */ 935 900 void typec_unregister_partner(struct typec_partner *partner) 936 901 { 937 - if (!IS_ERR_OR_NULL(partner)) 938 - device_unregister(&partner->dev); 902 + struct typec_port *port; 903 + 904 + if (IS_ERR_OR_NULL(partner)) 905 + return; 906 + 907 + port = to_typec_port(partner->dev.parent); 908 + 909 + if (port->usb2_dev) 910 + typec_partner_unlink_device(partner, port->usb2_dev); 911 + if (port->usb3_dev) 912 + typec_partner_unlink_device(partner, port->usb3_dev); 913 + 914 + device_unregister(&partner->dev); 939 915 } 940 916 EXPORT_SYMBOL_GPL(typec_unregister_partner); 941 917 ··· 1821 1775 return is_typec_partner(dev); 1822 1776 } 1823 1777 1778 + static struct typec_partner *typec_get_partner(struct typec_port *port) 1779 + { 1780 + struct device *dev; 1781 + 1782 + dev = device_find_child(&port->dev, NULL, partner_match); 1783 + if (!dev) 1784 + return NULL; 1785 + 1786 + return to_typec_partner(dev); 1787 + } 1788 + 1789 + static void typec_partner_attach(struct typec_connector *con, struct device *dev) 1790 + { 1791 + struct typec_port *port = container_of(con, struct typec_port, con); 1792 + struct typec_partner *partner = typec_get_partner(port); 1793 + struct usb_device *udev = to_usb_device(dev); 1794 + 1795 + if (udev->speed < USB_SPEED_SUPER) 1796 + port->usb2_dev = dev; 1797 + else 1798 + port->usb3_dev = dev; 1799 + 1800 + if (partner) { 1801 + typec_partner_link_device(partner, dev); 1802 + put_device(&partner->dev); 1803 + } 1804 + } 1805 + 1806 + static void typec_partner_deattach(struct typec_connector *con, struct device *dev) 1807 + { 1808 + struct typec_port *port = container_of(con, struct typec_port, con); 1809 + struct typec_partner *partner = typec_get_partner(port); 1810 + 1811 + if (partner) { 1812 + typec_partner_unlink_device(partner, dev); 1813 + put_device(&partner->dev); 1814 + } 1815 + 1816 + if (port->usb2_dev == dev) 1817 + port->usb2_dev = NULL; 1818 + else if (port->usb3_dev == dev) 1819 + port->usb3_dev = NULL; 1820 + } 1821 + 1824 1822 /** 1825 1823 * typec_set_data_role - Report data role change 1826 1824 * @port: The USB Type-C Port where the role was changed ··· 1874 1784 */ 1875 1785 void typec_set_data_role(struct typec_port *port, enum typec_data_role role) 1876 1786 { 1877 - struct device *partner_dev; 1787 + struct typec_partner *partner; 1878 1788 1879 1789 if (port->data_role == role) 1880 1790 return; ··· 1883 1793 sysfs_notify(&port->dev.kobj, NULL, "data_role"); 1884 1794 kobject_uevent(&port->dev.kobj, KOBJ_CHANGE); 1885 1795 1886 - partner_dev = device_find_child(&port->dev, NULL, partner_match); 1887 - if (!partner_dev) 1796 + partner = typec_get_partner(port); 1797 + if (!partner) 1888 1798 return; 1889 1799 1890 - if (to_typec_partner(partner_dev)->identity) 1891 - typec_product_type_notify(partner_dev); 1800 + if (partner->identity) 1801 + typec_product_type_notify(&partner->dev); 1892 1802 1893 - put_device(partner_dev); 1803 + put_device(&partner->dev); 1894 1804 } 1895 1805 EXPORT_SYMBOL_GPL(typec_set_data_role); 1896 1806 ··· 2341 2251 port->ops = cap->ops; 2342 2252 port->port_type = cap->type; 2343 2253 port->prefer_role = cap->prefer_role; 2254 + port->con.attach = typec_partner_attach; 2255 + port->con.deattach = typec_partner_deattach; 2344 2256 2345 2257 device_initialize(&port->dev); 2346 2258 port->dev.class = &typec_class;
+16
drivers/usb/typec/class.h
··· 8 8 9 9 struct typec_mux; 10 10 struct typec_switch; 11 + struct usb_device; 11 12 12 13 struct typec_plug { 13 14 struct device dev; ··· 36 35 enum usb_pd_svdm_ver svdm_version; 37 36 38 37 struct usb_power_delivery *pd; 38 + 39 + void (*attach)(struct typec_partner *partner, struct device *dev); 40 + void (*deattach)(struct typec_partner *partner, struct device *dev); 39 41 }; 40 42 41 43 struct typec_port { ··· 63 59 64 60 const struct typec_capability *cap; 65 61 const struct typec_operations *ops; 62 + 63 + struct typec_connector con; 64 + 65 + /* 66 + * REVISIT: Only USB devices for now. If there are others, these need to 67 + * be converted into a list. 68 + * 69 + * NOTE: These may be registered first before the typec_partner, so they 70 + * will always have to be kept here instead of struct typec_partner. 71 + */ 72 + struct device *usb2_dev; 73 + struct device *usb3_dev; 66 74 }; 67 75 68 76 #define to_typec_port(_dev_) container_of(_dev_, struct typec_port, dev)
+7 -2
drivers/usb/typec/port-mapper.c
··· 8 8 9 9 #include <linux/acpi.h> 10 10 #include <linux/component.h> 11 + #include <linux/usb.h> 11 12 12 13 #include "class.h" 13 14 14 15 static int typec_aggregate_bind(struct device *dev) 15 16 { 16 - return component_bind_all(dev, NULL); 17 + struct typec_port *port = to_typec_port(dev); 18 + 19 + return component_bind_all(dev, &port->con); 17 20 } 18 21 19 22 static void typec_aggregate_unbind(struct device *dev) 20 23 { 21 - component_unbind_all(dev, NULL); 24 + struct typec_port *port = to_typec_port(dev); 25 + 26 + component_unbind_all(dev, &port->con); 22 27 } 23 28 24 29 static const struct component_master_ops typec_aggregate_ops = {
+37
include/linux/usb/typec.h
··· 202 202 * @accessory: Audio, Debug or none. 203 203 * @identity: Discover Identity command data 204 204 * @pd_revision: USB Power Delivery Specification Revision if supported 205 + * @attach: Notification about attached USB device 206 + * @deattach: Notification about removed USB device 205 207 * 206 208 * Details about a partner that is attached to USB Type-C port. If @identity 207 209 * member exists when partner is registered, a directory named "identity" is ··· 219 217 enum typec_accessory accessory; 220 218 struct usb_pd_identity *identity; 221 219 u16 pd_revision; /* 0300H = "3.0" */ 220 + 221 + void (*attach)(struct typec_partner *partner, struct device *dev); 222 + void (*deattach)(struct typec_partner *partner, struct device *dev); 222 223 }; 223 224 224 225 /** ··· 339 334 int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_delivery *pd); 340 335 int typec_partner_set_usb_power_delivery(struct typec_partner *partner, 341 336 struct usb_power_delivery *pd); 337 + 338 + /** 339 + * struct typec_connector - Representation of Type-C port for external drivers 340 + * @attach: notification about device removal 341 + * @deattach: notification about device removal 342 + * 343 + * Drivers that control the USB and other ports (DisplayPorts, etc.), that are 344 + * connected to the Type-C connectors, can use these callbacks to inform the 345 + * Type-C connector class about connections and disconnections. That information 346 + * can then be used by the typec-port drivers to power on or off parts that are 347 + * needed or not needed - as an example, in USB mode if USB2 device is 348 + * enumerated, USB3 components (retimers, phys, and what have you) do not need 349 + * to be powered on. 350 + * 351 + * The attached (enumerated) devices will be liked with the typec-partner device. 352 + */ 353 + struct typec_connector { 354 + void (*attach)(struct typec_connector *con, struct device *dev); 355 + void (*deattach)(struct typec_connector *con, struct device *dev); 356 + }; 357 + 358 + static inline void typec_attach(struct typec_connector *con, struct device *dev) 359 + { 360 + if (con && con->attach) 361 + con->attach(con, dev); 362 + } 363 + 364 + static inline void typec_deattach(struct typec_connector *con, struct device *dev) 365 + { 366 + if (con && con->deattach) 367 + con->deattach(con, dev); 368 + } 342 369 343 370 #endif /* __LINUX_USB_TYPEC_H */