lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <aYX0KojDuGwM7Qjk@kuha>
Date: Fri, 6 Feb 2026 16:01:14 +0200
From: Heikki Krogerus <heikki.krogerus@...ux.intel.com>
To: Andrei Kuchynski <akuchynski@...omium.org>
Cc: Jameson Thies <jthies@...gle.com>, Benson Leung <bleung@...omium.org>,
	linux-usb@...r.kernel.org, linux-kernel@...r.kernel.org,
	Abhishek Pandit-Subedi <abhishekpandit@...omium.org>,
	Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
	Dmitry Baryshkov <dmitry.baryshkov@....qualcomm.com>,
	Pooja Katiyar <pooja.katiyar@...el.com>,
	Johan Hovold <johan@...nel.org>,
	Hsin-Te Yuan <yuanhsinte@...omium.org>, Madhu M <madhu.m@...el.com>,
	Venkat Jayaraman <venkat.jayaraman@...el.com>,
	Pengyu Luo <mitltlatltl@...il.com>,
	Fedor Pchelkin <boddah8794@...il.com>
Subject: Re: [PATCH v2] usb: typec: ucsi: Add Thunderbolt alternate mode
 support

On Fri, Feb 06, 2026 at 11:57:54AM +0000, Andrei Kuchynski wrote:
> Introduce support for Thunderbolt (TBT) alternate mode to the UCSI driver.
> This allows the driver to manage the entry and exit of TBT altmode by
> providing the necessary typec_altmode_ops.
> 
> ucsi_altmode_update_active() is invoked when the Connector Partner Changed
> bit is set in the GET_CONNECTOR_STATUS data. This ensures that the
> alternate mode's active state is synchronized with the current mode the
> connector is operating in.
> 
> Signed-off-by: Andrei Kuchynski <akuchynski@...omium.org>

Reviewed-by: Heikki Krogerus <heikki.krogerus@...ux.intel.com>

> ---
> Changes in V2:
> - Update copyright year to 2026.
> - Explicitly include all required headers.
> - No functional changes compared to V1.
> 
>  drivers/usb/typec/ucsi/Makefile      |   4 +
>  drivers/usb/typec/ucsi/thunderbolt.c | 212 +++++++++++++++++++++++++++
>  drivers/usb/typec/ucsi/ucsi.c        |  18 ++-
>  drivers/usb/typec/ucsi/ucsi.h        |  20 +++
>  4 files changed, 249 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 dbc571763eff6..c7e38bf01350d 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 0000000000000..434d3d8ea5b6e
> --- /dev/null
> +++ b/drivers/usb/typec/ucsi/thunderbolt.c
> @@ -0,0 +1,212 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * UCSI Thunderbolt Alternate Mode Support
> + *
> + * Copyright 2026 Google LLC
> + */
> +
> +#include <linux/usb/typec_tbt.h>
> +#include <linux/usb/pd_vdo.h>
> +#include <linux/err.h>
> +#include <linux/dev_printk.h>
> +#include <linux/device/devres.h>
> +#include <linux/gfp_types.h>
> +#include <linux/types.h>
> +#include <linux/usb/typec_altmode.h>
> +#include <linux/workqueue.h>
> +
> +#include "ucsi.h"
> +
> +/**
> + * struct ucsi_tbt - Thunderbolt Alternate Mode private data structure
> + * @con: Pointer to UCSI connector structure
> + * @alt: Pointer to typec altmode structure
> + * @work: Work structure
> + * @cam: An offset into the list of alternate modes supported by the PPM
> + * @header: VDO header
> + */
> +struct ucsi_tbt {
> +	struct ucsi_connector *con;
> +	struct typec_altmode *alt;
> +	struct work_struct work;
> +	int cam;
> +	u32 header;
> +};
> +
> +static void ucsi_thunderbolt_work(struct work_struct *work)
> +{
> +	struct ucsi_tbt *tbt = container_of(work, struct ucsi_tbt, work);
> +
> +	if (typec_altmode_vdm(tbt->alt, tbt->header, NULL, 0))
> +		dev_err(&tbt->alt->dev, "VDM 0x%x failed\n", tbt->header);
> +
> +	tbt->header = 0;
> +}
> +
> +static int ucsi_thunderbolt_set_altmode(struct ucsi_tbt *tbt,
> +					bool enter, u32 vdo)
> +{
> +	int svdm_version;
> +	int cmd;
> +	int ret;
> +	u64 command = UCSI_SET_NEW_CAM |
> +		      UCSI_CONNECTOR_NUMBER(tbt->con->num) |
> +		      UCSI_SET_NEW_CAM_SET_AM(tbt->cam) |
> +		      ((u64)vdo << 32);
> +
> +	if (enter)
> +		command |= (1 << 23);
> +
> +	ret = ucsi_send_command(tbt->con->ucsi, command, NULL, 0);
> +	if (ret < 0)
> +		return ret;
> +
> +	svdm_version = typec_altmode_get_svdm_version(tbt->alt);
> +	if (svdm_version < 0)
> +		return svdm_version;
> +
> +	if (enter)
> +		cmd = CMD_ENTER_MODE;
> +	else
> +		cmd = CMD_EXIT_MODE;
> +	tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
> +	tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
> +	tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
> +
> +	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_connector *con = tbt->con;
> +	u64 command;
> +	u8 cur = 0;
> +	int ret;
> +
> +	if (!ucsi_con_mutex_lock(con))
> +		return -ENOTCONN;
> +
> +	command = UCSI_GET_CURRENT_CAM | UCSI_CONNECTOR_NUMBER(con->num);
> +	ret = ucsi_send_command(con->ucsi, command, &cur, sizeof(cur));
> +	if (ret < 0) {
> +		if (con->ucsi->version > 0x0100)
> +			goto err_unlock;
> +		cur = 0xff;
> +	}
> +
> +	if (cur != 0xff) {
> +		if (cur >= UCSI_MAX_ALTMODES || con->port_altmode[cur] != alt)
> +			ret = -EBUSY;
> +		else
> +			ret = 0;
> +		goto err_unlock;
> +	}
> +
> +	ret = ucsi_thunderbolt_set_altmode(tbt, true, *vdo);
> +	ucsi_altmode_update_active(tbt->con);
> +
> +err_unlock:
> +	ucsi_con_mutex_unlock(con);
> +
> +	return ret;
> +}
> +
> +static int ucsi_thunderbolt_exit(struct typec_altmode *alt)
> +{
> +	struct ucsi_tbt *tbt = typec_altmode_get_drvdata(alt);
> +	int ret;
> +
> +	if (!ucsi_con_mutex_lock(tbt->con))
> +		return -ENOTCONN;
> +
> +	ret = ucsi_thunderbolt_set_altmode(tbt, false, 0);
> +
> +	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);
> +	int cmd_type = PD_VDO_CMDT(header);
> +	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);
> +		}
> +		tbt->header = VDO(USB_TYPEC_TBT_SID, 1, svdm_version, cmd);
> +		tbt->header |= VDO_OPOS(TYPEC_TBT_MODE);
> +		tbt->header |= VDO_CMDT(CMDT_RSP_ACK);
> +
> +		schedule_work(&tbt->work);
> +		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;
> +	INIT_WORK(&tbt->work, ucsi_thunderbolt_work);
> +	typec_altmode_set_drvdata(alt, tbt);
> +	typec_altmode_set_ops(alt, &ucsi_thunderbolt_ops);
> +
> +	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 251990475faa7..91b6c71dd7396 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"
> @@ -417,6 +418,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;
> @@ -647,12 +651,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;
> @@ -1318,6 +1325,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
>  
>  	if (con->partner && (change & UCSI_CONSTAT_PARTNER_CHANGE)) {
>  		ucsi_partner_change(con);
> +		ucsi_altmode_update_active(con);
>  
>  		/* Complete pending data role swap */
>  		if (!completion_done(&con->complete))
> diff --git a/drivers/usb/typec/ucsi/ucsi.h b/drivers/usb/typec/ucsi/ucsi.h
> index 4797b4aa1e35b..43a0d01ade8ff 100644
> --- a/drivers/usb/typec/ucsi/ucsi.h
> +++ b/drivers/usb/typec/ucsi/ucsi.h
> @@ -600,6 +600,26 @@ 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.53.0.rc2.204.g2597b5adb4-goog

-- 
heikki

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ