From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Thunderbolt 3 Alternate Mode entry flow is described in
USB Type-C Specification Release 2.0.
Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
Co-developed-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Signed-off-by: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
---
Changes in v4:
- Large refactor to use cable_altmodes
- Fixed ordering of cable mode enter/exit (SOP', SOP", then port for
enter; reverse order for exit)
- Other small fixes from v3 feedback
Changes in v3:
- Revert rename of TYPEC_TBT_MODE
- Remove mode from typec_device_id
Changes in v2:
- Use <linux/usb/typec_tbt.h> and add missing TBT_CABLE_ROUNDED
- Pass struct typec_thunderbolt_data to typec_altmode_notify
- Rename TYPEC_TBT_MODE to USB_TYPEC_TBT_MODE
- Use USB_TYPEC_TBT_SID and USB_TYPEC_TBT_MODE for device id
- Change module license to GPL due to checkpatch warning
Changes in v1:
- Delay cable + plug checks so that the module doesn't fail to probe
if cable + plug information isn't available by the time the partner
altmode is registered.
- Remove unncessary brace after if (IS_ERR(plug))
drivers/usb/typec/altmodes/Kconfig | 9 +
drivers/usb/typec/altmodes/Makefile | 2 +
drivers/usb/typec/altmodes/thunderbolt.c | 387 +++++++++++++++++++++++
drivers/usb/typec/class.c | 5 +-
include/linux/usb/typec.h | 2 +
include/linux/usb/typec_tbt.h | 1 +
6 files changed, 404 insertions(+), 2 deletions(-)
create mode 100644 drivers/usb/typec/altmodes/thunderbolt.c
diff --git a/drivers/usb/typec/altmodes/Kconfig b/drivers/usb/typec/altmodes/Kconfig
index 1a6b5e872b0d..7867fa7c405d 100644
--- a/drivers/usb/typec/altmodes/Kconfig
+++ b/drivers/usb/typec/altmodes/Kconfig
@@ -23,4 +23,13 @@ config TYPEC_NVIDIA_ALTMODE
To compile this driver as a module, choose M here: the
module will be called typec_nvidia.
+config TYPEC_TBT_ALTMODE
+ tristate "Thunderbolt3 Alternate Mode driver"
+ help
+ Select this option if you have Thunderbolt3 hardware on your
+ system.
+
+ To compile this driver as a module, choose M here: the
+ module will be called typec_thunderbolt.
+
endmenu
diff --git a/drivers/usb/typec/altmodes/Makefile b/drivers/usb/typec/altmodes/Makefile
index 45717548b396..508a68351bd2 100644
--- a/drivers/usb/typec/altmodes/Makefile
+++ b/drivers/usb/typec/altmodes/Makefile
@@ -4,3 +4,5 @@ obj-$(CONFIG_TYPEC_DP_ALTMODE) += typec_displayport.o
typec_displayport-y := displayport.o
obj-$(CONFIG_TYPEC_NVIDIA_ALTMODE) += typec_nvidia.o
typec_nvidia-y := nvidia.o
+obj-$(CONFIG_TYPEC_TBT_ALTMODE) += typec_thunderbolt.o
+typec_thunderbolt-y := thunderbolt.o
diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
new file mode 100644
index 000000000000..14e89e9a7691
--- /dev/null
+++ b/drivers/usb/typec/altmodes/thunderbolt.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * USB Typec-C Thuderbolt3 Alternate Mode driver
+ *
+ * Copyright (C) 2019 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/usb/pd_vdo.h>
+#include <linux/usb/typec_altmode.h>
+#include <linux/usb/typec_tbt.h>
+
+enum tbt_state {
+ TBT_STATE_IDLE,
+ TBT_STATE_SOP_P_ENTER,
+ TBT_STATE_SOP_PP_ENTER,
+ TBT_STATE_ENTER,
+ TBT_STATE_EXIT,
+ TBT_STATE_SOP_PP_EXIT,
+ TBT_STATE_SOP_P_EXIT
+};
+
+struct tbt_altmode {
+ enum tbt_state state;
+ struct typec_cable *cable;
+ struct typec_altmode *alt;
+ struct typec_altmode *plug[2];
+ u32 enter_vdo;
+
+ struct work_struct work;
+ struct mutex lock; /* device lock */
+};
+
+static bool tbt_ready(struct typec_altmode *alt);
+
+static int tbt_enter_mode(struct tbt_altmode *tbt)
+{
+ struct typec_altmode *plug = tbt->plug[TYPEC_PLUG_SOP_P];
+ u32 vdo;
+
+ vdo = tbt->alt->vdo & (TBT_VENDOR_SPECIFIC_B0 | TBT_VENDOR_SPECIFIC_B1);
+ vdo |= tbt->alt->vdo & TBT_INTEL_SPECIFIC_B0;
+ vdo |= TBT_MODE;
+
+ if (plug) {
+ if (typec_cable_is_active(tbt->cable))
+ vdo |= TBT_ENTER_MODE_ACTIVE_CABLE;
+
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_SPEED(plug->vdo));
+ vdo |= plug->vdo & TBT_CABLE_ROUNDED;
+ vdo |= plug->vdo & TBT_CABLE_OPTICAL;
+ vdo |= plug->vdo & TBT_CABLE_RETIMER;
+ vdo |= plug->vdo & TBT_CABLE_LINK_TRAINING;
+ } else {
+ vdo |= TBT_ENTER_MODE_CABLE_SPEED(TBT_CABLE_USB3_PASSIVE);
+ }
+
+ tbt->enter_vdo = vdo;
+ return typec_altmode_enter(tbt->alt, &vdo);
+}
+
+static void tbt_altmode_work(struct work_struct *work)
+{
+ struct tbt_altmode *tbt = container_of(work, struct tbt_altmode, work);
+ int ret;
+
+ mutex_lock(&tbt->lock);
+
+ switch (tbt->state) {
+ case TBT_STATE_SOP_P_ENTER:
+ ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_P, NULL);
+ if (ret) {
+ dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_P]->dev,
+ "failed to enter mode (%d)\n", ret);
+ goto disable_plugs;
+ }
+ break;
+ case TBT_STATE_SOP_PP_ENTER:
+ ret = typec_cable_altmode_enter(tbt->alt, TYPEC_PLUG_SOP_PP, NULL);
+ if (ret) {
+ dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_PP]->dev,
+ "failed to enter mode (%d)\n", ret);
+ goto disable_plugs;
+ }
+ break;
+ case TBT_STATE_ENTER:
+ ret = tbt_enter_mode(tbt);
+ if (ret)
+ dev_dbg(&tbt->alt->dev, "failed to enter mode (%d)\n",
+ ret);
+ break;
+ case TBT_STATE_EXIT:
+ typec_altmode_exit(tbt->alt);
+ break;
+ case TBT_STATE_SOP_PP_EXIT:
+ typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_PP);
+ break;
+ case TBT_STATE_SOP_P_EXIT:
+ typec_cable_altmode_exit(tbt->alt, TYPEC_PLUG_SOP_P);
+ break;
+ default:
+ break;
+ }
+
+ tbt->state = TBT_STATE_IDLE;
+
+ mutex_unlock(&tbt->lock);
+ return;
+
+disable_plugs:
+ for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+
+ tbt->plug[i] = NULL;
+ }
+
+ tbt->state = TBT_STATE_ENTER;
+ schedule_work(&tbt->work);
+ mutex_unlock(&tbt->lock);
+}
+
+/* MUST HOLD tbt->lock.
+ *
+ * If SOP' is available, enter that first (which will trigger a VDM response
+ * that will enter SOP" if available and then the port). If entering SOP' fails,
+ * stop attempting to enter either cable altmode (probably not supported) and
+ * directly enter the port altmode.
+ */
+static int tbt_enter_modes_ordered(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int ret = 0;
+
+ if (!tbt_ready(tbt->alt))
+ return -ENODEV;
+
+ if (tbt->plug[TYPEC_PLUG_SOP_P]) {
+ ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
+ if (ret < 0) {
+ for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+
+ tbt->plug[i] = NULL;
+ }
+ } else {
+ return ret;
+ }
+ }
+
+ return tbt_enter_mode(tbt);
+}
+
+static int tbt_cable_altmode_vdm(struct typec_altmode *alt,
+ enum typec_plug_index sop, const u32 hdr,
+ const u32 *vdo, int count)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int cmd_type = PD_VDO_CMDT(hdr);
+ int cmd = PD_VDO_CMD(hdr);
+
+ mutex_lock(&tbt->lock);
+
+ if (tbt->state != TBT_STATE_IDLE) {
+ mutex_unlock(&tbt->lock);
+ return -EBUSY;
+ }
+
+ switch (cmd_type) {
+ case CMDT_RSP_ACK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ /*
+ * Following the order described in USB Type-C Spec
+ * R2.0 Section 6.7.3: SOP', SOP", then port.
+ */
+ if (sop == TYPEC_PLUG_SOP_P) {
+ if (tbt->plug[TYPEC_PLUG_SOP_PP])
+ tbt->state = TBT_STATE_SOP_PP_ENTER;
+ else
+ tbt->state = TBT_STATE_ENTER;
+ } else if (sop == TYPEC_PLUG_SOP_PP)
+ tbt->state = TBT_STATE_ENTER;
+
+ break;
+ case CMD_EXIT_MODE:
+ /* Exit in opposite order: Port, SOP", then SOP'. */
+ if (sop == TYPEC_PLUG_SOP_PP)
+ tbt->state = TBT_STATE_SOP_P_EXIT;
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (tbt->state != TBT_STATE_IDLE)
+ schedule_work(&tbt->work);
+
+
+ mutex_unlock(&tbt->lock);
+ return 0;
+}
+
+static int tbt_altmode_vdm(struct typec_altmode *alt,
+ const u32 hdr, const u32 *vdo, int count)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ struct typec_thunderbolt_data data;
+ int cmd_type = PD_VDO_CMDT(hdr);
+ int cmd = PD_VDO_CMD(hdr);
+
+ mutex_lock(&tbt->lock);
+
+ if (tbt->state != TBT_STATE_IDLE) {
+ mutex_unlock(&tbt->lock);
+ return -EBUSY;
+ }
+
+ switch (cmd_type) {
+ case CMDT_RSP_ACK:
+ /* Port altmode is last to enter and first to exit. */
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ memset(&data, 0, sizeof(data));
+
+ data.device_mode = tbt->alt->vdo;
+ data.enter_vdo = tbt->enter_vdo;
+ if (tbt->plug[TYPEC_PLUG_SOP_P])
+ data.cable_mode = tbt->plug[TYPEC_PLUG_SOP_P]->vdo;
+
+ typec_altmode_notify(alt, TYPEC_STATE_MODAL, &data);
+ break;
+ case CMD_EXIT_MODE:
+ if (tbt->plug[TYPEC_PLUG_SOP_PP])
+ tbt->state = TBT_STATE_SOP_PP_EXIT;
+ else if (tbt->plug[TYPEC_PLUG_SOP_P])
+ tbt->state = TBT_STATE_SOP_P_EXIT;
+ break;
+ }
+ break;
+ case CMDT_RSP_NAK:
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ dev_warn(&alt->dev, "Enter Mode refused\n");
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (tbt->state != TBT_STATE_IDLE)
+ schedule_work(&tbt->work);
+
+ mutex_unlock(&tbt->lock);
+
+ return 0;
+}
+
+static int tbt_altmode_activate(struct typec_altmode *alt, int activate)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ int ret;
+
+ mutex_lock(&tbt->lock);
+
+ if (activate)
+ ret = tbt_enter_modes_ordered(alt);
+ else
+ ret = typec_altmode_exit(alt);
+
+ mutex_unlock(&tbt->lock);
+
+ return ret;
+}
+
+static const struct typec_altmode_ops tbt_altmode_ops = {
+ .vdm = tbt_altmode_vdm,
+ .activate = tbt_altmode_activate
+};
+
+static const struct typec_cable_ops tbt_cable_ops = {
+ .vdm = tbt_cable_altmode_vdm,
+};
+
+static int tbt_altmode_probe(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt;
+
+ tbt = devm_kzalloc(&alt->dev, sizeof(*tbt), GFP_KERNEL);
+ if (!tbt)
+ return -ENOMEM;
+
+ INIT_WORK(&tbt->work, tbt_altmode_work);
+ mutex_init(&tbt->lock);
+ tbt->alt = alt;
+
+ alt->desc = "Thunderbolt3";
+ typec_altmode_set_drvdata(alt, tbt);
+ typec_altmode_set_ops(alt, &tbt_altmode_ops);
+
+ if (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])
+ tbt->state = TBT_STATE_SOP_PP_ENTER;
+ else
+ tbt->state = TBT_STATE_ENTER;
+ schedule_work(&tbt->work);
+ }
+
+ return 0;
+}
+
+static void tbt_altmode_remove(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+
+ for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
+ if (tbt->plug[i])
+ typec_altmode_put_plug(tbt->plug[i]);
+ }
+
+ if (tbt->cable)
+ typec_cable_put(tbt->cable);
+}
+
+static bool tbt_ready(struct typec_altmode *alt)
+{
+ struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
+ struct typec_altmode *plug;
+
+ if (tbt->cable)
+ return true;
+
+ /* Thunderbolt 3 requires a cable with eMarker */
+ tbt->cable = typec_cable_get(typec_altmode2port(tbt->alt));
+ if (!tbt->cable)
+ return false;
+
+ /* We accept systems without SOP' or SOP''. This means the port altmode
+ * driver will be responsible for properly ordering entry/exit.
+ */
+ for (int i = 0; i < TYPEC_PLUG_SOP_PP + 1; i++) {
+ plug = typec_altmode_get_plug(tbt->alt, i);
+ if (IS_ERR(plug))
+ continue;
+
+ if (!plug || plug->svid != USB_TYPEC_TBT_SID)
+ break;
+
+ plug->desc = "Thunderbolt3";
+ plug->cable_ops = &tbt_cable_ops;
+ typec_altmode_set_drvdata(plug, tbt);
+
+ tbt->plug[i] = plug;
+ }
+
+ return true;
+}
+
+static const struct typec_device_id tbt_typec_id[] = {
+ { USB_TYPEC_TBT_SID },
+ { }
+};
+MODULE_DEVICE_TABLE(typec, tbt_typec_id);
+
+static struct typec_altmode_driver tbt_altmode_driver = {
+ .id_table = tbt_typec_id,
+ .probe = tbt_altmode_probe,
+ .remove = tbt_altmode_remove,
+ .driver = {
+ .name = "typec-thunderbolt",
+ }
+};
+module_typec_altmode_driver(tbt_altmode_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Thunderbolt3 USB Type-C Alternate Mode");
diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
index febe453b96be..b5e67a57762c 100644
--- a/drivers/usb/typec/class.c
+++ b/drivers/usb/typec/class.c
@@ -458,7 +458,8 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
if (attr == &dev_attr_active.attr)
- if (!adev->ops || !adev->ops->activate)
+ if (!is_typec_port(adev->dev.parent) &&
+ (!adev->ops || !adev->ops->activate))
return 0444;
return attr->mode;
@@ -563,7 +564,7 @@ typec_register_altmode(struct device *parent,
if (is_port) {
alt->attrs[3] = &dev_attr_supported_roles.attr;
- alt->adev.active = true; /* Enabled by default */
+ alt->adev.active = !desc->inactive; /* Enabled by default */
}
sprintf(alt->group_name, "mode%d", desc->mode);
diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
index d616b8807000..252af3f77039 100644
--- a/include/linux/usb/typec.h
+++ b/include/linux/usb/typec.h
@@ -140,6 +140,7 @@ int typec_cable_set_identity(struct typec_cable *cable);
* @mode: Index of the Mode
* @vdo: VDO returned by Discover Modes USB PD command
* @roles: Only for ports. DRP if the mode is available in both roles
+ * @inactive: Only for ports. Make this port inactive (default is active).
*
* Description of an Alternate Mode which a connector, cable plug or partner
* supports.
@@ -150,6 +151,7 @@ struct typec_altmode_desc {
u32 vdo;
/* Only used with ports */
enum typec_port_data roles;
+ bool inactive;
};
void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision);
diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h
index fa97d7e00f5c..55dcea12082c 100644
--- a/include/linux/usb/typec_tbt.h
+++ b/include/linux/usb/typec_tbt.h
@@ -44,6 +44,7 @@ struct typec_thunderbolt_data {
#define TBT_GEN3_NON_ROUNDED 0
#define TBT_GEN3_GEN4_ROUNDED_NON_ROUNDED 1
+#define TBT_CABLE_ROUNDED BIT(19)
#define TBT_CABLE_OPTICAL BIT(21)
#define TBT_CABLE_RETIMER BIT(22)
#define TBT_CABLE_LINK_TRAINING BIT(23)
--
2.47.0.338.g60cca15819-goog
Quoting Abhishek Pandit-Subedi (2024-12-06 15:38:13)
> diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
> new file mode 100644
> index 000000000000..14e89e9a7691
> --- /dev/null
> +++ b/drivers/usb/typec/altmodes/thunderbolt.c
> @@ -0,0 +1,387 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/**
Remove extra *, this isn't kerneldoc.
> + * USB Typec-C Thuderbolt3 Alternate Mode driver
> + *
> + * Copyright (C) 2019 Intel Corporation
> + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> + */
> +
> +#include <linux/delay.h>
Is this include used?
> +#include <linux/mutex.h>
> +#include <linux/module.h>
> +#include <linux/usb/pd_vdo.h>
> +#include <linux/usb/typec_altmode.h>
> +#include <linux/usb/typec_tbt.h>
Please include workqueue.h
> +
> +enum tbt_state {
> + TBT_STATE_IDLE,
> + TBT_STATE_SOP_P_ENTER,
> + TBT_STATE_SOP_PP_ENTER,
> + TBT_STATE_ENTER,
> + TBT_STATE_EXIT,
> + TBT_STATE_SOP_PP_EXIT,
> + TBT_STATE_SOP_P_EXIT
> +};
> +
> +struct tbt_altmode {
> + enum tbt_state state;
> + struct typec_cable *cable;
> + struct typec_altmode *alt;
> + struct typec_altmode *plug[2];
> + u32 enter_vdo;
> +
> + struct work_struct work;
> + struct mutex lock; /* device lock */
What does it protect? The whole struct tbt_altmode?
> +};
[...]
> +
> +/* MUST HOLD tbt->lock.
Use lockdep_assert_held(tbt->lock) and remove the comment?
> + *
> + * If SOP' is available, enter that first (which will trigger a VDM response
> + * that will enter SOP" if available and then the port). If entering SOP' fails,
> + * stop attempting to enter either cable altmode (probably not supported) and
> + * directly enter the port altmode.
> + */
> +static int tbt_enter_modes_ordered(struct typec_altmode *alt)
> +{
> + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
> + int ret = 0;
> +
> + if (!tbt_ready(tbt->alt))
> + return -ENODEV;
> +
> + if (tbt->plug[TYPEC_PLUG_SOP_P]) {
> + ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
> + if (ret < 0) {
> + for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
> + if (tbt->plug[i])
> + typec_altmode_put_plug(tbt->plug[i]);
> +
> + tbt->plug[i] = NULL;
> + }
> + } else {
> + return ret;
> + }
> + }
> +
> + return tbt_enter_mode(tbt);
> +}
> +
> +static int tbt_cable_altmode_vdm(struct typec_altmode *alt,
> + enum typec_plug_index sop, const u32 hdr,
> + const u32 *vdo, int count)
> +{
[...]
> + case CMD_EXIT_MODE:
> + /* Exit in opposite order: Port, SOP", then SOP'. */
> + if (sop == TYPEC_PLUG_SOP_PP)
> + tbt->state = TBT_STATE_SOP_P_EXIT;
> + break;
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (tbt->state != TBT_STATE_IDLE)
> + schedule_work(&tbt->work);
> +
> +
Nitpick: Why two newlines?
> + mutex_unlock(&tbt->lock);
> + return 0;
> +}
> +
[...]
> diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
> index febe453b96be..b5e67a57762c 100644
> --- a/drivers/usb/typec/class.c
> +++ b/drivers/usb/typec/class.c
> @@ -458,7 +458,8 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
> struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
>
> if (attr == &dev_attr_active.attr)
> - if (!adev->ops || !adev->ops->activate)
> + if (!is_typec_port(adev->dev.parent) &&
> + (!adev->ops || !adev->ops->activate))
> return 0444;
>
> return attr->mode;
> @@ -563,7 +564,7 @@ typec_register_altmode(struct device *parent,
>
> if (is_port) {
> alt->attrs[3] = &dev_attr_supported_roles.attr;
> - alt->adev.active = true; /* Enabled by default */
> + alt->adev.active = !desc->inactive; /* Enabled by default */
> }
>
> sprintf(alt->group_name, "mode%d", desc->mode);
> diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> index d616b8807000..252af3f77039 100644
> --- a/include/linux/usb/typec.h
> +++ b/include/linux/usb/typec.h
> @@ -140,6 +140,7 @@ int typec_cable_set_identity(struct typec_cable *cable);
> * @mode: Index of the Mode
> * @vdo: VDO returned by Discover Modes USB PD command
> * @roles: Only for ports. DRP if the mode is available in both roles
> + * @inactive: Only for ports. Make this port inactive (default is active).
> *
> * Description of an Alternate Mode which a connector, cable plug or partner
> * supports.
> @@ -150,6 +151,7 @@ struct typec_altmode_desc {
> u32 vdo;
> /* Only used with ports */
> enum typec_port_data roles;
> + bool inactive;
> };
>
> void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision);
These two files look like they can be a different patch? Or the commit
text can describe these changes.
On Tue, Dec 10, 2024 at 4:21 PM Stephen Boyd <swboyd@chromium.org> wrote:
>
> Quoting Abhishek Pandit-Subedi (2024-12-06 15:38:13)
> > diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c
> > new file mode 100644
> > index 000000000000..14e89e9a7691
> > --- /dev/null
> > +++ b/drivers/usb/typec/altmodes/thunderbolt.c
> > @@ -0,0 +1,387 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/**
>
> Remove extra *, this isn't kerneldoc.
Done
>
> > + * USB Typec-C Thuderbolt3 Alternate Mode driver
> > + *
> > + * Copyright (C) 2019 Intel Corporation
> > + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
> > + */
> > +
> > +#include <linux/delay.h>
>
> Is this include used?
Compiles without it so I'm guessing no.
>
> > +#include <linux/mutex.h>
> > +#include <linux/module.h>
> > +#include <linux/usb/pd_vdo.h>
> > +#include <linux/usb/typec_altmode.h>
> > +#include <linux/usb/typec_tbt.h>
>
> Please include workqueue.h
Done
>
> > +
> > +enum tbt_state {
> > + TBT_STATE_IDLE,
> > + TBT_STATE_SOP_P_ENTER,
> > + TBT_STATE_SOP_PP_ENTER,
> > + TBT_STATE_ENTER,
> > + TBT_STATE_EXIT,
> > + TBT_STATE_SOP_PP_EXIT,
> > + TBT_STATE_SOP_P_EXIT
> > +};
> > +
> > +struct tbt_altmode {
> > + enum tbt_state state;
> > + struct typec_cable *cable;
> > + struct typec_altmode *alt;
> > + struct typec_altmode *plug[2];
> > + u32 enter_vdo;
> > +
> > + struct work_struct work;
> > + struct mutex lock; /* device lock */
>
> What does it protect? The whole struct tbt_altmode?
This looks like it's protecting control flow (enter/exit/vdm can all
be triggered on probe, via .activate or potentially autonomously via
port driver triggering the alt-mode).
>
> > +};
> [...]
> > +
> > +/* MUST HOLD tbt->lock.
>
> Use lockdep_assert_held(tbt->lock) and remove the comment?
Done.
>
> > + *
> > + * If SOP' is available, enter that first (which will trigger a VDM response
> > + * that will enter SOP" if available and then the port). If entering SOP' fails,
> > + * stop attempting to enter either cable altmode (probably not supported) and
> > + * directly enter the port altmode.
> > + */
> > +static int tbt_enter_modes_ordered(struct typec_altmode *alt)
> > +{
> > + struct tbt_altmode *tbt = typec_altmode_get_drvdata(alt);
> > + int ret = 0;
> > +
> > + if (!tbt_ready(tbt->alt))
> > + return -ENODEV;
> > +
> > + if (tbt->plug[TYPEC_PLUG_SOP_P]) {
> > + ret = typec_cable_altmode_enter(alt, TYPEC_PLUG_SOP_P, NULL);
> > + if (ret < 0) {
> > + for (int i = TYPEC_PLUG_SOP_PP; i > 0; --i) {
> > + if (tbt->plug[i])
> > + typec_altmode_put_plug(tbt->plug[i]);
> > +
> > + tbt->plug[i] = NULL;
> > + }
> > + } else {
> > + return ret;
> > + }
> > + }
> > +
> > + return tbt_enter_mode(tbt);
> > +}
> > +
> > +static int tbt_cable_altmode_vdm(struct typec_altmode *alt,
> > + enum typec_plug_index sop, const u32 hdr,
> > + const u32 *vdo, int count)
> > +{
> [...]
> > + case CMD_EXIT_MODE:
> > + /* Exit in opposite order: Port, SOP", then SOP'. */
> > + if (sop == TYPEC_PLUG_SOP_PP)
> > + tbt->state = TBT_STATE_SOP_P_EXIT;
> > + break;
> > + }
> > + break;
> > + default:
> > + break;
> > + }
> > +
> > + if (tbt->state != TBT_STATE_IDLE)
> > + schedule_work(&tbt->work);
> > +
> > +
>
> Nitpick: Why two newlines?
Clang format missed it :(
>
> > + mutex_unlock(&tbt->lock);
> > + return 0;
> > +}
> > +
> [...]
> > diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c
> > index febe453b96be..b5e67a57762c 100644
> > --- a/drivers/usb/typec/class.c
> > +++ b/drivers/usb/typec/class.c
> > @@ -458,7 +458,8 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
> > struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
> >
> > if (attr == &dev_attr_active.attr)
> > - if (!adev->ops || !adev->ops->activate)
> > + if (!is_typec_port(adev->dev.parent) &&
> > + (!adev->ops || !adev->ops->activate))
> > return 0444;
> >
> > return attr->mode;
> > @@ -563,7 +564,7 @@ typec_register_altmode(struct device *parent,
> >
> > if (is_port) {
> > alt->attrs[3] = &dev_attr_supported_roles.attr;
> > - alt->adev.active = true; /* Enabled by default */
> > + alt->adev.active = !desc->inactive; /* Enabled by default */
> > }
> >
> > sprintf(alt->group_name, "mode%d", desc->mode);
> > diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h
> > index d616b8807000..252af3f77039 100644
> > --- a/include/linux/usb/typec.h
> > +++ b/include/linux/usb/typec.h
> > @@ -140,6 +140,7 @@ int typec_cable_set_identity(struct typec_cable *cable);
> > * @mode: Index of the Mode
> > * @vdo: VDO returned by Discover Modes USB PD command
> > * @roles: Only for ports. DRP if the mode is available in both roles
> > + * @inactive: Only for ports. Make this port inactive (default is active).
> > *
> > * Description of an Alternate Mode which a connector, cable plug or partner
> > * supports.
> > @@ -150,6 +151,7 @@ struct typec_altmode_desc {
> > u32 vdo;
> > /* Only used with ports */
> > enum typec_port_data roles;
> > + bool inactive;
> > };
> >
> > void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision);
>
> These two files look like they can be a different patch? Or the commit
> text can describe these changes.
I think earlier in the series, they were its own patch -- got merged
down into this over several refactors. I'll pull it out into its own
patch.
Hi Abhishek,
kernel test robot noticed the following build warnings:
[auto build test WARNING on v6.12]
[cannot apply to chrome-platform/for-next chrome-platform/for-firmware-next usb/usb-testing usb/usb-next usb/usb-linus masahiroy-kbuild/for-next masahiroy-kbuild/fixes linus/master v6.13-rc1 next-20241206]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Abhishek-Pandit-Subedi/usb-typec-Only-use-SVID-for-matching-altmodes/20241207-074104
base: v6.12
patch link: https://lore.kernel.org/r/20241206153813.v4.2.I3080b036e8de0b9957c57c1c3059db7149c5e549%40changeid
patch subject: [PATCH v4 2/7] usb: typec: Add driver for Thunderbolt 3 Alternate Mode
config: sh-allmodconfig (https://download.01.org/0day-ci/archive/20241208/202412080010.bWqlreGq-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241208/202412080010.bWqlreGq-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202412080010.bWqlreGq-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> drivers/usb/typec/altmodes/thunderbolt.c:16: warning: cannot understand function prototype: 'enum tbt_state '
vim +16 drivers/usb/typec/altmodes/thunderbolt.c
15
> 16 enum tbt_state {
17 TBT_STATE_IDLE,
18 TBT_STATE_SOP_P_ENTER,
19 TBT_STATE_SOP_PP_ENTER,
20 TBT_STATE_ENTER,
21 TBT_STATE_EXIT,
22 TBT_STATE_SOP_PP_EXIT,
23 TBT_STATE_SOP_P_EXIT
24 };
25
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2025 Red Hat, Inc.