From: Benson Leung <bleung@google.com>
To: Abhishek Pandit-Subedi <abhishekpandit@chromium.org>
Cc: heikki.krogerus@linux.intel.com, tzungbi@kernel.org,
linux-usb@vger.kernel.org, chrome-platform@lists.linux.dev,
jthies@google.com, akuchynski@google.com, pmalani@chromium.org,
dmitry.baryshkov@linaro.org,
Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
linux-kernel@vger.kernel.org, danielgeorgem@google.com
Subject: Re: [PATCH v3 2/7] usb: typec: Add driver for Thunderbolt 3 Alternate Mode
Date: Fri, 22 Nov 2024 18:50:54 +0000 [thread overview]
Message-ID: <Z0DRWhZ745-N0DFE@google.com> (raw)
In-Reply-To: <20241107112955.v3.2.I3080b036e8de0b9957c57c1c3059db7149c5e549@changeid>
[-- Attachment #1: Type: text/plain, Size: 12138 bytes --]
Hi Abhishek,
On Thu, Nov 07, 2024 at 11:29:55AM -0800, Abhishek Pandit-Subedi wrote:
> 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:
> * 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))
>
> The rest of this patch should be the same as Heikki's original RFC.
>
>
> 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
>
> drivers/usb/typec/altmodes/Kconfig | 9 +
> drivers/usb/typec/altmodes/Makefile | 2 +
> drivers/usb/typec/altmodes/thunderbolt.c | 308 +++++++++++++++++++++++
> include/linux/usb/typec_tbt.h | 1 +
> 4 files changed, 320 insertions(+)
> 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..a945b9d35a1d
> --- /dev/null
> +++ b/drivers/usb/typec/altmodes/thunderbolt.c
> @@ -0,0 +1,308 @@
> +// 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_altmode_enter(tbt->plug[TYPEC_PLUG_SOP_P], NULL);
> + if (ret)
> + dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_P]->dev,
> + "failed to enter mode (%d)\n", ret);
> + break;
> + case TBT_STATE_SOP_PP_ENTER:
> + ret = typec_altmode_enter(tbt->plug[TYPEC_PLUG_SOP_PP], NULL);
> + if (ret)
> + dev_dbg(&tbt->plug[TYPEC_PLUG_SOP_PP]->dev,
> + "failed to enter mode (%d)\n", ret);
> + 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_altmode_exit(tbt->plug[TYPEC_PLUG_SOP_PP]);
> + break;
> + case TBT_STATE_SOP_P_EXIT:
> + typec_altmode_exit(tbt->plug[TYPEC_PLUG_SOP_P]);
> + break;
> + default:
> + break;
> + }
> +
> + tbt->state = TBT_STATE_IDLE;
> +
> + mutex_unlock(&tbt->lock);
> +}
> +
> +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);
> + 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 describeded in USB Type-C Spec
> + * R2.0 Section 6.7.3.
> + */
> + if (alt == tbt->plug[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 (alt == tbt->plug[TYPEC_PLUG_SOP_PP]) {
> + tbt->state = TBT_STATE_ENTER;
> + } else {
> + struct typec_thunderbolt_data data;
> +
> + data.device_mode = tbt->alt->vdo;
> + data.cable_mode =
> + tbt->plug[TYPEC_PLUG_SOP_P] ?
> + tbt->plug[TYPEC_PLUG_SOP_P]
> + ->vdo :
> + 0;
> + data.enter_vdo = tbt->enter_vdo;
> +
> + typec_altmode_notify(alt, TYPEC_STATE_MODAL, &data);
> + }
> + break;
> + case CMD_EXIT_MODE:
> + if (alt == tbt->alt) {
> + 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;
> + } else if (alt == tbt->plug[TYPEC_PLUG_SOP_PP]) {
> + 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 (!tbt_ready(alt))
> + return -ENODEV;
You need to mutex_unlock(&tbt->lock); before the return.
Credit to danielgeorgem@google.com for his catching this in his code review.
> +
> + /* Preventing the user space from entering/exiting the cable alt mode */
> + if (alt != tbt->alt)
> + ret = -EPERM;
> + else if (activate)
> + ret = tbt_enter_mode(tbt);
> + 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 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_PP])
> + tbt->state = TBT_STATE_SOP_PP_ENTER;
> + else if (tbt->plug[TYPEC_PLUG_SOP_P])
> + tbt->state = TBT_STATE_SOP_P_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;
> +
> + /* Thundebolt 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_VENDOR_INTEL)
> + break;
> +
> + plug->desc = "Thunderbolt3";
> + plug->ops = &tbt_altmode_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",
> + .owner = THIS_MODULE,
> + }
> +};
> +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/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.277.g8800431eea-goog
>
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
next prev parent reply other threads:[~2024-11-22 18:51 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-07 19:29 [PATCH v3 0/7] Thunderbolt and DP altmode support for cros-ec-typec Abhishek Pandit-Subedi
2024-11-07 19:29 ` [PATCH v3 1/7] usb: typec: Only use SVID for matching altmodes Abhishek Pandit-Subedi
2024-11-08 11:41 ` Heikki Krogerus
2024-11-07 19:29 ` [PATCH v3 2/7] usb: typec: Add driver for Thunderbolt 3 Alternate Mode Abhishek Pandit-Subedi
2024-11-09 6:21 ` Dmitry Baryshkov
2024-11-14 3:51 ` Abhishek Pandit-Subedi
2024-11-22 18:50 ` Benson Leung [this message]
2024-12-06 22:27 ` Abhishek Pandit-Subedi
2024-11-07 19:29 ` [PATCH v3 3/7] usb: typec: Check port is active before enter mode on probe Abhishek Pandit-Subedi
2024-11-08 12:21 ` Heikki Krogerus
2024-11-08 13:17 ` Greg Kroah-Hartman
2024-11-08 18:28 ` Abhishek Pandit-Subedi
2024-11-07 19:29 ` [PATCH v3 4/7] platform/chrome: cros_ec_typec: Update partner altmode active Abhishek Pandit-Subedi
2024-11-07 19:29 ` [PATCH v3 5/7] platform/chrome: cros_ec_typec: Displayport support Abhishek Pandit-Subedi
2024-11-09 6:38 ` Dmitry Baryshkov
2024-11-14 4:13 ` Abhishek Pandit-Subedi
2024-11-12 0:16 ` Benson Leung
2024-11-07 19:29 ` [PATCH v3 6/7] platform/chrome: cros_ec_typec: Thunderbolt support Abhishek Pandit-Subedi
2024-11-09 6:41 ` Dmitry Baryshkov
2024-11-14 4:01 ` Abhishek Pandit-Subedi
2024-11-14 10:55 ` Dmitry Baryshkov
2024-12-06 22:23 ` Abhishek Pandit-Subedi
2024-12-06 23:30 ` Dmitry Baryshkov
2024-11-07 19:30 ` [PATCH v3 7/7] platform/chrome: cros_ec_typec: Disable tbt on port Abhishek Pandit-Subedi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=Z0DRWhZ745-N0DFE@google.com \
--to=bleung@google.com \
--cc=abhishekpandit@chromium.org \
--cc=akuchynski@google.com \
--cc=chrome-platform@lists.linux.dev \
--cc=danielgeorgem@google.com \
--cc=dmitry.baryshkov@linaro.org \
--cc=gregkh@linuxfoundation.org \
--cc=heikki.krogerus@linux.intel.com \
--cc=jthies@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=pmalani@chromium.org \
--cc=tzungbi@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.