* [RFC PATCH] usb: typec: ucsi: acpi: bootstrap PPM on HP systems with empty _DSM func 2
@ 2026-04-20 16:05 Francesco Orro
2026-04-20 17:23 ` [PATCH v7] usb: typec: ucsi: acpi: bootstrap PPM on " Francesco Orro
0 siblings, 1 reply; 2+ messages in thread
From: Francesco Orro @ 2026-04-20 16:05 UTC (permalink / raw)
To: linux-usb@vger.kernel.org; +Cc: Heikki Krogerus, Greg KH
Hi,
The attached RFC patch lets ucsi_acpi probe successfully on HP ZBook
Fury G1i 16 inch (and likely other HP workstations shipping the same
"UcsiAcpi" SSDT), where _DSM func 2 (READ) is a stub and UCSI_VERSION
stays 0. Details in the commit message below.
Main open question for the list: is there a known-good pattern for
ucsi_acpi drivers to avoid disrupting alt-mode state already negotiated
by BIOS? On cold boot with a Thunderbolt dock attached, the PPM_RESET
issued by this patch - and later by Linux UCSI core in ucsi_init() -
tears down the TBT alt-mode the BIOS had set up, and the DP tunnel to
the dock dies until the user physically replugs. My local workaround
is to late-load ucsi_acpi via a systemd service after the thunderbolt
driver has adopted the tunnels, but that feels like papering over a
design assumption I am missing.
Tested on one machine only. Happy to refine the patch if the direction
is acceptable.
Regards,
Francesco Orro
---
From: Francesco Orro <ncesco@interstellar.eu>
Date: Mon, 20 Apr 2026 16:40:00 +0200
Subject: [PATCH v7] usb: typec: ucsi: acpi: bootstrap PPM on systems with empty _DSM func 2
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>
---
--- 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;
^ permalink raw reply [flat|nested] 2+ messages in thread
* [PATCH v7] usb: typec: ucsi: acpi: bootstrap PPM on systems with empty _DSM func 2
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
0 siblings, 0 replies; 2+ messages in thread
From: Francesco Orro @ 2026-04-20 17:23 UTC (permalink / raw)
To: linux-usb; +Cc: Heikki Krogerus, Greg KH
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;
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-04-20 17:23 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH v7] usb: typec: ucsi: acpi: bootstrap PPM on " Francesco Orro
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox