public inbox for linux-usb@vger.kernel.org
 help / color / mirror / Atom feed
From: Francesco Orro <ncesco@interstellar.eu>
To: linux-usb@vger.kernel.org
Cc: Heikki Krogerus <heikki.krogerus@linux.intel.com>,
	Greg KH <gregkh@linuxfoundation.org>
Subject: [PATCH v7] usb: typec: ucsi: acpi: bootstrap PPM on systems with empty _DSM func 2
Date: Mon, 20 Apr 2026 19:23:41 +0200	[thread overview]
Message-ID: <20260420172343.84456-1-ncesco@interstellar.eu> (raw)
In-Reply-To: <f3M_fpjtt8FxDqGKcA84vqXmRbKzCBfpCrIK4-jCWvIscER51zkD8qD8FYpz75qZw51rMDRSkUyYlrBvLvdM8CGRY2l8TFVvr4MC1LdTzbc=@interstellar.eu>

On HP ZBook Fury G1i 16 inch (BIOS X96 01.03.04) the SSDT16 "UcsiAcpi"
exposes a _DSM function 2 (READ) whose body is empty, so UCSI_VERSION
stays 0x0000 after the read. ucsi_init() treats VERSION=0 as firmware
absent and bails with -ENODEV, so /sys/class/typec is empty and no
alt-mode info reaches userspace.

The PPM is alive: writing UCSI_PPM_RESET through _DSM function 1 (WRITE)
drives RESET_COMPLETE in CCI. We can therefore bootstrap the PPM
explicitly on probe when necessary and, once RESET_COMPLETE is observed,
default VERSION to UCSI 1.2 - which matches the semantics advertised by
the SSDT tables on this platform.

The bootstrap checks CCI first and returns early if RESET_COMPLETE is
already set, to avoid resetting a PPM left in a stable state by
firmware. Note that this early-return path was not exercised on the
tested platform: on cold boot CCI did not have RESET_COMPLETE at probe
time and the PPM_RESET was issued. Consequently, alt-mode state
negotiated during BIOS POST (in this case a Thunderbolt dock's TBT
alt-mode) was disrupted at boot. Linux UCSI core later calls
ucsi_reset_ppm() in ucsi_init() regardless, so the PPM reset on probe
is arguably not the root cause of the disruption, but the patch leaves
the door open to avoid the early reset when firmware does leave the
flag set.

Bootstrap failure is non-fatal: we log a warning and continue. If the
PPM later reaches RESET_COMPLETE asynchronously, read_version() still
recovers via the UCSI_CCI_RESET_COMPLETE check gated by the
needs_bootstrap flag.

The behaviour is gated by DMI because unconditionally issuing a
PPM_RESET on systems whose firmware _does_ populate VERSION correctly
would be aggressive and unjustified. The DMI match starts with HP ZBook
Fury G1i 16 inch; other vendors/models can be added as they are
confirmed.

Tested on HP ZBook Fury G1i 16 inch Mobile Workstation PC with kernel
6.19.13. Before the patch ucsi_acpi probe returns -ENODEV; after the
patch /sys/class/typec/port{0,1,2} appear with partner altmodes
exposed when a USB4/TBT device is connected.

Signed-off-by: Francesco Orro <ncesco@interstellar.eu>
---
Resending: previous send was mangled by quoted-printable encoding.
No content changes.

--- a/drivers/usb/typec/ucsi/ucsi_acpi.c	2026-04-18 10:46:48.000000000 +0200
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c	2026-04-20 17:47:45.529559324 +0200
@@ -9,6 +9,7 @@
 #include <linux/platform_device.h>
 #include <linux/module.h>
 #include <linux/acpi.h>
+#include <linux/delay.h>
 #include <linux/dmi.h>
 
 #include "ucsi.h"
@@ -17,11 +18,15 @@
 #define UCSI_DSM_FUNC_WRITE	1
 #define UCSI_DSM_FUNC_READ	2
 
+#define UCSI_ACPI_BOOTSTRAP_RETRIES	20
+#define UCSI_ACPI_BOOTSTRAP_DELAY_MS	50
+
 struct ucsi_acpi {
 	struct device *dev;
 	struct ucsi *ucsi;
 	void *base;
 	bool check_bogus_event;
+	bool needs_bootstrap;
 	guid_t guid;
 	u64 cmd;
 };
@@ -46,6 +51,7 @@
 {
 	struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
 	int ret;
+	u32 cci;
 
 	ret = ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_READ);
 	if (ret)
@@ -53,6 +59,21 @@
 
 	memcpy(version, ua->base + UCSI_VERSION, sizeof(*version));
 
+	/*
+	 * Some firmwares (observed on HP ZBook Fury 16 G1i, SSDT16
+	 * "UcsiAcpi") leave the VERSION field untouched by _DSM func 2.
+	 * If the PPM has reached RESET_COMPLETE - typically because the
+	 * firmware (or our bootstrap on probe) left it in that state -
+	 * fall back to UCSI 1.2 which matches what those SSDTs advertise.
+	 */
+	if (!*version && ua->needs_bootstrap) {
+		memcpy(&cci, ua->base + UCSI_CCI, sizeof(cci));
+		if (cci & UCSI_CCI_RESET_COMPLETE) {
+			dev_info(ua->dev, "VERSION unpopulated; defaulting to UCSI 1.2 after PPM RESET_COMPLETE\n");
+			*version = UCSI_VERSION_1_2;
+		}
+	}
+
 	return 0;
 }
 
@@ -143,6 +164,56 @@
 	.async_control = ucsi_acpi_async_control
 };
 
+/*
+ * DMI list of systems whose UCSI ACPI firmware does not populate VERSION
+ * from _DSM func 2 (READ). Entries here opt into ucsi_acpi_bootstrap_ppm()
+ * at probe time.
+ */
+static const struct dmi_system_id ucsi_acpi_bootstrap_quirk[] = {
+	{
+		.ident = "HP ZBook Fury G1i 16 inch",
+		.matches = {
+			DMI_MATCH(DMI_SYS_VENDOR, "HP"),
+			DMI_MATCH(DMI_PRODUCT_NAME,
+				  "HP ZBook Fury G1i 16 inch"),
+		},
+	},
+	{ }
+};
+
+/*
+ * Drive PPM_RESET via _DSM WRITE and wait for RESET_COMPLETE, but only if
+ * the firmware hasn't already left the PPM in that state. Skipping the
+ * reset avoids disrupting alt mode state already negotiated by firmware
+ * (e.g. a Thunderbolt dock attached at boot whose TBT alt mode entry
+ * would otherwise be torn down by a fresh PPM reset).
+ */
+static int ucsi_acpi_bootstrap_ppm(struct ucsi_acpi *ua)
+{
+	u64 cmd = UCSI_PPM_RESET;
+	u32 cci;
+	int retries;
+
+	memcpy(&cci, ua->base + UCSI_CCI, sizeof(cci));
+	if (cci & UCSI_CCI_RESET_COMPLETE) {
+		dev_info(ua->dev, "PPM already in RESET_COMPLETE, skipping bootstrap\n");
+		return 0;
+	}
+
+	memcpy(ua->base + UCSI_CONTROL, &cmd, sizeof(cmd));
+	if (ucsi_acpi_dsm(ua, UCSI_DSM_FUNC_WRITE))
+		return -EIO;
+
+	for (retries = UCSI_ACPI_BOOTSTRAP_RETRIES; retries > 0; retries--) {
+		msleep(UCSI_ACPI_BOOTSTRAP_DELAY_MS);
+		memcpy(&cci, ua->base + UCSI_CCI, sizeof(cci));
+		if (cci & UCSI_CCI_RESET_COMPLETE)
+			return 0;
+	}
+
+	return -ETIMEDOUT;
+}
+
 static const struct dmi_system_id ucsi_acpi_quirks[] = {
 	{
 		.matches = {
@@ -201,6 +272,15 @@
 
 	ua->dev = &pdev->dev;
 
+	if (dmi_check_system(ucsi_acpi_bootstrap_quirk)) {
+		ua->needs_bootstrap = true;
+		ret = ucsi_acpi_bootstrap_ppm(ua);
+		if (ret)
+			dev_warn(&pdev->dev,
+				 "PPM bootstrap did not complete (%d); continuing anyway\n",
+				 ret);
+	}
+
 	id = dmi_first_match(ucsi_acpi_quirks);
 	if (id)
 		ops = id->driver_data;

      reply	other threads:[~2026-04-20 17:23 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-20 16:05 [RFC PATCH] usb: typec: ucsi: acpi: bootstrap PPM on HP systems with empty _DSM func 2 Francesco Orro
2026-04-20 17:23 ` Francesco Orro [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260420172343.84456-1-ncesco@interstellar.eu \
    --to=ncesco@interstellar.eu \
    --cc=gregkh@linuxfoundation.org \
    --cc=heikki.krogerus@linux.intel.com \
    --cc=linux-usb@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox