From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9D6D5C4167B for ; Thu, 7 Dec 2023 09:08:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231475AbjLGJH6 (ORCPT ); Thu, 7 Dec 2023 04:07:58 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59704 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229379AbjLGJHz (ORCPT ); Thu, 7 Dec 2023 04:07:55 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 413EFD4A for ; Thu, 7 Dec 2023 01:08:00 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-5d33b70fce8so6351037b3.0 for ; Thu, 07 Dec 2023 01:08:00 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940079; x=1702544879; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=IlwDNRF0jV105tsk3ywp0IxXP2QGZQspw7+2aSCQaIs=; b=1kq/U8owp4nheMbZQxXpxRt5JbLN0eG5qvfbCjftEraaRoKPsZisDdBKyqeAbqKBjx jXY+18ItAMvxcXeU/oRTt2e5yAGS6Uo/taUONZJsrFjRlFWruqzN1F2W1PI/omXoGZFU DmtM34ZM4ch7GGn6cjxnF0h9R9mJsUB/fpWnST2Ricmkph6EqHcq1dhaYZA9GEE3GmcA XqJoxNK6ly1JIWUiEHTBZHT8IxiS2PmLIcAeiXWpn87GETZCGqQtu/Eu0FjyMuzdOQqc KLMkIXVcj5kMhAcuHPmuWnnB+HQYvB5kDgLmM/cJnMr6Q5ZmiywzhYxedr/jjd8dGbVM cGDQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940079; x=1702544879; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=IlwDNRF0jV105tsk3ywp0IxXP2QGZQspw7+2aSCQaIs=; b=BAjmaQBGIf089q/gIdyS6LDtH48Ztiavz5yVi8W81nYwBWOtCF8ShcTeUbvZjbyynA Tuf3rJlgoDzgT54bBSoe+iic5CT8SOOQSzLjeXAfLF+LAsDW8zJQc9Mb6ak4thSFVy50 cs/O4Y2FMxm9u3CoDyQkjWfc/7xnqPQyEvafsHykBQ81e1xGJFbF9elNwIrgbEL0KjJA dqgZQ/C6bH3DjEXJIubNXQ5z4LwfmmL0r5rFrpQagHtCr1zlJ0vJtXQTxm7eHojtTzZ/ CL3jDBhbSlZKsYBdu3cdblKqr+l0y2NJc2s/zdDzphf5+g/yxIvjvMC57ZvLNT/0GOIo 6jkA== X-Gm-Message-State: AOJu0YyUjZ/OldyOCdA2t00I1/YmRqC03SMz5efog6GHlMAiQ129kM3p aQ/WVxO00XM51Zlic5wQIqHVddLW7q4KrEg= X-Google-Smtp-Source: AGHT+IHZEEQjZoEnOBsKzlP2Auy7Gh6l/teur99y5GR9ePRQwA0i3osCNMscCbr1fquiC9fxvx+zjW2/SXsp3/g= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a81:fe09:0:b0:5cb:1bf4:ce09 with SMTP id j9-20020a81fe09000000b005cb1bf4ce09mr34135ywn.2.1701940079525; Thu, 07 Dec 2023 01:07:59 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:32 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=15225; i=rdbabiera@google.com; h=from:subject; bh=3vbCFGH2UJtseAJZxfRyPH6azfTtU2voKWoUAVpBJZg=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3VGH8jbHqwQa2s0vbXhgynlZ+z//5tOc+t9W7pXyv TIx7vycjlIWBjEOBlkxRRZd/zyDG1dSt8zhrDGGmcPKBDKEgYtTACayYDXDfw+xuZfn32j9pXxG r+FScGDajbupBt0/pommNEyYvEVQdxrDP+vWusb18ap3TT8+dvRav2ljFRfbD8mQG01rD5Rybre 6wg4A X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-13-rdbabiera@google.com> Subject: [PATCH v1 01/10] usb: typec: bus: provide transmit type for alternate mode drivers From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add enum tcpm_altmode_transmit_type that Alternate Mode drivers can use to communicate which SOP type to send a SVDM on to the tcpm, and that the tcpm can use to communicate a received SVDM' SOP type to the Alternate Mode drivers. Update all typec_altmode_ops users to use tcpm_altmode_transmit_type, and drop all messages that are not TYPEC_ALTMODE_SOP. Default all calls that require sop_type as input to TYPEC_ALTMODE_SOP. Signed-off-by: RD Babiera --- drivers/platform/chrome/cros_typec_vdm.c | 12 +++++++++-- drivers/usb/typec/altmodes/displayport.c | 15 +++++++------- drivers/usb/typec/bus.c | 17 ++++++++++------ drivers/usb/typec/class.c | 2 +- drivers/usb/typec/tcpm/tcpm.c | 15 ++++++++------ drivers/usb/typec/ucsi/displayport.c | 18 +++++++++++++--- include/linux/usb/typec_altmode.h | 26 ++++++++++++++++++------ 7 files changed, 74 insertions(+), 31 deletions(-) diff --git a/drivers/platform/chrome/cros_typec_vdm.c b/drivers/platform/ch= rome/cros_typec_vdm.c index 3f632fd35000..ff33e5305866 100644 --- a/drivers/platform/chrome/cros_typec_vdm.c +++ b/drivers/platform/chrome/cros_typec_vdm.c @@ -92,7 +92,8 @@ void cros_typec_handle_vdm_response(struct cros_typec_dat= a *typec, int port_num) svid, port_num); } =20 -static int cros_typec_port_amode_enter(struct typec_altmode *amode, u32 *v= do) +static int cros_typec_port_amode_enter(struct typec_altmode *amode, u32 *v= do, + enum typec_altmode_transmit_type sop_type) { struct cros_typec_port *port =3D typec_altmode_get_drvdata(amode); struct ec_params_typec_control req =3D { @@ -102,6 +103,9 @@ static int cros_typec_port_amode_enter(struct typec_alt= mode *amode, u32 *vdo) struct typec_vdm_req vdm_req =3D {}; u32 hdr; =20 + if (sop_type !=3D TYPEC_ALTMODE_SOP) + return 0; + hdr =3D VDO(amode->svid, 1, SVDM_VER_2_0, CMD_ENTER_MODE); hdr |=3D VDO_OPOS(amode->mode); =20 @@ -118,7 +122,8 @@ static int cros_typec_port_amode_enter(struct typec_alt= mode *amode, u32 *vdo) } =20 static int cros_typec_port_amode_vdm(struct typec_altmode *amode, const u3= 2 hdr, - const u32 *vdo, int cnt) + const u32 *vdo, int cnt, + enum typec_altmode_transmit_type sop_type) { struct cros_typec_port *port =3D typec_altmode_get_drvdata(amode); struct ec_params_typec_control req =3D { @@ -128,6 +133,9 @@ static int cros_typec_port_amode_vdm(struct typec_altmo= de *amode, const u32 hdr, struct typec_vdm_req vdm_req =3D {}; int i; =20 + if (sop_type !=3D TYPEC_ALTMODE_SOP) + return 0; + vdm_req.vdm_data[0] =3D hdr; vdm_req.vdm_data_objects =3D cnt; for (i =3D 1; i < cnt; i++) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/a= ltmodes/displayport.c index f81bec0c7b86..5ed470069c3e 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -202,7 +202,7 @@ static int dp_altmode_configure_vdm(struct dp_altmode *= dp, u32 conf) return ret; } =20 - ret =3D typec_altmode_vdm(dp->alt, header, &conf, 2); + ret =3D typec_altmode_vdm(dp->alt, header, &conf, 2, TYPEC_ALTMODE_SOP); if (ret) dp_altmode_notify(dp); =20 @@ -221,7 +221,7 @@ static void dp_altmode_work(struct work_struct *work) =20 switch (dp->state) { case DP_STATE_ENTER: - ret =3D typec_altmode_enter(dp->alt, NULL); + ret =3D typec_altmode_enter(dp->alt, NULL, TYPEC_ALTMODE_SOP); if (ret && ret !=3D -EBUSY) dev_err(&dp->alt->dev, "failed to enter mode\n"); break; @@ -231,7 +231,7 @@ static void dp_altmode_work(struct work_struct *work) break; header =3D DP_HEADER(dp, svdm_version, DP_CMD_STATUS_UPDATE); vdo =3D 1; - ret =3D typec_altmode_vdm(dp->alt, header, &vdo, 2); + ret =3D typec_altmode_vdm(dp->alt, header, &vdo, 2, TYPEC_ALTMODE_SOP); if (ret) dev_err(&dp->alt->dev, "unable to send Status Update command (%d)\n", @@ -244,7 +244,7 @@ static void dp_altmode_work(struct work_struct *work) "unable to send Configure command (%d)\n", ret); break; case DP_STATE_EXIT: - if (typec_altmode_exit(dp->alt)) + if (typec_altmode_exit(dp->alt, TYPEC_ALTMODE_SOP)) dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); break; default: @@ -283,7 +283,8 @@ static void dp_altmode_attention(struct typec_altmode *= alt, const u32 vdo) } =20 static int dp_altmode_vdm(struct typec_altmode *alt, - const u32 hdr, const u32 *vdo, int count) + const u32 hdr, const u32 *vdo, int count, + enum typec_altmode_transmit_type sop_type) { struct dp_altmode *dp =3D typec_altmode_get_drvdata(alt); int cmd_type =3D PD_VDO_CMDT(hdr); @@ -350,8 +351,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt, =20 static int dp_altmode_activate(struct typec_altmode *alt, int activate) { - return activate ? typec_altmode_enter(alt, NULL) : - typec_altmode_exit(alt); + return activate ? typec_altmode_enter(alt, NULL, TYPEC_ALTMODE_SOP) : + typec_altmode_exit(alt, TYPEC_ALTMODE_SOP); } =20 static const struct typec_altmode_ops dp_altmode_ops =3D { diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index e95ec7e382bb..c9c6e55bed9b 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -117,13 +117,15 @@ EXPORT_SYMBOL_GPL(typec_altmode_notify); * typec_altmode_enter - Enter Mode * @adev: The alternate mode * @vdo: VDO for the Enter Mode command + * @sop_type: SOP* target for the Enter Mode command * * The alternate mode drivers use this function to enter mode. The port dr= ivers * use this to inform the alternate mode drivers that the partner has init= iated * Enter Mode command. If the alternate mode does not require VDO, @vdo mu= st be * NULL. */ -int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo) +int typec_altmode_enter(struct typec_altmode *adev, u32 *vdo, + enum typec_altmode_transmit_type sop_type) { struct altmode *partner =3D to_altmode(adev)->partner; struct typec_altmode *pdev =3D &partner->adev; @@ -144,17 +146,18 @@ int typec_altmode_enter(struct typec_altmode *adev, u= 32 *vdo) return ret; =20 /* Enter Mode */ - return pdev->ops->enter(pdev, vdo); + return pdev->ops->enter(pdev, vdo, sop_type); } EXPORT_SYMBOL_GPL(typec_altmode_enter); =20 /** * typec_altmode_exit - Exit Mode * @adev: The alternate mode + * @sop_type: SOP* target for the Exit Mode command * * The partner of @adev has initiated Exit Mode command. */ -int typec_altmode_exit(struct typec_altmode *adev) +int typec_altmode_exit(struct typec_altmode *adev, enum typec_altmode_tran= smit_type sop_type) { struct altmode *partner =3D to_altmode(adev)->partner; struct typec_altmode *pdev =3D &partner->adev; @@ -172,7 +175,7 @@ int typec_altmode_exit(struct typec_altmode *adev) return ret; =20 /* Exit Mode command */ - return pdev->ops->exit(pdev); + return pdev->ops->exit(pdev, sop_type); } EXPORT_SYMBOL_GPL(typec_altmode_exit); =20 @@ -206,13 +209,15 @@ EXPORT_SYMBOL_GPL(typec_altmode_attention); * @header: VDM Header * @vdo: Array of Vendor Defined Data Objects * @count: Number of Data Objects + * @sop_type: SOP* target for the VDM * * The alternate mode drivers use this function for SVID specific communic= ation * with the partner. The port drivers use it to deliver the Structured VDMs * received from the partners to the alternate mode drivers. */ int typec_altmode_vdm(struct typec_altmode *adev, - const u32 header, const u32 *vdo, int count) + const u32 header, const u32 *vdo, int count, + enum typec_altmode_transmit_type sop_type) { struct typec_altmode *pdev; struct altmode *altmode; @@ -230,7 +235,7 @@ int typec_altmode_vdm(struct typec_altmode *adev, if (!pdev->ops || !pdev->ops->vdm) return -EOPNOTSUPP; =20 - return pdev->ops->vdm(pdev, header, vdo, count); + return pdev->ops->vdm(pdev, header, vdo, count, sop_type); } EXPORT_SYMBOL_GPL(typec_altmode_vdm); =20 diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 2e0451bd336e..7514766df195 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -375,7 +375,7 @@ static ssize_t active_store(struct device *dev, struct = device_attribute *attr, =20 /* Make sure that the partner exits the mode before disabling */ if (altmode->partner && !enter && altmode->partner->adev.active) - typec_altmode_exit(&altmode->partner->adev); + typec_altmode_exit(&altmode->partner->adev, TYPEC_ALTMODE_SOP); } else if (altmode->partner) { if (enter && !altmode->partner->adev.active) { dev_warn(dev, "port has the mode disabled\n"); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index ff67553b6932..795e3145b0c2 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -1862,13 +1862,13 @@ static void tcpm_handle_vdm_request(struct tcpm_por= t *port, break; case ADEV_NOTIFY_USB_AND_QUEUE_VDM: WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL)); - typec_altmode_vdm(adev, p[0], &p[1], cnt); + typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP); break; case ADEV_QUEUE_VDM: - typec_altmode_vdm(adev, p[0], &p[1], cnt); + typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP); break; case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL: - if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) { + if (typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP)) { int svdm_version =3D typec_get_negotiated_svdm_version( port->typec_port); if (svdm_version < 0) @@ -2219,7 +2219,8 @@ static int tcpm_validate_caps(struct tcpm_port *port,= const u32 *pdo, return 0; } =20 -static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo) +static int tcpm_altmode_enter(struct typec_altmode *altmode, u32 *vdo, + enum typec_altmode_transmit_type tx_sop_type) { struct tcpm_port *port =3D typec_altmode_get_drvdata(altmode); int svdm_version; @@ -2236,7 +2237,8 @@ static int tcpm_altmode_enter(struct typec_altmode *a= ltmode, u32 *vdo) return 0; } =20 -static int tcpm_altmode_exit(struct typec_altmode *altmode) +static int tcpm_altmode_exit(struct typec_altmode *altmode, + enum typec_altmode_transmit_type tx_sop_type) { struct tcpm_port *port =3D typec_altmode_get_drvdata(altmode); int svdm_version; @@ -2254,7 +2256,8 @@ static int tcpm_altmode_exit(struct typec_altmode *al= tmode) } =20 static int tcpm_altmode_vdm(struct typec_altmode *altmode, - u32 header, const u32 *data, int count) + u32 header, const u32 *data, int count, + enum typec_altmode_transmit_type tx_sop_type) { struct tcpm_port *port =3D typec_altmode_get_drvdata(altmode); =20 diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/= displayport.c index d9d3c91125ca..9043defbb86c 100644 --- a/drivers/usb/typec/ucsi/displayport.c +++ b/drivers/usb/typec/ucsi/displayport.c @@ -45,7 +45,8 @@ struct ucsi_dp { * -EOPNOTSUPP. */ =20 -static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo) +static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo, + enum typec_altmode_transmit_type sop_type) { struct ucsi_dp *dp =3D typec_altmode_get_drvdata(alt); struct ucsi *ucsi =3D dp->con->ucsi; @@ -54,6 +55,9 @@ static int ucsi_displayport_enter(struct typec_altmode *a= lt, u32 *vdo) u8 cur =3D 0; int ret; =20 + if (sop_type !=3D TYPEC_ALTMODE_SOP) + return 0; + mutex_lock(&dp->con->lock); =20 if (!dp->override && dp->initialized) { @@ -105,13 +109,17 @@ static int ucsi_displayport_enter(struct typec_altmod= e *alt, u32 *vdo) return ret; } =20 -static int ucsi_displayport_exit(struct typec_altmode *alt) +static int ucsi_displayport_exit(struct typec_altmode *alt, + enum typec_altmode_transmit_type sop_type) { struct ucsi_dp *dp =3D typec_altmode_get_drvdata(alt); int svdm_version; u64 command; int ret =3D 0; =20 + if (sop_type !=3D TYPEC_ALTMODE_SOP) + return 0; + mutex_lock(&dp->con->lock); =20 if (!dp->override) { @@ -195,13 +203,17 @@ static int ucsi_displayport_configure(struct ucsi_dp = *dp) } =20 static int ucsi_displayport_vdm(struct typec_altmode *alt, - u32 header, const u32 *data, int count) + u32 header, const u32 *data, int count, + enum typec_altmode_transmit_type sop_type) { struct ucsi_dp *dp =3D typec_altmode_get_drvdata(alt); int cmd_type =3D PD_VDO_CMDT(header); int cmd =3D PD_VDO_CMD(header); int svdm_version; =20 + if (sop_type !=3D TYPEC_ALTMODE_SOP) + return 0; + mutex_lock(&dp->con->lock); =20 if (!dp->override && dp->initialized) { diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_al= tmode.h index 28aeef8f9e7b..4d527d92457d 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -34,6 +34,16 @@ struct typec_altmode { =20 #define to_typec_altmode(d) container_of(d, struct typec_altmode, dev) =20 +/** + * These are used by the Alternate Mode drivers to tell the tcpm to transm= it + * over the selected SOP type, and are used by the tcpm to communicate the + * received VDM SOP type to the Alternate Mode drivers. + */ +enum typec_altmode_transmit_type { + TYPEC_ALTMODE_SOP, + TYPEC_ALTMODE_SOP_PRIME, +}; + static inline void typec_altmode_set_drvdata(struct typec_altmode *altmode, void *data) { @@ -55,21 +65,25 @@ static inline void *typec_altmode_get_drvdata(struct ty= pec_altmode *altmode) * @activate: User callback for Enter/Exit Mode */ struct typec_altmode_ops { - int (*enter)(struct typec_altmode *altmode, u32 *vdo); - int (*exit)(struct typec_altmode *altmode); + int (*enter)(struct typec_altmode *altmode, u32 *vdo, + enum typec_altmode_transmit_type sop_type); + int (*exit)(struct typec_altmode *altmode, + enum typec_altmode_transmit_type sop_type); void (*attention)(struct typec_altmode *altmode, u32 vdo); int (*vdm)(struct typec_altmode *altmode, const u32 hdr, - const u32 *vdo, int cnt); + const u32 *vdo, int cnt, enum typec_altmode_transmit_type sop_type); int (*notify)(struct typec_altmode *altmode, unsigned long conf, void *data); int (*activate)(struct typec_altmode *altmode, int activate); }; =20 -int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo); -int typec_altmode_exit(struct typec_altmode *altmode); +int typec_altmode_enter(struct typec_altmode *altmode, u32 *vdo, + enum typec_altmode_transmit_type sop_type); +int typec_altmode_exit(struct typec_altmode *altmode, enum typec_altmode_t= ransmit_type sop_type); int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo); int typec_altmode_vdm(struct typec_altmode *altmode, - const u32 header, const u32 *vdo, int count); + const u32 header, const u32 *vdo, int count, + enum typec_altmode_transmit_type sop_type); int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf, void *data); const struct typec_altmode * --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 39B2FC10DC3 for ; Thu, 7 Dec 2023 09:08:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231580AbjLGJIA (ORCPT ); Thu, 7 Dec 2023 04:08:00 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59708 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231387AbjLGJH4 (ORCPT ); Thu, 7 Dec 2023 04:07:56 -0500 Received: from mail-yw1-x114a.google.com (mail-yw1-x114a.google.com [IPv6:2607:f8b0:4864:20::114a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 01E54137 for ; Thu, 7 Dec 2023 01:08:02 -0800 (PST) Received: by mail-yw1-x114a.google.com with SMTP id 00721157ae682-5d98fde753eso6626637b3.2 for ; Thu, 07 Dec 2023 01:08:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940081; x=1702544881; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=MuwO1i75ULAaVoZs1xyVi1Y8jYwy5X8JcJl1W/tPnh8=; b=s3thMgXTMJT6LdOho98Ub/eTaBCw2+LhL+MA+ITPyoPLLcQLOniE7k+OZ6JsUyJC6h EqAtq/YQEhtpQMA4gJU1yWj2V7O7mbpx/NT9pXyeU3/3nCqJYeaGDHKtdEW/O5yIzeR5 0UqStJp4MvaPY/VzsFNslz99RYypz8eIZRrbGf1mv5Fmfx1dGrqyD/mof5miv3IFzV3T K5HGz10fWaE7jEC5/3v2+5KZbEiHJeWVjeQOAFDVAj9PtJNddI2UEQa/DXjJ6DD7d5uc pI8r0SVjdVkGUEqXgQTK2iUGokPGYYj+K5Pt9gEwTk7Pn6vWaeYq6BCCuuyMWCeepww5 ZvHw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940081; x=1702544881; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MuwO1i75ULAaVoZs1xyVi1Y8jYwy5X8JcJl1W/tPnh8=; b=rs09Vu3Lo2vNbH/ndlbK4E80tPKL7dNJCFr0f0Ckw4Dfy4G/fgSzpCcO/PDGmQoPz4 Yzi43JQq/CVRN6pvu7rEMukWGKO9nZFcGdBUYUI2WUsOx9wZwJAdS/EFF1939dVYZToO F4mM2jlhb9uOXZaM4wiK9jhsjDtFAlLy1ldcrGVUHmgRioYSUa5LB+YUUHc6ppBZ6mfX 20uiFz5Oj4Uu6Bqekvg6fo/JEao3jcJ3ATiICET4i3G4yreVMszBnwLJA/jUMX/T0Xbh BkKDe9+uwBBqkBha09HsN0ojSoSLK9NbMcc74AAoIZemPnsCbVWaFhpRtqYDaOVRBpLv zSmA== X-Gm-Message-State: AOJu0Yw0pRdTOLQ8REWTBy0U5RRQLq7QcNbAhLuroxG0pocJ/Q8k8Y/h BMY0GTe7xo3b6v2kGvwBV2HyjuFJrC8eOpQ= X-Google-Smtp-Source: AGHT+IHsWlxxa/LP64vEBaMBpQ5GH1v+h1RJAoSuyJIrlkHzy7oBcam9q6nEHFOhWQKQ4PXh5iAneho56WSnQ10= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a05:690c:408b:b0:5d3:d6c3:dc6 with SMTP id gb11-20020a05690c408b00b005d3d6c30dc6mr30642ywb.7.1701940081217; Thu, 07 Dec 2023 01:08:01 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:33 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=9747; i=rdbabiera@google.com; h=from:subject; bh=aiPdmHgbqxhZVP1Ijw5Y52oNJyZpFyMwwjjUZtB41UU=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3dGvZOxvzg/3Lwg/FT6td69RcoFCTlnm5n3JK1lEX v2I//C8o5SFQYyDQVZMkUXXP8/gxpXULXM4a4xh5rAygQxh4OIUgIksL2JkODCjOGdds0xS68SN N6Wu2fB6mko17AvI2v7b/uOunLsy1YwM85IWaGskPUgWzOEUsPr2t2O+83bn8gshjlbb1S7JSJ1 lBwA= X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-14-rdbabiera@google.com> Subject: [PATCH v1 02/10] usb: typec: tcpci: enable reception of SOP' messages From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add cable_comm_capable to tcpci_data for tcpci drivers to indicate that the port tcpc is capable of communicating to cables over SOP. A corresponding tcpci callback tcpci_cable_comm_capable returns this value. The tcpm will primarily use this in later patches to determine if the port can transmit and receive SOP' messages. tcpci_set_pd_rx now utilizes the cable_comm_capable flag to determine if TCPC_RX_DETECT_SOP1 should be added to the bitfield when enabling PD message reception. In the tcpci, add a definition for TCPC_RX_BUF_FRAME_TYPE_SOP1 as described in the Type C Port Controller Interface Specification Section 4.4.14. Maxim based tcpci drivers are capable of SOP' communication, so the cable_comm_capable flag is set to true. process_rx now takes the SOP' into account and passes the value to tcpm_pd_receive. tcpm_pd_receive adds the SOP type as a parameter, and passes it within the pd_rx_event struct for tcpm_pd_rx_handler to use. For now, the handler drops all SOP' messages. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpci.c | 15 +++++++++++++-- drivers/usb/typec/tcpm/tcpci_maxim_core.c | 20 +++++++++++++++++--- drivers/usb/typec/tcpm/tcpm.c | 10 +++++++++- include/linux/usb/tcpci.h | 4 ++++ include/linux/usb/tcpm.h | 7 ++++++- 5 files changed, 49 insertions(+), 7 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 0ee3e6e29bb1..8ea4ed159a13 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -445,8 +445,11 @@ static int tcpci_set_pd_rx(struct tcpc_dev *tcpc, bool= enable) unsigned int reg =3D 0; int ret; =20 - if (enable) + if (enable) { reg =3D TCPC_RX_DETECT_SOP | TCPC_RX_DETECT_HARD_RESET; + if (tcpci->data->cable_comm_capable) + reg |=3D TCPC_RX_DETECT_SOP1; + } ret =3D regmap_write(tcpci->regmap, TCPC_RX_DETECT, reg); if (ret < 0) return ret; @@ -584,6 +587,13 @@ static int tcpci_pd_transmit(struct tcpc_dev *tcpc, en= um tcpm_transmit_type type return 0; } =20 +static bool tcpci_cable_comm_capable(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci =3D tcpc_to_tcpci(tcpc); + + return tcpci->data->cable_comm_capable; +} + static int tcpci_init(struct tcpc_dev *tcpc) { struct tcpci *tcpci =3D tcpc_to_tcpci(tcpc); @@ -712,7 +722,7 @@ irqreturn_t tcpci_irq(struct tcpci *tcpci) /* Read complete, clear RX status alert bit */ tcpci_write16(tcpci, TCPC_ALERT, TCPC_ALERT_RX_STATUS); =20 - tcpm_pd_receive(tcpci->port, &msg); + tcpm_pd_receive(tcpci->port, &msg, TCPC_TX_SOP); } =20 if (tcpci->data->vbus_vsafe0v && (status & TCPC_ALERT_EXTENDED_STATUS)) { @@ -793,6 +803,7 @@ struct tcpci *tcpci_register_port(struct device *dev, s= truct tcpci_data *data) tcpci->tcpc.enable_frs =3D tcpci_enable_frs; tcpci->tcpc.frs_sourcing_vbus =3D tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable =3D tcpci_set_partner_usb_comm_c= apable; + tcpci->tcpc.cable_comm_capable =3D tcpci_cable_comm_capable; =20 if (tcpci->data->check_contaminant) tcpci->tcpc.check_contaminant =3D tcpci_check_contaminant; diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/= tcpm/tcpci_maxim_core.c index 7fb966fd639b..548c583ab1a1 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -128,6 +128,7 @@ static void process_rx(struct max_tcpci_chip *chip, u16= status) u8 count, frame_type, rx_buf[TCPC_RECEIVE_BUFFER_LEN]; int ret, payload_index; u8 *rx_buf_ptr; + enum tcpm_transmit_type rx_type; =20 /* * READABLE_BYTE_COUNT: Indicates the number of bytes in the RX_BUF_BYTE_= x registers @@ -142,11 +143,23 @@ static void process_rx(struct max_tcpci_chip *chip, u= 16 status) =20 count =3D rx_buf[TCPC_RECEIVE_BUFFER_COUNT_OFFSET]; frame_type =3D rx_buf[TCPC_RECEIVE_BUFFER_FRAME_TYPE_OFFSET]; + switch (frame_type) { + case TCPC_RX_BUF_FRAME_TYPE_SOP1: + rx_type =3D TCPC_TX_SOP_PRIME; + break; + case TCPC_RX_BUF_FRAME_TYPE_SOP: + rx_type =3D TCPC_TX_SOP; + break; + default: + rx_type =3D TCPC_TX_SOP; + break; + } =20 - if (count =3D=3D 0 || frame_type !=3D TCPC_RX_BUF_FRAME_TYPE_SOP) { + if (count =3D=3D 0 || (frame_type !=3D TCPC_RX_BUF_FRAME_TYPE_SOP && + frame_type !=3D TCPC_RX_BUF_FRAME_TYPE_SOP1)) { max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS); dev_err(chip->dev, "%s\n", count =3D=3D 0 ? "error: count is 0" : - "error frame_type is not SOP"); + "error frame_type is not SOP/SOP'"); return; } =20 @@ -183,7 +196,7 @@ static void process_rx(struct max_tcpci_chip *chip, u16= status) if (ret < 0) return; =20 - tcpm_pd_receive(chip->port, &msg); + tcpm_pd_receive(chip->port, &msg, rx_type); } =20 static int max_tcpci_set_vbus(struct tcpci *tcpci, struct tcpci_data *tdat= a, bool source, bool sink) @@ -478,6 +491,7 @@ static int max_tcpci_probe(struct i2c_client *client) chip->data.vbus_vsafe0v =3D true; chip->data.set_partner_usb_comm_capable =3D max_tcpci_set_partner_usb_com= m_capable; chip->data.check_contaminant =3D max_tcpci_check_contaminant; + chip->data.cable_comm_capable =3D true; =20 max_tcpci_init_regs(chip); chip->tcpci =3D tcpci_register_port(chip->dev, &chip->data); diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 795e3145b0c2..5f9a4691f626 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -506,6 +506,7 @@ struct pd_rx_event { struct kthread_work work; struct tcpm_port *port; struct pd_message msg; + enum tcpm_transmit_type rx_sop_type; }; =20 static const char * const pd_rev[] =3D { @@ -2972,6 +2973,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *w= ork) const struct pd_message *msg =3D &event->msg; unsigned int cnt =3D pd_header_cnt_le(msg->header); struct tcpm_port *port =3D event->port; + enum tcpm_transmit_type rx_sop_type =3D event->rx_sop_type; =20 mutex_lock(&port->lock); =20 @@ -2982,6 +2984,10 @@ static void tcpm_pd_rx_handler(struct kthread_work *= work) enum pd_ctrl_msg_type type =3D pd_header_type_le(msg->header); unsigned int msgid =3D pd_header_msgid_le(msg->header); =20 + /* Ignore SOP' for now */ + if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) + goto done; + /* * USB PD standard, 6.6.1.2: * "... if MessageID value in a received Message is the @@ -3019,7 +3025,8 @@ static void tcpm_pd_rx_handler(struct kthread_work *w= ork) kfree(event); } =20 -void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg) +void tcpm_pd_receive(struct tcpm_port *port, const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { struct pd_rx_event *event; =20 @@ -3029,6 +3036,7 @@ void tcpm_pd_receive(struct tcpm_port *port, const st= ruct pd_message *msg) =20 kthread_init_work(&event->work, tcpm_pd_rx_handler); event->port =3D port; + event->rx_sop_type =3D rx_sop_type; memcpy(&event->msg, msg, sizeof(*msg)); kthread_queue_work(port->wq, &event->work); } diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 467e8045e9f8..62c518cca934 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -145,6 +145,7 @@ #define TCPC_RX_BYTE_CNT 0x30 #define TCPC_RX_BUF_FRAME_TYPE 0x31 #define TCPC_RX_BUF_FRAME_TYPE_SOP 0 +#define TCPC_RX_BUF_FRAME_TYPE_SOP1 1 #define TCPC_RX_HDR 0x32 #define TCPC_RX_DATA 0x34 /* through 0x4f */ =20 @@ -188,6 +189,8 @@ struct tcpci; * Optional; Enables TCPC to autonously discharge vbus on disconnect. * @vbus_vsafe0v: * optional; Set when TCPC can detect whether vbus is at VSAFE0V. + * @cable_comm_capable + * optional; Set when TCPC can communicate with cable plugs over SOP' * @set_partner_usb_comm_capable: * Optional; The USB Communications Capable bit indicates if port * partner is capable of communication over the USB data lines @@ -204,6 +207,7 @@ struct tcpci_data { unsigned char TX_BUF_BYTE_x_hidden:1; unsigned char auto_discharge_disconnect:1; unsigned char vbus_vsafe0v:1; + unsigned char cable_comm_capable:1; =20 int (*init)(struct tcpci *tcpci, struct tcpci_data *data); int (*set_vconn)(struct tcpci *tcpci, struct tcpci_data *data, diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 65fac5e1f317..41d1ac9c8bbf 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -119,6 +119,9 @@ enum tcpm_transmit_type { * at the end of the deboumce period or when the port is still * toggling. Chip level drivers are expected to check for contaminant * and call tcpm_clean_port when the port is clean. + * @cable_comm_capable + * Optional; Returns whether cable communication over SOP' is supported + * by the tcpc */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -154,6 +157,7 @@ struct tcpc_dev { bool (*is_vbus_vsafe0v)(struct tcpc_dev *dev); void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); void (*check_contaminant)(struct tcpc_dev *dev); + bool (*cable_comm_capable)(struct tcpc_dev *dev); }; =20 struct tcpm_port; @@ -166,7 +170,8 @@ void tcpm_cc_change(struct tcpm_port *port); void tcpm_sink_frs(struct tcpm_port *port); void tcpm_sourcing_vbus(struct tcpm_port *port); void tcpm_pd_receive(struct tcpm_port *port, - const struct pd_message *msg); + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type); void tcpm_pd_transmit_complete(struct tcpm_port *port, enum tcpm_transmit_status status); void tcpm_pd_hard_reset(struct tcpm_port *port); --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 92E09C4167B for ; Thu, 7 Dec 2023 09:08:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231748AbjLGJIE (ORCPT ); Thu, 7 Dec 2023 04:08:04 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59714 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231508AbjLGJH6 (ORCPT ); Thu, 7 Dec 2023 04:07:58 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D41D8D4A for ; Thu, 7 Dec 2023 01:08:03 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db7d201be93so1026380276.2 for ; Thu, 07 Dec 2023 01:08:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940083; x=1702544883; darn=vger.kernel.org; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:from:to:cc:subject:date:message-id :reply-to; bh=o14MGum+47AYWK8DkKGYzjB+QMQESr1NeJbUnOfv+2g=; b=pkaNSg+ub7nGua+JAwYC/Lk7cmaoGKuzEaXp6ZCAHInLwqdq2thPFzZRAAGcZgJbBE o62StJ8wvDYw1nusJs6gQF038iVSkoiHib1fE1II1BZJkA9Lm0fIdabmrJI80GnR8kNZ MELEcdX2ZYDsdxPFY+9d3/H2Q1Ejweg0IN5+NCvajQ7AbwePe2LsuiwfoV88H/0URjcx KP6wzl0u5XGTmOhPEk61VmpxJQ/UQMp59RpW4i9J9NE4D0/iOHi+GsGIgI96tlVemcIG +Org3u2jfZAwt2giVhFDXkp5jJ/lCYhTSl3y+aF1CHRdlIwmU9StctIK6PNtV/9fQ5i+ zQYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940083; x=1702544883; h=content-transfer-encoding:cc:to:from:subject:message-id:references :mime-version:in-reply-to:date:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to; bh=o14MGum+47AYWK8DkKGYzjB+QMQESr1NeJbUnOfv+2g=; b=i1pJQx66PF3hfN9d8B6v0Ig61oqACNCHq0kkN31pfbSmNY2yjfbEUwEfM4q4YtuGmH qPLyEedFAjaSuGhNYo6NZVJhO3daRVBwsHE/2oGswYhoUKBpX283SidwmRv4xXK9uBAe NJcR4N7b6cw/zRRVKqm452oD2mmI1kdAER80UUw/S5jonfMHP7orW8nOfnw+TEQPRhIK KiRTWwcXqhsJciUAKlFzCAqGMMyk4Yk5+f/g91l9RRMsmUbMjj+EM8n0VTpnrfy7ZPml QE/4GetDNsRpYnbV6/rOo9frSykjFye6ap2K2D2slIgp7qXRD7zSoEBlXr7nRH7eBwA4 29bg== X-Gm-Message-State: AOJu0YxQv/WmjDSPqCQVdXGarUW9h4bP6qh+qRJugRWevzGbFuwZvl1T jKFkT1di6q6azYF3LMS4I/pheJ4DvibYqow= X-Google-Smtp-Source: AGHT+IESqp84idXc+q5u9MDFeaSljtOtaoFQha272YyPKZC83BfNU4qv0DtqVTF2X+Ygtv8drGCkXxJ3QEblTWk= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a25:7489:0:b0:d9a:ec95:9687 with SMTP id p131-20020a257489000000b00d9aec959687mr24783ybc.11.1701940082998; Thu, 07 Dec 2023 01:08:02 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:34 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=9939; i=rdbabiera@google.com; h=from:subject; bh=I/HoHGVc7Oaex23bh6N4rq+2IpkSlsE3QSHESQ/ktpQ=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3dGPVk3cq3a6ccleW9kk15ORnYuORJfcDBReUNc78 VnotoLDHaUsDGIcDLJiiiy6/nkGN66kbpnDWWMMM4eVCWQIAxenAExk009GhtMlMfuiVouZBT9e viWzJKX/4pucB/fndm3UdN86g/O/dD8jw+rGdZN7lNUezMpZW6dzwawzSmfGms6JtZ/2pLtvnn9 Kgx0A X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-15-rdbabiera@google.com> Subject: [PATCH v1 03/10] usb: typec: tcpm: process receive and transmission of sop' messages From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Content-Transfer-Encoding: quoted-printable Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" Add negotiated revision and tx/rx message ids to tcpm_port specific to SOP'. tx_sop_type is added to the tcpm_port to determine whether the current constructed message will be sent over SOP or SOP' if not sent immediately. tcpm_pd_transmit now accounts for the transmit SOP* type, and updates the message ids accordingly. SOP* messages are currently not sent by the tcpm. tcpm_pd_rx_handler updates the received message ids. SOP* messages are not processed afterwards. The handler also calls tcpm_can_communicate_sop_prime to determine if a SOP' message is directed towards the port, and drops SOP' messages it should not respond to. tcpm_can_communicate_sop_prime is added as a helper to determine whether the port is capable of communicating over SOP' at a given moment. Being the Vconn source is a requirement in Power Delivery 3.0 but only a recommendation in Power Delviery 2.0. Because the port should ensure that the cable is powered before communication, always enforce the port is the Vconn source regardless of revision. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpm.c | 152 ++++++++++++++++++++++++++++++++-- 1 file changed, 143 insertions(+), 9 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 5f9a4691f626..313928f93d0c 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -493,6 +493,35 @@ struct tcpm_port { * transitions. */ bool potential_contaminant; + + /* SOP* Related Fields */ + /* + * tx_sop_type determines which SOP* a message is being sent on. + * For messages that are queued and not sent immediately such as in + * tcpm_queue_message or messages that send after state changes, + * the tx_sop_type should be set accordingly. + */ + enum tcpm_transmit_type tx_sop_type; + /* + * Prior to discovering the port partner's Specification Revision, the + * Vconn source and cable plug will use the lower of their two revisions. + * + * When the port partner's Specification Revision is discovered, the foll= owing + * rules are put in place. + * 1. If the cable revision (1) is lower than the revision negotiated + * between the port and partner (2), the port and partner will communicate + * on revision (2), but the port and cable will communicate on revision (= 1). + * 2. If the cable revision (1) is higher than the revision negotiated + * between the port and partner (2), the port and partner will communicate + * on revision (2), and the port and cable will communicate on revision (= 2) + * as well. + */ + unsigned int negotiated_rev_prime; + /* + * Each SOP* type must maintain their own tx and rx message IDs + */ + unsigned int message_id_prime; + unsigned int rx_msgid_prime; #ifdef CONFIG_DEBUG_FS struct dentry *dentry; struct mutex logbuffer_lock; /* log buffer access lock */ @@ -882,19 +911,32 @@ static void tcpm_ams_finish(struct tcpm_port *port) } =20 static int tcpm_pd_transmit(struct tcpm_port *port, - enum tcpm_transmit_type type, + enum tcpm_transmit_type tx_sop_type, const struct pd_message *msg) { unsigned long timeout; int ret; + unsigned int negotiated_rev; + + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + negotiated_rev =3D port->negotiated_rev_prime; + break; + case TCPC_TX_SOP: + negotiated_rev =3D port->negotiated_rev; + break; + default: + negotiated_rev =3D port->negotiated_rev; + break; + } =20 if (msg) tcpm_log(port, "PD TX, header: %#x", le16_to_cpu(msg->header)); else - tcpm_log(port, "PD TX, type: %#x", type); + tcpm_log(port, "PD TX, type: %#x", tx_sop_type); =20 reinit_completion(&port->tx_complete); - ret =3D port->tcpc->pd_transmit(port->tcpc, type, msg, port->negotiated_r= ev); + ret =3D port->tcpc->pd_transmit(port->tcpc, tx_sop_type, msg, negotiated_= rev); if (ret < 0) return ret; =20 @@ -907,7 +949,20 @@ static int tcpm_pd_transmit(struct tcpm_port *port, =20 switch (port->tx_status) { case TCPC_TX_SUCCESS: - port->message_id =3D (port->message_id + 1) & PD_HEADER_ID_MASK; + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + port->message_id_prime =3D (port->message_id_prime + 1) & + PD_HEADER_ID_MASK; + break; + case TCPC_TX_SOP: + port->message_id =3D (port->message_id + 1) & + PD_HEADER_ID_MASK; + break; + default: + port->message_id =3D (port->message_id + 1) & + PD_HEADER_ID_MASK; + break; + } /* * USB PD rev 2.0, 8.3.2.2.1: * USB PD rev 3.0, 8.3.2.1.3: @@ -1592,6 +1647,57 @@ static void tcpm_register_partner_altmodes(struct tc= pm_port *port) =20 #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_he= ader) =20 +/* + * Helper to determine whether the port is capable of SOP' communication a= t the + * current point in time. + */ +static bool tcpm_can_communicate_sop_prime(struct tcpm_port *port) +{ + /* Check to see if tcpc supports SOP' communication */ + if (!port->tcpc->cable_comm_capable || !port->tcpc->cable_comm_capable(po= rt->tcpc)) + return false; + /* + * Power Delivery 2.0 Section 6.3.11 + * Before communicating with a Cable Plug a Port Should ensure that it + * is the Vconn Source and that the Cable Plugs are powered by + * performing a Vconn swap if necessary. Since it cannot be guaranteed + * that the present Vconn Source is supplying Vconn, the only means to + * ensure that the Cable Plugs are powered is for a Port wishing to + * communicate with a Cable Plug is to become the Vconn Source. + * + * Power Delivery 3.0 Section 6.3.11 + * Before communicating with a Cable Plug a Port Shall ensure that it + * is the Vconn source. + */ + if (port->vconn_role !=3D TYPEC_SOURCE) + return false; + /* + * Power Delivery 2.0 Section 2.4.4 + * When no Contract or an Implicit Contract is in place the Source can + * communicate with a Cable Plug using SOP' packets in order to discover + * its characteristics. + * + * Power Delivery 3.0 Section 2.4.4 + * When no Contract or an Implicit Contract is in place only the Source + * port that is supplying Vconn is allowed to send packets to a Cable + * Plug and is allowed to respond to packets from the Cable Plug. + */ + if (!port->explicit_contract) + return port->pwr_role =3D=3D TYPEC_SOURCE; + if (port->negotiated_rev =3D=3D PD_REV30) + return true; + /* + * Power Delivery 2.0 Section 2.4.4 + * + * When an Explicit Contract is in place the DFP (either the Source or + * the Sink) can communicate with the Cable Plug(s) using SOP=E2=80=99/SO= P=E2=80=9D + * Packets (see Figure 2-3). + */ + if (port->negotiated_rev =3D=3D PD_REV20) + return port->data_role =3D=3D TYPEC_HOST; + return false; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action) @@ -2984,8 +3090,12 @@ static void tcpm_pd_rx_handler(struct kthread_work *= work) enum pd_ctrl_msg_type type =3D pd_header_type_le(msg->header); unsigned int msgid =3D pd_header_msgid_le(msg->header); =20 - /* Ignore SOP' for now */ - if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) + /* + * Drop SOP' messages if cannot receive via + * tcpm_can_communicate_sop_prime + */ + if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME && + !tcpm_can_communicate_sop_prime(port)) goto done; =20 /* @@ -2997,16 +3107,33 @@ static void tcpm_pd_rx_handler(struct kthread_work = *work) * Message). Note: this shall not apply to the Soft_Reset * Message which always has a MessageID value of zero." */ - if (msgid =3D=3D port->rx_msgid && type !=3D PD_CTRL_SOFT_RESET) + switch (rx_sop_type) { + case TCPC_TX_SOP_PRIME: + if (msgid =3D=3D port->rx_msgid_prime) + goto done; + port->rx_msgid_prime =3D msgid; + /* For now, don't do anything with SOP' Messages */ goto done; - port->rx_msgid =3D msgid; + case TCPC_TX_SOP: + if (msgid =3D=3D port->rx_msgid && + type !=3D PD_CTRL_SOFT_RESET) + goto done; + port->rx_msgid =3D msgid; + break; + default: + if (msgid =3D=3D port->rx_msgid && + type !=3D PD_CTRL_SOFT_RESET) + goto done; + port->rx_msgid =3D msgid; + break; + } =20 /* * If both ends believe to be DFP/host, we have a data role * mismatch. */ if (!!(le16_to_cpu(msg->header) & PD_HEADER_DATA_ROLE) =3D=3D - (port->data_role =3D=3D TYPEC_HOST)) { + (port->data_role =3D=3D TYPEC_HOST) && rx_sop_type =3D=3D TCPC_TX_SO= P) { tcpm_log(port, "Data role mismatch, initiating error recovery"); tcpm_set_state(port, ERROR_RECOVERY, 0); @@ -3711,6 +3838,7 @@ static void tcpm_reset_port(struct tcpm_port *port) * we can check tcpm_pd_rx_handler() if we had seen it before. */ port->rx_msgid =3D -1; + port->rx_msgid_prime =3D -1; =20 port->tcpc->set_pd_rx(port->tcpc, false); tcpm_init_vbus(port); /* also disables charging */ @@ -4025,8 +4153,11 @@ static void run_state_machine(struct tcpm_port *port) port->pwr_opmode =3D TYPEC_PWR_MODE_USB; port->caps_count =3D 0; port->negotiated_rev =3D PD_MAX_REV; + port->negotiated_rev_prime =3D PD_MAX_REV; port->message_id =3D 0; + port->message_id_prime =3D 0; port->rx_msgid =3D -1; + port->rx_msgid_prime =3D -1; port->explicit_contract =3D false; /* SNK -> SRC POWER/FAST_ROLE_SWAP finished */ if (port->ams =3D=3D POWER_ROLE_SWAP || @@ -4266,8 +4397,11 @@ static void run_state_machine(struct tcpm_port *port) typec_set_pwr_opmode(port->typec_port, opmode); port->pwr_opmode =3D TYPEC_PWR_MODE_USB; port->negotiated_rev =3D PD_MAX_REV; + port->negotiated_rev_prime =3D PD_MAX_REV; port->message_id =3D 0; + port->message_id_prime =3D 0; port->rx_msgid =3D -1; + port->rx_msgid_prime =3D -1; port->explicit_contract =3D false; =20 if (port->ams =3D=3D POWER_ROLE_SWAP || --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B992CC4167B for ; Thu, 7 Dec 2023 09:08:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232252AbjLGJIP (ORCPT ); Thu, 7 Dec 2023 04:08:15 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59716 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231573AbjLGJIA (ORCPT ); Thu, 7 Dec 2023 04:08:00 -0500 Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 2086DD7E for ; Thu, 7 Dec 2023 01:08:05 -0800 (PST) Received: by mail-pl1-x64a.google.com with SMTP id d9443c01a7336-1d08acaab7fso4912645ad.1 for ; Thu, 07 Dec 2023 01:08:05 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940084; x=1702544884; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=0ee9+vLb9jHxLxxtSEpDCi2PEBtLJvw/yxQpz8RWhRI=; b=QTolsXGwR37l69xAbt4L+irg1NTixnJz/dMqEejHRAauEBI2hLybM36OiAPGtgwxMG 4tm8/IKjKXGGVz7EVZ9ZYzBs9/osLQ79Y+l7YanoUHGn0xUDdi1GeKZ/JvoZ22lyjNeD K/jal3VPSPNLmT1RH+vQVIbqQhF1JwXcfap9/WoNjTwA2ic/2utqcJ3pPKfrfQTByFWY v+hjO+5gMV8RJGvuazoW5I9YdbJO2ZHHsc1w8oxHntqeDVdN+3ATt81TUc2LmZgVLPJQ c5qh4YiHQhBDBe4y6YOVyHm8w0F0Sjy08on+VKCLIMKyu6OdeLi03ibxU2dzVshXYYv2 tnOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940084; x=1702544884; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=0ee9+vLb9jHxLxxtSEpDCi2PEBtLJvw/yxQpz8RWhRI=; b=r0QpdrW3OVQSuyKgbfZuqLuR/p3VudtTEN8mThqShXpCyTJ7jBV78g97ZC/j+S8jfU GeyKz/h3+fR/fjtJJ+i0YySmgdSQo9/9mx0I/JwE35ou7PDYWmcYiUiCLyuD+u/m23dE IowUIiKv0fjPjtNRyMLhpofENxQPaWsk8Eth4/C1SjQviuHvSjWq5CEyAp72PmqeHtG3 Y800i0JOsiu0dg7icDDUz04RI6OBdFjj1W6ckQoKwAnVQQ+L3oa9jrFAir+9bwjjPVw1 cuYUBzqyAhUA7sX0iNvhqm/VoKEsf+e4Bfw+YD5vbKUclz1ZigTDqVJKMsuVngKWlirf cFYA== X-Gm-Message-State: AOJu0YxOPelzzHCFOi8wulYIscB3l3T7RqbgxBAX1v2552gIyEivfR4E GhrGIHbSYtIdLm4Sih5VxI79IXwP2Fj9E5A= X-Google-Smtp-Source: AGHT+IE144gbtvb/3pDM6DkUh1Jq1aglFiSsnUUvihpi0/LENYXSfNd9IxT6JKSeI29VryIJPTPqdtWONXT5+BI= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a17:902:dac5:b0:1d0:b595:4ea4 with SMTP id q5-20020a170902dac500b001d0b5954ea4mr28890plx.4.1701940084535; Thu, 07 Dec 2023 01:08:04 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:35 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=14654; i=rdbabiera@google.com; h=from:subject; bh=uzf9MVQzNy9YwxgF5wuGq6Gnc3qD0Q+pL2//fBHt9Rk=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3TGiRywSvAMPy0/96KceIsW3xv3bDK9Hfwsv/p60P UDzyzOhjlIWBjEOBlkxRRZd/zyDG1dSt8zhrDGGmcPKBDKEgYtTACZy+zcjwzW+cgtrYa9VensW MZpOKOtMuV15N+irlHxfctkpcadbMxj+O23Zwtw0+7vxdv7E6z9fVac9+WUUNUMs5tYMRVv+NKf fnAA= X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-16-rdbabiera@google.com> Subject: [PATCH v1 04/10] usb: typec: tcpm: add control message support for SOP' From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add tx_sop_type to tcpm_pd_send_control and rx_sop_type to tcpm_pd_ctrl_request. TCPC_TX_SOP is added to all pd_send_control calls, with the exception of TCPC_TX_SOP_PRIME being added to pd_send_control for a SOFT_RESET message sent after a Vconn swap that makes the port the Vconn source. After the SOFT_RESET is accepted, tcpm_pd_ctrl_request resets the proper protocol layer depending on the rx_sop_type. VCONN_SWAP_TURN_ON_VCONN redirects to a new state, VCONN_SWAP_SEND_SOFT_RESET. This state sends SOFT_RESET over SOP' before transitioning to the ready state if applicable. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpm.c | 159 ++++++++++++++++++++++++---------- include/linux/usb/pd.h | 1 + 2 files changed, 115 insertions(+), 45 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 313928f93d0c..d1c6157a2ea3 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -108,6 +108,7 @@ S(VCONN_SWAP_WAIT_FOR_VCONN), \ S(VCONN_SWAP_TURN_ON_VCONN), \ S(VCONN_SWAP_TURN_OFF_VCONN), \ + S(VCONN_SWAP_SEND_SOFT_RESET), \ \ S(FR_SWAP_SEND), \ S(FR_SWAP_SEND_TIMEOUT), \ @@ -2391,7 +2392,8 @@ static inline enum tcpm_state ready_state(struct tcpm= _port *port) } =20 static int tcpm_pd_send_control(struct tcpm_port *port, - enum pd_ctrl_msg_type type); + enum pd_ctrl_msg_type type, + enum tcpm_transmit_type tx_sop_type); =20 static void tcpm_handle_alert(struct tcpm_port *port, const __le32 *payloa= d, int cnt) @@ -2745,10 +2747,12 @@ static void tcpm_pps_complete(struct tcpm_port *por= t, int result) } =20 static void tcpm_pd_ctrl_request(struct tcpm_port *port, - const struct pd_message *msg) + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { enum pd_ctrl_msg_type type =3D pd_header_type_le(msg->header); enum tcpm_state next_state; + unsigned int rev =3D pd_header_rev_le(msg->header); =20 /* * Stop VDM state machine if interrupted by other Messages while NOT_SUPP= is allowed in @@ -2913,6 +2917,16 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *p= ort, case SOFT_RESET_SEND: if (port->ams =3D=3D SOFT_RESET_AMS) tcpm_ams_finish(port); + /* + * SOP' Soft Reset is done after Vconn Swap, + * which returns to ready state + */ + if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + if (rev < port->negotiated_rev_prime) + port->negotiated_rev_prime =3D rev; + tcpm_set_state(port, ready_state(port), 0); + break; + } if (port->pwr_role =3D=3D TYPEC_SOURCE) { port->upcoming_state =3D SRC_SEND_CAPABILITIES; tcpm_ams_start(port, POWER_NEGOTIATION); @@ -3112,8 +3126,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *w= ork) if (msgid =3D=3D port->rx_msgid_prime) goto done; port->rx_msgid_prime =3D msgid; - /* For now, don't do anything with SOP' Messages */ - goto done; + break; case TCPC_TX_SOP: if (msgid =3D=3D port->rx_msgid && type !=3D PD_CTRL_SOFT_RESET) @@ -3143,7 +3156,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *w= ork) else if (cnt) tcpm_pd_data_request(port, msg); else - tcpm_pd_ctrl_request(port, msg); + tcpm_pd_ctrl_request(port, msg, rx_sop_type); } } =20 @@ -3170,17 +3183,40 @@ void tcpm_pd_receive(struct tcpm_port *port, const = struct pd_message *msg, EXPORT_SYMBOL_GPL(tcpm_pd_receive); =20 static int tcpm_pd_send_control(struct tcpm_port *port, - enum pd_ctrl_msg_type type) + enum pd_ctrl_msg_type type, + enum tcpm_transmit_type tx_sop_type) { struct pd_message msg; =20 memset(&msg, 0, sizeof(msg)); - msg.header =3D PD_HEADER_LE(type, port->pwr_role, - port->data_role, - port->negotiated_rev, - port->message_id, 0); + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + msg.header =3D PD_HEADER_LE(type, + 0, /* Cable Plug Indicator for DFP/UFP */ + 0, /* Reserved */ + port->negotiated_rev, + port->message_id_prime, + 0); + break; + case TCPC_TX_SOP: + msg.header =3D PD_HEADER_LE(type, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 0); + break; + default: + msg.header =3D PD_HEADER_LE(type, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + 0); + break; + } =20 - return tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + return tcpm_pd_transmit(port, tx_sop_type, &msg); } =20 /* @@ -3199,13 +3235,13 @@ static bool tcpm_send_queued_message(struct tcpm_po= rt *port) =20 switch (queued_message) { case PD_MSG_CTRL_WAIT: - tcpm_pd_send_control(port, PD_CTRL_WAIT); + tcpm_pd_send_control(port, PD_CTRL_WAIT, TCPC_TX_SOP); break; case PD_MSG_CTRL_REJECT: - tcpm_pd_send_control(port, PD_CTRL_REJECT); + tcpm_pd_send_control(port, PD_CTRL_REJECT, TCPC_TX_SOP); break; case PD_MSG_CTRL_NOT_SUPP: - tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP); + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); break; case PD_MSG_DATA_SINK_CAP: ret =3D tcpm_pd_send_sink_caps(port); @@ -4220,7 +4256,7 @@ static void run_state_machine(struct tcpm_port *port) case SRC_NEGOTIATE_CAPABILITIES: ret =3D tcpm_pd_check_request(port); if (ret < 0) { - tcpm_pd_send_control(port, PD_CTRL_REJECT); + tcpm_pd_send_control(port, PD_CTRL_REJECT, TCPC_TX_SOP); if (!port->explicit_contract) { tcpm_set_state(port, SRC_WAIT_NEW_CAPABILITIES, 0); @@ -4228,7 +4264,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, SRC_READY, 0); } } else { - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_set_partner_usb_comm_capable(port, !!(port->sink_request & RDO_USB_COMM)); tcpm_set_state(port, SRC_TRANSITION_SUPPLY, @@ -4237,7 +4273,7 @@ static void run_state_machine(struct tcpm_port *port) break; case SRC_TRANSITION_SUPPLY: /* XXX: regulator_set_voltage(vbus, ...) */ - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); port->explicit_contract =3D true; typec_set_pwr_opmode(port->typec_port, TYPEC_PWR_MODE_PD); port->pwr_opmode =3D TYPEC_PWR_MODE_PD; @@ -4721,7 +4757,7 @@ static void run_state_machine(struct tcpm_port *port) /* remove existing capabilities */ usb_power_delivery_unregister_capabilities(port->partner_source_caps); port->partner_source_caps =3D NULL; - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_ams_finish(port); if (port->pwr_role =3D=3D TYPEC_SOURCE) { port->upcoming_state =3D SRC_SEND_CAPABILITIES; @@ -4738,28 +4774,41 @@ static void run_state_machine(struct tcpm_port *por= t) tcpm_ams_start(port, SOFT_RESET_AMS); break; case SOFT_RESET_SEND: - port->message_id =3D 0; - port->rx_msgid =3D -1; - /* remove existing capabilities */ - usb_power_delivery_unregister_capabilities(port->partner_source_caps); - port->partner_source_caps =3D NULL; - if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET)) - tcpm_set_state_cond(port, hard_reset_state(port), 0); - else - tcpm_set_state_cond(port, hard_reset_state(port), - PD_T_SENDER_RESPONSE); + /* + * Power Delivery 3.0 Section 6.3.13 + * + * A Soft_Reset Message Shall be targeted at a specific entity + * depending on the type of SOP* packet used. + */ + if (port->tx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + port->message_id_prime =3D 0; + port->rx_msgid_prime =3D -1; + tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP_PRIME); + tcpm_set_state_cond(port, ready_state(port), PD_T_SENDER_RESPONSE); + } else { + port->message_id =3D 0; + port->rx_msgid =3D -1; + /* remove existing capabilities */ + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps =3D NULL; + if (tcpm_pd_send_control(port, PD_CTRL_SOFT_RESET, TCPC_TX_SOP)) + tcpm_set_state_cond(port, hard_reset_state(port), 0); + else + tcpm_set_state_cond(port, hard_reset_state(port), + PD_T_SENDER_RESPONSE); + } break; =20 /* DR_Swap states */ case DR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_DR_SWAP); + tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) port->send_discover =3D true; tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) port->send_discover =3D true; tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); @@ -4783,7 +4832,7 @@ static void run_state_machine(struct tcpm_port *port) break; =20 case FR_SWAP_SEND: - if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP)) { + if (tcpm_pd_send_control(port, PD_CTRL_FR_SWAP, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4803,7 +4852,7 @@ static void run_state_machine(struct tcpm_port *port) break; case FR_SWAP_SNK_SRC_SOURCE_VBUS_APPLIED: tcpm_set_pwr_role(port, TYPEC_SOURCE); - if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { + if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4813,11 +4862,11 @@ static void run_state_machine(struct tcpm_port *por= t) =20 /* PR_Swap states */ case PR_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_set_state(port, PR_SWAP_START, 0); break; case PR_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_PR_SWAP); + tcpm_pd_send_control(port, PD_CTRL_PR_SWAP, TCPC_TX_SOP); tcpm_set_state_cond(port, PR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4859,7 +4908,7 @@ static void run_state_machine(struct tcpm_port *port) * supply is turned off" */ tcpm_set_pwr_role(port, TYPEC_SINK); - if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY)) { + if (tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP)) { tcpm_set_state(port, ERROR_RECOVERY, 0); break; } @@ -4906,17 +4955,17 @@ static void run_state_machine(struct tcpm_port *por= t) * Source." */ tcpm_set_pwr_role(port, TYPEC_SOURCE); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); tcpm_set_state(port, SRC_STARTUP, PD_T_SWAP_SRC_START); break; =20 case VCONN_SWAP_ACCEPT: - tcpm_pd_send_control(port, PD_CTRL_ACCEPT); + tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); tcpm_ams_finish(port); tcpm_set_state(port, VCONN_SWAP_START, 0); break; case VCONN_SWAP_SEND: - tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP); + tcpm_pd_send_control(port, PD_CTRL_VCONN_SWAP, TCPC_TX_SOP); tcpm_set_state(port, VCONN_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4935,14 +4984,34 @@ static void run_state_machine(struct tcpm_port *por= t) PD_T_VCONN_SOURCE_ON); break; case VCONN_SWAP_TURN_ON_VCONN: - tcpm_set_vconn(port, true); - tcpm_pd_send_control(port, PD_CTRL_PS_RDY); - tcpm_set_state(port, ready_state(port), 0); + ret =3D tcpm_set_vconn(port, true); + tcpm_pd_send_control(port, PD_CTRL_PS_RDY, TCPC_TX_SOP); + /* + * USB PD 3.0 Section 6.4.4.3.1 + * + * Note that a Cable Plug or VPD will not be ready for PD + * Communication until tVCONNStable after VCONN has been applied + */ + if (!ret) + tcpm_set_state(port, VCONN_SWAP_SEND_SOFT_RESET, + PD_T_VCONN_STABLE); + else + tcpm_set_state(port, ready_state(port), 0); break; case VCONN_SWAP_TURN_OFF_VCONN: tcpm_set_vconn(port, false); tcpm_set_state(port, ready_state(port), 0); break; + case VCONN_SWAP_SEND_SOFT_RESET: + tcpm_swap_complete(port, port->swap_status); + if (tcpm_can_communicate_sop_prime(port)) { + port->tx_sop_type =3D TCPC_TX_SOP_PRIME; + port->upcoming_state =3D SOFT_RESET_SEND; + tcpm_ams_start(port, SOFT_RESET_AMS); + } else { + tcpm_set_state(port, ready_state(port), 0); + } + break; =20 case DR_SWAP_CANCEL: case PR_SWAP_CANCEL: @@ -4978,7 +5047,7 @@ static void run_state_machine(struct tcpm_port *port) } break; case GET_STATUS_SEND: - tcpm_pd_send_control(port, PD_CTRL_GET_STATUS); + tcpm_pd_send_control(port, PD_CTRL_GET_STATUS, TCPC_TX_SOP); tcpm_set_state(port, GET_STATUS_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4986,7 +5055,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ready_state(port), 0); break; case GET_PPS_STATUS_SEND: - tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS); + tcpm_pd_send_control(port, PD_CTRL_GET_PPS_STATUS, TCPC_TX_SOP); tcpm_set_state(port, GET_PPS_STATUS_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; @@ -4994,7 +5063,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_set_state(port, ready_state(port), 0); break; case GET_SINK_CAP: - tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP); + tcpm_pd_send_control(port, PD_CTRL_GET_SINK_CAP, TCPC_TX_SOP); tcpm_set_state(port, GET_SINK_CAP_TIMEOUT, PD_T_SENDER_RESPONSE); break; case GET_SINK_CAP_TIMEOUT: @@ -5034,7 +5103,7 @@ static void run_state_machine(struct tcpm_port *port) =20 /* Chunk state */ case CHUNK_NOT_SUPP: - tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP); + tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); tcpm_set_state(port, port->pwr_role =3D=3D TYPEC_SOURCE ? SRC_READY : SN= K_READY, 0); break; default: diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index eb626af0e4e7..d50098fb16b5 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -483,6 +483,7 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_T_BIST_CONT_MODE 50 /* 30 - 60 ms */ #define PD_T_SINK_TX 16 /* 16 - 20 ms */ #define PD_T_CHUNK_NOT_SUPP 42 /* 40 - 50 ms */ +#define PD_T_VCONN_STABLE 50 =20 #define PD_T_DRP_TRY 100 /* 75 - 150 ms */ #define PD_T_DRP_TRYWAIT 600 /* 400 - 800 ms */ --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 0C776C4167B for ; Thu, 7 Dec 2023 09:08:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231518AbjLGJIR (ORCPT ); Thu, 7 Dec 2023 04:08:17 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:59752 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231607AbjLGJIB (ORCPT ); Thu, 7 Dec 2023 04:08:01 -0500 Received: from mail-pg1-x549.google.com (mail-pg1-x549.google.com [IPv6:2607:f8b0:4864:20::549]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13BBB10C4 for ; Thu, 7 Dec 2023 01:08:07 -0800 (PST) Received: by mail-pg1-x549.google.com with SMTP id 41be03b00d2f7-5c668b87db3so565833a12.3 for ; Thu, 07 Dec 2023 01:08:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940086; x=1702544886; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=yvDg/R2gSLn48edjZ4SZ5kD8LZGAyJ7uj0ZHgiqVOPk=; b=KWa4rV1AwKwkQd5LP3epf2bquSIv6c279YeEhmCSwGUEWUmspRvM1+dY+oPSlT2cNl HOsLQShKnSVgcZRRjuhN9CyfU5duqzbTWaVWq+n9euAWKqpNUir3gvPbsD1z8D70nwyg 2141/BDPZIltPEC4wLGAcr2LJVnalZYF05vPDiF6RxAbZM1JBmP/JTuKyArtUuQp50vq nM30reo7d8jkLnacktcm+Wp6vDdo9iSXOCMBG+RqleMnLyk6nPnYw+Uliyy2Q0fZ/MVf +rAnVRMQR/BLFe9jQ7LF3nqaG26auwl8GfCZe2WztoNMOX/dCK0pFB6YilP3iI6ytwll o7Mw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940086; x=1702544886; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=yvDg/R2gSLn48edjZ4SZ5kD8LZGAyJ7uj0ZHgiqVOPk=; b=NzRFdyWyMl1xC3N5QSj8stRY0rLFPZvBJMKWvZugypuV9I7N4tsSVQly7gCGkp+C21 AthjBAGJyiOsM7Gb2Zc5Y3+hww6df4+TDU7WnZsTDVtI+ZUwmgsFQaeDcRsEl+lTYvnK AsmH+4XRgYVY2BQaH0GZhQ+wdS1Ed4MDbp7qJwEX+ZB7MQzyjfTePob0ahvxpLeRF7Ji 0rguoQh9KewdmHpcY03AUlath0ANbby1hc8lkldIgeKPpbVs+IZMSYM8bLFKpvFVl3Ot 8h1s/gU8yWvQ+oPQfo/whcNIcrMSThkGjqRexFXLF4D2qw3WRBp7w9+2tgGrfZP2rqDV 9BvA== X-Gm-Message-State: AOJu0Yze/povlsT7g8GabCa+Ujd/9xkRBXGwH47Yu0vYfRhs+jRRyH4A sBh9oBjg3SZQYOHaxhNgvoYLwhCgj0GIROA= X-Google-Smtp-Source: AGHT+IGFS1y8GzVlmb5r97QSOC3E0Vl4Ayu3baL38b/OTdUA7IOvPkeE2QGWnc1JLw5uUPTfjYG2EqpVj+M152Q= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a17:902:dac3:b0:1d0:bf46:ad73 with SMTP id q3-20020a170902dac300b001d0bf46ad73mr27994plx.4.1701940086149; Thu, 07 Dec 2023 01:08:06 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:36 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=6626; i=rdbabiera@google.com; h=from:subject; bh=pigUdCpa03znkcBnwrc9DOizk0IUCBc0sAIBBmegE2I=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3TGdbf99qth/7vt1uniD8t/Ds3hsLW/9kVTWchc6p 3DScrN7RykLgxgHg6yYIouuf57BjSupW+Zw1hjDzGFlAhnCwMUpABNRm8zIsP9S2HfH6/tWHFdb 9dhk427nBubUCNdl9sJyXJ9eaM3Yb83wV1xno6nWZOMJbdfT0tdb6Eo1a5d468/cJnH1hsPNwxY 6nAA= X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-17-rdbabiera@google.com> Subject: [PATCH v1 05/10] usb: typec: tcpci: add attempt_vconn_swap_discovery callback From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add attempt_vconn_swap_discovery callback to determine whether the TCPM should perform a Vconn swap following Discover Identity on SOP. The tcpci will return false unless chip level drivers implement the callback. Maxim based TCPCs will return true unless the last connection resulted in a Vconn Over Current Fault, which may be the result of the Vconn swap. In addition to the port resetting, the TCPCI will veto the next Vconn swap from occurring. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpci.c | 11 +++++++++++ drivers/usb/typec/tcpm/tcpci_maxim.h | 1 + drivers/usb/typec/tcpm/tcpci_maxim_core.c | 17 ++++++++++++++++- include/linux/usb/tcpci.h | 9 +++++++++ include/linux/usb/tcpm.h | 9 +++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index 8ea4ed159a13..40c7b6224c74 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -594,6 +594,16 @@ static bool tcpci_cable_comm_capable(struct tcpc_dev *= tcpc) return tcpci->data->cable_comm_capable; } =20 +static bool tcpci_attempt_vconn_swap_discovery(struct tcpc_dev *tcpc) +{ + struct tcpci *tcpci =3D tcpc_to_tcpci(tcpc); + + if (tcpci->data->attempt_vconn_swap_discovery) + return tcpci->data->attempt_vconn_swap_discovery(tcpci, tcpci->data); + + return false; +} + static int tcpci_init(struct tcpc_dev *tcpc) { struct tcpci *tcpci =3D tcpc_to_tcpci(tcpc); @@ -804,6 +814,7 @@ struct tcpci *tcpci_register_port(struct device *dev, s= truct tcpci_data *data) tcpci->tcpc.frs_sourcing_vbus =3D tcpci_frs_sourcing_vbus; tcpci->tcpc.set_partner_usb_comm_capable =3D tcpci_set_partner_usb_comm_c= apable; tcpci->tcpc.cable_comm_capable =3D tcpci_cable_comm_capable; + tcpci->tcpc.attempt_vconn_swap_discovery =3D tcpci_attempt_vconn_swap_dis= covery; =20 if (tcpci->data->check_contaminant) tcpci->tcpc.check_contaminant =3D tcpci_check_contaminant; diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.h b/drivers/usb/typec/tcpm/= tcpci_maxim.h index 2c1c4d161b0d..78ff3b73ee7e 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.h +++ b/drivers/usb/typec/tcpm/tcpci_maxim.h @@ -62,6 +62,7 @@ struct max_tcpci_chip { struct i2c_client *client; struct tcpm_port *port; enum contamiant_state contaminant_state; + bool veto_vconn_swap; }; =20 static inline int max_tcpci_read16(struct max_tcpci_chip *chip, unsigned i= nt reg, u16 *val) diff --git a/drivers/usb/typec/tcpm/tcpci_maxim_core.c b/drivers/usb/typec/= tcpm/tcpci_maxim_core.c index 548c583ab1a1..6acc9f741bc3 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim_core.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim_core.c @@ -322,8 +322,10 @@ static irqreturn_t _max_tcpci_irq(struct max_tcpci_chi= p *chip, u16 status) if (ret < 0) return ret; =20 - if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) + if (reg_status & TCPC_FAULT_STATUS_VCONN_OC) { + chip->veto_vconn_swap =3D true; tcpm_port_error_recovery(chip->port); + } } =20 if (status & TCPC_ALERT_EXTND) { @@ -457,6 +459,18 @@ static void max_tcpci_check_contaminant(struct tcpci *= tcpci, struct tcpci_data * tcpm_port_clean(chip->port); } =20 +static bool max_tcpci_attempt_vconn_swap_discovery(struct tcpci *tcpci, st= ruct tcpci_data *tdata) +{ + struct max_tcpci_chip *chip =3D tdata_to_max_tcpci(tdata); + + if (chip->veto_vconn_swap) { + chip->veto_vconn_swap =3D false; + return false; + } + + return true; +} + static int max_tcpci_probe(struct i2c_client *client) { int ret; @@ -492,6 +506,7 @@ static int max_tcpci_probe(struct i2c_client *client) chip->data.set_partner_usb_comm_capable =3D max_tcpci_set_partner_usb_com= m_capable; chip->data.check_contaminant =3D max_tcpci_check_contaminant; chip->data.cable_comm_capable =3D true; + chip->data.attempt_vconn_swap_discovery =3D max_tcpci_attempt_vconn_swap_= discovery; =20 max_tcpci_init_regs(chip); chip->tcpci =3D tcpci_register_port(chip->dev, &chip->data); diff --git a/include/linux/usb/tcpci.h b/include/linux/usb/tcpci.h index 62c518cca934..e9da75125292 100644 --- a/include/linux/usb/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -201,6 +201,14 @@ struct tcpci; * Chip level drivers are expected to check for contaminant and call * tcpm_clean_port when the port is clean to put the port back into * toggling state. + * @attempt_vconn_swap_discovery: + * Optional; The callback is called by the TCPM when the result of + * a Discover Identity request indicates that the port partner is + * a receptacle capable of modal operation. Chip level TCPCI drivers + * can implement their own policy to determine if and when a Vconn + * swap following Discover Identity on SOP' occurs. + * Return true when the TCPM is allowed to request a Vconn swap + * after Discovery Identity on SOP. */ struct tcpci_data { struct regmap *regmap; @@ -219,6 +227,7 @@ struct tcpci_data { void (*set_partner_usb_comm_capable)(struct tcpci *tcpci, struct tcpci_da= ta *data, bool capable); void (*check_contaminant)(struct tcpci *tcpci, struct tcpci_data *data); + bool (*attempt_vconn_swap_discovery)(struct tcpci *tcpci, struct tcpci_da= ta *data); }; =20 struct tcpci *tcpci_register_port(struct device *dev, struct tcpci_data *d= ata); diff --git a/include/linux/usb/tcpm.h b/include/linux/usb/tcpm.h index 41d1ac9c8bbf..6671427f7eeb 100644 --- a/include/linux/usb/tcpm.h +++ b/include/linux/usb/tcpm.h @@ -122,6 +122,14 @@ enum tcpm_transmit_type { * @cable_comm_capable * Optional; Returns whether cable communication over SOP' is supported * by the tcpc + * @attempt_vconn_swap_discovery: + * Optional; The callback is called by the TCPM when the result of + * a Discover Identity request indicates that the port partner is + * a receptacle capable of modal operation. Chip level TCPCI drivers + * can implement their own policy to determine if and when a Vconn + * swap following Discover Identity on SOP' occurs. + * Return true when the TCPM is allowed to request a Vconn swap + * after Discovery Identity on SOP. */ struct tcpc_dev { struct fwnode_handle *fwnode; @@ -158,6 +166,7 @@ struct tcpc_dev { void (*set_partner_usb_comm_capable)(struct tcpc_dev *dev, bool enable); void (*check_contaminant)(struct tcpc_dev *dev); bool (*cable_comm_capable)(struct tcpc_dev *dev); + bool (*attempt_vconn_swap_discovery)(struct tcpc_dev *dev); }; =20 struct tcpm_port; --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id EC3C3C10DCE for ; Thu, 7 Dec 2023 09:08:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235226AbjLGJIX (ORCPT ); Thu, 7 Dec 2023 04:08:23 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60576 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231633AbjLGJIM (ORCPT ); Thu, 7 Dec 2023 04:08:12 -0500 Received: from mail-pl1-x64a.google.com (mail-pl1-x64a.google.com [IPv6:2607:f8b0:4864:20::64a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6EC7019A for ; Thu, 7 Dec 2023 01:08:08 -0800 (PST) Received: by mail-pl1-x64a.google.com with SMTP id d9443c01a7336-1cf74396cbeso6745955ad.2 for ; Thu, 07 Dec 2023 01:08:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940088; x=1702544888; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=jk5h25qYaL8QizzGrkg3KEn1TzRDQgKBfW7d8ssHKRE=; b=tubaNT7LCnz71chq8LPhkyGL9GB7tPsqqNxY1isoYLwgMtRNADOGeFImptPC1LmScG 2Cvv9qNUA/y6bv5NHx8uVdtEy6UVapts7LA1RLDkKfxSMHSq2YqIchoarZKfkpCYlfwJ Al3BKzKvv5oWJicsOlhNi3T49obTRAJ3axMurjTRRCF+AkwOTrFikpFDmN8oVgYHyIq3 IomzQo7j2703OUgkpJC6mcYUmI+OD0BrrAVyco4B6NOCNxtFflv17TtdxdPG6N0R5CFo YVXc+uMh/dXPyT3ijWFKfJx3wKoPTz+CUYa7iYAl0KFzlbXOdgL0ageSLF7i3+yfJtjt 24rA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940088; x=1702544888; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=jk5h25qYaL8QizzGrkg3KEn1TzRDQgKBfW7d8ssHKRE=; b=ZpfEoywR+3cysxIvugym85GwSWCO1QnSE/g6kBJ9hFm5Tfh0gvq5fPXEkwrJF23ELz KUKML1e1E018CsKYso1nWODgbZq9+7wQnh2I/yXWRoY0H88tKjzhUi7RXwUBD3s7z/SG PyT9jUWOqYDKrzy7YGkw/pwIBybHIfOaL+J/MUuHI8FzYlpg6xV00mGk8HX4Zv8D2pcy zuh6aYcAyIwSY5QLaTzGjzWWmZIsRcwKrh7VILFKIU6WghOv/UR0jrz3IEEKdRmznM0g dzdsX3+Akz3OYCsGZYoPtR1GGj6V4rIW1T6RuMzXftottSCOhOQTprUbpWPWp5rC1Uvm oEEg== X-Gm-Message-State: AOJu0YzuCVdNDMNLJIP6oLUOMqBUms0ugd52aKhwS3IiRWnP29mV4k2A WMro70COzk4yZhIr2vShHAJyqh+jhJTOZMk= X-Google-Smtp-Source: AGHT+IG6MJfySP8fvSPNMsnjc+Dgw98hG6V4ZDf+JT1QXG5rC/xtPEPTACUCy5FlFkfxo5nndINAwF93D7r/xqY= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a17:903:3304:b0:1d0:c3e7:d4c with SMTP id jk4-20020a170903330400b001d0c3e70d4cmr25844plb.2.1701940087928; Thu, 07 Dec 2023 01:08:07 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:37 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=29196; i=rdbabiera@google.com; h=from:subject; bh=lAs4yLlDbnmwtFMl5ELcGb2z4gYasy8I9TYZjnwlCck=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3bFuRxTmhP6V+CCprsn7d5ZLdX7aA8kzGy98OcZp9 vCgOdP+jlIWBjEOBlkxRRZd/zyDG1dSt8zhrDGGmcPKBDKEgYtTACZSuI6R4U09q8Md1n0u+ybO X7wyPUk2dEXJmvA8R44PV8UVWp5NYmNkuBPFOtfp3H0VjW1z9vUuXPNT2rTex8j39/KDP+w6Fv3 fywUA X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-18-rdbabiera@google.com> Subject: [PATCH v1 06/10] usb: typec: tcpm: add discover identity support for SOP' From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add data message handling and Discover Identity SVDM over SOP' This patch contains the following changes: 1. class Add typec_cable_set_svdm_version and typec_get_cable_svdm version symbols. Cables can operate under a lower PD revision than the port partner, and the max SVDM version is tied to the PD revision. 2. pd_vdo Add VDO indices for active and passive cables, add documentation to reflect expected number of objects. 3. tcpm Add typec_cable and typec_plug to tcpm_port to maintain cable and plug information. tcpm_port also adds send_discover_prime to indicate that Discover Identity should be sent out of the ready state. tcpm_queue_vdm and tcpm_send_vdm now take the SOP* type when transmitting messages. tcpm_handle_vdm_request and tcpm_pd_svdm also use the SOP* type. tcpm_pd_svdm handles Discover Identity messages for SOP and SOP'. In the SOP case, the port uses tcpm_attempt_vconn_swap_discovery to determine if a Vconn swap is needed for cable communication. Otherwise, the port will send Discover Identity on SOP' if it can, or default to Discover SVIDs. svdm_consume_identity_sop_prime consumes the result of Discover Identity on SOP'. It fills out cable identity and description, and it registers the cable. The SOP' plug is registered as well. The VDM state machine is adjusted to construct messages based on the SOP* type. If a transmission error occurs after the max number of retries for Discover Identity over SOP', then the port will send Discover SVIDs over SOP. Signed-off-by: RD Babiera --- drivers/usb/typec/class.c | 40 ++++ drivers/usb/typec/class.h | 1 + drivers/usb/typec/tcpm/tcpm.c | 388 ++++++++++++++++++++++++++++++---- include/linux/usb/pd_vdo.h | 8 +- include/linux/usb/typec.h | 3 + 5 files changed, 392 insertions(+), 48 deletions(-) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 7514766df195..83b10971ec5d 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -1205,6 +1205,46 @@ int typec_cable_set_identity(struct typec_cable *cab= le) } EXPORT_SYMBOL_GPL(typec_cable_set_identity); =20 +/** + * typec_get_cable_svdm_version - Get negotiated SVDM Version + * @port: USB Type-C Port. + * + * Get the negotiated SVDM Version. The Version is set to the port default + * value based on the PD Revision during cable registration, and updated a= fter + * a successful Discover Identity if the negotiated value is less than the + * default value. + * + * Returns usb_pd_svdm_ver if the cable has been registered otherwise -ENO= DEV. + */ +int typec_get_cable_svdm_version(struct typec_port *port) +{ + enum usb_pd_svdm_ver svdm_version; + struct device *cable_dev; + + cable_dev =3D device_find_child(&port->dev, NULL, cable_match); + if (!cable_dev) + return -ENODEV; + + svdm_version =3D to_typec_cable(cable_dev)->svdm_version; + put_device(cable_dev); + + return svdm_version; +} +EXPORT_SYMBOL_GPL(typec_get_cable_svdm_version); + +/** + * typec_cable_set_svdm_version - Set negotiated Structured VDM (SVDM) Ver= sion + * @cable: USB Type-C Active Cable that supports SVDM + * @svdm_version: Negotiated SVDM Version + * + * This routine is used to save the negotiated SVDM Version. + */ +void typec_cable_set_svdm_version(struct typec_cable *cable, + enum usb_pd_svdm_ver svdm_version) +{ + cable->svdm_version =3D svdm_version; +} + /** * typec_register_cable - Register a USB Type-C Cable * @port: The USB Type-C Port the cable is connected to diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index c36761ba3f59..759b98355eeb 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -23,6 +23,7 @@ struct typec_cable { struct usb_pd_identity *identity; unsigned int active:1; u16 pd_revision; /* 0300H =3D "3.0" */ + enum usb_pd_svdm_ver svdm_version; }; =20 struct typec_partner { diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index d1c6157a2ea3..2b650b419421 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -319,6 +319,12 @@ struct tcpm_port { struct typec_partner_desc partner_desc; struct typec_partner *partner; =20 + struct usb_pd_identity cable_ident; + struct typec_cable_desc cable_desc; + struct typec_cable *cable; + struct typec_plug_desc plug_prime_desc; + struct typec_plug *plug_prime; + enum typec_cc_status cc_req; enum typec_cc_status src_rp; /* work only if pd_supported =3D=3D false */ =20 @@ -496,6 +502,12 @@ struct tcpm_port { bool potential_contaminant; =20 /* SOP* Related Fields */ + /* + * Flag to determine if SOP' Discover Identity is available. The flag + * is set if Discover Identity on SOP' does not immediately follow + * Discover Identity on SOP. + */ + bool send_discover_prime; /* * tx_sop_type determines which SOP* a message is being sent on. * For messages that are queued and not sent immediately such as in @@ -1501,7 +1513,7 @@ static int tcpm_ams_start(struct tcpm_port *port, enu= m tcpm_ams ams) * VDM/VDO handling functions */ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header, - const u32 *data, int cnt) + const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) { u32 vdo_hdr =3D port->vdo_data[0]; =20 @@ -1509,7 +1521,10 @@ static void tcpm_queue_vdm(struct tcpm_port *port, c= onst u32 header, =20 /* If is sending discover_identity, handle received message first */ if (PD_VDO_SVDM(vdo_hdr) && PD_VDO_CMD(vdo_hdr) =3D=3D CMD_DISCOVER_IDENT= ) { - port->send_discover =3D true; + if (tx_sop_type =3D=3D TCPC_TX_SOP_PRIME) + port->send_discover_prime =3D true; + else + port->send_discover =3D true; mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); } else { /* Make sure we are not still processing a previous VDM packet */ @@ -1524,6 +1539,8 @@ static void tcpm_queue_vdm(struct tcpm_port *port, co= nst u32 header, port->vdm_state =3D VDM_STATE_READY; port->vdm_sm_running =3D true; =20 + port->tx_sop_type =3D tx_sop_type; + mod_vdm_delayed_work(port, 0); } =20 @@ -1531,7 +1548,7 @@ static void tcpm_queue_vdm_unlocked(struct tcpm_port = *port, const u32 header, const u32 *data, int cnt) { mutex_lock(&port->lock); - tcpm_queue_vdm(port, header, data, cnt); + tcpm_queue_vdm(port, header, data, cnt, TCPC_TX_SOP); mutex_unlock(&port->lock); } =20 @@ -1553,6 +1570,63 @@ static void svdm_consume_identity(struct tcpm_port *= port, const u32 *p, int cnt) PD_PRODUCT_PID(product), product & 0xffff); } =20 +static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const = u32 *p, int cnt) +{ + u32 idh =3D p[VDO_INDEX_IDH]; + u32 product =3D p[VDO_INDEX_PRODUCT]; + int svdm_version; + + /* + * Attempt to consume identity only if cable currently is not set + */ + if (!IS_ERR_OR_NULL(port->cable)) + goto register_plug; + + /* Reset cable identity */ + memset(&port->cable_ident, 0, sizeof(port->cable_ident)); + + /* Fill out id header, cert, product, cable VDO 1 */ + port->cable_ident.id_header =3D idh; + port->cable_ident.cert_stat =3D p[VDO_INDEX_CSTAT]; + port->cable_ident.product =3D product; + port->cable_ident.vdo[0] =3D p[VDO_INDEX_CABLE_1]; + + /* Fill out cable desc, infer svdm_version from pd revision */ + port->cable_desc.type =3D (enum typec_plug_type) (VDO_TYPEC_CABLE_TYPE(p[= VDO_INDEX_CABLE_1]) + + USB_PLUG_TYPE_A); + port->cable_desc.active =3D PD_IDH_PTYPE(idh) =3D=3D IDH_PTYPE_ACABLE ? 1= : 0; + /* Log PD Revision and additional cable VDO from negotiated revision */ + switch (port->negotiated_rev_prime) { + case PD_REV30: + port->cable_desc.pd_revision =3D 0x0300; + if (port->cable_desc.active) + port->cable_ident.vdo[1] =3D p[VDO_INDEX_CABLE_2]; + break; + case PD_REV20: + port->cable_desc.pd_revision =3D 0x0200; + break; + default: + port->cable_desc.pd_revision =3D 0x0200; + break; + } + port->cable_desc.identity =3D &port->cable_ident; + /* Register Cable, set identity and svdm_version */ + port->cable =3D typec_register_cable(port->typec_port, &port->cable_desc); + if (IS_ERR_OR_NULL(port->cable)) + return; + typec_cable_set_identity(port->cable); + /* Get SVDM version */ + svdm_version =3D PD_VDO_SVDM_VER(p[VDO_INDEX_HDR]); + typec_cable_set_svdm_version(port->cable, svdm_version); + +register_plug: + if (IS_ERR_OR_NULL(port->plug_prime)) { + port->plug_prime_desc.index =3D TYPEC_PLUG_SOP_P; + port->plug_prime =3D typec_register_plug(port->cable, + &port->plug_prime_desc); + } +} + static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int c= nt) { struct pd_mode_data *pmdata =3D &port->mode_data; @@ -1647,6 +1721,7 @@ static void tcpm_register_partner_altmodes(struct tcp= m_port *port) } =20 #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_he= ader) +#define supports_host(port) PD_IDH_HOST_SUPP((port->partner_ident.id_heade= r)) =20 /* * Helper to determine whether the port is capable of SOP' communication a= t the @@ -1699,9 +1774,35 @@ static bool tcpm_can_communicate_sop_prime(struct tc= pm_port *port) return false; } =20 +static bool tcpm_attempt_vconn_swap_discovery(struct tcpm_port *port) +{ + if (!port->tcpc->attempt_vconn_swap_discovery) + return false; + + /* Port is already source, no need to perform swap */ + if (port->vconn_role =3D=3D TYPEC_SOURCE) + return false; + + /* + * Partner needs to support Alternate Modes with modal support. If + * partner is also capable of being a USB Host, it could be a device + * that supports Alternate Modes as the DFP. + */ + if (!supports_modal(port) || supports_host(port)) + return false; + + if ((port->negotiated_rev =3D=3D PD_REV20 && port->data_role =3D=3D TYPEC= _HOST) || + port->negotiated_rev =3D=3D PD_REV30) + return port->tcpc->attempt_vconn_swap_discovery(port->tcpc); + + return false; +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, - enum adev_actions *adev_action) + enum adev_actions *adev_action, + enum tcpm_transmit_type rx_sop_type, + enum tcpm_transmit_type *response_tx_sop_type) { struct typec_port *typec =3D port->typec_port; struct typec_altmode *pdev; @@ -1711,6 +1812,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struc= t typec_altmode *adev, int cmd_type; int cmd; int i; + int ret; =20 cmd_type =3D PD_VDO_CMDT(p[0]); cmd =3D PD_VDO_CMD(p[0]); @@ -1723,9 +1825,28 @@ static int tcpm_pd_svdm(struct tcpm_port *port, stru= ct typec_altmode *adev, pdev =3D typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); =20 - svdm_version =3D typec_get_negotiated_svdm_version(typec); - if (svdm_version < 0) - return 0; + switch (rx_sop_type) { + case TCPC_TX_SOP_PRIME: + svdm_version =3D typec_get_cable_svdm_version(typec); + /* + * Update SVDM version if cable was discovered before port partner. + */ + if (!IS_ERR_OR_NULL(port->cable) && + PD_VDO_SVDM_VER(p[0]) < svdm_version) { + typec_cable_set_svdm_version(port->cable, svdm_version); + } + break; + case TCPC_TX_SOP: + svdm_version =3D typec_get_negotiated_svdm_version(typec); + if (svdm_version < 0) + return 0; + break; + default: + svdm_version =3D typec_get_negotiated_svdm_version(typec); + if (svdm_version < 0) + return 0; + break; + } =20 switch (cmd_type) { case CMDT_INIT: @@ -1795,22 +1916,88 @@ static int tcpm_pd_svdm(struct tcpm_port *port, str= uct typec_altmode *adev, (VDO_SVDM_VERS(typec_get_negotiated_svdm_version(typec))); break; case CMDT_RSP_ACK: - /* silently drop message if we are not connected */ - if (IS_ERR_OR_NULL(port->partner)) + /* + * Silently drop message if we are not connected, but can process + * if SOP' Discover Identity prior to explicit contract. + */ + if (IS_ERR_OR_NULL(port->partner) && + !(rx_sop_type =3D=3D TCPC_TX_SOP_PRIME && cmd =3D=3D CMD_DISCOVER_ID= ENT)) break; =20 tcpm_ams_finish(port); =20 switch (cmd) { + /* + * SVDM Command Flow for SOP and SOP': + * SOP Discover Identity + * SOP' Discover Identity + * SOP Discover SVIDs + * Discover Modes + * + * Perform Discover SOP' if the port can communicate with cable + * plug. + */ case CMD_DISCOVER_IDENT: - if (PD_VDO_SVDM_VER(p[0]) < svdm_version) - typec_partner_set_svdm_version(port->partner, - PD_VDO_SVDM_VER(p[0])); - /* 6.4.4.3.1 */ - svdm_consume_identity(port, p, cnt); - response[0] =3D VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(ty= pec), - CMD_DISCOVER_SVID); - rlen =3D 1; + switch (rx_sop_type) { + case TCPC_TX_SOP: + if (PD_VDO_SVDM_VER(p[0]) < svdm_version) { + typec_partner_set_svdm_version(port->partner, + PD_VDO_SVDM_VER(p[0])); + if (!IS_ERR_OR_NULL(port->cable) && + (typec_get_cable_svdm_version(port->typec_port) > + svdm_version)) + typec_cable_set_svdm_version(port->cable, + svdm_version); + } + /* 6.4.4.3.1 */ + svdm_consume_identity(port, p, cnt); + /* Attempt Vconn swap, delay SOP' discovery if necessary */ + if (tcpm_attempt_vconn_swap_discovery(port)) { + port->send_discover_prime =3D true; + port->upcoming_state =3D VCONN_SWAP_SEND; + ret =3D tcpm_ams_start(port, VCONN_SWAP); + if (!ret) + return 0; + port->upcoming_state =3D INVALID_STATE; + port->send_discover_prime =3D false; + } + + /* + * Attempt Discover Identity on SOP' if the + * cable was not discovered previously, and use + * the SVDM version of the partner to probe. + */ + if (IS_ERR_OR_NULL(port->cable) && + tcpm_can_communicate_sop_prime(port)) { + *response_tx_sop_type =3D TCPC_TX_SOP_PRIME; + port->send_discover_prime =3D true; + response[0] =3D VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_IDENT); + rlen =3D 1; + } else { + *response_tx_sop_type =3D TCPC_TX_SOP; + response[0] =3D VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_SVID); + rlen =3D 1; + } + break; + case TCPC_TX_SOP_PRIME: + /* + * svdm_consume_identity_sop_prime will determine + * the svdm_version for the cable moving forward. + */ + svdm_consume_identity_sop_prime(port, p, cnt); + *response_tx_sop_type =3D TCPC_TX_SOP; + response[0] =3D VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version(typec), + CMD_DISCOVER_SVID); + rlen =3D 1; + break; + default: + return 0; + } break; case CMD_DISCOVER_SVID: /* 6.4.4.3.2 */ @@ -1896,13 +2083,15 @@ static void tcpm_pd_handle_msg(struct tcpm_port *po= rt, enum tcpm_ams ams); =20 static void tcpm_handle_vdm_request(struct tcpm_port *port, - const __le32 *payload, int cnt) + const __le32 *payload, int cnt, + enum tcpm_transmit_type rx_sop_type) { enum adev_actions adev_action =3D ADEV_NONE; struct typec_altmode *adev; u32 p[PD_MAX_PAYLOAD]; u32 response[8] =3D { }; int i, rlen =3D 0; + enum tcpm_transmit_type response_tx_sop_type =3D TCPC_TX_SOP; =20 for (i =3D 0; i < cnt; i++) p[i] =3D le32_to_cpu(payload[i]); @@ -1937,7 +2126,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port = *port, * - We will send NAK and the flag will be cleared in the state machine. */ port->vdm_sm_running =3D true; - rlen =3D tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action); + rlen =3D tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action, + rx_sop_type, &response_tx_sop_type); } else { if (port->negotiated_rev >=3D PD_REV30) tcpm_pd_handle_msg(port, PD_MSG_CTRL_NOT_SUPP, NONE_AMS); @@ -2005,19 +2195,38 @@ static void tcpm_handle_vdm_request(struct tcpm_por= t *port, mutex_lock(&port->lock); =20 if (rlen > 0) - tcpm_queue_vdm(port, response[0], &response[1], rlen - 1); + tcpm_queue_vdm(port, response[0], &response[1], rlen - 1, response_tx_so= p_type); else port->vdm_sm_running =3D false; } =20 static void tcpm_send_vdm(struct tcpm_port *port, u32 vid, int cmd, - const u32 *data, int count) + const u32 *data, int count, enum tcpm_transmit_type tx_sop_type) { - int svdm_version =3D typec_get_negotiated_svdm_version(port->typec_port); + int svdm_version; u32 header; =20 - if (svdm_version < 0) - return; + switch (tx_sop_type) { + case TCPC_TX_SOP_PRIME: + /* + * If the port partner is discovered, then the port partner's + * SVDM Version will be returned + */ + svdm_version =3D typec_get_cable_svdm_version(port->typec_port); + if (svdm_version < 0) + svdm_version =3D SVDM_VER_MAX; + break; + case TCPC_TX_SOP: + svdm_version =3D typec_get_negotiated_svdm_version(port->typec_port); + if (svdm_version < 0) + return; + break; + default: + svdm_version =3D typec_get_negotiated_svdm_version(port->typec_port); + if (svdm_version < 0) + return; + break; + } =20 if (WARN_ON(count > VDO_MAX_SIZE - 1)) count =3D VDO_MAX_SIZE - 1; @@ -2026,7 +2235,7 @@ static void tcpm_send_vdm(struct tcpm_port *port, u32= vid, int cmd, header =3D VDO(vid, ((vid & USB_SID_PD) =3D=3D USB_SID_PD) ? 1 : (PD_VDO_CMD(cmd) <=3D CMD_ATTENTION), svdm_version, cmd); - tcpm_queue_vdm(port, header, data, count); + tcpm_queue_vdm(port, header, data, count, tx_sop_type); } =20 static unsigned int vdm_ready_timeout(u32 vdm_hdr) @@ -2060,6 +2269,7 @@ static void vdm_run_state_machine(struct tcpm_port *p= ort) struct pd_message msg; int i, res =3D 0; u32 vdo_hdr =3D port->vdo_data[0]; + u32 response[8] =3D { }; =20 switch (port->vdm_state) { case VDM_STATE_READY: @@ -2084,7 +2294,17 @@ static void vdm_run_state_machine(struct tcpm_port *= port) case CMD_DISCOVER_IDENT: res =3D tcpm_ams_start(port, DISCOVER_IDENTITY); if (res =3D=3D 0) { - port->send_discover =3D false; + switch (port->tx_sop_type) { + case TCPC_TX_SOP_PRIME: + port->send_discover_prime =3D false; + break; + case TCPC_TX_SOP: + port->send_discover =3D false; + break; + default: + port->send_discover =3D false; + break; + } } else if (res =3D=3D -EAGAIN) { port->vdo_data[0] =3D 0; mod_send_discover_delayed_work(port, @@ -2153,19 +2373,50 @@ static void vdm_run_state_machine(struct tcpm_port = *port) tcpm_ams_finish(port); } else { tcpm_ams_finish(port); + if (port->tx_sop_type =3D=3D TCPC_TX_SOP) + break; + /* Handle SOP' Transmission Errors */ + switch (PD_VDO_CMD(vdo_hdr)) { + /* + * If Discover Identity fails on SOP', then resume + * discovery process on SOP only. + */ + case CMD_DISCOVER_IDENT: + port->vdo_data[0] =3D 0; + response[0] =3D VDO(USB_SID_PD, 1, + typec_get_negotiated_svdm_version( + port->typec_port), + CMD_DISCOVER_SVID); + tcpm_queue_vdm(port, response[0], &response[1], + 0, TCPC_TX_SOP); + break; + default: + break; + } } break; case VDM_STATE_SEND_MESSAGE: /* Prepare and send VDM */ memset(&msg, 0, sizeof(msg)); - msg.header =3D PD_HEADER_LE(PD_DATA_VENDOR_DEF, - port->pwr_role, - port->data_role, - port->negotiated_rev, - port->message_id, port->vdo_count); + if (port->tx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + msg.header =3D PD_HEADER_LE(PD_DATA_VENDOR_DEF, + 0, /* Power Role for SOP' is 0 */ + 0, /* Data Role for SOP' is 0 */ + port->negotiated_rev_prime, + port->message_id_prime, + port->vdo_count); + } else { + msg.header =3D PD_HEADER_LE(PD_DATA_VENDOR_DEF, + port->pwr_role, + port->data_role, + port->negotiated_rev, + port->message_id, + port->vdo_count); + } + for (i =3D 0; i < port->vdo_count; i++) msg.payload[i] =3D cpu_to_le32(port->vdo_data[i]); - res =3D tcpm_pd_transmit(port, TCPC_TX_SOP, &msg); + res =3D tcpm_pd_transmit(port, port->tx_sop_type, &msg); if (res < 0) { port->vdm_state =3D VDM_STATE_ERR_SEND; } else { @@ -2555,7 +2806,8 @@ static int tcpm_register_sink_caps(struct tcpm_port *= port) } =20 static void tcpm_pd_data_request(struct tcpm_port *port, - const struct pd_message *msg) + const struct pd_message *msg, + enum tcpm_transmit_type rx_sop_type) { enum pd_data_msg_type type =3D pd_header_type_le(msg->header); unsigned int cnt =3D pd_header_cnt_le(msg->header); @@ -2596,8 +2848,11 @@ static void tcpm_pd_data_request(struct tcpm_port *p= ort, break; } =20 - if (rev < PD_MAX_REV) + if (rev < PD_MAX_REV) { port->negotiated_rev =3D rev; + if (port->negotiated_rev_prime > port->negotiated_rev) + port->negotiated_rev_prime =3D port->negotiated_rev; + } =20 if (port->pwr_role =3D=3D TYPEC_SOURCE) { if (port->ams =3D=3D GET_SOURCE_CAPABILITIES) @@ -2648,8 +2903,11 @@ static void tcpm_pd_data_request(struct tcpm_port *p= ort, break; } =20 - if (rev < PD_MAX_REV) + if (rev < PD_MAX_REV) { port->negotiated_rev =3D rev; + if (port->negotiated_rev_prime > port->negotiated_rev) + port->negotiated_rev_prime =3D port->negotiated_rev; + } =20 if (port->pwr_role !=3D TYPEC_SOURCE || cnt !=3D 1) { tcpm_pd_handle_msg(port, @@ -2705,7 +2963,7 @@ static void tcpm_pd_data_request(struct tcpm_port *po= rt, NONE_AMS); break; case PD_DATA_VENDOR_DEF: - tcpm_handle_vdm_request(port, msg->payload, cnt); + tcpm_handle_vdm_request(port, msg->payload, cnt, rx_sop_type); break; case PD_DATA_BIST: port->bist_request =3D le32_to_cpu(msg->payload[0]); @@ -3154,7 +3412,7 @@ static void tcpm_pd_rx_handler(struct kthread_work *w= ork) if (le16_to_cpu(msg->header) & PD_HEADER_EXT_HDR) tcpm_pd_ext_msg_request(port, msg); else if (cnt) - tcpm_pd_data_request(port, msg); + tcpm_pd_data_request(port, msg, rx_sop_type); else tcpm_pd_ctrl_request(port, msg, rx_sop_type); } @@ -3811,6 +4069,7 @@ static int tcpm_src_attach(struct tcpm_port *port) =20 port->attached =3D true; port->send_discover =3D true; + port->send_discover_prime =3D false; =20 return 0; =20 @@ -3827,6 +4086,15 @@ static int tcpm_src_attach(struct tcpm_port *port) =20 static void tcpm_typec_disconnect(struct tcpm_port *port) { + /* + * Unregister plug/cable outside of port->connected because cable can + * be discovered before SRC_READY/SNK_READY states where port->connected + * is set. + */ + typec_unregister_plug(port->plug_prime); + typec_unregister_cable(port->cable); + port->plug_prime =3D NULL; + port->cable =3D NULL; if (port->connected) { typec_partner_set_usb_power_delivery(port->partner, NULL); typec_unregister_partner(port->partner); @@ -3949,6 +4217,7 @@ static int tcpm_snk_attach(struct tcpm_port *port) =20 port->attached =3D true; port->send_discover =3D true; + port->send_discover_prime =3D false; =20 return 0; } @@ -4310,14 +4579,23 @@ static void run_state_machine(struct tcpm_port *por= t) * 6.4.4.3.1 Discover Identity * "The Discover Identity Command Shall only be sent to SOP when there i= s an * Explicit Contract." - * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus us= ing - * port->explicit_contract to decide whether to send the command. + * + * Discover Identity on SOP' should be discovered prior to the + * ready state, but if done after a Vconn Swap following Discover + * Identity on SOP then the discovery process can be run here + * as well. */ if (port->explicit_contract) { - tcpm_set_initial_svdm_version(port); + if (port->send_discover_prime) { + port->tx_sop_type =3D TCPC_TX_SOP_PRIME; + } else { + port->tx_sop_type =3D TCPC_TX_SOP; + tcpm_set_initial_svdm_version(port); + } mod_send_discover_delayed_work(port, 0); } else { port->send_discover =3D false; + port->send_discover_prime =3D false; } =20 /* @@ -4608,14 +4886,23 @@ static void run_state_machine(struct tcpm_port *por= t) * 6.4.4.3.1 Discover Identity * "The Discover Identity Command Shall only be sent to SOP when there i= s an * Explicit Contract." - * For now, this driver only supports SOP for DISCOVER_IDENTITY, thus us= ing - * port->explicit_contract. + * + * Discover Identity on SOP' should be discovered prior to the + * ready state, but if done after a Vconn Swap following Discover + * Identity on SOP then the discovery process can be run here + * as well. */ if (port->explicit_contract) { - tcpm_set_initial_svdm_version(port); + if (port->send_discover_prime) { + port->tx_sop_type =3D TCPC_TX_SOP_PRIME; + } else { + port->tx_sop_type =3D TCPC_TX_SOP; + tcpm_set_initial_svdm_version(port); + } mod_send_discover_delayed_work(port, 0); } else { port->send_discover =3D false; + port->send_discover_prime =3D false; } =20 power_supply_changed(port->psy); @@ -4656,6 +4943,7 @@ static void run_state_machine(struct tcpm_port *port) tcpm_unregister_altmodes(port); port->nr_sink_caps =3D 0; port->send_discover =3D true; + port->send_discover_prime =3D false; if (port->pwr_role =3D=3D TYPEC_SOURCE) tcpm_set_state(port, SRC_HARD_RESET_VBUS_OFF, PD_T_PS_HARD_RESET); @@ -4802,15 +5090,20 @@ static void run_state_machine(struct tcpm_port *por= t) /* DR_Swap states */ case DR_SWAP_SEND: tcpm_pd_send_control(port, PD_CTRL_DR_SWAP, TCPC_TX_SOP); - if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) + if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) { port->send_discover =3D true; + port->send_discover_prime =3D false; + } tcpm_set_state_cond(port, DR_SWAP_SEND_TIMEOUT, PD_T_SENDER_RESPONSE); break; case DR_SWAP_ACCEPT: tcpm_pd_send_control(port, PD_CTRL_ACCEPT, TCPC_TX_SOP); - if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) + if (port->data_role =3D=3D TYPEC_DEVICE || port->negotiated_rev > PD_REV= 20) { port->send_discover =3D true; + port->send_discover_prime =3D false; + } + tcpm_set_state_cond(port, DR_SWAP_CHANGE_DR, 0); break; case DR_SWAP_SEND_TIMEOUT: @@ -5820,11 +6113,12 @@ static void tcpm_send_discover_work(struct kthread_= work *work) =20 mutex_lock(&port->lock); /* No need to send DISCOVER_IDENTITY anymore */ - if (!port->send_discover) + if (!port->send_discover && !port->send_discover_prime) goto unlock; =20 if (port->data_role =3D=3D TYPEC_DEVICE && port->negotiated_rev < PD_REV3= 0) { port->send_discover =3D false; + port->send_discover_prime =3D false; goto unlock; } =20 @@ -5834,7 +6128,7 @@ static void tcpm_send_discover_work(struct kthread_wo= rk *work) goto unlock; } =20 - tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0); + tcpm_send_vdm(port, USB_SID_PD, CMD_DISCOVER_IDENT, NULL, 0, port->tx_sop= _type); =20 unlock: mutex_unlock(&port->lock); diff --git a/include/linux/usb/pd_vdo.h b/include/linux/usb/pd_vdo.h index 3a747938cdab..136ba02aeeae 100644 --- a/include/linux/usb/pd_vdo.h +++ b/include/linux/usb/pd_vdo.h @@ -86,12 +86,15 @@ * * Request is simply properly formatted SVDM header * - * Response is 4 data objects: + * Response is 4 data objects for Power Delivery 2.0 and Passive Cables for + * Power Delivery 3.0. Active Cables in Power Delivery 3.0 have 5 data obj= ects. * [0] :: SVDM header * [1] :: Identitiy header * [2] :: Cert Stat VDO * [3] :: (Product | Cable) VDO + * [4] :: Cable VDO 1 * [4] :: AMA VDO + * [5] :: Cable VDO 2 * */ #define VDO_INDEX_HDR 0 @@ -100,6 +103,8 @@ #define VDO_INDEX_CABLE 3 #define VDO_INDEX_PRODUCT 3 #define VDO_INDEX_AMA 4 +#define VDO_INDEX_CABLE_1 4 +#define VDO_INDEX_CABLE_2 5 =20 /* * SVDM Identity Header @@ -150,6 +155,7 @@ #define PD_IDH_MODAL_SUPP(vdo) ((vdo) & (1 << 26)) #define PD_IDH_DFP_PTYPE(vdo) (((vdo) >> 23) & 0x7) #define PD_IDH_CONN_TYPE(vdo) (((vdo) >> 21) & 0x3) +#define PD_IDH_HOST_SUPP(vdo) ((vdo) & (1 << 31)) =20 /* * Cert Stat VDO diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index a05d6f6f2536..d652060ba8fe 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -303,6 +303,9 @@ void typec_unregister_cable(struct typec_cable *cable); struct typec_cable *typec_cable_get(struct typec_port *port); void typec_cable_put(struct typec_cable *cable); int typec_cable_is_active(struct typec_cable *cable); +int typec_get_cable_svdm_version(struct typec_port *port); +void typec_cable_set_svdm_version(struct typec_cable *cable, + enum usb_pd_svdm_ver svdm_version); =20 struct typec_plug *typec_register_plug(struct typec_cable *cable, struct typec_plug_desc *desc); --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E069AC4167B for ; Thu, 7 Dec 2023 09:08:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S235204AbjLGJIV (ORCPT ); Thu, 7 Dec 2023 04:08:21 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:60564 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231558AbjLGJIM (ORCPT ); Thu, 7 Dec 2023 04:08:12 -0500 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3966FD5C for ; Thu, 7 Dec 2023 01:08:10 -0800 (PST) Received: by mail-pl1-x649.google.com with SMTP id d9443c01a7336-1d05f06d312so4998475ad.0 for ; Thu, 07 Dec 2023 01:08:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940089; x=1702544889; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=znqUQgbDyGs2yHI5XAyPSDU3w/SigHyVPnlMFTmu+CA=; b=qh+2EABLfQ/SreRZgAi9zfyFayYMHK29dzS2Wcl4dPfBNvLA5vMvSrTvNM6pxA/xnP su6m2keU4bcBjubCmLsRiwn3MvBtKH8tnvaHYcxPiDp8pmTghDFsi47fHP5cpXLcy3d4 cDOmOy3i2tlY8OJ5iTgfZN+Z2eEZIg0p2sqz/3jPAIiX51JIGokaX03OIFMxK0vKGuTo VBBNR7Dul0l7iJfVAYH599HU1ybQKwd+9k4AWU/dmO5aLdwGiO9+vjYTvZ86uwMi4chk E6j845tgAkMlTvRSFvTdlHthO8glfbohV6Y8//LV/PkpW/WjK/k2sBjKpkWlKdyPRfdt OnKw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940089; x=1702544889; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=znqUQgbDyGs2yHI5XAyPSDU3w/SigHyVPnlMFTmu+CA=; b=JXl9QAzHESAD4QxmAaEjd4BDszFUBgQR3QUMvUSOdvZ2n2ZXW1iBAYLXNs/0W8rUJv KmV2YwTICfKgoADsL/k42jwHdCxOOjUfhxDrgPpMfSMg1Lc2YR4MjfRuoTiWCOrkKtrd b5sr0d4FxZoDi+BSoSOORiBg/WvMxMXKNdkwdm2npi16AMYJ3v+gKS2Uvla7Yul+zFEH pVDMWaJQk5GEC3yE5nH6g6ZY641i8y5tFu2qwDnOuVOnphf/F7akvNLrnH1sqiI8Gy+H Z/7MMOTCvvlwRMLCCNRlb1jfBXD8lp+HwnbSIbH+lEqKQlJWR+ZBTJZb+5JlBMufg+5S Rs8A== X-Gm-Message-State: AOJu0YyY9gcMR+K04VctssOvEKvmHTUgomyndV8TqnpQOylGDaGYfl1r XVbZaTRTQL/1w4Y59/QOBYR9xx/UN3yk3bE= X-Google-Smtp-Source: AGHT+IGCK+2bl6ADzAUM/jG97tfMH17DmF8B9kJMt9q3pqUQJxliqtVIlm8QzihmYwo4Y16lhk33Af1c+GFCI9o= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a17:903:492:b0:1d0:559e:4130 with SMTP id jj18-20020a170903049200b001d0559e4130mr23446plb.9.1701940089680; Thu, 07 Dec 2023 01:08:09 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:38 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=4417; i=rdbabiera@google.com; h=from:subject; bh=PMhwKTq+HFv3EMRYrPK9u2LnZd//sBDn+vc2LOClaUo=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3bFSVneLzI5w/F5jtvleR0/HhXd/7m40O3OyOvvbH a43r9xkO0pZGMQ4GGTFFFl0/fMMblxJ3TKHs8YYZg4rE8gQBi5OAZjIQ1GG/x77D66fkBGqsOMZ 63TjLZfeR4fKzFu9WEfbq4rho/Ol8oOMDCtWBO74vLbnfM0uX6/Eh6xrgr9M+nrqRtdOm3OT3tz tbuUDAA== X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-19-rdbabiera@google.com> Subject: [PATCH v1 07/10] usb: typec: tcpm: add state machine support for SRC_VDM_IDENTITY_REQUEST From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add SRC_VDM_IDENTITY_REQUEST state which first enters after SRC_STARTUP. The state sends Discover Identity on SOP' and transitions to SRC_SEND_CAPABILITIES. SRC_SEND_CAPABILITIES will transition back into SRC_VDM_IDENTITY_REQUEST instead of retrying immediately. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpm.c | 44 ++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 2b650b419421..f55bcbe6f031 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -146,7 +146,9 @@ S(PORT_RESET_WAIT_OFF), \ \ S(AMS_START), \ - S(CHUNK_NOT_SUPP) + S(CHUNK_NOT_SUPP), \ + \ + S(SRC_VDM_IDENTITY_REQUEST) =20 #define FOREACH_AMS(S) \ S(NONE_AMS), \ @@ -1989,6 +1991,14 @@ static int tcpm_pd_svdm(struct tcpm_port *port, stru= ct typec_altmode *adev, * the svdm_version for the cable moving forward. */ svdm_consume_identity_sop_prime(port, p, cnt); + /* + * If received before explicit contract, continue + * to SRC_SEND_CAPABILITIES + */ + if (port->state =3D=3D SRC_VDM_IDENTITY_REQUEST) { + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); + return 0; + } *response_tx_sop_type =3D TCPC_TX_SOP; response[0] =3D VDO(USB_SID_PD, 1, typec_get_negotiated_svdm_version(typec), @@ -2283,7 +2293,8 @@ static void vdm_run_state_machine(struct tcpm_port *p= ort) * if there's traffic or we're not in PDO ready state don't send * a VDM. */ - if (port->state !=3D SRC_READY && port->state !=3D SNK_READY) { + if (port->state !=3D SRC_READY && port->state !=3D SNK_READY && + port->state !=3D SRC_VDM_IDENTITY_REQUEST) { port->vdm_sm_running =3D false; break; } @@ -2359,13 +2370,22 @@ static void vdm_run_state_machine(struct tcpm_port = *port) tcpm_ams_finish(port); break; case VDM_STATE_ERR_SEND: + /* + * When sending Discover Identity to SOP' before establishing an + * explicit contract, do not retry. Instead, weave sending + * Source_Capabilities over SOP and Discover Identity over SOP'. + */ + if (port->state =3D=3D SRC_VDM_IDENTITY_REQUEST) { + tcpm_ams_finish(port); + port->vdm_state =3D VDM_STATE_DONE; + tcpm_set_state(port, SRC_SEND_CAPABILITIES, 0); /* * A partner which does not support USB PD will not reply, * so this is not a fatal error. At the same time, some * devices may not return GoodCRC under some circumstances, * so we need to retry. */ - if (port->vdm_retries < 3) { + } else if (port->vdm_retries < 3) { tcpm_log(port, "VDM Tx error, retry"); port->vdm_retries++; port->vdm_state =3D VDM_STATE_READY; @@ -4483,8 +4503,12 @@ static void run_state_machine(struct tcpm_port *port) } ret =3D tcpm_pd_send_source_caps(port); if (ret < 0) { - tcpm_set_state(port, SRC_SEND_CAPABILITIES, - PD_T_SEND_SOURCE_CAP); + if (tcpm_can_communicate_sop_prime(port) && + IS_ERR_OR_NULL(port->cable)) + tcpm_set_state(port, SRC_VDM_IDENTITY_REQUEST, 0); + else + tcpm_set_state(port, SRC_SEND_CAPABILITIES, + PD_T_SEND_SOURCE_CAP); } else { /* * Per standard, we should clear the reset counter here. @@ -5399,6 +5423,13 @@ static void run_state_machine(struct tcpm_port *port) tcpm_pd_send_control(port, PD_CTRL_NOT_SUPP, TCPC_TX_SOP); tcpm_set_state(port, port->pwr_role =3D=3D TYPEC_SOURCE ? SRC_READY : SN= K_READY, 0); break; + /* Cable states */ + case SRC_VDM_IDENTITY_REQUEST: + port->send_discover_prime =3D true; + port->tx_sop_type =3D TCPC_TX_SOP_PRIME; + mod_send_discover_delayed_work(port, 0); + port->upcoming_state =3D SRC_SEND_CAPABILITIES; + break; default: WARN(1, "Unexpected port state %d\n", port->state); break; @@ -6123,7 +6154,8 @@ static void tcpm_send_discover_work(struct kthread_wo= rk *work) } =20 /* Retry if the port is not idle */ - if ((port->state !=3D SRC_READY && port->state !=3D SNK_READY) || port->v= dm_sm_running) { + if ((port->state !=3D SRC_READY && port->state !=3D SNK_READY && + port->state !=3D SRC_VDM_IDENTITY_REQUEST) || port->vdm_sm_running) { mod_send_discover_delayed_work(port, SEND_DISCOVER_RETRY_MS); goto unlock; } --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id B66FEC4167B for ; Thu, 7 Dec 2023 09:08:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1378464AbjLGJI1 (ORCPT ); Thu, 7 Dec 2023 04:08:27 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37400 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231815AbjLGJIO (ORCPT ); Thu, 7 Dec 2023 04:08:14 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 26C6010D1 for ; Thu, 7 Dec 2023 01:08:12 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5c941936f7fso3110307b3.0 for ; Thu, 07 Dec 2023 01:08:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940091; x=1702544891; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=tOxIUa0mOiY57OqfSzMpsv9qbznTXMq+FnNhc0wRkZY=; b=qu+xmwFDABJgeWs8T6h224GLiYm8LCf87oLhr7IoG1qLcraubXDkBRAgxdrF5p0CdN 80OWAKbSxM6sYeXwWyBY3bZyQxPIdryvRqHm+Cp5ZoAj4QLxbOwK/II3rR4c24x8JVz4 EQrf4Y4gvCEk4ywZfO0m6XLHirCNsy0HfcYidotODztWCSI/lLWGBXA+T6K+ajNOpqb7 YS4Usu7jA/AA0rSpiwpSYklZGx90tmlC6XmQBP5iZD8pamTtbHDynpmCqyOLyUwdaQdq C2QjKyScrGspxhZuw/o0gDpTxengs7qSp/meF7J/XmdFx86/rTXBV7v44uOL/MLu6nHe 2U4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940091; x=1702544891; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=tOxIUa0mOiY57OqfSzMpsv9qbznTXMq+FnNhc0wRkZY=; b=GoVw7GjncXrKPbn2ziQq2fWh93m5Ls4Fnd0Irwv241x8Vt7u56RF6Cg9Tas4YwRX9V LwwOnpiFb6TadRgEylqeDjGEgzM9rN5wbNvS/rXtkG8tzc7Iel5GAHl+O2rTRPG9kovN 3jqQxPd1YgKhB/beDfSYObgkUyzeMdzQgFMKg1YXq3/bB7Wk+10p9V7yQ4ohRyIVT+M1 wzbJbUKlygnBW/97GNhD+XVsPLxulq1AMFja9XV7ACusHMOvUz4ODW5Jz7um4fXBGlOw hxL6wPIm+oHG4hcyNn/oETcj1OQGQeQBhOUdD9asxRidFmmlThE1mu/aPpDZdwtw/7yu R3eA== X-Gm-Message-State: AOJu0Yzln1aAlMLS9ZwdFS1rDR/dmc5j0j8Yak+ARpXNjDLcMHQH4EX4 UfThZLAMoPYZtB+n+hJ0kKYfrx6FQUU7/Ww= X-Google-Smtp-Source: AGHT+IFhZ3nR+8KirEVwsULnaxyUKLQfH88N7uw+lgRjFtxWBKXn/USasJNfSEPdIrcrjCboEILo6ZjPrWGzpVM= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a25:bccd:0:b0:d9a:ca20:1911 with SMTP id l13-20020a25bccd000000b00d9aca201911mr120118ybm.4.1701940091379; Thu, 07 Dec 2023 01:08:11 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:39 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=16551; i=rdbabiera@google.com; h=from:subject; bh=GyGKDkjvo0LOYMx9j3TE67tGed7UrJIe0wrGn8Pp3D8=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3XE31geyvFn3ob3SR9OxSDwl/+bawLS06ocTWox2H 85KPCfXUcrCIMbBICumyKLrn2dw40rqljmcNcYwc1iZQIYwcHEKwESWWDD8d0g/4fA+/6laXIHv Zct3XfIHa5WUNbY9sBDQuD05ceNcLYb/0fcSpK+sZlNYclJpmQhLkJPBX58vOz5Z9P9sefRTzvY gBwA= X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-20-rdbabiera@google.com> Subject: [PATCH v1 08/10] usb: typec: tcpm: add mode data message support for SOP' From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Adds Discover SVIDs and Discover Modes support for SOP' and Alt Mode SVDM support over SOP'. tcpm_port adds separate Alt Mode data for SOP'. svdm_consume_svids and svdm_consume_modes take the received SVDM's SOP* type to store svids/modes separately, and tcpm_register_plug_altmodes registers the active cable's alt modes. In tcpm_pd_svdm, the port will send Discover SVIDs to SOP' after Discover Modes on SOP if the connected cable is an active cable. Discover Modes on SOP' is sent following Discover SVIDs on SOP. Registering partner alt modes is delayed when an active cable is present until Discover Modes completes on SOP', or if the Discover SVIDs/Discover Modes request on SOP' encounters a transmission error. tcpm_queue_vdm_unlocked now takes tx_sop_type as input, and two helpers are implemented to convert between typec_altmode_transmit_type and tcpm_transmit_type. Alt Mode drivers will now receive SVDMs over SOP' and can send when applicable. Signed-off-by: RD Babiera --- drivers/usb/typec/tcpm/tcpm.c | 260 ++++++++++++++++++++++++++++------ 1 file changed, 218 insertions(+), 42 deletions(-) diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index f55bcbe6f031..b84cc4755efe 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -465,7 +465,9 @@ struct tcpm_port { =20 /* Alternate mode data */ struct pd_mode_data mode_data; + struct pd_mode_data mode_data_prime; struct typec_altmode *partner_altmode[ALTMODE_DISCOVERY_MAX]; + struct typec_altmode *plug_prime_altmode[ALTMODE_DISCOVERY_MAX]; struct typec_altmode *port_altmode[ALTMODE_DISCOVERY_MAX]; =20 /* Deadline in jiffies to exit src_try_wait state */ @@ -1547,10 +1549,10 @@ static void tcpm_queue_vdm(struct tcpm_port *port, = const u32 header, } =20 static void tcpm_queue_vdm_unlocked(struct tcpm_port *port, const u32 head= er, - const u32 *data, int cnt) + const u32 *data, int cnt, enum tcpm_transmit_type tx_sop_type) { mutex_lock(&port->lock); - tcpm_queue_vdm(port, header, data, cnt, TCPC_TX_SOP); + tcpm_queue_vdm(port, header, data, cnt, tx_sop_type); mutex_unlock(&port->lock); } =20 @@ -1629,9 +1631,11 @@ static void svdm_consume_identity_sop_prime(struct t= cpm_port *port, const u32 *p } } =20 -static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int c= nt) +static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int c= nt, + enum tcpm_transmit_type rx_sop_type) { - struct pd_mode_data *pmdata =3D &port->mode_data; + struct pd_mode_data *pmdata =3D rx_sop_type =3D=3D TCPC_TX_SOP_PRIME ? + &port->mode_data_prime : &port->mode_data; int i; =20 for (i =3D 1; i < cnt; i++) { @@ -1677,14 +1681,30 @@ static bool svdm_consume_svids(struct tcpm_port *po= rt, const u32 *p, int cnt) return false; } =20 -static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int c= nt) +static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int c= nt, + enum tcpm_transmit_type rx_sop_type) + { struct pd_mode_data *pmdata =3D &port->mode_data; struct typec_altmode_desc *paltmode; int i; =20 - if (pmdata->altmodes >=3D ARRAY_SIZE(port->partner_altmode)) { - /* Already logged in svdm_consume_svids() */ + switch (rx_sop_type) { + case TCPC_TX_SOP_PRIME: + pmdata =3D &port->mode_data_prime; + if (pmdata->altmodes >=3D ARRAY_SIZE(port->plug_prime_altmode)) { + /* Already logged in svdm_consume_svids() */ + return; + } + break; + case TCPC_TX_SOP: + pmdata =3D &port->mode_data; + if (pmdata->altmodes >=3D ARRAY_SIZE(port->partner_altmode)) { + /* Already logged in svdm_consume_svids() */ + return; + } + break; + default: return; } =20 @@ -1722,7 +1742,28 @@ static void tcpm_register_partner_altmodes(struct tc= pm_port *port) } } =20 +static void tcpm_register_plug_altmodes(struct tcpm_port *port) +{ + struct pd_mode_data *modep =3D &port->mode_data_prime; + struct typec_altmode *altmode; + int i; + + typec_plug_set_num_altmodes(port->plug_prime, modep->altmodes); + + for (i =3D 0; i < modep->altmodes; i++) { + altmode =3D typec_plug_register_altmode(port->plug_prime, + &modep->altmode_desc[i]); + if (IS_ERR(altmode)) { + tcpm_log(port, "Failed to register plug SVID 0x%04x", + modep->altmode_desc[i].svid); + altmode =3D NULL; + } + port->plug_prime_altmode[i] =3D altmode; + } +} + #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_he= ader) +#define supports_modal_cable(port) PD_IDH_MODAL_SUPP((port)->cable_ident.i= d_header) #define supports_host(port) PD_IDH_HOST_SUPP((port->partner_ident.id_heade= r)) =20 /* @@ -1800,6 +1841,14 @@ static bool tcpm_attempt_vconn_swap_discovery(struct= tcpm_port *port) return false; } =20 +static bool tcpm_cable_vdm_supported(struct tcpm_port *port) +{ + return !IS_ERR_OR_NULL(port->cable) && + typec_cable_is_active(port->cable) && + supports_modal_cable(port) && + tcpm_can_communicate_sop_prime(port); +} + static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev, const u32 *p, int cnt, u32 *response, enum adev_actions *adev_action, @@ -1807,8 +1856,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struc= t typec_altmode *adev, enum tcpm_transmit_type *response_tx_sop_type) { struct typec_port *typec =3D port->typec_port; - struct typec_altmode *pdev; - struct pd_mode_data *modep; + struct typec_altmode *pdev, *pdev_prime; + struct pd_mode_data *modep, *modep_prime; int svdm_version; int rlen =3D 0; int cmd_type; @@ -1822,13 +1871,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, str= uct typec_altmode *adev, tcpm_log(port, "Rx VDM cmd 0x%x type %d cmd %d len %d", p[0], cmd_type, cmd, cnt); =20 - modep =3D &port->mode_data; - - pdev =3D typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX, - PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0])); - switch (rx_sop_type) { case TCPC_TX_SOP_PRIME: + modep_prime =3D &port->mode_data_prime; + pdev_prime =3D typec_match_altmode(port->plug_prime_altmode, + ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), + PD_VDO_OPOS(p[0])); svdm_version =3D typec_get_cable_svdm_version(typec); /* * Update SVDM version if cable was discovered before port partner. @@ -1839,11 +1888,21 @@ static int tcpm_pd_svdm(struct tcpm_port *port, str= uct typec_altmode *adev, } break; case TCPC_TX_SOP: + modep =3D &port->mode_data; + pdev =3D typec_match_altmode(port->partner_altmode, + ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), + PD_VDO_OPOS(p[0])); svdm_version =3D typec_get_negotiated_svdm_version(typec); if (svdm_version < 0) return 0; break; default: + modep =3D &port->mode_data; + pdev =3D typec_match_altmode(port->partner_altmode, + ALTMODE_DISCOVERY_MAX, + PD_VDO_VID(p[0]), + PD_VDO_OPOS(p[0])); svdm_version =3D typec_get_negotiated_svdm_version(typec); if (svdm_version < 0) return 0; @@ -1935,6 +1994,9 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struc= t typec_altmode *adev, * SOP' Discover Identity * SOP Discover SVIDs * Discover Modes + * (Active Cables) + * SOP' Discover SVIDs + * Discover Modes * * Perform Discover SOP' if the port can communicate with cable * plug. @@ -2010,37 +2072,92 @@ static int tcpm_pd_svdm(struct tcpm_port *port, str= uct typec_altmode *adev, } break; case CMD_DISCOVER_SVID: + *response_tx_sop_type =3D rx_sop_type; /* 6.4.4.3.2 */ - if (svdm_consume_svids(port, p, cnt)) { + if (svdm_consume_svids(port, p, cnt, rx_sop_type)) { response[0] =3D VDO(USB_SID_PD, 1, svdm_version, CMD_DISCOVER_SVID); rlen =3D 1; - } else if (modep->nsvids && supports_modal(port)) { - response[0] =3D VDO(modep->svids[0], 1, svdm_version, - CMD_DISCOVER_MODES); - rlen =3D 1; + } else { + if (rx_sop_type =3D=3D TCPC_TX_SOP) { + if (modep->nsvids && supports_modal(port)) { + response[0] =3D VDO(modep->svids[0], 1, svdm_version, + CMD_DISCOVER_MODES); + rlen =3D 1; + } + } else if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + if (modep_prime->nsvids) { + response[0] =3D VDO(modep_prime->svids[0], 1, + svdm_version, CMD_DISCOVER_MODES); + rlen =3D 1; + } + } } break; case CMD_DISCOVER_MODES: - /* 6.4.4.3.3 */ - svdm_consume_modes(port, p, cnt); - modep->svid_index++; - if (modep->svid_index < modep->nsvids) { - u16 svid =3D modep->svids[modep->svid_index]; - response[0] =3D VDO(svid, 1, svdm_version, CMD_DISCOVER_MODES); - rlen =3D 1; - } else { - tcpm_register_partner_altmodes(port); + if (rx_sop_type =3D=3D TCPC_TX_SOP) { + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + modep->svid_index++; + if (modep->svid_index < modep->nsvids) { + u16 svid =3D modep->svids[modep->svid_index]; + *response_tx_sop_type =3D TCPC_TX_SOP; + response[0] =3D VDO(svid, 1, svdm_version, + CMD_DISCOVER_MODES); + rlen =3D 1; + } else if (tcpm_cable_vdm_supported(port)) { + *response_tx_sop_type =3D TCPC_TX_SOP_PRIME; + response[0] =3D VDO(USB_SID_PD, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_SVID); + rlen =3D 1; + } else { + tcpm_register_partner_altmodes(port); + } + } else if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + /* 6.4.4.3.3 */ + svdm_consume_modes(port, p, cnt, rx_sop_type); + modep_prime->svid_index++; + if (modep_prime->svid_index < modep_prime->nsvids) { + u16 svid =3D modep_prime->svids[modep_prime->svid_index]; + *response_tx_sop_type =3D TCPC_TX_SOP_PRIME; + response[0] =3D VDO(svid, 1, + typec_get_cable_svdm_version(typec), + CMD_DISCOVER_MODES); + rlen =3D 1; + } else { + tcpm_register_plug_altmodes(port); + tcpm_register_partner_altmodes(port); + } } break; case CMD_ENTER_MODE: - if (adev && pdev) - *adev_action =3D ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL; + if (rx_sop_type =3D=3D TCPC_TX_SOP) { + if (adev && pdev) { + typec_altmode_update_active(pdev, true); + *adev_action =3D ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL; + } + } else if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + if (adev && pdev_prime) { + typec_altmode_update_active(pdev_prime, true); + *adev_action =3D ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL; + } + } return 0; case CMD_EXIT_MODE: - if (adev && pdev) { - /* Back to USB Operation */ - *adev_action =3D ADEV_NOTIFY_USB_AND_QUEUE_VDM; - return 0; + if (rx_sop_type =3D=3D TCPC_TX_SOP) { + if (adev && pdev) { + typec_altmode_update_active(pdev, false); + /* Back to USB Operation */ + *adev_action =3D ADEV_NOTIFY_USB_AND_QUEUE_VDM; + return 0; + } + } else if (rx_sop_type =3D=3D TCPC_TX_SOP_PRIME) { + if (adev && pdev_prime) { + typec_altmode_update_active(pdev_prime, false); + /* Back to USB Operation */ + *adev_action =3D ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL; + return 0; + } } break; case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15): @@ -2092,6 +2209,30 @@ static void tcpm_pd_handle_msg(struct tcpm_port *por= t, enum pd_msg_request message, enum tcpm_ams ams); =20 +static enum typec_altmode_transmit_type tcpm_to_altmode_tx_type(enum tcpm_= transmit_type tx_type) +{ + switch (tx_type) { + case TCPC_TX_SOP_PRIME: + return TYPEC_ALTMODE_SOP_PRIME; + case TCPC_TX_SOP: + return TYPEC_ALTMODE_SOP; + default: + return TYPEC_ALTMODE_SOP; + } +} + +static enum tcpm_transmit_type altmode_to_tcpm_tx_type(enum typec_altmode_= transmit_type tx_type) +{ + switch (tx_type) { + case TYPEC_ALTMODE_SOP_PRIME: + return TCPC_TX_SOP_PRIME; + case TYPEC_ALTMODE_SOP: + return TCPC_TX_SOP; + default: + return TCPC_TX_SOP; + } +} + static void tcpm_handle_vdm_request(struct tcpm_port *port, const __le32 *payload, int cnt, enum tcpm_transmit_type rx_sop_type) @@ -2170,13 +2311,16 @@ static void tcpm_handle_vdm_request(struct tcpm_por= t *port, break; case ADEV_NOTIFY_USB_AND_QUEUE_VDM: WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL)); - typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP); + typec_altmode_vdm(adev, p[0], &p[1], cnt, + tcpm_to_altmode_tx_type(rx_sop_type)); break; case ADEV_QUEUE_VDM: - typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP); + typec_altmode_vdm(adev, p[0], &p[1], cnt, + tcpm_to_altmode_tx_type(rx_sop_type)); break; case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL: - if (typec_altmode_vdm(adev, p[0], &p[1], cnt, TYPEC_ALTMODE_SOP)) { + if (typec_altmode_vdm(adev, p[0], &p[1], cnt, + tcpm_to_altmode_tx_type(rx_sop_type))) { int svdm_version =3D typec_get_negotiated_svdm_version( port->typec_port); if (svdm_version < 0) @@ -2410,6 +2554,16 @@ static void vdm_run_state_machine(struct tcpm_port *= port) tcpm_queue_vdm(port, response[0], &response[1], 0, TCPC_TX_SOP); break; + /* + * If Discover SVIDs or Discover Modes fail, then + * proceed with Alt Mode discovery process on SOP. + */ + case CMD_DISCOVER_SVID: + tcpm_register_partner_altmodes(port); + break; + case CMD_DISCOVER_MODES: + tcpm_register_partner_altmodes(port); + break; default: break; } @@ -2605,14 +2759,20 @@ static int tcpm_altmode_enter(struct typec_altmode = *altmode, u32 *vdo, int svdm_version; u32 header; =20 - svdm_version =3D typec_get_negotiated_svdm_version(port->typec_port); + if (tx_sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME && !tcpm_cable_vdm_support= ed(port)) + return -EPERM; + + svdm_version =3D tx_sop_type =3D=3D TYPEC_ALTMODE_SOP ? + typec_get_negotiated_svdm_version(port->typec_port) : + typec_get_cable_svdm_version(port->typec_port); if (svdm_version < 0) return svdm_version; =20 header =3D VDO(altmode->svid, vdo ? 2 : 1, svdm_version, CMD_ENTER_MODE); header |=3D VDO_OPOS(altmode->mode); =20 - tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0); + tcpm_queue_vdm_unlocked(port, header, vdo, vdo ? 1 : 0, + altmode_to_tcpm_tx_type(tx_sop_type)); return 0; } =20 @@ -2623,14 +2783,20 @@ static int tcpm_altmode_exit(struct typec_altmode *= altmode, int svdm_version; u32 header; =20 - svdm_version =3D typec_get_negotiated_svdm_version(port->typec_port); + if (tx_sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME && !tcpm_cable_vdm_support= ed(port)) + return -EPERM; + + svdm_version =3D tx_sop_type =3D=3D TYPEC_ALTMODE_SOP ? + typec_get_negotiated_svdm_version(port->typec_port) : + typec_get_cable_svdm_version(port->typec_port); if (svdm_version < 0) return svdm_version; =20 header =3D VDO(altmode->svid, 1, svdm_version, CMD_EXIT_MODE); header |=3D VDO_OPOS(altmode->mode); =20 - tcpm_queue_vdm_unlocked(port, header, NULL, 0); + tcpm_queue_vdm_unlocked(port, header, NULL, 0, + altmode_to_tcpm_tx_type(tx_sop_type)); return 0; } =20 @@ -2640,7 +2806,11 @@ static int tcpm_altmode_vdm(struct typec_altmode *al= tmode, { struct tcpm_port *port =3D typec_altmode_get_drvdata(altmode); =20 - tcpm_queue_vdm_unlocked(port, header, data, count - 1); + if (tx_sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME && !tcpm_cable_vdm_support= ed(port)) + return -EPERM; + + tcpm_queue_vdm_unlocked(port, header, data, count - 1, + altmode_to_tcpm_tx_type(tx_sop_type)); =20 return 0; } @@ -4126,14 +4296,20 @@ static void tcpm_typec_disconnect(struct tcpm_port = *port) static void tcpm_unregister_altmodes(struct tcpm_port *port) { struct pd_mode_data *modep =3D &port->mode_data; + struct pd_mode_data *modep_prime =3D &port->mode_data_prime; int i; =20 for (i =3D 0; i < modep->altmodes; i++) { typec_unregister_altmode(port->partner_altmode[i]); port->partner_altmode[i] =3D NULL; } + for (i =3D 0; i < modep_prime->altmodes; i++) { + typec_unregister_altmode(port->plug_prime_altmode[i]); + port->plug_prime_altmode[i] =3D NULL; + } =20 memset(modep, 0, sizeof(*modep)); + memset(modep_prime, 0, sizeof(*modep_prime)); } =20 static void tcpm_set_partner_usb_comm_capable(struct tcpm_port *port, bool= capable) --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3B139C4167B for ; Thu, 7 Dec 2023 09:08:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1378501AbjLGJI3 (ORCPT ); Thu, 7 Dec 2023 04:08:29 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37372 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231923AbjLGJIO (ORCPT ); Thu, 7 Dec 2023 04:08:14 -0500 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id E856C10D7 for ; Thu, 7 Dec 2023 01:08:13 -0800 (PST) Received: by mail-yb1-xb49.google.com with SMTP id 3f1490d57ef6-db084a0a2e9so926710276.2 for ; Thu, 07 Dec 2023 01:08:13 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940093; x=1702544893; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=HcBBD4Ve+EnK0sA+7Oy5D//EjO8x9y3uJFnX5ru4dqg=; b=R1k2KInWwMKp3FaZeicuPuxjpIC4omF+lvWnZW9xb+o4TPOC4cXMSm3JoOPI7ehZho 4dgwpFwpuTCCcEmQQSHk2Glin72Z5peq152L29i6Mt69wJaGrCxj6i9A5Xa9L0Skd5Wr 4neiygWZXaQ5OGo//MS4YXkBRFzSxNK5PoumRff9Mf3QIZaaIF4TYyUeS3SEONe0j5xu 5szVzDv7jOAUMllrGRVvx/4TnrkkwYnGGUL5e4A2MIktGyvoNWQg9Cn+DtI4fqQSOaq4 wYijLzP8G18+HbLj33Du/XQPV5MURAyl40cRpYpfPuEBCQZDSd2wBR9h1+QkByFFNIoR sOvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940093; x=1702544893; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=HcBBD4Ve+EnK0sA+7Oy5D//EjO8x9y3uJFnX5ru4dqg=; b=EAwuOTM2pw7J6xrL1hNPgKdWLSk+DJTCbWhosZgmyt08GEYhJvFvDtBrUKO8f+eSDE DEFRYFWSs0jnu+lAegYnv4Yu58zwP3V+NELYcv58ZgMAxWOSOojx22VzkGC1xkXljq5M CclZdRlsa4bRreFF53ZK6QeH0K089uUvuu30a9d5kgzn7l2ywu6/G7OkSDvEgQw5LvKV FVSXPBEBfdODZdcbZqYrxQ5NR1g9fTHKadsTiUpIZqHg+hiizGaDPbO3tuDRyKj3Iuin Gw7aXWfJ5nqlY/vhfrL84v+bhSsRAxrOyFcDI1yAPIX74NIJTAos5UTomh0tBFgYKk6Y O9kA== X-Gm-Message-State: AOJu0YxdsDydG+AFI+xwSYxZ2kFG7R/hu1IMA/TvYdscuyhCSOFi3gKK C4/ntA46Ux0aJ36CAMgQgjjTX2GzNbXd8ig= X-Google-Smtp-Source: AGHT+IEqB/j07MaxmdfrGiH6cpjewMzzjxAe0aX1PjsLmlRZovJi+MS6ODabqF7RJKBqphBvuoBj2o30uU2WZ9g= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a25:ca92:0:b0:db5:47c1:e82d with SMTP id a140-20020a25ca92000000b00db547c1e82dmr25759ybg.6.1701940093019; Thu, 07 Dec 2023 01:08:13 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:40 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=1088; i=rdbabiera@google.com; h=from:subject; bh=Go8QEr2NQzBwcUwozRZWWtLanmmY65Hze1InDnkaM4E=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3XGTak3qtp3775Bx7lDlfW9pvog9Gps3eD66ctWcI UTu/wyLjlIWBjEOBlkxRRZd/zyDG1dSt8zhrDGGmcPKBDKEgYtTACaSbc3I8D56Tv+fy5e8F1y8 UHys9lNUUx1X6rJnZ1dtesd5oCVKKJPhn62M8nfGR7/9De/2LjC8UpU5w/h/+G3xeLMbPhP/rL8 XyAUA X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-21-rdbabiera@google.com> Subject: [PATCH v1 09/10] usb: typec: altmodes: add typec_altmode_get_cable_svdm_version From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add typec_altmode_get_cable_svdm_version to return the cable's negotiated svdm_version. Signed-off-by: RD Babiera --- include/linux/usb/typec_altmode.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_al= tmode.h index 4d527d92457d..1ad581c5a930 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -156,6 +156,16 @@ typec_altmode_get_svdm_version(struct typec_altmode *a= ltmode) return typec_get_negotiated_svdm_version(typec_altmode2port(altmode)); } =20 +/** + * typec_altmode_get__cable_svdm_version - Get negotiated SVDM version for= cable plug + * @altmode: Handle to the alternate mode + */ +static inline int +typec_altmode_get_cable_svdm_version(struct typec_altmode *altmode) +{ + return typec_get_cable_svdm_version(typec_altmode2port(altmode)); +} + /** * struct typec_altmode_driver - USB Type-C alternate mode device driver * @id_table: Null terminated array of SVIDs --=20 2.43.0.rc2.451.g8631bc7472-goog From nobody Fri Dec 19 07:56:00 2025 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E09C8C4167B for ; Thu, 7 Dec 2023 09:08:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1378678AbjLGJIp (ORCPT ); Thu, 7 Dec 2023 04:08:45 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:37312 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232043AbjLGJIO (ORCPT ); Thu, 7 Dec 2023 04:08:14 -0500 Received: from mail-yw1-x1149.google.com (mail-yw1-x1149.google.com [IPv6:2607:f8b0:4864:20::1149]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BA777D5B for ; Thu, 7 Dec 2023 01:08:15 -0800 (PST) Received: by mail-yw1-x1149.google.com with SMTP id 00721157ae682-5d3a1e5f8d6so6649467b3.3 for ; Thu, 07 Dec 2023 01:08:15 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1701940095; x=1702544895; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=+XWVfbECKkYVkmbHjWZO+h6sR43SxXC7QmqywYAISO0=; b=zzRN7Acl4h7bJrdhOhfPFbht2ZvXXbBUeEEqBKCXHacasICpHsu2PoqNmRnzyLzUl2 nLyh7Zu0D1Tu6LHhAaKiSqveacJQCSkxcLYVDl4RWTLUit0mJejy2nL2Fs1Bae1aYHKQ v2e6tI8Ugj5snuNgQa+aTgjgocsdV8iZGc5Oiw7XckPFZXqDA3bPcNqIdf5Lk5A8PDkn XaHvEBYpaUo2FOcdeXTVpZfk1jGmnEdmo0i37w/91gL2pzz/8yED7lQfpejnGTl+1Rk1 TSYvWq11LLp1yVrvbDX7BiqjKaEAjMxOFfYH0yUTkadllGObPnyX5u1uaRhXC7wGGvjR 04xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1701940095; x=1702544895; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=+XWVfbECKkYVkmbHjWZO+h6sR43SxXC7QmqywYAISO0=; b=DqkAHnzD89howh1kPHkyG7xbMpEmNrxtdIZ+gdERp5K/ESiwlgZNadV/NXNsuE0GCY 2RTWvjGA3gk6KejzmfBI1vSC/Nu1yV9X57fWfWy2kuhsmtV9fBYaIYjwRaGvrDgn2lWb tcDB6rsLnEla+LB64Vh7oOQSAbSziTGkDQYy+HONiCKToYinxa4QxYoWCciO/5QPZN/v rfsiNn9Ie+5YgTHXRWFWpFFdxdej5nt33eHpXzF9YN+3PVD6XkXzp1dhQ63hfd8VPNNO aZ2KKCoUIljmPG2N2Ua4zK40GcN44fuwseyJgIimRqvcbLYF88ub8Q+PIFm6B4b96Y8A XZyA== X-Gm-Message-State: AOJu0Yzvh/sTVyZmMSZrnxEMTaTO0xjlDvLrFNDNUbqb/tHrUHizOvT3 PR8R8UIKk5eqpooKsN8iD4H93uFDVQAT+N8= X-Google-Smtp-Source: AGHT+IF6/n+XCgXEAd+gDT11AwSGUccx/1U0K8Jdi+mE1mBuUrtIEK2jbtHRYfxy+4WA6vTqloGig2LgrWIARtI= X-Received: from rdbabiera.c.googlers.com ([fda3:e722:ac3:cc00:7f:e700:c0a8:18a8]) (user=rdbabiera job=sendgmr) by 2002:a05:690c:fcd:b0:5d9:712e:202b with SMTP id dg13-20020a05690c0fcd00b005d9712e202bmr39599ywb.6.1701940095023; Thu, 07 Dec 2023 01:08:15 -0800 (PST) Date: Thu, 7 Dec 2023 09:07:41 +0000 In-Reply-To: <20231207090738.15721-12-rdbabiera@google.com> Mime-Version: 1.0 References: <20231207090738.15721-12-rdbabiera@google.com> X-Developer-Key: i=rdbabiera@google.com; a=openpgp; fpr=639A331F1A21D691815CE090416E17CA2BBBD5C8 X-Developer-Signature: v=1; a=openpgp-sha256; l=13047; i=rdbabiera@google.com; h=from:subject; bh=LXtNBwuIotDleeOrfFY5zqTZHszmGVHgznxqotaxm8Q=; b=owGbwMvMwCFW0bfok0KS4TbG02pJDKmF3XHSjD4lRgGpK8SntNww1/8+12zHv3PtGs+Wbyxbf MHsguy8jlIWBjEOBlkxRRZd/zyDG1dSt8zhrDGGmcPKBDKEgYtTACaSncTwh3+1zpGz1fI1i3ee kLzV2uhpmNs4V/sAh3n70RU17/9X2TH8s3+wzGKLe5Rvg+EPMd/kbE92vp9POkVCExv1ezwWsdr wAwA= X-Mailer: git-send-email 2.43.0.rc2.451.g8631bc7472-goog Message-ID: <20231207090738.15721-22-rdbabiera@google.com> Subject: [PATCH v1 10/10] usb: typec: altmodes/displayport: add SOP' support From: RD Babiera To: heikki.krogerus@linux.intel.com, linux@roeck-us.net, gregkh@linuxfoundation.org, pmalani@chromium.org, bleung@chromium.org, chrome-platform@lists.linux.dev, linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org Cc: badhri@google.com, tzungbi@kernel.org, utkarsh.h.patel@intel.com, andriy.shevchenko@linux.intel.com, RD Babiera Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Implement active cable VDM support for SOP' according to the DisplayPort Alt Mode 2.0 specification. When probing the DisplayPort driver, the state machine will transition to Enter Mode on SOP' if an active cable altmode is detected. The SVDM flow is as followed: (1) Enter Mode SOP' (2) Enter Mode SOP (3) Status Update SOP (4) Configure SOP' (5) Configure SOP Status Update on SOP' after Enter Mode is optional and not implemented for now. When exiting the alt mode, send Exit Mode over SOP' after SOP. Should an altmode vdm fail on SOP', the DisplayPort driver will drop its reference to the plug and attempt to continue in SOP operation. Add new dp_state enums DP_STATE_ENTER_PRIME, DP_STATE_CONFIGURE_PRIME, and DP_STATE_EXIT_PRIME. dp_altmode adds typec_displayport_data for the cable plug to store the plug configuration and adds a typec_altmode reference for the cable plug. dp_altmode_configure takes the cable pin assignment capabilities into account when deciding on pin configuration. Received Exit Mode ACKs are handled by a new dp_exit_mode_handler for line count restraints when accounting for SOP* transmit type. dp_altmode_activate now attempts to enter on SOP' if applicable, and will attempt to enter on SOP if that fails. Signed-off-by: RD Babiera --- drivers/usb/typec/altmodes/displayport.c | 166 +++++++++++++++++++---- 1 file changed, 139 insertions(+), 27 deletions(-) diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/a= ltmodes/displayport.c index 5ed470069c3e..583f99338710 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -50,13 +50,17 @@ enum { enum dp_state { DP_STATE_IDLE, DP_STATE_ENTER, + DP_STATE_ENTER_PRIME, DP_STATE_UPDATE, DP_STATE_CONFIGURE, + DP_STATE_CONFIGURE_PRIME, DP_STATE_EXIT, + DP_STATE_EXIT_PRIME, }; =20 struct dp_altmode { struct typec_displayport_data data; + struct typec_displayport_data data_prime; =20 enum dp_state state; bool hpd; @@ -67,6 +71,7 @@ struct dp_altmode { struct typec_altmode *alt; const struct typec_altmode *port; struct fwnode_handle *connector_fwnode; + struct typec_altmode *plug_prime; }; =20 static int dp_altmode_notify(struct dp_altmode *dp) @@ -99,12 +104,18 @@ static int dp_altmode_configure(struct dp_altmode *dp,= u8 con) conf |=3D DP_CONF_UFP_U_AS_DFP_D; pin_assign =3D DP_CAP_UFP_D_PIN_ASSIGN(dp->alt->vdo) & DP_CAP_DFP_D_PIN_ASSIGN(dp->port->vdo); + /* Account for active cable capabilities */ + if (dp->plug_prime) + pin_assign &=3D DP_CAP_DFP_D_PIN_ASSIGN(dp->plug_prime->vdo); break; case DP_STATUS_CON_UFP_D: case DP_STATUS_CON_BOTH: /* NOTE: First acting as DP source */ conf |=3D DP_CONF_UFP_U_AS_UFP_D; pin_assign =3D DP_CAP_PIN_ASSIGN_UFP_D(dp->alt->vdo) & - DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo); + DP_CAP_PIN_ASSIGN_DFP_D(dp->port->vdo); + /* Account for active cable capabilities */ + if (dp->plug_prime) + pin_assign &=3D DP_CAP_UFP_D_PIN_ASSIGN(dp->plug_prime->vdo); break; default: break; @@ -130,6 +141,8 @@ static int dp_altmode_configure(struct dp_altmode *dp, = u8 con) } =20 dp->data.conf =3D conf; + if (dp->plug_prime) + dp->data_prime.conf =3D conf; =20 return 0; } @@ -143,13 +156,17 @@ static int dp_altmode_status_update(struct dp_altmode= *dp) =20 if (configured && (dp->data.status & DP_STATUS_SWITCH_TO_USB)) { dp->data.conf =3D 0; - dp->state =3D DP_STATE_CONFIGURE; + dp->data_prime.conf =3D 0; + dp->state =3D dp->plug_prime ? DP_STATE_CONFIGURE_PRIME : + DP_STATE_CONFIGURE; } else if (dp->data.status & DP_STATUS_EXIT_DP_MODE) { dp->state =3D DP_STATE_EXIT; + /* Partner is connected but not configured */ } else if (!(con & DP_CONF_CURRENTLY(dp->data.conf))) { ret =3D dp_altmode_configure(dp, con); if (!ret) { - dp->state =3D DP_STATE_CONFIGURE; + dp->state =3D dp->plug_prime ? DP_STATE_CONFIGURE_PRIME : + DP_STATE_CONFIGURE; if (dp->hpd !=3D hpd) { dp->hpd =3D hpd; dp->pending_hpd =3D true; @@ -185,9 +202,12 @@ static int dp_altmode_configured(struct dp_altmode *dp) return dp_altmode_notify(dp); } =20 -static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf) +static int dp_altmode_configure_vdm(struct dp_altmode *dp, u32 conf, + enum typec_altmode_transmit_type sop_type) { - int svdm_version =3D typec_altmode_get_svdm_version(dp->alt); + int svdm_version =3D sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME ? + typec_altmode_get_cable_svdm_version(dp->alt) : + typec_altmode_get_svdm_version(dp->alt); u32 header; int ret; =20 @@ -202,7 +222,7 @@ static int dp_altmode_configure_vdm(struct dp_altmode *= dp, u32 conf) return ret; } =20 - ret =3D typec_altmode_vdm(dp->alt, header, &conf, 2, TYPEC_ALTMODE_SOP); + ret =3D typec_altmode_vdm(dp->alt, header, &conf, 2, sop_type); if (ret) dp_altmode_notify(dp); =20 @@ -223,7 +243,20 @@ static void dp_altmode_work(struct work_struct *work) case DP_STATE_ENTER: ret =3D typec_altmode_enter(dp->alt, NULL, TYPEC_ALTMODE_SOP); if (ret && ret !=3D -EBUSY) - dev_err(&dp->alt->dev, "failed to enter mode\n"); + dev_err(&dp->alt->dev, "partner failed to enter mode\n"); + break; + case DP_STATE_ENTER_PRIME: + ret =3D typec_altmode_enter(dp->alt, NULL, TYPEC_ALTMODE_SOP_PRIME); + /* + * If we fail to enter Alt Mode on SOP', then we should drop the + * plug from the driver and attempt to run the driver without + * it. + */ + if (ret && ret !=3D -EBUSY) { + dev_err(&dp->alt->dev, "plug failed to enter mode\n"); + dp->state =3D DP_STATE_ENTER; + goto disable_prime; + } break; case DP_STATE_UPDATE: svdm_version =3D typec_altmode_get_svdm_version(dp->alt); @@ -238,15 +271,30 @@ static void dp_altmode_work(struct work_struct *work) ret); break; case DP_STATE_CONFIGURE: - ret =3D dp_altmode_configure_vdm(dp, dp->data.conf); + ret =3D dp_altmode_configure_vdm(dp, dp->data.conf, TYPEC_ALTMODE_SOP); if (ret) dev_err(&dp->alt->dev, "unable to send Configure command (%d)\n", ret); break; + case DP_STATE_CONFIGURE_PRIME: + ret =3D dp_altmode_configure_vdm(dp, dp->data_prime.conf, + TYPEC_ALTMODE_SOP_PRIME); + if (ret) { + dev_err(&dp->plug_prime->dev, + "unable to send Configure command (%d)\n", + ret); + dp->state =3D DP_STATE_CONFIGURE; + goto disable_prime; + } + break; case DP_STATE_EXIT: if (typec_altmode_exit(dp->alt, TYPEC_ALTMODE_SOP)) dev_err(&dp->alt->dev, "Exit Mode Failed!\n"); break; + case DP_STATE_EXIT_PRIME: + if (typec_altmode_exit(dp->alt, TYPEC_ALTMODE_SOP_PRIME)) + dev_err(&dp->plug_prime->dev, "Exit Mode Failed!\n"); + break; default: break; } @@ -254,6 +302,12 @@ static void dp_altmode_work(struct work_struct *work) dp->state =3D DP_STATE_IDLE; =20 mutex_unlock(&dp->lock); + return; + +disable_prime: + typec_altmode_put_plug(dp->plug_prime); + dp->plug_prime =3D NULL; + schedule_work(&dp->work); } =20 static void dp_altmode_attention(struct typec_altmode *alt, const u32 vdo) @@ -282,6 +336,32 @@ static void dp_altmode_attention(struct typec_altmode = *alt, const u32 vdo) mutex_unlock(&dp->lock); } =20 +static void dp_exit_mode_handler(struct dp_altmode *dp, enum typec_altmode= _transmit_type sop_type) +{ + if (sop_type =3D=3D TYPEC_ALTMODE_SOP) { + dp->data.status =3D 0; + dp->data.conf =3D 0; + if (dp->hpd) { + drm_connector_oob_hotplug_event(dp->connector_fwnode, + connector_status_disconnected); + dp->hpd =3D false; + sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); + } + /* + * Delay updating active because driver will not be allowed to send VDMs= otherwise + */ + if (dp->plug_prime) + dp->state =3D DP_STATE_EXIT_PRIME; + else + typec_altmode_update_active(dp->alt, false); + } else if (sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME) { + dp->data_prime.status =3D 0; + dp->data_prime.conf =3D 0; + typec_altmode_update_active(dp->plug_prime, false); + typec_altmode_update_active(dp->alt, false); + } +} + static int dp_altmode_vdm(struct typec_altmode *alt, const u32 hdr, const u32 *vdo, int count, enum typec_altmode_transmit_type sop_type) @@ -302,26 +382,27 @@ static int dp_altmode_vdm(struct typec_altmode *alt, case CMDT_RSP_ACK: switch (cmd) { case CMD_ENTER_MODE: - typec_altmode_update_active(alt, true); - dp->state =3D DP_STATE_UPDATE; + if (sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME) { + if (dp->plug_prime) + typec_altmode_update_active(dp->plug_prime, true); + dp->state =3D DP_STATE_ENTER; + } else { + typec_altmode_update_active(alt, true); + dp->state =3D DP_STATE_UPDATE; + } break; case CMD_EXIT_MODE: - typec_altmode_update_active(alt, false); - dp->data.status =3D 0; - dp->data.conf =3D 0; - if (dp->hpd) { - drm_connector_oob_hotplug_event(dp->connector_fwnode, - connector_status_disconnected); - dp->hpd =3D false; - sysfs_notify(&dp->alt->dev.kobj, "displayport", "hpd"); - } + dp_exit_mode_handler(dp, sop_type); break; case DP_CMD_STATUS_UPDATE: dp->data.status =3D *vdo; ret =3D dp_altmode_status_update(dp); break; case DP_CMD_CONFIGURE: - ret =3D dp_altmode_configured(dp); + if (sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME) + dp->state =3D DP_STATE_CONFIGURE; + else + ret =3D dp_altmode_configured(dp); break; default: break; @@ -330,8 +411,16 @@ static int dp_altmode_vdm(struct typec_altmode *alt, case CMDT_RSP_NAK: switch (cmd) { case DP_CMD_CONFIGURE: - dp->data.conf =3D 0; - ret =3D dp_altmode_configured(dp); + if (sop_type =3D=3D TYPEC_ALTMODE_SOP_PRIME) { + dp->data_prime.conf =3D 0; + /* Attempt to configure on SOP, drop plug */ + typec_altmode_put_plug(dp->plug_prime); + dp->plug_prime =3D NULL; + dp->state =3D DP_STATE_CONFIGURE; + } else { + dp->data.conf =3D 0; + ret =3D dp_altmode_configured(dp); + } break; default: break; @@ -351,8 +440,23 @@ static int dp_altmode_vdm(struct typec_altmode *alt, =20 static int dp_altmode_activate(struct typec_altmode *alt, int activate) { - return activate ? typec_altmode_enter(alt, NULL, TYPEC_ALTMODE_SOP) : - typec_altmode_exit(alt, TYPEC_ALTMODE_SOP); + struct dp_altmode *dp =3D typec_altmode_get_drvdata(alt); + int ret; + + if (activate) { + if (dp->plug_prime) { + ret =3D typec_altmode_enter(alt, NULL, TYPEC_ALTMODE_SOP_PRIME); + if (ret < 0) { + typec_altmode_put_plug(dp->plug_prime); + dp->plug_prime =3D NULL; + } else { + return ret; + } + } + return typec_altmode_enter(alt, NULL, TYPEC_ALTMODE_SOP); + } else { + return typec_altmode_exit(alt, TYPEC_ALTMODE_SOP); + } } =20 static const struct typec_altmode_ops dp_altmode_ops =3D { @@ -400,7 +504,7 @@ configuration_store(struct device *dev, struct device_a= ttribute *attr, conf |=3D con; =20 if (dp->alt->active) { - ret =3D dp_altmode_configure_vdm(dp, conf); + ret =3D dp_altmode_configure_vdm(dp, conf, TYPEC_ALTMODE_SOP); if (ret) goto err_unlock; } @@ -502,7 +606,7 @@ pin_assignment_store(struct device *dev, struct device_= attribute *attr, =20 /* Only send Configure command if a configuration has been set */ if (dp->alt->active && DP_CONF_CURRENTLY(dp->data.conf)) { - ret =3D dp_altmode_configure_vdm(dp, conf); + ret =3D dp_altmode_configure_vdm(dp, conf, TYPEC_ALTMODE_SOP); if (ret) goto out_unlock; } @@ -575,6 +679,7 @@ static const struct attribute_group dp_altmode_group = =3D { int dp_altmode_probe(struct typec_altmode *alt) { const struct typec_altmode *port =3D typec_altmode_get_partner(alt); + struct typec_altmode *plug =3D typec_altmode_get_plug(alt, TYPEC_PLUG_SOP= _P); struct fwnode_handle *fwnode; struct dp_altmode *dp; int ret; @@ -604,6 +709,11 @@ int dp_altmode_probe(struct typec_altmode *alt) alt->desc =3D "DisplayPort"; alt->ops =3D &dp_altmode_ops; =20 + if (plug) + plug->desc =3D "Displayport"; + + dp->plug_prime =3D plug; + fwnode =3D dev_fwnode(alt->dev.parent->parent); /* typec_port fwnode */ if (fwnode_property_present(fwnode, "displayport")) dp->connector_fwnode =3D fwnode_find_reference(fwnode, "displayport", 0); @@ -614,7 +724,8 @@ int dp_altmode_probe(struct typec_altmode *alt) =20 typec_altmode_set_drvdata(alt, dp); =20 - dp->state =3D DP_STATE_ENTER; + dp->state =3D plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; + schedule_work(&dp->work); =20 return 0; @@ -627,6 +738,7 @@ void dp_altmode_remove(struct typec_altmode *alt) =20 sysfs_remove_group(&alt->dev.kobj, &dp_altmode_group); cancel_work_sync(&dp->work); + typec_altmode_put_plug(dp->plug_prime); =20 if (dp->connector_fwnode) { drm_connector_oob_hotplug_event(dp->connector_fwnode, --=20 2.43.0.rc2.451.g8631bc7472-goog