Skip to content

Commit 027b304

Browse files
Andrei Kuchynskigregkh
authored andcommitted
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>
1 parent 4ec1287 commit 027b304

3 files changed

Lines changed: 101 additions & 1 deletion

File tree

Documentation/ABI/testing/sysfs-class-typec

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ Description: Lists the supported USB Modes. The default USB mode that is used
162162
- usb3 (USB 3.2)
163163
- usb4 (USB4)
164164

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+
165176
USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
166177

167178
What: /sys/class/typec/<port>-partner/accessory_mode

drivers/usb/typec/class.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf)
445445
}
446446
static DEVICE_ATTR_RO(svid);
447447

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+
448524
static struct attribute *typec_altmode_attrs[] = {
449525
&dev_attr_active.attr,
450526
&dev_attr_mode.attr,
451527
&dev_attr_svid.attr,
452528
&dev_attr_vdo.attr,
529+
&dev_attr_priority.attr,
453530
NULL
454531
};
455532

@@ -459,11 +536,15 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
459536
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
460537
struct typec_port *port = typec_altmode2port(adev);
461538

462-
if (attr == &dev_attr_active.attr)
539+
if (attr == &dev_attr_active.attr) {
463540
if (!is_typec_port(adev->dev.parent)) {
464541
if (!port->mode_control || !adev->ops || !adev->ops->activate)
465542
return 0444;
466543
}
544+
} else if (attr == &dev_attr_priority.attr) {
545+
if (!is_typec_port(adev->dev.parent) || !port->mode_control)
546+
return 0;
547+
}
467548

468549
return attr->mode;
469550
}
@@ -2498,6 +2579,7 @@ typec_port_register_altmode(struct typec_port *port,
24982579
struct typec_altmode *adev;
24992580
struct typec_mux *mux;
25002581
struct typec_retimer *retimer;
2582+
int ret;
25012583

25022584
mux = typec_mux_get(&port->dev);
25032585
if (IS_ERR(mux))
@@ -2516,6 +2598,12 @@ typec_port_register_altmode(struct typec_port *port,
25162598
} else {
25172599
to_altmode(adev)->mux = mux;
25182600
to_altmode(adev)->retimer = retimer;
2601+
2602+
ret = typec_mode_set_priority(adev, 0);
2603+
if (ret) {
2604+
typec_unregister_altmode(adev);
2605+
return ERR_PTR(ret);
2606+
}
25192607
}
25202608

25212609
return adev;

include/linux/usb/typec_altmode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ struct typec_altmode {
3636
int mode;
3737
u32 vdo;
3838
unsigned int active:1;
39+
u8 priority;
3940

4041
char *desc;
4142
const struct typec_altmode_ops *ops;

0 commit comments

Comments
 (0)