* [PATCH RFC 0/8] USB Type-C alternate mode selection
@ 2025-12-01 12:25 Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 1/8] usb: typec: Implement " Andrei Kuchynski
` (8 more replies)
0 siblings, 9 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:25 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
This patch series introduces functionality to the USB Type-C Alternate Mode
negotiation process by implementing a priority-based selection mechanism.
Currently, DisplayPort and Thunderbolt drivers initiate a mode entry
separately within their respective probe functions. The Power Delivery
Controller (PDC) retains the ability to activate either USB4 mode or
Alternate Modes based on its internal policy.
The mode selection mechanism disables Alternate Modes to be entered by
their respective drivers and the PDC. Instead, a priority-ordered approach
is used to activate the most desirable mode.
A new `priority` field is added to the `typec_altmode` structure to store
a numerical priority value, with all priorities being unique.
If the port driver supports the mode selection feature, it must set the
`mode_selection` boolean field within the `typec_altmode` structure. This
indicates to the alternate mode drivers that they are not to activate the
altmode separately.
The mode selection process is managed by three API functions:
- `typec_mode_selection_start`
- `typec_altmode_state_update`
- `typec_mode_selection_delete`
When a partner device is connected, the `typec_mode_selection_start`
function executes the following steps:
- It compiles a priority-ordered list of Alternate Modes that are mutually
supported by both the port and the partner.
- A dedicated mode selection task is subsequently initiated on the Work
Queue.
- This task attempts to activate a mode by starting with the
highest-priority altmode on the list. Alternate modes are identified with
their SVIDs. Activation/Deactivation performed via `activate` typec_altmode
operation. The process stops as soon as a mode is successfully entered.
Otherwise, after a timeout or if an error occurs, the next alternative mode
will be activated.
The `typec_altmode_state_update` function is invoked by the port driver to
communicate the current mode of the Type-C connector.
The `typec_mode_selection_delete` function is responsible for stopping the
currently running mode selection process and releasing all associated
system resources.
USB4 activation can be handled in two distinct ways:
- Treated like an Alternate Mode, using associated sysfs attributes -
`activate` port attribute to enable/disable the mode, `activate` partner
attribute to activate/deactivate the mode, `priority` to keep modes
priority.
- Like a separate USB mode representing in sysfs via `usb_capabily` ports
attribute to enable the mode on the port and `usb_mode` partner attribute
to activate the mode. In this scenario, USB4 is the highest-priority mode,
without the need for a separate priority field. It is put on the top of the
preferred list if it is supported by the partner (partner->usb_capability
has USB_CAPABILITY_USB4 bit set) and is supported and enabled on the port
(port->usb_mode is USB_MODE_USB4).
This patch series implements the second approach. It identifies the USB4
mode via its SVID 0xFF00. Instead of using the typec_altmode_ops activate()
function, activation is handled via the typec_operations enter_usb_mode()
function.
Mode selection is initiated only once during partner registration, and only
if the port driver provides support for this feature. Subsequent
mode-switching activities can be managed via existing sysfs entries. Any
modifications to altmode priorities are relevant only to future
connections.
This series was tested on an Android OS device with kernel 6.17, PDC:
TI TPS6699, Realtek RTS5453.
This series depends on the 'USB Type-C alternate mode priorities' series:
https://lore.kernel.org/all/20251124124639.1101335-1-akuchynski@chromium.org/
Andrei Kuchynski (8):
usb: typec: Implement mode selection
usb: typec: Integrate USB4 into the mode selection process
usb: typec: Introduce mode_selection bit
usb: typec: ucsi: Support mode selection to activate altmodes
usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi
usb: typec: ucsi: Implement enter_usb_mode operation
usb: typec: ucsi: Support for Thunderbolt alt mode
platform/chrome: cros_ec_typec: Enforce priority-based mode selection
drivers/platform/chrome/cros_ec_typec.c | 47 ++-
drivers/platform/chrome/cros_typec_altmode.c | 8 +-
drivers/usb/typec/Makefile | 2 +-
drivers/usb/typec/altmodes/displayport.c | 6 +-
drivers/usb/typec/altmodes/thunderbolt.c | 2 +-
drivers/usb/typec/class.c | 1 +
drivers/usb/typec/class.h | 2 +
drivers/usb/typec/mode_selection.c | 308 +++++++++++++++++++
drivers/usb/typec/ucsi/Makefile | 4 +
drivers/usb/typec/ucsi/cros_ec_ucsi.c | 44 +++
drivers/usb/typec/ucsi/thunderbolt.c | 199 ++++++++++++
drivers/usb/typec/ucsi/ucsi.c | 56 +++-
drivers/usb/typec/ucsi/ucsi.h | 27 ++
include/linux/usb/typec.h | 1 +
include/linux/usb/typec_altmode.h | 43 +++
15 files changed, 728 insertions(+), 22 deletions(-)
create mode 100644 drivers/usb/typec/mode_selection.c
create mode 100644 drivers/usb/typec/ucsi/thunderbolt.c
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH RFC 1/8] usb: typec: Implement mode selection
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
@ 2025-12-01 12:25 ` Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 2/8] usb: typec: Integrate USB4 into the mode selection process Andrei Kuchynski
` (7 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:25 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
The mode selection process is controlled by the following API functions,
which allow to initiate and complete mode entry based on the priority of
each mode:
`typec_mode_selection_start` function compiles a priority list of supported
Alternate Modes.
`typec_altmode_state_update` function is invoked by the port driver to
communicate the current mode of the Type-C connector.
`typec_mode_selection_delete` function stops the currently running mode
selection process and releases all associated system resources.
`mode_selection_work_fn` task attempts to activate modes. The process stops
on success; otherwise, it proceeds to the next mode after a timeout or
error.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/Makefile | 2 +-
drivers/usb/typec/class.h | 2 +
drivers/usb/typec/mode_selection.c | 285 +++++++++++++++++++++++++++++
include/linux/usb/typec_altmode.h | 40 ++++
4 files changed, 328 insertions(+), 1 deletion(-)
create mode 100644 drivers/usb/typec/mode_selection.c
diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
index 7a368fea61bc..8a6a1c663eb6 100644
--- a/drivers/usb/typec/Makefile
+++ b/drivers/usb/typec/Makefile
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC) += typec.o
-typec-y := class.o mux.o bus.o pd.o retimer.o
+typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o
typec-$(CONFIG_ACPI) += port-mapper.o
obj-$(CONFIG_TYPEC) += altmodes/
obj-$(CONFIG_TYPEC_TCPM) += tcpm/
diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h
index 2e89a83c2eb7..d3435936ee7c 100644
--- a/drivers/usb/typec/class.h
+++ b/drivers/usb/typec/class.h
@@ -9,6 +9,7 @@
struct typec_mux;
struct typec_switch;
struct usb_device;
+struct mode_selection;
struct typec_plug {
struct device dev;
@@ -39,6 +40,7 @@ struct typec_partner {
u8 usb_capability;
struct usb_power_delivery *pd;
+ struct mode_selection *sel;
void (*attach)(struct typec_partner *partner, struct device *dev);
void (*deattach)(struct typec_partner *partner, struct device *dev);
diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c
new file mode 100644
index 000000000000..1cf8a4dcd742
--- /dev/null
+++ b/drivers/usb/typec/mode_selection.c
@@ -0,0 +1,285 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2025 Google LLC.
+ */
+
+#include <linux/types.h>
+#include <linux/list_sort.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+#include "class.h"
+#include "bus.h"
+
+/**
+ * struct mode_state - State tracking for a specific Type-C alternate mode
+ * @svid: Standard or Vendor ID of the Alternate Mode
+ * @priority: mode priority.
+ * @error: outcome of the last attempt to enter the mode.
+ * @list: list head to link this mode state into a prioritized list
+ */
+struct mode_state {
+ u16 svid;
+ u8 priority;
+ int error;
+ struct list_head list;
+};
+
+/**
+ * struct mode_selection - Manages the selection and state of Alternate Modes
+ * @mode_list: prioritized list of available Alternate Modes
+ * @lock: mutex to protect mode_list
+ * @work: work structure
+ * @partner: handle to the Type-C partner device
+ * @active_svid: svid of currently active mode
+ * @timeout: timeout for a mode entry attempt, ms
+ * @delay: delay between mode entry/exit attempts, ms
+ */
+struct mode_selection {
+ struct list_head mode_list;
+ struct mutex lock;
+ struct delayed_work work;
+ struct typec_partner *partner;
+ u16 active_svid;
+ unsigned int timeout;
+ unsigned int delay;
+};
+
+struct mode_order {
+ u16 svid;
+ int enter;
+ int result;
+};
+
+static int activate_altmode(struct device *dev, void *data)
+{
+ if (is_typec_altmode(dev)) {
+ struct typec_altmode *alt = to_typec_altmode(dev);
+ struct mode_order *order = (struct mode_order *)data;
+
+ if (order->svid == alt->svid) {
+ if (alt->ops && alt->ops->activate)
+ order->result = alt->ops->activate(alt, order->enter);
+ else
+ order->result = -EOPNOTSUPP;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mode_selection_activate(struct mode_selection *sel,
+ const u16 svid, const int enter)
+
+ __must_hold(&sel->lock)
+{
+ struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV};
+
+ /*
+ * The port driver may acquire its internal mutex during alternate mode
+ * activation. Since this is the same mutex that may be held during the
+ * execution of typec_altmode_state_update(), it is crucial to release
+ * sel->mutex before activation to avoid potential deadlock.
+ * Note that sel->mode_list must remain invariant throughout this unlocked
+ * interval.
+ */
+ mutex_unlock(&sel->lock);
+ device_for_each_child(&sel->partner->dev, &order, activate_altmode);
+ mutex_lock(&sel->lock);
+
+ return order.result;
+}
+
+static void mode_list_clean(struct mode_selection *sel)
+{
+ struct mode_state *ms, *tmp;
+
+ list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) {
+ list_del(&ms->list);
+ kfree(ms);
+ }
+}
+
+/**
+ * mode_selection_work_fn() - Alternate mode activation task
+ * @work: work structure
+ *
+ * - If the Alternate Mode currently prioritized at the top of the list is already
+ * active, the entire selection process is considered finished.
+ * - If a different Alternate Mode is currently active, the system must exit that
+ * active mode first before attempting any new entry.
+ *
+ * The function then checks the result of the attempt to entre the current mode,
+ * stored in the `ms->error` field:
+ * - if the attempt FAILED, the mode is deactivated and removed from the list.
+ * - `ms->error` value of 0 signifies that the mode has not yet been activated.
+ * Once successfully activated, the task is scheduled for subsequent entry after
+ * a timeout period. The alternate mode driver is expected to call back with the
+ * actual mode entry result via `typec_altmode_state_update()`.
+ */
+static void mode_selection_work_fn(struct work_struct *work)
+{
+ struct mode_selection *sel = container_of(work,
+ struct mode_selection, work.work);
+ struct mode_state *ms;
+ unsigned int delay = sel->delay;
+ int result;
+
+ mutex_lock(&sel->lock);
+
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (!ms) {
+ mutex_unlock(&sel->lock);
+ return;
+ }
+
+ if (sel->active_svid == ms->svid) {
+ dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid);
+ mode_list_clean(sel);
+ } else if (sel->active_svid != 0) {
+ result = mode_selection_activate(sel, sel->active_svid, 0);
+ if (result) {
+ dev_dbg(&sel->partner->dev, "enable to exit %x altmode\n",
+ sel->active_svid);
+ mode_list_clean(sel);
+ }
+ } else if (ms->error) {
+ dev_dbg(&sel->partner->dev, "%x: entry error %pe\n",
+ ms->svid, ERR_PTR(ms->error));
+ mode_selection_activate(sel, ms->svid, 0);
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ result = mode_selection_activate(sel, ms->svid, 1);
+ if (result) {
+ dev_dbg(&sel->partner->dev, "%x: activation error %pe\n",
+ ms->svid, ERR_PTR(result));
+ list_del(&ms->list);
+ kfree(ms);
+ } else {
+ delay = sel->timeout;
+ ms->error = -ETIMEDOUT;
+ }
+ }
+
+ if (!list_empty(&sel->mode_list))
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+ mutex_unlock(&sel->lock);
+}
+
+void typec_altmode_state_update(struct typec_partner *partner, const u16 svid,
+ const int error)
+{
+ struct mode_selection *sel = partner->sel;
+ struct mode_state *ms;
+
+ if (sel) {
+ mutex_lock(&sel->lock);
+ ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list);
+ if (ms && ms->svid == svid) {
+ ms->error = error;
+ cancel_delayed_work(&sel->work);
+ schedule_delayed_work(&sel->work, 0);
+ }
+ if (!error)
+ sel->active_svid = svid;
+ else
+ sel->active_svid = 0;
+ mutex_unlock(&sel->lock);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_altmode_state_update);
+
+static int compare_priorities(void *priv,
+ const struct list_head *a, const struct list_head *b)
+{
+ const struct mode_state *msa = container_of(a, struct mode_state, list);
+ const struct mode_state *msb = container_of(b, struct mode_state, list);
+
+ if (msa->priority < msb->priority)
+ return -1;
+ return 1;
+}
+
+static struct mode_state *create_mode_entry(u16 svid, u8 priority)
+{
+ struct mode_state *ms = kzalloc(sizeof(struct mode_state), GFP_KERNEL);
+
+ if (ms) {
+ ms->svid = svid;
+ ms->priority = priority;
+ INIT_LIST_HEAD(&ms->list);
+ }
+
+ return ms;
+}
+
+static int altmode_add_to_list(struct device *dev, void *data)
+{
+ if (is_typec_altmode(dev)) {
+ struct list_head *list = (struct list_head *)data;
+ struct typec_altmode *altmode = to_typec_altmode(dev);
+ const struct typec_altmode *pdev = typec_altmode_get_partner(altmode);
+ struct mode_state *ms;
+
+ if (pdev && altmode->ops && altmode->ops->activate) {
+ ms = create_mode_entry(pdev->svid, pdev->priority);
+ if (!ms)
+ return -ENOMEM;
+ list_add_tail(&ms->list, list);
+ }
+ }
+ return 0;
+}
+
+int typec_mode_selection_start(struct typec_partner *partner,
+ const unsigned int delay, const unsigned int timeout)
+{
+ struct mode_selection *sel;
+ int ret;
+
+ if (partner->sel)
+ return -EALREADY;
+
+ sel = kzalloc(sizeof(struct mode_selection), GFP_KERNEL);
+ if (!sel)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&sel->mode_list);
+
+ ret = device_for_each_child(
+ &partner->dev, &sel->mode_list, altmode_add_to_list);
+
+ if (ret || list_empty(&sel->mode_list)) {
+ mode_list_clean(sel);
+ kfree(sel);
+ return ret;
+ }
+
+ list_sort(NULL, &sel->mode_list, compare_priorities);
+ sel->partner = partner;
+ sel->delay = delay;
+ sel->timeout = timeout;
+ mutex_init(&sel->lock);
+ partner->sel = sel;
+ INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn);
+ schedule_delayed_work(&sel->work, msecs_to_jiffies(delay));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_start);
+
+void typec_mode_selection_delete(struct typec_partner *partner)
+{
+ struct mode_selection *sel = partner->sel;
+
+ if (sel) {
+ partner->sel = NULL;
+ cancel_delayed_work_sync(&sel->work);
+ mode_list_clean(sel);
+ mutex_destroy(&sel->lock);
+ kfree(sel);
+ }
+}
+EXPORT_SYMBOL_GPL(typec_mode_selection_delete);
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
index 2c3b6bec2eca..a240d8264b92 100644
--- a/include/linux/usb/typec_altmode.h
+++ b/include/linux/usb/typec_altmode.h
@@ -232,4 +232,44 @@ void typec_altmode_unregister_driver(struct typec_altmode_driver *drv);
module_driver(__typec_altmode_driver, typec_altmode_register_driver, \
typec_altmode_unregister_driver)
+/**
+ * typec_mode_selection_start - Start an alternate mode selection process
+ * @partner: Handle to the Type-C partner device
+ * @delay: Delay between mode entry/exit attempts, ms
+ * @timeout: Timeout for a mode entry attempt, ms
+ *
+ * This function initiates the process of attempting to enter an Alternate Mode
+ * supported by the connected Type-C partner.
+ * Returns 0 on success, or a negative error code on failure.
+ */
+int typec_mode_selection_start(struct typec_partner *partner,
+ const unsigned int delay, const unsigned int timeout);
+
+/**
+ * typec_altmode_state_update - Report the current status of an Alternate Mode
+ * negotiation
+ * @partner: Handle to the Type-C partner device
+ * @svid: Standard or Vendor ID of the Alternate Mode. A value of 0 should be
+ * passed if no mode is currently active
+ * @result: Result of the entry operation. This should be 0 on success, or a
+ * negative error code if the negotiation failed
+ *
+ * This function should be called by an Alternate Mode driver to report the
+ * result of an asynchronous alternate mode entry request. It signals what the
+ * current active SVID is (or 0 if none) and the success or failure status of
+ * the last attempt.
+ */
+void typec_altmode_state_update(struct typec_partner *partner, const u16 svid,
+ const int result);
+
+/**
+ * typec_mode_selection_delete - Delete an alternate mode selection instance
+ * @partner: Handle to the Type-C partner device.
+ *
+ * This function cancels a pending alternate mode selection request that was
+ * previously started with typec_mode_selection_start().
+ * This is typically called when the partner disconnects.
+ */
+void typec_mode_selection_delete(struct typec_partner *partner);
+
#endif /* __USB_TYPEC_ALTMODE_H */
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 2/8] usb: typec: Integrate USB4 into the mode selection process
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 1/8] usb: typec: Implement " Andrei Kuchynski
@ 2025-12-01 12:25 ` Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 3/8] usb: typec: Introduce mode_selection bit Andrei Kuchynski
` (6 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:25 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
USB4 is defined as the most preferred mode and is placed at the top of the
mode priority list. This ensures that if the port and partner support USB4,
activation is attempted first.
Activation is handled via the `enter_usb_mode` function. System control
(enabling/disabling) over the mode is exposed through the `usb_capability`
sysfs port attribute.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/mode_selection.c | 27 +++++++++++++++++++++++++--
include/linux/usb/typec_altmode.h | 2 ++
2 files changed, 27 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c
index 1cf8a4dcd742..957e09813831 100644
--- a/drivers/usb/typec/mode_selection.c
+++ b/drivers/usb/typec/mode_selection.c
@@ -74,6 +74,7 @@ static int mode_selection_activate(struct mode_selection *sel,
__must_hold(&sel->lock)
{
+ struct typec_port *port = to_typec_port(sel->partner->dev.parent);
struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV};
/*
@@ -85,7 +86,14 @@ static int mode_selection_activate(struct mode_selection *sel,
* interval.
*/
mutex_unlock(&sel->lock);
- device_for_each_child(&sel->partner->dev, &order, activate_altmode);
+ if (svid == USB_TYPEC_USB4_SID) {
+ if (port->ops && port->ops->enter_usb_mode)
+ order.result = port->ops->enter_usb_mode(port,
+ enter ? USB_MODE_USB4 : USB_MODE_USB3);
+ else
+ order.result = -EOPNOTSUPP;
+ } else
+ device_for_each_child(&sel->partner->dev, &order, activate_altmode);
mutex_lock(&sel->lock);
return order.result;
@@ -236,7 +244,9 @@ static int altmode_add_to_list(struct device *dev, void *data)
int typec_mode_selection_start(struct typec_partner *partner,
const unsigned int delay, const unsigned int timeout)
{
+ struct typec_port *port = to_typec_port(partner->dev.parent);
struct mode_selection *sel;
+ struct mode_state *ms;
int ret;
if (partner->sel)
@@ -251,13 +261,26 @@ int typec_mode_selection_start(struct typec_partner *partner,
ret = device_for_each_child(
&partner->dev, &sel->mode_list, altmode_add_to_list);
+ if (!ret) {
+ list_sort(NULL, &sel->mode_list, compare_priorities);
+
+ if (port->usb_mode == USB_MODE_USB4 &&
+ partner->usb_capability & USB_CAPABILITY_USB4 &&
+ port->ops && port->ops->enter_usb_mode) {
+ ms = create_mode_entry(USB_TYPEC_USB4_SID, 0);
+ if (!ms)
+ ret = -ENOMEM;
+ else
+ list_add(&ms->list, &sel->mode_list);
+ }
+ }
+
if (ret || list_empty(&sel->mode_list)) {
mode_list_clean(sel);
kfree(sel);
return ret;
}
- list_sort(NULL, &sel->mode_list, compare_priorities);
sel->partner = partner;
sel->delay = delay;
sel->timeout = timeout;
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
index a240d8264b92..faf72e4d6ceb 100644
--- a/include/linux/usb/typec_altmode.h
+++ b/include/linux/usb/typec_altmode.h
@@ -9,6 +9,8 @@
#define MODE_DISCOVERY_MAX 6
+#define USB_TYPEC_USB4_SID 0xFF00
+
struct typec_altmode_ops;
/**
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 3/8] usb: typec: Introduce mode_selection bit
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 1/8] usb: typec: Implement " Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 2/8] usb: typec: Integrate USB4 into the mode selection process Andrei Kuchynski
@ 2025-12-01 12:25 ` Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 4/8] usb: typec: ucsi: Support mode selection to activate altmodes Andrei Kuchynski
` (5 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:25 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
The port driver sets this bit for an alternate mode description to indicate
support for the mode selection feature. Once set, individual Alt Mode
drivers will no longer attempt to activate their respective modes within
their probe functions. This prevents race conditions and non-prioritized
activation.
The bit is not set by default. If left unset, the system retains the
current behavior where Alt Mode drivers manage their own activation logic.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/altmodes/displayport.c | 6 ++++--
drivers/usb/typec/altmodes/thunderbolt.c | 2 +-
drivers/usb/typec/class.c | 1 +
include/linux/usb/typec.h | 1 +
include/linux/usb/typec_altmode.h | 1 +
5 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
index 8d111ad3b71b..49d5a123e4c4 100644
--- a/drivers/usb/typec/altmodes/displayport.c
+++ b/drivers/usb/typec/altmodes/displayport.c
@@ -800,8 +800,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
if (plug)
typec_altmode_set_drvdata(plug, dp);
- dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
- schedule_work(&dp->work);
+ if (!alt->mode_selection) {
+ dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER;
+ schedule_work(&dp->work);
+ }
return 0;
}
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
index 6eadf7835f8f..c4c5da6154da 100644
--- a/drivers/usb/typec/altmodes/thunderbolt.c
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -307,7 +307,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt)
typec_altmode_set_drvdata(alt, tbt);
typec_altmode_set_ops(alt, &tbt_altmode_ops);
- if (tbt_ready(alt)) {
+ if (!alt->mode_selection && tbt_ready(alt)) {
if (tbt->plug[TYPEC_PLUG_SOP_P])
tbt->state = TBT_STATE_SOP_P_ENTER;
else if (tbt->plug[TYPEC_PLUG_SOP_PP])
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index 049d1829be98..cffe3c7d1671 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -639,6 +639,7 @@ typec_register_altmode(struct device *parent,
alt->adev.svid = desc->svid;
alt->adev.mode = desc->mode;
alt->adev.vdo = desc->vdo;
+ alt->adev.mode_selection = desc->mode_selection;
alt->roles = desc->roles;
alt->id = id;
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index 59d5fd7e4ff4..7a9dec9b0775 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -152,6 +152,7 @@ struct typec_altmode_desc {
/* Only used with ports */
enum typec_port_data roles;
bool inactive;
+ bool mode_selection;
};
void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision);
diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
index faf72e4d6ceb..c72065fddb7b 100644
--- a/include/linux/usb/typec_altmode.h
+++ b/include/linux/usb/typec_altmode.h
@@ -31,6 +31,7 @@ struct typec_altmode {
u32 vdo;
unsigned int active:1;
u8 priority;
+ bool mode_selection;
char *desc;
const struct typec_altmode_ops *ops;
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 4/8] usb: typec: ucsi: Support mode selection to activate altmodes
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (2 preceding siblings ...)
2025-12-01 12:25 ` [PATCH RFC 3/8] usb: typec: Introduce mode_selection bit Andrei Kuchynski
@ 2025-12-01 12:26 ` Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 5/8] usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi Andrei Kuchynski
` (4 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:26 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
If the ucsi port driver supports modes selection, it should implement
`add_partner_altmodes` and `remove_partner_altmodes` ucsi operations. With
these operations the driver can manage the mode selection process.
Once partner altmodes are registered, `add_partner_altmodes` is called to
start the mode selection. When the partner is unregistered,
`remove_partner_altmodes` is supposed to stop any ongoing processes and
clean up the resources.
`typec_altmode_state_update` informes mode selection about the current mode
of the Type-C connector.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/ucsi/ucsi.c | 17 +++++++++++++++++
drivers/usb/typec/ucsi/ucsi.h | 4 ++++
2 files changed, 21 insertions(+)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 82c3efd72639..ee96c42e9e27 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -331,6 +331,7 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
{
const struct typec_altmode *altmode = NULL;
u64 command;
+ u16 svid = 0;
int ret;
u8 cur;
int i;
@@ -355,6 +356,15 @@ void ucsi_altmode_update_active(struct ucsi_connector *con)
for (i = 0; con->partner_altmode[i]; i++)
typec_altmode_update_active(con->partner_altmode[i],
con->partner_altmode[i] == altmode);
+
+ if ((con->ucsi->version >= UCSI_VERSION_3_0 &&
+ UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN4)) ||
+ (con->ucsi->version >= UCSI_VERSION_2_0 &&
+ UCSI_CONSTAT(con, PARTNER_FLAG_USB4_GEN3)))
+ svid = USB_TYPEC_USB4_SID;
+ else if (altmode)
+ svid = altmode->svid;
+ typec_altmode_state_update(con->partner, svid, 0);
}
static int ucsi_altmode_next_mode(struct typec_altmode **alt, u16 svid)
@@ -635,6 +645,8 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
desc.vdo = alt[j].mid;
desc.svid = alt[j].svid;
desc.roles = TYPEC_PORT_DRD;
+ desc.mode_selection = con->ucsi->ops->add_partner_altmodes &&
+ con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE;
ret = ucsi_register_altmode(con, &desc, recipient);
if (ret)
@@ -858,6 +870,9 @@ static int ucsi_check_altmodes(struct ucsi_connector *con)
"con%d: failed to register partner alt modes (%d)\n",
con->num, ret);
+ if (con->ucsi->ops->add_partner_altmodes)
+ con->ucsi->ops->add_partner_altmodes(con);
+
/* Ignoring the errors in this case. */
if (con->partner_altmode[0]) {
num_partner_am = ucsi_get_num_altmode(con->partner_altmode);
@@ -1154,6 +1169,8 @@ static void ucsi_unregister_partner(struct ucsi_connector *con)
return;
typec_set_mode(con->port, TYPEC_STATE_SAFE);
+ if (con->ucsi->ops->remove_partner_altmodes)
+ con->ucsi->ops->remove_partner_altmodes(con);
typec_partner_set_usb_power_delivery(con->partner, NULL);
ucsi_unregister_partner_pdos(con);
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index f946b728c373..6e77bdbdaeae 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -75,6 +75,8 @@ struct dentry;
* @update_altmodes: Squashes duplicate DP altmodes
* @update_connector: Update connector capabilities before registering
* @connector_status: Updates connector status, called holding connector lock
+ * @add_partner_altmodes: Start mode selection
+ * @remove_partner_altmodes: Clean mode selection
*
* Read and write routines for UCSI interface. @sync_write must wait for the
* Command Completion Event from the PPM before returning, and @async_write must
@@ -93,6 +95,8 @@ struct ucsi_operations {
struct ucsi_altmode *updated);
void (*update_connector)(struct ucsi_connector *con);
void (*connector_status)(struct ucsi_connector *con);
+ void (*add_partner_altmodes)(struct ucsi_connector *con);
+ void (*remove_partner_altmodes)(struct ucsi_connector *con);
};
struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops);
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 5/8] usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (3 preceding siblings ...)
2025-12-01 12:26 ` [PATCH RFC 4/8] usb: typec: ucsi: Support mode selection to activate altmodes Andrei Kuchynski
@ 2025-12-01 12:26 ` Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 6/8] usb: typec: ucsi: Implement enter_usb_mode operation Andrei Kuchynski
` (3 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:26 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
The mode selection sequence is initiated by the driver after all partner
alternate modes have been successfully registered.
To prevent the Power Delivery Controller (PDC) from activating alternate
modes, the driver disables all alternate modes on the connector:
- During the connector registration
- Upon partner disconnection
When a partner is disconnected, the driver also stops the mode selection
process and releases resources via `typec_mode_selection_delete`.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/ucsi/cros_ec_ucsi.c | 44 +++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/drivers/usb/typec/ucsi/cros_ec_ucsi.c b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
index d753f2188e25..988a159ed778 100644
--- a/drivers/usb/typec/ucsi/cros_ec_ucsi.c
+++ b/drivers/usb/typec/ucsi/cros_ec_ucsi.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/wait.h>
+#include <linux/usb/typec_altmode.h>
#include "ucsi.h"
@@ -33,6 +34,11 @@
/* Number of times to attempt recovery from a write timeout before giving up. */
#define WRITE_TMO_CTR_MAX 5
+/* Delay between mode entry/exit attempts, ms */
+static const unsigned int mode_selection_delay = 1000;
+/* Timeout for a mode entry attempt, ms */
+static const unsigned int mode_selection_timeout = 4000;
+
struct cros_ucsi_data {
struct device *dev;
struct ucsi *ucsi;
@@ -133,6 +139,41 @@ static int cros_ucsi_sync_control(struct ucsi *ucsi, u64 cmd, u32 *cci)
return ret;
}
+static void cros_ucsi_disable_altmodes(struct ucsi_connector *con)
+{
+ struct cros_ucsi_data *udata = ucsi_get_drvdata(con->ucsi);
+ u64 command = UCSI_SET_NEW_CAM | UCSI_CONNECTOR_NUMBER(con->num) |
+ UCSI_SET_NEW_CAM_SET_AM((u64)0xFF);
+ int ret;
+
+ con->ucsi->message_in_size = 0;
+ ret = ucsi_send_command(con->ucsi, command);
+ if (ret < 0)
+ dev_err(udata->dev,
+ "Unable to disable alt-modes on port %d\n", con->num);
+}
+
+static void cros_ucsi_update_connector(struct ucsi_connector *con)
+{
+ if (con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE)
+ cros_ucsi_disable_altmodes(con);
+}
+
+static void cros_ucsi_add_partner_altmodes(struct ucsi_connector *con)
+{
+ if (con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE)
+ typec_mode_selection_start(con->partner,
+ mode_selection_delay, mode_selection_timeout);
+}
+
+static void cros_ucsi_remove_partner_altmodes(struct ucsi_connector *con)
+{
+ if (con->ucsi->cap.features & UCSI_CAP_ALT_MODE_OVERRIDE) {
+ typec_mode_selection_delete(con->partner);
+ cros_ucsi_disable_altmodes(con);
+ }
+}
+
static const struct ucsi_operations cros_ucsi_ops = {
.read_version = cros_ucsi_read_version,
.read_cci = cros_ucsi_read_cci,
@@ -140,6 +181,9 @@ static const struct ucsi_operations cros_ucsi_ops = {
.read_message_in = cros_ucsi_read_message_in,
.async_control = cros_ucsi_async_control,
.sync_control = cros_ucsi_sync_control,
+ .update_connector = cros_ucsi_update_connector,
+ .add_partner_altmodes = cros_ucsi_add_partner_altmodes,
+ .remove_partner_altmodes = cros_ucsi_remove_partner_altmodes,
};
static void cros_ucsi_work(struct work_struct *work)
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 6/8] usb: typec: ucsi: Implement enter_usb_mode operation
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (4 preceding siblings ...)
2025-12-01 12:26 ` [PATCH RFC 5/8] usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi Andrei Kuchynski
@ 2025-12-01 12:26 ` Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 7/8] usb: typec: ucsi: Support for Thunderbolt alt mode Andrei Kuchynski
` (2 subsequent siblings)
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:26 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
`enter_usb_mode` uses the SET_USB UCSI command to manage USB modes such as
USB3, and USB4 for current and future connections.
It allows port drivers and user-space applications (via the "usb_mode"
partner attribute) to enable and disable USB modes on a connector.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/ucsi/ucsi.c | 22 ++++++++++++++++++++++
drivers/usb/typec/ucsi/ucsi.h | 3 +++
2 files changed, 25 insertions(+)
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index ee96c42e9e27..3d4c277bcd49 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1624,7 +1624,29 @@ static int ucsi_pr_swap(struct typec_port *port, enum typec_role role)
return ret;
}
+static int ucsi_enter_usb_mode(struct typec_port *port, enum usb_mode mode)
+{
+ struct ucsi_connector *con = typec_get_drvdata(port);
+ u64 command;
+ int ret;
+
+ command = UCSI_SET_USB | UCSI_CONNECTOR_NUMBER(con->num);
+ if (mode == USB_MODE_USB3)
+ command |= UCSI_USB3_ENABLE;
+ else if (mode == USB_MODE_USB4)
+ command |= UCSI_USB4_ENABLE;
+
+ if (!ucsi_con_mutex_lock(con))
+ return -ENOTCONN;
+ con->ucsi->message_in_size = 0;
+ ret = ucsi_send_command(con->ucsi, command);
+ ucsi_con_mutex_unlock(con);
+
+ return ret;
+}
+
static const struct typec_operations ucsi_ops = {
+ .enter_usb_mode = ucsi_enter_usb_mode,
.dr_set = ucsi_dr_swap,
.pr_set = ucsi_pr_swap
};
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index 6e77bdbdaeae..a5ef35d9dce5 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -219,6 +219,9 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_GET_PD_MESSAGE_TYPE_IDENTITY 4
#define UCSI_GET_PD_MESSAGE_TYPE_REVISION 5
+/* SET_USB command bits */
+#define UCSI_USB3_ENABLE ((u64)1 << 23)
+#define UCSI_USB4_ENABLE ((u64)1 << 24)
/* -------------------------------------------------------------------------- */
/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 7/8] usb: typec: ucsi: Support for Thunderbolt alt mode
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (5 preceding siblings ...)
2025-12-01 12:26 ` [PATCH RFC 6/8] usb: typec: ucsi: Implement enter_usb_mode operation Andrei Kuchynski
@ 2025-12-01 12:26 ` Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 8/8] platform/chrome: cros_ec_typec: Enforce priority-based mode selection Andrei Kuchynski
2025-12-11 13:40 ` [PATCH RFC 0/8] USB Type-C alternate " Heikki Krogerus
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:26 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
This makes it possible to bind a driver to a Thunderbolt
alt mode adapter devices.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/usb/typec/ucsi/Makefile | 4 +
drivers/usb/typec/ucsi/thunderbolt.c | 199 +++++++++++++++++++++++++++
drivers/usb/typec/ucsi/ucsi.c | 17 ++-
drivers/usb/typec/ucsi/ucsi.h | 20 +++
4 files changed, 235 insertions(+), 5 deletions(-)
create mode 100644 drivers/usb/typec/ucsi/thunderbolt.c
diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile
index dbc571763eff..c7e38bf01350 100644
--- a/drivers/usb/typec/ucsi/Makefile
+++ b/drivers/usb/typec/ucsi/Makefile
@@ -17,6 +17,10 @@ ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
typec_ucsi-y += displayport.o
endif
+ifneq ($(CONFIG_TYPEC_TBT_ALTMODE),)
+ typec_ucsi-y += thunderbolt.o
+endif
+
obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o
obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o
obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o
diff --git a/drivers/usb/typec/ucsi/thunderbolt.c b/drivers/usb/typec/ucsi/thunderbolt.c
new file mode 100644
index 000000000000..b48aba30fb9f
--- /dev/null
+++ b/drivers/usb/typec/ucsi/thunderbolt.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * UCSI Thunderbolt Alternate Mode Support
+ *
+ * Copyright 2025 Google LLC
+ */
+
+#include <linux/usb/typec_tbt.h>
+#include <linux/usb/pd_vdo.h>
+
+#include "ucsi.h"
+
+struct ucsi_tbt {
+ struct ucsi_connector *con;
+ struct typec_altmode *alt;
+ struct work_struct work;
+
+ int cam;
+ u32 header;
+};
+
+static int ucsi_thunderbolt_send_set_new_cam(struct ucsi_tbt *tbt,
+ const int enter)
+{
+ int ret;
+ const u64 command = UCSI_SET_NEW_CAM |
+ UCSI_CONNECTOR_NUMBER(tbt->con->num) |
+ UCSI_SET_NEW_CAM_SET_ENTER(enter) |
+ UCSI_SET_NEW_CAM_SET_AM(tbt->cam);
+
+ tbt->con->ucsi->message_in_size = 0;
+ ret = ucsi_send_command(tbt->con->ucsi, command);
+
+ return ret < 0 ? ret : 0;
+}
+
+static void ucsi_thunderbolt_work(struct work_struct *work)
+{
+ struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work);
+ const int ret = typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0);
+
+ if (ret)
+ dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header);
+}
+
+static int ucsi_thunderbolt_send_vdm(struct ucsi_tbt *tbt,
+ const int cmd, const int cmdt,
+ const int svdm_version)
+{
+ if (svdm_version < 0)
+ return svdm_version;
+
+ tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
+ tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
+ tbt->header |= VDO_CMDT(cmdt);
+
+ schedule_work(&tbt->work);
+
+ return 0;
+}
+
+static int ucsi_thunderbolt_enter(struct typec_altmode *alt, u32 *vdo)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ struct ucsi *ucsi = tbt->con->ucsi;
+ int svdm_version;
+ u64 command;
+ u8 cur = 0;
+ int ret = 0;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(tbt->con->num);
+ ucsi->message_in_size = 0;
+ ret = ucsi_send_command(ucsi, command);
+ if (ret < 0) {
+ if (tbt->con->ucsi->version > 0x0100)
+ goto err_unlock;
+ cur = 0xff;
+ } else {
+ memcpy(&cur, ucsi->message_in, ucsi->message_in_size);
+ }
+
+ if (cur != 0xff) {
+ ret = tbt->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ } else {
+ ret = ucsi_thunderbolt_send_set_new_cam(tbt, 1);
+ if (!ret) {
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ ret = ucsi_thunderbolt_send_vdm(tbt, CMD_ENTER_MODE, CMDT_RSP_ACK,
+ svdm_version);
+ }
+ }
+
+err_unlock:
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_exit(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ int svdm_version;
+ int ret;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ ret = ucsi_thunderbolt_send_set_new_cam(tbt, 0);
+ if (!ret) {
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ ret = ucsi_thunderbolt_send_vdm(tbt, CMD_EXIT_MODE, CMDT_RSP_ACK,
+ svdm_version);
+ }
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return ret;
+}
+
+static int ucsi_thunderbolt_vdm(struct typec_altmode *alt,
+ u32 header, const u32 *data, int count)
+{
+ struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
+ const int cmd_type = PD_VDO_CMDT(header);
+ const int cmd = PD_VDO_CMD(header);
+ int svdm_version;
+
+ if (!ucsi_con_mutex_lock(tbt->con))
+ return -ENOTCONN;
+
+ svdm_version = typec_altmode_get_svdm_version(alt);
+ if (svdm_version < 0) {
+ ucsi_con_mutex_unlock(tbt->con);
+ return svdm_version;
+ }
+
+ switch (cmd_type) {
+ case CMDT_INIT:
+ if (PD_VDO_SVDM_VER(header) < svdm_version) {
+ svdm_version = PD_VDO_SVDM_VER(header);
+ typec_partner_set_svdm_version(tbt->con->partner, svdm_version);
+ }
+ ucsi_thunderbolt_send_vdm(tbt, cmd, CMDT_RSP_ACK, svdm_version);
+ break;
+ default:
+ break;
+ }
+
+ ucsi_con_mutex_unlock(tbt->con);
+
+ return 0;
+}
+
+static const struct typec_altmode_ops ucsi_thunderbolt_ops = {
+ .enter = ucsi_thunderbolt_enter,
+ .exit = ucsi_thunderbolt_exit,
+ .vdm = ucsi_thunderbolt_vdm,
+};
+
+struct typec_altmode *ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ struct typec_altmode *alt;
+ struct ucsi_tbt *tbt;
+
+ alt = typec_port_register_altmode(con->port, desc);
+ if (IS_ERR(alt) || !override)
+ return alt;
+
+ tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
+ if (!tbt) {
+ typec_unregister_altmode(alt);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ tbt->cam = offset;
+ tbt->con = con;
+ tbt->alt = alt;
+ typec_altmode_set_drvdata(alt, tbt);
+ typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops);
+ INIT_WORK(&tbt->work, ucsi_thunderbolt_work);
+
+ return alt;
+}
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *alt)
+{
+ struct ucsi_tbt *tbt;
+
+ if (alt) {
+ tbt = typec_altmode_get_drvdata(alt);
+ if (tbt)
+ cancel_work_sync(&tbt->work);
+ }
+}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 3d4c277bcd49..d2b00b3a8fd1 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
#include "ucsi.h"
#include "trace.h"
@@ -442,6 +443,9 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
alt = ucsi_register_displayport(con, override,
i, desc);
break;
+ case USB_TYPEC_TBT_SID:
+ alt = ucsi_register_thunderbolt(con, override, i, desc);
+ break;
default:
alt = typec_port_register_altmode(con->port, desc);
break;
@@ -678,12 +682,15 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
}
while (adev[i]) {
- if (recipient == UCSI_RECIPIENT_SOP &&
- (adev[i]->svid == USB_TYPEC_DP_SID ||
- (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
- adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
+ if (recipient == UCSI_RECIPIENT_SOP) {
pdev = typec_altmode_get_partner(adev[i]);
- ucsi_displayport_remove_partner((void *)pdev);
+
+ if (adev[i]->svid == USB_TYPEC_DP_SID ||
+ (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
+ adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))
+ ucsi_displayport_remove_partner((void *)pdev);
+ else if (adev[i]->svid == USB_TYPEC_TBT_SID)
+ ucsi_thunderbolt_remove_partner((void *)pdev);
}
typec_unregister_altmode(adev[i]);
adev[i++] = NULL;
diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
index a5ef35d9dce5..b405cce554af 100644
--- a/drivers/usb/typec/ucsi/ucsi.h
+++ b/drivers/usb/typec/ucsi/ucsi.h
@@ -242,6 +242,7 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
#define UCSI_ERROR_SET_SINK_PATH_REJECTED BIT(14)
#define UCSI_SET_NEW_CAM_ENTER(x) (((x) >> 23) & 0x1)
+#define UCSI_SET_NEW_CAM_SET_ENTER(x) (((x) & 1) << 23)
#define UCSI_SET_NEW_CAM_GET_AM(x) (((x) >> 24) & 0xff)
#define UCSI_SET_NEW_CAM_AM_MASK (0xff << 24)
#define UCSI_SET_NEW_CAM_SET_AM(x) (((x) & 0xff) << 24)
@@ -613,6 +614,25 @@ static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */
+#if IS_ENABLED(CONFIG_TYPEC_TBT_ALTMODE)
+struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc);
+
+void ucsi_thunderbolt_remove_partner(struct typec_altmode *adev);
+#else
+static inline struct typec_altmode *
+ucsi_register_thunderbolt(struct ucsi_connector *con,
+ bool override, int offset,
+ struct typec_altmode_desc *desc)
+{
+ return typec_port_register_altmode(con->port, desc);
+}
+static inline void
+ucsi_thunderbolt_remove_partner(struct typec_altmode *adev) { }
+#endif /* CONFIG_TYPEC_TBT_ALTMODE */
+
#ifdef CONFIG_DEBUG_FS
void ucsi_debugfs_init(void);
void ucsi_debugfs_exit(void);
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH RFC 8/8] platform/chrome: cros_ec_typec: Enforce priority-based mode selection
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (6 preceding siblings ...)
2025-12-01 12:26 ` [PATCH RFC 7/8] usb: typec: ucsi: Support for Thunderbolt alt mode Andrei Kuchynski
@ 2025-12-01 12:26 ` Andrei Kuchynski
2025-12-11 13:40 ` [PATCH RFC 0/8] USB Type-C alternate " Heikki Krogerus
8 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-01 12:26 UTC (permalink / raw)
To: Heikki Krogerus, Abhishek Pandit-Subedi, Benson Leung,
Jameson Thies, Tzung-Bi Shih, linux-usb, chrome-platform
Cc: Guenter Roeck, Greg Kroah-Hartman, Dmitry Baryshkov,
Christian A. Ehrhardt, Abel Vesa, Pooja Katiyar, Pavan Holla,
Madhu M, Venkat Jayaraman, linux-kernel, Andrei Kuchynski
The driver sets mode_selection bit for each Alternate mode, thereby
preventing individual altmode drivers from activating their respective
modes. Once the registration of all Alternate Modes is complete, the driver
invokes typec_mode_selection_start to initiate the mode selection process
based on mode priorities.
The driver communicates the current Type-C mode to the mode selection
process via typec_altmode_state_update.
Signed-off-by: Andrei Kuchynski <akuchynski@chromium.org>
---
drivers/platform/chrome/cros_ec_typec.c | 47 +++++++++++++++-----
drivers/platform/chrome/cros_typec_altmode.c | 8 +++-
2 files changed, 42 insertions(+), 13 deletions(-)
diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c
index c0806c562bb9..cd827b1822e2 100644
--- a/drivers/platform/chrome/cros_ec_typec.c
+++ b/drivers/platform/chrome/cros_ec_typec.c
@@ -27,6 +27,11 @@
BIT(DP_PIN_ASSIGN_D) | \
BIT(DP_PIN_ASSIGN_E)))
+/* Delay between mode entry/exit attempts, ms */
+static const unsigned int mode_selection_delay = 1000;
+/* Timeout for a mode entry attempt, ms */
+static const unsigned int mode_selection_timeout = 4000;
+
static void cros_typec_role_switch_quirk(struct fwnode_handle *fwnode)
{
#ifdef CONFIG_ACPI
@@ -325,6 +330,7 @@ static void cros_typec_remove_partner(struct cros_typec_data *typec,
if (!port->partner)
return;
+ typec_mode_selection_delete(port->partner);
cros_typec_unregister_altmodes(typec, port_num, true);
typec_partner_set_usb_power_delivery(port->partner, NULL);
@@ -400,17 +406,6 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
struct typec_altmode_desc desc;
struct typec_altmode *amode;
- /* All PD capable CrOS devices are assumed to support DP altmode. */
- memset(&desc, 0, sizeof(desc));
- desc.svid = USB_TYPEC_DP_SID;
- desc.mode = USB_TYPEC_DP_MODE;
- desc.vdo = DP_PORT_VDO;
- amode = cros_typec_register_displayport(port, &desc,
- typec->ap_driven_altmode);
- if (IS_ERR(amode))
- return PTR_ERR(amode);
- port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
-
/*
* Register TBT compatibility alt mode. The EC will not enter the mode
* if it doesn't support it and it will not enter automatically by
@@ -428,6 +423,17 @@ static int cros_typec_register_port_altmodes(struct cros_typec_data *typec,
port->port_altmode[CROS_EC_ALTMODE_TBT] = amode;
}
+ /* All PD capable CrOS devices are assumed to support DP altmode. */
+ memset(&desc, 0, sizeof(desc));
+ desc.svid = USB_TYPEC_DP_SID;
+ desc.mode = USB_TYPEC_DP_MODE;
+ desc.vdo = DP_PORT_VDO;
+ amode = cros_typec_register_displayport(port, &desc,
+ typec->ap_driven_altmode);
+ if (IS_ERR(amode))
+ return PTR_ERR(amode);
+ port->port_altmode[CROS_EC_ALTMODE_DP] = amode;
+
port->state.alt = NULL;
port->state.mode = TYPEC_STATE_USB;
port->state.data = NULL;
@@ -742,6 +748,7 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
enum typec_orientation orientation;
struct cros_typec_altmode_node *node;
int ret;
+ u16 active_svid = 0;
ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO,
&req, sizeof(req), &resp, sizeof(resp));
@@ -780,10 +787,13 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
if (port->mux_flags & USB_PD_MUX_USB4_ENABLED) {
ret = cros_typec_enable_usb4(typec, port_num, pd_ctrl);
+ active_svid = USB_TYPEC_USB4_SID;
} else if (port->mux_flags & USB_PD_MUX_TBT_COMPAT_ENABLED) {
ret = cros_typec_enable_tbt(typec, port_num, pd_ctrl);
+ active_svid = USB_TYPEC_TBT_SID;
} else if (port->mux_flags & USB_PD_MUX_DP_ENABLED) {
ret = cros_typec_enable_dp(typec, port_num, pd_ctrl);
+ active_svid = USB_TYPEC_DP_SID;
} else if (port->mux_flags & USB_PD_MUX_SAFE_MODE) {
ret = cros_typec_usb_safe_state(port);
} else if (port->mux_flags & USB_PD_MUX_USB_ENABLED) {
@@ -799,6 +809,9 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num,
port->mux_flags);
}
+ if (port->partner)
+ typec_altmode_state_update(port->partner, active_svid, ret);
+
/* Iterate all partner alt-modes and set the active alternate mode. */
list_for_each_entry(node, &port->partner_mode_list, list) {
typec_altmode_update_active(
@@ -899,6 +912,7 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_
memset(&desc, 0, sizeof(desc));
desc.svid = sop_disc->svids[i].svid;
desc.mode = j + 1;
+ desc.mode_selection = typec->ap_driven_altmode;
desc.vdo = sop_disc->svids[i].mode_vdo[j];
if (is_partner)
@@ -940,6 +954,17 @@ static int cros_typec_register_altmodes(struct cros_typec_data *typec, int port_
goto err_cleanup;
}
+ /* Once all partner alt-modes are added, we should also trigger
+ * mode selection.
+ */
+ if (is_partner && typec->ap_driven_altmode) {
+ ret = typec_mode_selection_start(port->partner,
+ mode_selection_delay, mode_selection_timeout);
+ if (ret < 0)
+ dev_err(typec->dev,
+ "Unable to run mode selection on port%d partner\n", port_num);
+ }
+
return 0;
err_cleanup:
diff --git a/drivers/platform/chrome/cros_typec_altmode.c b/drivers/platform/chrome/cros_typec_altmode.c
index 557340b53af0..ee4f2a9dd68a 100644
--- a/drivers/platform/chrome/cros_typec_altmode.c
+++ b/drivers/platform/chrome/cros_typec_altmode.c
@@ -41,12 +41,16 @@ static void cros_typec_altmode_work(struct work_struct *work)
{
struct cros_typec_altmode_data *data =
container_of(work, struct cros_typec_altmode_data, work);
+ int ret;
mutex_lock(&data->lock);
- if (typec_altmode_vdm(data->alt, data->header, data->vdo_data,
- data->vdo_size))
+ ret = typec_altmode_vdm(data->alt, data->header, data->vdo_data,
+ data->vdo_size);
+ if (ret) {
dev_err(&data->alt->dev, "VDM 0x%x failed\n", data->header);
+ typec_altmode_state_update(data->port->partner, data->sid, ret);
+ }
data->header = 0;
data->vdo_data = NULL;
--
2.52.0.158.g65b55ccf14-goog
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 0/8] USB Type-C alternate mode selection
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
` (7 preceding siblings ...)
2025-12-01 12:26 ` [PATCH RFC 8/8] platform/chrome: cros_ec_typec: Enforce priority-based mode selection Andrei Kuchynski
@ 2025-12-11 13:40 ` Heikki Krogerus
2025-12-11 14:22 ` Heikki Krogerus
8 siblings, 1 reply; 14+ messages in thread
From: Heikki Krogerus @ 2025-12-11 13:40 UTC (permalink / raw)
To: Andrei Kuchynski
Cc: Abhishek Pandit-Subedi, Benson Leung, Jameson Thies,
Tzung-Bi Shih, linux-usb, chrome-platform, Guenter Roeck,
Greg Kroah-Hartman, Dmitry Baryshkov, Christian A. Ehrhardt,
Abel Vesa, Pooja Katiyar, Pavan Holla, Madhu M, Venkat Jayaraman,
linux-kernel
Mon, Dec 01, 2025 at 12:25:56PM +0000, Andrei Kuchynski kirjoitti:
> This patch series introduces functionality to the USB Type-C Alternate Mode
> negotiation process by implementing a priority-based selection mechanism.
>
> Currently, DisplayPort and Thunderbolt drivers initiate a mode entry
> separately within their respective probe functions. The Power Delivery
> Controller (PDC) retains the ability to activate either USB4 mode or
> Alternate Modes based on its internal policy.
> The mode selection mechanism disables Alternate Modes to be entered by
> their respective drivers and the PDC. Instead, a priority-ordered approach
> is used to activate the most desirable mode.
>
> A new `priority` field is added to the `typec_altmode` structure to store
> a numerical priority value, with all priorities being unique.
> If the port driver supports the mode selection feature, it must set the
> `mode_selection` boolean field within the `typec_altmode` structure. This
> indicates to the alternate mode drivers that they are not to activate the
> altmode separately.
>
> The mode selection process is managed by three API functions:
> - `typec_mode_selection_start`
> - `typec_altmode_state_update`
> - `typec_mode_selection_delete`
>
> When a partner device is connected, the `typec_mode_selection_start`
> function executes the following steps:
> - It compiles a priority-ordered list of Alternate Modes that are mutually
> supported by both the port and the partner.
> - A dedicated mode selection task is subsequently initiated on the Work
> Queue.
> - This task attempts to activate a mode by starting with the
> highest-priority altmode on the list. Alternate modes are identified with
> their SVIDs. Activation/Deactivation performed via `activate` typec_altmode
> operation. The process stops as soon as a mode is successfully entered.
> Otherwise, after a timeout or if an error occurs, the next alternative mode
> will be activated.
>
> The `typec_altmode_state_update` function is invoked by the port driver to
> communicate the current mode of the Type-C connector.
>
> The `typec_mode_selection_delete` function is responsible for stopping the
> currently running mode selection process and releasing all associated
> system resources.
>
> USB4 activation can be handled in two distinct ways:
> - Treated like an Alternate Mode, using associated sysfs attributes -
> `activate` port attribute to enable/disable the mode, `activate` partner
> attribute to activate/deactivate the mode, `priority` to keep modes
> priority.
> - Like a separate USB mode representing in sysfs via `usb_capabily` ports
> attribute to enable the mode on the port and `usb_mode` partner attribute
> to activate the mode. In this scenario, USB4 is the highest-priority mode,
> without the need for a separate priority field. It is put on the top of the
> preferred list if it is supported by the partner (partner->usb_capability
> has USB_CAPABILITY_USB4 bit set) and is supported and enabled on the port
> (port->usb_mode is USB_MODE_USB4).
>
> This patch series implements the second approach. It identifies the USB4
> mode via its SVID 0xFF00. Instead of using the typec_altmode_ops activate()
> function, activation is handled via the typec_operations enter_usb_mode()
> function.
> Mode selection is initiated only once during partner registration, and only
> if the port driver provides support for this feature. Subsequent
> mode-switching activities can be managed via existing sysfs entries. Any
> modifications to altmode priorities are relevant only to future
> connections.
>
> This series was tested on an Android OS device with kernel 6.17, PDC:
> TI TPS6699, Realtek RTS5453.
> This series depends on the 'USB Type-C alternate mode priorities' series:
> https://lore.kernel.org/all/20251124124639.1101335-1-akuchynski@chromium.org/
Without going into the code review yet, I'm okay with this in general,
except with the artificial SID for the USB4. I still don't understand
why do you guys think we should use that instead of an USB4 specific
device type?
I think somebody said earlier that the user space can't see the device
type of the alt modes? If that's really the case, then I think there
is some bigger issue here. Are you really sure that if you check the
device type of an alternate mode for example with udevadm, it does not
say DEVTYPE=typec_alternate_mode ?
% udevadm info -q property --property=DEVTYPE /sys/bus/typec/devices/port0-partner.0
DEVTYPE=typec_alternate_mode
Br,
> Andrei Kuchynski (8):
> usb: typec: Implement mode selection
> usb: typec: Integrate USB4 into the mode selection process
> usb: typec: Introduce mode_selection bit
> usb: typec: ucsi: Support mode selection to activate altmodes
> usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi
> usb: typec: ucsi: Implement enter_usb_mode operation
> usb: typec: ucsi: Support for Thunderbolt alt mode
> platform/chrome: cros_ec_typec: Enforce priority-based mode selection
>
> drivers/platform/chrome/cros_ec_typec.c | 47 ++-
> drivers/platform/chrome/cros_typec_altmode.c | 8 +-
> drivers/usb/typec/Makefile | 2 +-
> drivers/usb/typec/altmodes/displayport.c | 6 +-
> drivers/usb/typec/altmodes/thunderbolt.c | 2 +-
> drivers/usb/typec/class.c | 1 +
> drivers/usb/typec/class.h | 2 +
> drivers/usb/typec/mode_selection.c | 308 +++++++++++++++++++
> drivers/usb/typec/ucsi/Makefile | 4 +
> drivers/usb/typec/ucsi/cros_ec_ucsi.c | 44 +++
> drivers/usb/typec/ucsi/thunderbolt.c | 199 ++++++++++++
> drivers/usb/typec/ucsi/ucsi.c | 56 +++-
> drivers/usb/typec/ucsi/ucsi.h | 27 ++
> include/linux/usb/typec.h | 1 +
> include/linux/usb/typec_altmode.h | 43 +++
> 15 files changed, 728 insertions(+), 22 deletions(-)
> create mode 100644 drivers/usb/typec/mode_selection.c
> create mode 100644 drivers/usb/typec/ucsi/thunderbolt.c
>
> --
> 2.52.0.158.g65b55ccf14-goog
--
heikki
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 0/8] USB Type-C alternate mode selection
2025-12-11 13:40 ` [PATCH RFC 0/8] USB Type-C alternate " Heikki Krogerus
@ 2025-12-11 14:22 ` Heikki Krogerus
2025-12-16 14:57 ` Andrei Kuchynski
0 siblings, 1 reply; 14+ messages in thread
From: Heikki Krogerus @ 2025-12-11 14:22 UTC (permalink / raw)
To: Andrei Kuchynski
Cc: Abhishek Pandit-Subedi, Benson Leung, Jameson Thies,
Tzung-Bi Shih, linux-usb, chrome-platform, Guenter Roeck,
Greg Kroah-Hartman, Dmitry Baryshkov, Christian A. Ehrhardt,
Abel Vesa, Pooja Katiyar, Pavan Holla, Madhu M, Venkat Jayaraman,
linux-kernel
Thu, Dec 11, 2025 at 03:40:24PM +0200, Heikki Krogerus kirjoitti:
> Without going into the code review yet, I'm okay with this in general,
> except with the artificial SID for the USB4. I still don't understand
> why do you guys think we should use that instead of an USB4 specific
> device type?
>
> I think somebody said earlier that the user space can't see the device
> type of the alt modes? If that's really the case, then I think there
> is some bigger issue here. Are you really sure that if you check the
> device type of an alternate mode for example with udevadm, it does not
> say DEVTYPE=typec_alternate_mode ?
>
> % udevadm info -q property --property=DEVTYPE /sys/bus/typec/devices/port0-partner.0
> DEVTYPE=typec_alternate_mode
Or just use grep :)
% grep DEVTYPE /sys/bus/typec/devices/port0-partner.0/uevent
DEVTYPE=typec_alternate_mode
So, if that really does not work, then there is a bug somewhere that
we obviously need to fix.
Please note that the port altmodes are now also part of the bus.
Br,
--
heikki
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 0/8] USB Type-C alternate mode selection
2025-12-11 14:22 ` Heikki Krogerus
@ 2025-12-16 14:57 ` Andrei Kuchynski
2025-12-23 11:16 ` Heikki Krogerus
0 siblings, 1 reply; 14+ messages in thread
From: Andrei Kuchynski @ 2025-12-16 14:57 UTC (permalink / raw)
To: Heikki Krogerus
Cc: Abhishek Pandit-Subedi, Benson Leung, Jameson Thies,
Tzung-Bi Shih, linux-usb, chrome-platform, Guenter Roeck,
Greg Kroah-Hartman, Dmitry Baryshkov, Christian A. Ehrhardt,
Abel Vesa, Pooja Katiyar, Pavan Holla, Madhu M, Venkat Jayaraman,
linux-kernel
On Thu, Dec 11, 2025 at 3:23 PM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> Thu, Dec 11, 2025 at 03:40:24PM +0200, Heikki Krogerus kirjoitti:
> > Without going into the code review yet, I'm okay with this in general,
> > except with the artificial SID for the USB4. I still don't understand
> > why do you guys think we should use that instead of an USB4 specific
> > device type?
> >
> > I think somebody said earlier that the user space can't see the device
> > type of the alt modes? If that's really the case, then I think there
> > is some bigger issue here. Are you really sure that if you check the
> > device type of an alternate mode for example with udevadm, it does not
> > say DEVTYPE=typec_alternate_mode ?
> >
> > % udevadm info -q property --property=DEVTYPE /sys/bus/typec/devices/port0-partner.0
> > DEVTYPE=typec_alternate_mode
>
> Or just use grep :)
>
> % grep DEVTYPE /sys/bus/typec/devices/port0-partner.0/uevent
> DEVTYPE=typec_alternate_mode
>
> So, if that really does not work, then there is a bug somewhere that
> we obviously need to fix.
>
> Please note that the port altmodes are now also part of the bus.
>
> Br,
>
> --
> heikki
Thank you for the review, Heikki.
The USB4 SID is utilized for distinguishing between USB4 and alternate
modes internally and is not exposed to user-space. This represents internal
implementation detail, for example the boolean variable `is_alternate`
could serve the same purpose as the SID.
This patch series introduces no new sysfs entries; the only new attribute,
`priority`, was introduced in the mode priority series, available at
https://lore.kernel.org/all/20251124124639.1101335-1-akuchynski@chromium.org/
It is possible to use already existing `usb_capabily` and `usb_mode`
attributes to manage USB4 mode, allowing verification of USB4 support on
both the port and the partner. The activation of USB4 is accomplished
through the implementation of the `enter_usb_mode` typec operation.
I would like your opinion on whether using a USB4 device type would be a
better approach.
Please note that patches 6 and 7 are not part of the mode selection series.
They are not yet complete and are provided only to demonstrate how the
entire feature should work.
I confirm there is no issue with the DEVTYPE. Thank you.
Thanks,
Andrei
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 0/8] USB Type-C alternate mode selection
2025-12-16 14:57 ` Andrei Kuchynski
@ 2025-12-23 11:16 ` Heikki Krogerus
2026-01-07 9:02 ` Andrei Kuchynski
0 siblings, 1 reply; 14+ messages in thread
From: Heikki Krogerus @ 2025-12-23 11:16 UTC (permalink / raw)
To: Andrei Kuchynski
Cc: Abhishek Pandit-Subedi, Benson Leung, Jameson Thies,
Tzung-Bi Shih, linux-usb, chrome-platform, Guenter Roeck,
Greg Kroah-Hartman, Dmitry Baryshkov, Christian A. Ehrhardt,
Abel Vesa, Pooja Katiyar, Pavan Holla, Madhu M, Venkat Jayaraman,
linux-kernel
Hi,
Tue, Dec 16, 2025 at 03:57:40PM +0100, Andrei Kuchynski kirjoitti:
> On Thu, Dec 11, 2025 at 3:23 PM Heikki Krogerus
> <heikki.krogerus@linux.intel.com> wrote:
> >
> > Thu, Dec 11, 2025 at 03:40:24PM +0200, Heikki Krogerus kirjoitti:
> > > Without going into the code review yet, I'm okay with this in general,
> > > except with the artificial SID for the USB4. I still don't understand
> > > why do you guys think we should use that instead of an USB4 specific
> > > device type?
> > >
> > > I think somebody said earlier that the user space can't see the device
> > > type of the alt modes? If that's really the case, then I think there
> > > is some bigger issue here. Are you really sure that if you check the
> > > device type of an alternate mode for example with udevadm, it does not
> > > say DEVTYPE=typec_alternate_mode ?
> > >
> > > % udevadm info -q property --property=DEVTYPE /sys/bus/typec/devices/port0-partner.0
> > > DEVTYPE=typec_alternate_mode
> >
> > Or just use grep :)
> >
> > % grep DEVTYPE /sys/bus/typec/devices/port0-partner.0/uevent
> > DEVTYPE=typec_alternate_mode
> >
> > So, if that really does not work, then there is a bug somewhere that
> > we obviously need to fix.
> >
> > Please note that the port altmodes are now also part of the bus.
> >
> > Br,
> >
> > --
> > heikki
>
> Thank you for the review, Heikki.
>
> The USB4 SID is utilized for distinguishing between USB4 and alternate
> modes internally and is not exposed to user-space. This represents internal
> implementation detail, for example the boolean variable `is_alternate`
> could serve the same purpose as the SID.
> This patch series introduces no new sysfs entries; the only new attribute,
> `priority`, was introduced in the mode priority series, available at
> https://lore.kernel.org/all/20251124124639.1101335-1-akuchynski@chromium.org/
>
> It is possible to use already existing `usb_capabily` and `usb_mode`
> attributes to manage USB4 mode, allowing verification of USB4 support on
> both the port and the partner. The activation of USB4 is accomplished
> through the implementation of the `enter_usb_mode` typec operation.
>
> I would like your opinion on whether using a USB4 device type would be a
> better approach.
The device for the USB4 mode will need to have its own device type in
any case, but I'm indeed mainly concerned about how we expose the USB4
mode device to the user space.
As a kernel internal implementation detail the custom SID is probable
fine for now, although I was actually hoping that we could improve the
API a bit. So something like typec_register_mode() type of API. You
probable could introduce something like this for that API:
struct typec_mode {
/* enum typec_accessory accessory; */
enum usb_mode usb; /* or just USB4 flag */
struct typec_altmode_desc *altmode; /* NULL with USB4 */
};
> Please note that patches 6 and 7 are not part of the mode selection series.
> They are not yet complete and are provided only to demonstrate how the
> entire feature should work.
Got it.
> I confirm there is no issue with the DEVTYPE. Thank you.
Okay, good. So there are no bugs at least :)
Br,
--
heikki
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH RFC 0/8] USB Type-C alternate mode selection
2025-12-23 11:16 ` Heikki Krogerus
@ 2026-01-07 9:02 ` Andrei Kuchynski
0 siblings, 0 replies; 14+ messages in thread
From: Andrei Kuchynski @ 2026-01-07 9:02 UTC (permalink / raw)
To: Heikki Krogerus
Cc: Abhishek Pandit-Subedi, Benson Leung, Jameson Thies,
Tzung-Bi Shih, linux-usb, chrome-platform, Guenter Roeck,
Greg Kroah-Hartman, Dmitry Baryshkov, Christian A. Ehrhardt,
Abel Vesa, Pooja Katiyar, Pavan Holla, Madhu M, Venkat Jayaraman,
linux-kernel
On Tue, Dec 23, 2025 at 12:17 PM Heikki Krogerus
<heikki.krogerus@linux.intel.com> wrote:
>
> Hi,
>
> Tue, Dec 16, 2025 at 03:57:40PM +0100, Andrei Kuchynski kirjoitti:
> > On Thu, Dec 11, 2025 at 3:23 PM Heikki Krogerus
> > <heikki.krogerus@linux.intel.com> wrote:
> > >
> > > Thu, Dec 11, 2025 at 03:40:24PM +0200, Heikki Krogerus kirjoitti:
> > > > Without going into the code review yet, I'm okay with this in general,
> > > > except with the artificial SID for the USB4. I still don't understand
> > > > why do you guys think we should use that instead of an USB4 specific
> > > > device type?
> > > >
> > > > I think somebody said earlier that the user space can't see the device
> > > > type of the alt modes? If that's really the case, then I think there
> > > > is some bigger issue here. Are you really sure that if you check the
> > > > device type of an alternate mode for example with udevadm, it does not
> > > > say DEVTYPE=typec_alternate_mode ?
> > > >
> > > > % udevadm info -q property --property=DEVTYPE /sys/bus/typec/devices/port0-partner.0
> > > > DEVTYPE=typec_alternate_mode
> > >
> > > Or just use grep :)
> > >
> > > % grep DEVTYPE /sys/bus/typec/devices/port0-partner.0/uevent
> > > DEVTYPE=typec_alternate_mode
> > >
> > > So, if that really does not work, then there is a bug somewhere that
> > > we obviously need to fix.
> > >
> > > Please note that the port altmodes are now also part of the bus.
> > >
> > > Br,
> > >
> > > --
> > > heikki
> >
> > Thank you for the review, Heikki.
> >
> > The USB4 SID is utilized for distinguishing between USB4 and alternate
> > modes internally and is not exposed to user-space. This represents internal
> > implementation detail, for example the boolean variable `is_alternate`
> > could serve the same purpose as the SID.
> > This patch series introduces no new sysfs entries; the only new attribute,
> > `priority`, was introduced in the mode priority series, available at
> > https://lore.kernel.org/all/20251124124639.1101335-1-akuchynski@chromium.org/
> >
> > It is possible to use already existing `usb_capabily` and `usb_mode`
> > attributes to manage USB4 mode, allowing verification of USB4 support on
> > both the port and the partner. The activation of USB4 is accomplished
> > through the implementation of the `enter_usb_mode` typec operation.
> >
> > I would like your opinion on whether using a USB4 device type would be a
> > better approach.
>
> The device for the USB4 mode will need to have its own device type in
> any case, but I'm indeed mainly concerned about how we expose the USB4
> mode device to the user space.
>
> As a kernel internal implementation detail the custom SID is probable
> fine for now, although I was actually hoping that we could improve the
> API a bit. So something like typec_register_mode() type of API. You
> probable could introduce something like this for that API:
>
> struct typec_mode {
> /* enum typec_accessory accessory; */
> enum usb_mode usb; /* or just USB4 flag */
> struct typec_altmode_desc *altmode; /* NULL with USB4 */
> };
Got it.
If you don’t have objections regarding the mode selection, I will proceed
with sending the current patch series, omitting the USB4 support.
The support for USB4 mode will be in a subsequent series.
Thanks,
Andrei
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-01-07 9:02 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-01 12:25 [PATCH RFC 0/8] USB Type-C alternate mode selection Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 1/8] usb: typec: Implement " Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 2/8] usb: typec: Integrate USB4 into the mode selection process Andrei Kuchynski
2025-12-01 12:25 ` [PATCH RFC 3/8] usb: typec: Introduce mode_selection bit Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 4/8] usb: typec: ucsi: Support mode selection to activate altmodes Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 5/8] usb: typec: ucsi: Enforce mode selection for cros_ec_ucsi Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 6/8] usb: typec: ucsi: Implement enter_usb_mode operation Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 7/8] usb: typec: ucsi: Support for Thunderbolt alt mode Andrei Kuchynski
2025-12-01 12:26 ` [PATCH RFC 8/8] platform/chrome: cros_ec_typec: Enforce priority-based mode selection Andrei Kuchynski
2025-12-11 13:40 ` [PATCH RFC 0/8] USB Type-C alternate " Heikki Krogerus
2025-12-11 14:22 ` Heikki Krogerus
2025-12-16 14:57 ` Andrei Kuchynski
2025-12-23 11:16 ` Heikki Krogerus
2026-01-07 9:02 ` Andrei Kuchynski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).