* [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
@ 2026-04-02 12:04 François Scala
2026-04-02 12:18 ` Greg KH
2026-04-02 13:26 ` Heikki Krogerus
0 siblings, 2 replies; 8+ messages in thread
From: François Scala @ 2026-04-02 12:04 UTC (permalink / raw)
To: linux-usb; +Cc: Heikki Krogerus, François Scala
From: François Scala <francois@scala.name>
The partner field in the altmode struct is a single pointer and the sysfs
symlink uses a fixed name. This cause an error when a second partner is
registered.
sysfs: cannot create duplicate filename '/devices/platform/USBC000:00/typec/port0/port0.0/partner'
The field is replaced by an array of pointers and the symlink use the
device name to avoid conflict.
/sys/devices/platform/USBC000:00/typec/port0/port0.0/
lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.0 -> ../port0-partner/port0-partner.0
lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.1 -> ../port0-partner/port0-partner.1
Signed-off-by: François Scala <francois@scala.name>
Thanks
François
---
drivers/usb/typec/bus.c | 133 ++++++++++++++++++++++++++++++++------
drivers/usb/typec/bus.h | 8 ++-
drivers/usb/typec/class.c | 99 +++++++++++++++++++++++++---
3 files changed, 209 insertions(+), 31 deletions(-)
diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
index e84b134a3381..e09ceb844342 100644
--- a/drivers/usb/typec/bus.c
+++ b/drivers/usb/typec/bus.c
@@ -7,6 +7,8 @@
*/
#include <linux/usb/pd_vdo.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
#include "bus.h"
#include "class.h"
@@ -62,7 +64,7 @@ static int typec_altmode_set_state(struct typec_altmode *adev,
bool is_port = is_typec_port(adev->dev.parent);
struct altmode *port_altmode;
- port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner;
+ port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partners[0];
return typec_altmode_set_switches(port_altmode, conf, data);
}
@@ -89,6 +91,7 @@ int typec_altmode_notify(struct typec_altmode *adev,
bool is_port;
struct altmode *altmode;
struct altmode *partner;
+ int partner_idx;
int ret;
if (!adev)
@@ -96,11 +99,17 @@ int typec_altmode_notify(struct typec_altmode *adev,
altmode = to_altmode(adev);
- if (!altmode->partner)
+ partner_idx = typec_altmode_get_partner_idx_by_name(altmode, dev_name(&adev->dev));
+ if (partner_idx == -1) {
+ dev_info(&adev->dev, "%s adev is not in the partners array\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!altmode->partners[partner_idx])
return -ENODEV;
is_port = is_typec_port(adev->dev.parent);
- partner = altmode->partner;
+ partner = altmode->partners[partner_idx];
ret = typec_altmode_set_switches(is_port ? altmode : partner, conf, data);
if (ret)
@@ -125,13 +134,18 @@ EXPORT_SYMBOL_GPL(typec_altmode_notify);
*/
int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo)
{
- struct altmode *partner = to_altmode(adev)->partner;
- struct typec_altmode *pdev = &partner->adev;
+ struct altmode *partner;
+ struct typec_altmode *pdev;
int ret;
+ partner = to_altmode(adev)->partners[0];
+ pdev = &partner->adev;
+
if (!adev || adev->active)
return 0;
+ typec_altmode_dump("typec_altmode_enter partner", partner);
+
if (!pdev->ops || !pdev->ops->enter)
return -EOPNOTSUPP;
@@ -156,13 +170,15 @@ EXPORT_SYMBOL_GPL(typec_altmode_enter);
*/
int typec_altmode_exit(struct typec_altmode *adev)
{
- struct altmode *partner = to_altmode(adev)->partner;
+ struct altmode *partner = to_altmode(adev)->partners[0];
struct typec_altmode *pdev = &partner->adev;
int ret;
if (!adev || !adev->active)
return 0;
+ typec_altmode_dump("typec_altmode_exit partner", partner);
+
if (!pdev->ops || !pdev->ops->exit)
return -EOPNOTSUPP;
@@ -185,7 +201,7 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
*/
int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
{
- struct altmode *partner = to_altmode(adev)->partner;
+ struct altmode *partner = to_altmode(adev)->partners[0];
struct typec_altmode *pdev;
if (!partner)
@@ -220,12 +236,15 @@ int typec_altmode_vdm(struct typec_altmode *adev,
if (!adev)
return 0;
+
altmode = to_altmode(adev);
- if (!altmode->partner)
+ typec_altmode_dump("typec_altmode_vdm altmode", altmode);
+
+ if (!altmode->partners[0])
return -ENODEV;
- pdev = &altmode->partner->adev;
+ pdev = &altmode->partners[0]->adev;
if (!pdev->ops || !pdev->ops->vdm)
return -EOPNOTSUPP;
@@ -237,10 +256,10 @@ EXPORT_SYMBOL_GPL(typec_altmode_vdm);
const struct typec_altmode *
typec_altmode_get_partner(struct typec_altmode *adev)
{
- if (!adev || !to_altmode(adev)->partner)
+ if (!adev || !to_altmode(adev)->partners[0])
return NULL;
- return &to_altmode(adev)->partner->adev;
+ return &to_altmode(adev)->partners[0]->adev;
}
EXPORT_SYMBOL_GPL(typec_altmode_get_partner);
@@ -258,7 +277,7 @@ EXPORT_SYMBOL_GPL(typec_altmode_get_partner);
*/
int typec_cable_altmode_enter(struct typec_altmode *adev, enum typec_plug_index sop, u32 *vdo)
{
- struct altmode *partner = to_altmode(adev)->partner;
+ struct altmode *partner = to_altmode(adev)->partners[0];
struct typec_altmode *pdev;
if (!adev || adev->active)
@@ -288,7 +307,7 @@ EXPORT_SYMBOL_GPL(typec_cable_altmode_enter);
*/
int typec_cable_altmode_exit(struct typec_altmode *adev, enum typec_plug_index sop)
{
- struct altmode *partner = to_altmode(adev)->partner;
+ struct altmode *partner = to_altmode(adev)->partners[0];
struct typec_altmode *pdev;
if (!adev || !adev->active)
@@ -330,9 +349,9 @@ int typec_cable_altmode_vdm(struct typec_altmode *adev, enum typec_plug_index so
altmode = to_altmode(adev);
if (is_typec_plug(adev->dev.parent)) {
- if (!altmode->partner)
+ if (!altmode->partners[0])
return -ENODEV;
- pdev = &altmode->partner->adev;
+ pdev = &altmode->partners[0]->adev;
} else {
if (!altmode->plug[sop])
return -ENODEV;
@@ -360,7 +379,7 @@ EXPORT_SYMBOL_GPL(typec_cable_altmode_vdm);
struct typec_altmode *typec_altmode_get_plug(struct typec_altmode *adev,
enum typec_plug_index index)
{
- struct altmode *port = to_altmode(adev)->partner;
+ struct altmode *port = to_altmode(adev)->partners[0];
if (port->plug[index]) {
get_device(&port->plug[index]->adev.dev);
@@ -494,17 +513,58 @@ static int typec_uevent(const struct device *dev, struct kobj_uevent_env *env)
return add_uevent_var(env, "MODALIAS=typec:id%04X", altmode->svid);
}
+void typec_altmode_dump(char *name, struct altmode *alt)
+{
+ if (alt) {
+ for (int i = 0; i < TYPEC_ALTMODE_MAX_PARTNERS; i++) {
+ if (alt->partners[i]) {
+ dev_info(&alt->adev.dev, "%s[%p]->partners[%d] = %p / adev name = %s",
+ name,
+ alt,
+ i,
+ alt->partners[i],
+ dev_name(&alt->partners[i]->adev.dev)
+ );
+ }
+ }
+ for (int i = 0; i < 2; i++) {
+ if (alt->plug[i]) {
+ dev_info(&alt->adev.dev, "%s[%p]->plug[%d] = %p / adev name = %s",
+ name,
+ alt,
+ i,
+ alt->plug[i],
+ dev_name(&alt->plug[i]->adev.dev)
+ );
+ }
+ }
+ } else {
+ dev_info(&alt->adev.dev, "alt == NULL !");
+ }
+}
+
static int typec_altmode_create_links(struct altmode *alt)
{
- struct device *port_dev = &alt->partner->adev.dev;
+ struct device *port_dev = &alt->partners[0]->adev.dev;
struct device *dev = &alt->adev.dev;
int err;
+ dev_info(&alt->adev.dev, "%s \"port\" -> %s\n",
+ __func__,
+ dev_name(port_dev)
+ );
+
err = sysfs_create_link(&dev->kobj, &port_dev->kobj, "port");
if (err)
return err;
- err = sysfs_create_link(&port_dev->kobj, &dev->kobj, "partner");
+ dev_info(port_dev, "%s \"%s\" -> %s\n",
+ __func__,
+ dev_name(port_dev),
+ dev_name(dev)
+ );
+
+ err = sysfs_create_link(&port_dev->kobj, &dev->kobj, dev_name(dev));
if (err)
sysfs_remove_link(&dev->kobj, "port");
@@ -513,7 +573,17 @@ static int typec_altmode_create_links(struct altmode *alt)
static void typec_altmode_remove_links(struct altmode *alt)
{
- sysfs_remove_link(&alt->partner->adev.dev.kobj, "partner");
+ struct altmode *partner = alt->partners[0];
+
+ dev_info(&partner->adev.dev, "%s \"%s\"\n",
+ __func__,
+ dev_name(&alt->adev.dev)
+ );
+
+ sysfs_remove_link(&partner->adev.dev.kobj, dev_name(&alt->adev.dev));
+
+ dev_info(&alt->adev.dev, "%s \"port\"\n", __func__);
+
sysfs_remove_link(&alt->adev.dev.kobj, "port");
}
@@ -525,7 +595,7 @@ static int typec_probe(struct device *dev)
int ret;
/* Fail if the port does not support the alternate mode */
- if (!altmode->partner)
+ if (!altmode->partners[0])
return -ENODEV;
ret = typec_altmode_create_links(altmode);
@@ -561,6 +631,29 @@ static void typec_remove(struct device *dev)
adev->ops = NULL;
}
+int typec_altmode_get_partner_idx_by_name(struct altmode *altmode, const char *name)
+{
+ if (!altmode)
+ return -1;
+
+ for (int i = 0; i < TYPEC_ALTMODE_MAX_PARTNERS; i++) {
+ if (!altmode->partners[i])
+ continue;
+ if (!strcmp(name, dev_name(&altmode->partners[i]->adev.dev)))
+ return i;
+ }
+ return -1;
+}
+
+bool typec_altmode_partners_is_empty(struct altmode *altmode)
+{
+ for (int i = 0; i < TYPEC_ALTMODE_MAX_PARTNERS; i++) {
+ if (altmode->partners[i])
+ return false;
+ }
+ return true;
+}
+
const struct bus_type typec_bus = {
.name = "typec",
.dev_groups = typec_groups,
diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h
index 7df5deb1dd3a..3db1d407c322 100644
--- a/drivers/usb/typec/bus.h
+++ b/drivers/usb/typec/bus.h
@@ -8,6 +8,8 @@
struct typec_mux;
struct typec_retimer;
+#define TYPEC_ALTMODE_MAX_PARTNERS 2
+
struct altmode {
unsigned int id;
struct typec_altmode adev;
@@ -21,10 +23,14 @@ struct altmode {
struct attribute_group group;
const struct attribute_group *groups[2];
- struct altmode *partner;
+ struct altmode *partners[TYPEC_ALTMODE_MAX_PARTNERS];
struct altmode *plug[2];
};
#define to_altmode(d) container_of(d, struct altmode, adev)
+void typec_altmode_dump(char *name, struct altmode *alt);
+int typec_altmode_get_partner_idx_by_name(struct altmode *altmode, const char *name);
+bool typec_altmode_partners_is_empty(struct altmode *altmode);
+
#endif /* __USB_TYPEC_ALTMODE_H__ */
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 831430909471..0ad9d7488bb0 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -241,6 +241,15 @@ static int altmode_match(struct device *dev, const void *data)
return (adev->svid == id->svid);
}
+static int typec_altmode_partners_get_free_slot(struct altmode *altmode)
+{
+ for (int i = 0; i < TYPEC_ALTMODE_MAX_PARTNERS; i++) {
+ if (altmode->partners[i] == NULL)
+ return i;
+ }
+ return -1;
+}
+
static void typec_altmode_set_partner(struct altmode *altmode)
{
struct typec_altmode *adev = &altmode->adev;
@@ -253,9 +262,18 @@ static void typec_altmode_set_partner(struct altmode *altmode)
if (!dev)
return;
+ dev_info(&altmode->adev.dev, "%s dev %s\n",
+ __func__,
+ dev_name(dev)
+ );
+
+ typec_altmode_dump("altmode before set_partner", altmode);
+
/* Bind the port alt mode to the partner/plug alt mode. */
partner = to_altmode(to_typec_altmode(dev));
- altmode->partner = partner;
+ typec_altmode_dump("partner before set_partner", partner);
+
+ altmode->partners[0] = partner;
/* Bind the partner/plug alt mode to the port alt mode. */
if (is_typec_plug(adev->dev.parent)) {
@@ -263,18 +281,51 @@ static void typec_altmode_set_partner(struct altmode *altmode)
partner->plug[plug->index] = altmode;
} else {
- partner->partner = altmode;
+ int free_spot = typec_altmode_partners_get_free_slot(partner);
+
+ if (free_spot == -1) {
+ dev_info(&altmode->adev.dev, "typec altmode, no free slot in partners table");
+ return;
+ }
+ partner->partners[free_spot] = altmode;
}
+
+ typec_altmode_dump("altmode after set_partner", altmode);
+ typec_altmode_dump("partner after set_partner", partner);
+
}
static void typec_altmode_put_partner(struct altmode *altmode)
{
- struct altmode *partner = altmode->partner;
+ struct altmode *partner = altmode->partners[0];
struct typec_altmode *adev;
struct typec_altmode *partner_adev;
- if (!partner)
+ if (!partner) {
+ dev_info(&altmode->adev.dev, "%s partner is already NULL\n", __func__);
+ return;
+ }
+
+ typec_altmode_dump("altmode before put_partner", altmode);
+ typec_altmode_dump("partner before put_partner", partner);
+
+ int partner_idx = typec_altmode_get_partner_idx_by_name(
+ partner,
+ dev_name(&altmode->adev.dev)
+ );
+
+ dev_info(&altmode->adev.dev, "%s partner_idx = %d\n", __func__, partner_idx);
+
+ if (partner_idx == -1) {
+ dev_info(&altmode->adev.dev, "partner_idx altmod not found in partners list");
return;
+ }
+
+ dev_info(&altmode->adev.dev, "%s altmode->adev = %p partner->adev = %p\n",
+ __func__,
+ &altmode->adev,
+ &partner->adev
+ );
adev = &altmode->adev;
partner_adev = &partner->adev;
@@ -284,9 +335,22 @@ static void typec_altmode_put_partner(struct altmode *altmode)
partner->plug[plug->index] = NULL;
} else {
- partner->partner = NULL;
+ partner->partners[partner_idx] = NULL;
}
- put_device(&partner_adev->dev);
+
+ typec_altmode_dump("partner after set to NULL", partner);
+
+ if (typec_altmode_partners_is_empty(partner)) {
+ dev_info(&altmode->adev.dev, "%s partner->partners is empty -> put_device(...)\n",
+ __func__
+ );
+ put_device(&partner_adev->dev);
+ } else {
+ dev_info(&altmode->adev.dev, "%s partner->partners is not empty -> keeping it\n",
+ __func__
+ );
+ }
+
}
/**
@@ -370,9 +434,17 @@ static ssize_t active_store(struct device *dev, struct device_attribute *attr,
{
struct typec_altmode *adev = to_typec_altmode(dev);
struct altmode *altmode = to_altmode(adev);
+ int partner_idx = typec_altmode_get_partner_idx_by_name(altmode, dev_name(dev));
bool enter;
int ret;
+ dev_info(dev, "%s buf %s altmode %s partner_idx %d\n",
+ __func__,
+ buf,
+ dev_name(&altmode->adev.dev),
+ partner_idx
+ );
+
ret = kstrtobool(buf, &enter);
if (ret)
return ret;
@@ -380,14 +452,21 @@ static ssize_t active_store(struct device *dev, struct device_attribute *attr,
if (adev->active == enter)
return size;
+ if (partner_idx == -1) {
+ dev_warn(dev, "dev is not in the altmode partners array");
+ return 0;
+ }
+
if (is_typec_port(adev->dev.parent)) {
typec_altmode_update_active(adev, enter);
/* Make sure that the partner exits the mode before disabling */
- if (altmode->partner && !enter && altmode->partner->adev.active)
- typec_altmode_exit(&altmode->partner->adev);
- } else if (altmode->partner) {
- if (enter && !altmode->partner->adev.active) {
+ if (altmode->partners[partner_idx]
+ && !enter
+ && altmode->partners[partner_idx]->adev.active)
+ typec_altmode_exit(&altmode->partners[partner_idx]->adev);
+ } else if (altmode->partners[partner_idx]) {
+ if (enter && !altmode->partners[partner_idx]->adev.active) {
dev_warn(dev, "port has the mode disabled\n");
return -EPERM;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 12:04 [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners François Scala
@ 2026-04-02 12:18 ` Greg KH
2026-04-02 12:27 ` François Scala
2026-04-02 13:26 ` Heikki Krogerus
1 sibling, 1 reply; 8+ messages in thread
From: Greg KH @ 2026-04-02 12:18 UTC (permalink / raw)
To: François Scala; +Cc: linux-usb, Heikki Krogerus
On Thu, Apr 02, 2026 at 02:04:32PM +0200, François Scala wrote:
> From: François Scala <francois@scala.name>
>
> The partner field in the altmode struct is a single pointer and the sysfs
> symlink uses a fixed name. This cause an error when a second partner is
> registered.
>
> sysfs: cannot create duplicate filename '/devices/platform/USBC000:00/typec/port0/port0.0/partner'
>
> The field is replaced by an array of pointers and the symlink use the
> device name to avoid conflict.
>
> /sys/devices/platform/USBC000:00/typec/port0/port0.0/
> lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.0 -> ../port0-partner/port0-partner.0
> lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.1 -> ../port0-partner/port0-partner.1
>
> Signed-off-by: François Scala <francois@scala.name>
>
> Thanks
> François
No need for the "Thanks" here :)
And what commit id does this fix?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 12:18 ` Greg KH
@ 2026-04-02 12:27 ` François Scala
2026-04-02 13:35 ` Greg KH
0 siblings, 1 reply; 8+ messages in thread
From: François Scala @ 2026-04-02 12:27 UTC (permalink / raw)
To: Greg KH; +Cc: linux-usb, Heikki Krogerus
On 02/04/2026 14.18, Greg KH wrote:
> And what commit id does this fix?
The altmode struct was added in commit
8a37d87d72f0c69f837229c04d2fcd7117ea57e7
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 12:04 [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners François Scala
2026-04-02 12:18 ` Greg KH
@ 2026-04-02 13:26 ` Heikki Krogerus
2026-04-02 18:10 ` François Scala
1 sibling, 1 reply; 8+ messages in thread
From: Heikki Krogerus @ 2026-04-02 13:26 UTC (permalink / raw)
To: François Scala; +Cc: linux-usb
Hi,
On Thu, Apr 02, 2026 at 02:04:32PM +0200, François Scala wrote:
> From: François Scala <francois@scala.name>
>
> The partner field in the altmode struct is a single pointer and the sysfs
> symlink uses a fixed name. This cause an error when a second partner is
> registered.
>
> sysfs: cannot create duplicate filename '/devices/platform/USBC000:00/typec/port0/port0.0/partner'
I think what you seeing here is either a bug in our ucsi driver, or
(more likely) a problem in the firmware.
> The field is replaced by an array of pointers and the symlink use the
> device name to avoid conflict.
>
> /sys/devices/platform/USBC000:00/typec/port0/port0.0/
> lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.0 -> ../port0-partner/port0-partner.0
> lrwxrwxrwx 1 root root 0 Apr 2 09:12 port0-partner.1 -> ../port0-partner/port0-partner.1
No. You can not have more than a single partner per mode. Let's figure
out the root issue. Please check the svids of the partner altmodes:
grep . /sys/class/typec/port0-partner/port0-partner.*/svid
Then provide the trace output from the ucsi driver, dmesg output
(full), and also acpidump of your system. The ucsi trace you can
get like this (assuming you have the debugfs mounted at
/sys/kernel/debug):
cd /sys/kernel/debug/tracing
echo 1 > events/ucsi/enable
# plug-in your device
# wait for a few seconds
cat trace
For acpidump you need the acpica-tools installed:
acpidump -o my_acpi.dump
thanks,
--
heikki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 12:27 ` François Scala
@ 2026-04-02 13:35 ` Greg KH
0 siblings, 0 replies; 8+ messages in thread
From: Greg KH @ 2026-04-02 13:35 UTC (permalink / raw)
To: François Scala; +Cc: linux-usb, Heikki Krogerus
On Thu, Apr 02, 2026 at 02:27:13PM +0200, François Scala wrote:
>
> On 02/04/2026 14.18, Greg KH wrote:
> > And what commit id does this fix?
>
> The altmode struct was added in commit
> 8a37d87d72f0c69f837229c04d2fcd7117ea57e7
Great, when you resend this, and properly add all of the needed
maintainers that scripts/get_maintainer.pl tells you to add, can you add
the correct "Fixes:" tag line as well?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 13:26 ` Heikki Krogerus
@ 2026-04-02 18:10 ` François Scala
2026-04-07 12:17 ` Heikki Krogerus
0 siblings, 1 reply; 8+ messages in thread
From: François Scala @ 2026-04-02 18:10 UTC (permalink / raw)
To: Heikki Krogerus; +Cc: linux-usb
Hi,
On 02/04/2026 15.26, Heikki Krogerus wrote:
> No. You can not have more than a single partner per mode. Let's figure
> out the root issue. Please check the svids of the partner altmodes:
>
> grep . /sys/class/typec/port0-partner/port0-partner.*/svid
/sys/class/typec/port2-partner/port2-partner.0/svid:8087
/sys/class/typec/port2-partner/port2-partner.1/svid:8087
> Then provide the trace output from the ucsi driver, dmesg output
> (full), and also acpidump of your system. The ucsi trace you can
> get like this (assuming you have the debugfs mounted at
> /sys/kernel/debug):
>
> cd /sys/kernel/debug/tracing
That /sys/kernel/tracing (without /debug).
The traces with port3 (tb4) and port0 (tb5):
# entries-in-buffer/entries-written: 19/19 #P:16
#
# _-----=> irqs-off/BH-disabled
# / _----=> need-resched
# | / _---=> hardirq/softirq
# || / _--=> preempt-depth
# ||| / _-=> migrate-disable
# |||| / delay
# TASK-PID CPU# ||||| TIMESTAMP FUNCTION
# | | | ||||| | |
kworker/5:2-321 [005] ..... 465.001617:
ucsi_connector_change: port3 status: change=4000, opmode=4, connected=1,
sourcing=1, partner_flags=1, partner_type=2, request_data_obj=00000000,
BC status=1
kworker/5:2-321 [005] ..... 465.111779:
ucsi_connector_change: port3 status: change=0060, opmode=3, connected=1,
sourcing=1, partner_flags=1, partner_type=2, request_data_obj=13800000,
BC status=1
kworker/8:1-174 [008] ..... 465.429999:
ucsi_connector_change: port3 status: change=1000, opmode=5, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=13800000,
BC status=1
kworker/8:1-174 [008] ..... 465.532708:
ucsi_connector_change: port3 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=42c4b12c,
BC status=1
kworker/10:1-178 [010] ..... 465.883679:
ucsi_connector_change: port3 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=42c7a9ea,
BC status=1
kworker/3:0-13120 [003] ..... 466.330330:
ucsi_connector_change: port3 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=82c7d1f4,
BC status=1
kworker/u64:2-356 [005] ..... 466.890372:
ucsi_register_altmode: partner alt mode: svid 8087, mode 1 vdo 8087a843
kworker/u64:2-356 [005] ..... 467.005141:
ucsi_register_altmode: partner alt mode: svid 8087, mode 2 vdo 1
kworker/2:1-177 [002] ..... 471.027602:
ucsi_connector_change: port3 status: change=0200, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=82c7d1f4,
BC status=1
kworker/2:1-177 [002] ..... 1140.998711:
ucsi_connector_change: port3 status: change=4000, opmode=1, connected=0,
sourcing=0, partner_flags=0, partner_type=0, request_data_obj=82c7d1f4,
BC status=0
kworker/0:2-15093 [000] ..... 1164.065528:
ucsi_connector_change: port0 status: change=4000, opmode=4, connected=1,
sourcing=1, partner_flags=1, partner_type=2, request_data_obj=00000000,
BC status=1
kworker/0:2-15093 [000] ..... 1164.173696:
ucsi_connector_change: port0 status: change=0060, opmode=3, connected=1,
sourcing=1, partner_flags=1, partner_type=2, request_data_obj=13800000,
BC status=1
kworker/7:0-13116 [007] ..... 1164.508267:
ucsi_connector_change: port0 status: change=1000, opmode=5, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=13800000,
BC status=1
kworker/7:0-13116 [007] ..... 1164.609765:
ucsi_connector_change: port0 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=42c4b12c,
BC status=1
kworker/6:3-360 [006] ..... 1165.119848:
ucsi_connector_change: port0 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=82c7d1f4,
BC status=1
kworker/6:3-360 [006] ..... 1165.208511:
ucsi_connector_change: port0 status: change=0060, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=82c7d1f4,
BC status=1
kworker/u64:3-1005 [010] ..... 1166.339613:
ucsi_register_altmode: partner alt mode: svid 8087, mode 1 vdo 8087a843
kworker/u64:3-1005 [011] ..... 1166.454835:
ucsi_register_altmode: partner alt mode: svid 8087, mode 2 vdo 1
kworker/1:3-191 [001] ..... 1170.026494:
ucsi_connector_change: port0 status: change=0200, opmode=3, connected=1,
sourcing=0, partner_flags=1, partner_type=2, request_data_obj=82c7d1f4,
BC status=1
> For acpidump you need the acpica-tools installed:
>
> acpidump -o my_acpi.dump
The file is quite large, how can I share it ? Or do you need a specific
part ?
-rw-r--r-- 1 fs fs 4.7M Apr 2 19:05 acpi-port3-20260402.dump
Thanks
François
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
2026-04-02 18:10 ` François Scala
@ 2026-04-07 12:17 ` Heikki Krogerus
[not found] ` <d627d2fe-415f-4489-b4fa-ec0575a33239@scala.name>
0 siblings, 1 reply; 8+ messages in thread
From: Heikki Krogerus @ 2026-04-07 12:17 UTC (permalink / raw)
To: François Scala; +Cc: linux-usb
Hi François,
Thank you for the info.
On Thu, Apr 02, 2026 at 08:10:09PM +0200, François Scala wrote:
> Hi,
>
> On 02/04/2026 15.26, Heikki Krogerus wrote:
> > No. You can not have more than a single partner per mode. Let's figure
> > out the root issue. Please check the svids of the partner altmodes:
> >
> > grep . /sys/class/typec/port0-partner/port0-partner.*/svid
>
> /sys/class/typec/port2-partner/port2-partner.0/svid:8087
> /sys/class/typec/port2-partner/port2-partner.1/svid:8087
Okay, the other svid should almost certainly be ff01 (the DisplayPort
Alternate Mode), most likely the second one (port2-partner.1). 8087 is
the Thunderbolt Alternate Mode, and it should now be always companied
by the DisplayPort Alternate Mode.
> kworker/5:2-321 [005] ..... 465.001617: ucsi_connector_change:
> port3 status: change=4000, opmode=4, connected=1, sourcing=1,
> partner_flags=1, partner_type=2, request_data_obj=00000000, BC status=1
> kworker/5:2-321 [005] ..... 465.111779: ucsi_connector_change:
> port3 status: change=0060, opmode=3, connected=1, sourcing=1,
> partner_flags=1, partner_type=2, request_data_obj=13800000, BC status=1
> kworker/8:1-174 [008] ..... 465.429999: ucsi_connector_change:
> port3 status: change=1000, opmode=5, connected=1, sourcing=0,
> partner_flags=1, partner_type=2, request_data_obj=13800000, BC status=1
> kworker/8:1-174 [008] ..... 465.532708: ucsi_connector_change:
> port3 status: change=0060, opmode=3, connected=1, sourcing=0,
> partner_flags=1, partner_type=2, request_data_obj=42c4b12c, BC status=1
> kworker/10:1-178 [010] ..... 465.883679: ucsi_connector_change:
> port3 status: change=0060, opmode=3, connected=1, sourcing=0,
> partner_flags=1, partner_type=2, request_data_obj=42c7a9ea, BC status=1
> kworker/3:0-13120 [003] ..... 466.330330: ucsi_connector_change:
> port3 status: change=0060, opmode=3, connected=1, sourcing=0,
> partner_flags=1, partner_type=2, request_data_obj=82c7d1f4, BC status=1
> kworker/u64:2-356 [005] ..... 466.890372: ucsi_register_altmode:
> partner alt mode: svid 8087, mode 1 vdo 8087a843
Here the vdo seems to be corrupted in the response to the
GET_ALTERNATE_MODES command.
As you can see, the first 16-bits in the vdo is clearly the TBT svid
again even though the vdo should contain the actual Vendor Defined Object
for the TBT alt mode.
> kworker/u64:2-356 [005] ..... 467.005141: ucsi_register_altmode:
> partner alt mode: svid 8087, mode 2 vdo 1
And here the firmware is returning the TBT SVID again for the second
time, now with vdo that looks almost valid to me. This should not
happen.
The responses to the GET_ALTERNATE_MODES look similar to what we see
on some of the Dell laptops. The response seems almost as if it's
customised some how. It's almost as if the first run of the command
will return some kind of a list of the SVIDs, and the standard
responses start only after the second run of the command.
> > For acpidump you need the acpica-tools installed:
> >
> > acpidump -o my_acpi.dump
>
> The file is quite large, how can I share it ? Or do you need a specific part
> ?
>
> -rw-r--r-- 1 fs fs 4.7M Apr 2 19:05 acpi-port3-20260402.dump
It does not look that big. Compress and send to me (drop the list if
you prefer)?
I still have no idea which system are you running? Can you send the
dmesg output from freshly booted machine?
thanks,
--
heikki
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners
[not found] ` <d627d2fe-415f-4489-b4fa-ec0575a33239@scala.name>
@ 2026-04-08 10:00 ` Heikki Krogerus
0 siblings, 0 replies; 8+ messages in thread
From: Heikki Krogerus @ 2026-04-08 10:00 UTC (permalink / raw)
To: François Scala; +Cc: linux-usb
Hi,
> I'm running Archlinux with kernel 7.0-rc7 with my patch.
>
> Please find attached the dmesg files (with and without altmode).
Thanks. So this is a known Dell specific issue. There is already one
proposal for a workaround:
https://lore.kernel.org/linux-usb/20251224070022.4082182-1-acelan.kao@canonical.com/
Br,
--
heikki
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-04-08 10:01 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-02 12:04 [PATCH] usb: typec: altmode: Fix altmode to handle multiple parners François Scala
2026-04-02 12:18 ` Greg KH
2026-04-02 12:27 ` François Scala
2026-04-02 13:35 ` Greg KH
2026-04-02 13:26 ` Heikki Krogerus
2026-04-02 18:10 ` François Scala
2026-04-07 12:17 ` Heikki Krogerus
[not found] ` <d627d2fe-415f-4489-b4fa-ec0575a33239@scala.name>
2026-04-08 10:00 ` Heikki Krogerus
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox