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

Configure Feed

Select the types of activity you want to include in your feed.

usb: typec: Expose alternate mode priority via sysfs

This patch introduces a priority sysfs attribute to the USB Type-C
alternate mode port interface. This new attribute allows user-space to
configure the numeric priority of alternate modes managing their preferred
order of operation. If a new priority value conflicts with an existing
mode's priority, the priorities of the conflicting mode and all subsequent
modes are automatically incremented to ensure uniqueness.

Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
Reviewed-by: Benson Leung <bleung@chromium.org>
Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Link: https://patch.msgid.link/20260119131824.2529334-4-akuchynski@chromium.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Andrei Kuchynski and committed by
Greg Kroah-Hartman
027b304c 4ec12873

+101 -1
+11
Documentation/ABI/testing/sysfs-class-typec
··· 162 162 - usb3 (USB 3.2) 163 163 - usb4 (USB4) 164 164 165 + What: /sys/class/typec/<port>/<alt-mode>/priority 166 + Date: July 2025 167 + Contact: Andrei Kuchynski <akuchynski@chromium.org> 168 + Description: 169 + Displays and allows setting the priority for a specific alternate mode. 170 + The priority is an integer in the range 0-255. A lower numerical value 171 + indicates a higher priority (0 is the highest). 172 + If the new value is already in use by another mode, the priority of the 173 + conflicting mode and any subsequent modes will be incremented until they 174 + are all unique. 175 + 165 176 USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) 166 177 167 178 What: /sys/class/typec/<port>-partner/accessory_mode
+89 -1
drivers/usb/typec/class.c
··· 445 445 } 446 446 static DEVICE_ATTR_RO(svid); 447 447 448 + static int increment_duplicated_priority(struct device *dev, void *data) 449 + { 450 + if (is_typec_port_altmode(dev)) { 451 + struct typec_altmode **alt_target = (struct typec_altmode **)data; 452 + struct typec_altmode *alt = to_typec_altmode(dev); 453 + 454 + if (alt != *alt_target && alt->priority == (*alt_target)->priority) { 455 + alt->priority++; 456 + *alt_target = alt; 457 + return 1; 458 + } 459 + } 460 + return 0; 461 + } 462 + 463 + static int find_duplicated_priority(struct device *dev, void *data) 464 + { 465 + if (is_typec_port_altmode(dev)) { 466 + struct typec_altmode **alt_target = (struct typec_altmode **)data; 467 + struct typec_altmode *alt = to_typec_altmode(dev); 468 + 469 + if (alt != *alt_target && alt->priority == (*alt_target)->priority) 470 + return 1; 471 + } 472 + return 0; 473 + } 474 + 475 + static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority) 476 + { 477 + struct typec_port *port = to_typec_port(alt->dev.parent); 478 + const u8 old_priority = alt->priority; 479 + int res = 1; 480 + 481 + alt->priority = priority; 482 + while (res) { 483 + res = device_for_each_child(&port->dev, &alt, find_duplicated_priority); 484 + if (res) { 485 + alt->priority++; 486 + if (alt->priority == 0) { 487 + alt->priority = old_priority; 488 + return -EOVERFLOW; 489 + } 490 + } 491 + } 492 + 493 + res = 1; 494 + alt->priority = priority; 495 + while (res) 496 + res = device_for_each_child(&port->dev, &alt, 497 + increment_duplicated_priority); 498 + 499 + return 0; 500 + } 501 + 502 + static ssize_t priority_store(struct device *dev, 503 + struct device_attribute *attr, 504 + const char *buf, size_t size) 505 + { 506 + u8 val; 507 + int err = kstrtou8(buf, 10, &val); 508 + 509 + if (!err) 510 + err = typec_mode_set_priority(to_typec_altmode(dev), val); 511 + 512 + if (!err) 513 + return size; 514 + return err; 515 + } 516 + 517 + static ssize_t priority_show(struct device *dev, 518 + struct device_attribute *attr, char *buf) 519 + { 520 + return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority); 521 + } 522 + static DEVICE_ATTR_RW(priority); 523 + 448 524 static struct attribute *typec_altmode_attrs[] = { 449 525 &dev_attr_active.attr, 450 526 &dev_attr_mode.attr, 451 527 &dev_attr_svid.attr, 452 528 &dev_attr_vdo.attr, 529 + &dev_attr_priority.attr, 453 530 NULL 454 531 }; 455 532 ··· 536 459 struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); 537 460 struct typec_port *port = typec_altmode2port(adev); 538 461 539 - if (attr == &dev_attr_active.attr) 462 + if (attr == &dev_attr_active.attr) { 540 463 if (!is_typec_port(adev->dev.parent)) { 541 464 if (!port->mode_control || !adev->ops || !adev->ops->activate) 542 465 return 0444; 543 466 } 467 + } else if (attr == &dev_attr_priority.attr) { 468 + if (!is_typec_port(adev->dev.parent) || !port->mode_control) 469 + return 0; 470 + } 544 471 545 472 return attr->mode; 546 473 } ··· 2579 2498 struct typec_altmode *adev; 2580 2499 struct typec_mux *mux; 2581 2500 struct typec_retimer *retimer; 2501 + int ret; 2582 2502 2583 2503 mux = typec_mux_get(&port->dev); 2584 2504 if (IS_ERR(mux)) ··· 2598 2516 } else { 2599 2517 to_altmode(adev)->mux = mux; 2600 2518 to_altmode(adev)->retimer = retimer; 2519 + 2520 + ret = typec_mode_set_priority(adev, 0); 2521 + if (ret) { 2522 + typec_unregister_altmode(adev); 2523 + return ERR_PTR(ret); 2524 + } 2601 2525 } 2602 2526 2603 2527 return adev;
+1
include/linux/usb/typec_altmode.h
··· 36 36 int mode; 37 37 u32 vdo; 38 38 unsigned int active:1; 39 + u8 priority; 39 40 40 41 char *desc; 41 42 const struct typec_altmode_ops *ops;