* [RFC 01/12] ACPI: Introduce DT-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 02/12] arm64: acpi: Cleanup acpi=[on|off|force] handling Hans de Goede
` (13 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
ATM on platforms which support DT or ACPI as firmware interfaces ACPI gets
fully disabled when DT is in use.
In some cases it is interesting to at least parse the ACPI tables and
possibly also use parts of them.
One specific example of this is Windows on Arm laptops with Qualcomm
Snapdragon processors where Windows boots using information from the ACPI
tables but Linux uses a device-tree description of the hardware instead.
Since Windows is the factory OS, these DT descriptions need to be created
by hand.
Having the ACPI tables available at runtime allows the kernel to use some
information from these ACPI tables while still using device-tree as its
main information source.
Having the tables available at runtime can also help with creating and
improving these DT descriptions.
This patch prepares for a new DT-ACPI hybrid mode controlled by a new
acpi_dt_hybrid flag. When enabled this makes the kernel booting in DT
mode still load and parse the ACPI tables, without taking any further
actions like creating devices.
In this case the ACPI subsys will still parse ACPI tables and populate
/sys/firmware/acpi and /sys/bus/acpi/devices.
Note this patch itself is a no-op, since all the arch/*/include/asm/acpi.h
files define the new acpi_dt_hybrid variable to 0. All the added extra
"if (acpi_disabled)" checks are in paths which already check for this
earlier in the code-path. The new checks only come into play when
acpi_dt_hybrid is set to 1.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
arch/arm64/include/asm/acpi.h | 1 +
arch/loongarch/include/asm/acpi.h | 1 +
arch/riscv/include/asm/acpi.h | 3 ++-
arch/x86/include/asm/acpi.h | 1 +
drivers/acpi/acpi_apd.c | 3 +++
drivers/acpi/acpi_memhotplug.c | 3 +++
drivers/acpi/acpi_processor.c | 3 +++
drivers/acpi/arm64/init.c | 2 ++
drivers/acpi/bus.c | 6 +++---
drivers/acpi/scan.c | 4 ++++
drivers/acpi/tables.c | 4 ++--
drivers/firmware/efi/efi-bgrt.c | 2 +-
12 files changed, 26 insertions(+), 7 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index 8a54ca6ba602..3116bb872f47 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -76,6 +76,7 @@ typedef u64 phys_cpuid_t;
#define PHYS_CPUID_INVALID INVALID_HWID
#define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */
+#define acpi_dt_hybrid 0 /* No DT-ACPI hybrid mode on ARM64 */
extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
diff --git a/arch/loongarch/include/asm/acpi.h b/arch/loongarch/include/asm/acpi.h
index eda9d4d0a493..5f725bba6e87 100644
--- a/arch/loongarch/include/asm/acpi.h
+++ b/arch/loongarch/include/asm/acpi.h
@@ -12,6 +12,7 @@
#include <asm/suspend.h>
#ifdef CONFIG_ACPI
+#define acpi_dt_hybrid 0 /* No DT-ACPI hybrid mode on loongarch */
extern int acpi_strict;
extern int acpi_disabled;
extern int acpi_pci_disabled;
diff --git a/arch/riscv/include/asm/acpi.h b/arch/riscv/include/asm/acpi.h
index 26ab37c171bc..b72586fe650e 100644
--- a/arch/riscv/include/asm/acpi.h
+++ b/arch/riscv/include/asm/acpi.h
@@ -22,7 +22,8 @@ typedef u64 phys_cpuid_t;
void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size);
#define acpi_os_ioremap acpi_os_ioremap
-#define acpi_strict 1 /* No out-of-spec workarounds on RISC-V */
+#define acpi_strict 1 /* No out-of-spec workarounds on RISC-V */
+#define acpi_dt_hybrid 0 /* No DT-ACPI hybrid mode on RISC-V */
extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h
index a03aa6f999d1..ae1d3d588c06 100644
--- a/arch/x86/include/asm/acpi.h
+++ b/arch/x86/include/asm/acpi.h
@@ -25,6 +25,7 @@
#endif
#ifdef CONFIG_ACPI
+#define acpi_dt_hybrid 0 /* No DT-ACPI hybrid mode on x86 */
extern int acpi_lapic;
extern int acpi_ioapic;
extern int acpi_noirq;
diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c
index bed0791c17fc..0f9e22046e0a 100644
--- a/drivers/acpi/acpi_apd.c
+++ b/drivers/acpi/acpi_apd.c
@@ -263,5 +263,8 @@ static struct acpi_scan_handler apd_handler = {
void __init acpi_apd_init(void)
{
+ if (acpi_disabled)
+ return;
+
acpi_scan_add_handler(&apd_handler);
}
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 1d7dfe4ee9a6..00469a4b8897 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -361,6 +361,9 @@ static struct acpi_scan_handler memory_device_handler = {
void __init acpi_memory_hotplug_init(void)
{
+ if (acpi_disabled)
+ return;
+
acpi_scan_add_handler(&memory_device_handler);
}
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index 00775b91bd41..f4ddd5e126e3 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -785,6 +785,9 @@ bool acpi_duplicate_processor_id(int proc_id)
void __init acpi_processor_init(void)
{
+ if (acpi_disabled)
+ return;
+
acpi_processor_check_duplicates();
acpi_scan_add_handler_with_hotplug(&processor_handler, "processor");
acpi_scan_add_handler(&processor_container_handler);
diff --git a/drivers/acpi/arm64/init.c b/drivers/acpi/arm64/init.c
index 7a47d8095a7d..9826c0b35715 100644
--- a/drivers/acpi/arm64/init.c
+++ b/drivers/acpi/arm64/init.c
@@ -4,6 +4,8 @@
void __init acpi_arch_init(void)
{
+ if (acpi_disabled)
+ return;
if (IS_ENABLED(CONFIG_ACPI_AGDI))
acpi_agdi_init();
if (IS_ENABLED(CONFIG_ACPI_APMT))
diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index 2ec095e2009e..1d5ea66cb99e 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -1290,7 +1290,7 @@ void __init acpi_early_init(void)
{
acpi_status status;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_dt_hybrid)
return;
pr_info("Core revision %08x\n", ACPI_CA_VERSION);
@@ -1361,7 +1361,7 @@ void __init acpi_subsystem_init(void)
{
acpi_status status;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_dt_hybrid)
return;
status = acpi_enable_subsystem(~ACPI_NO_ACPI_ENABLE);
@@ -1494,7 +1494,7 @@ static int __init acpi_init(void)
{
int result;
- if (acpi_disabled) {
+ if (acpi_disabled && !acpi_dt_hybrid) {
pr_info("Interpreter disabled.\n");
return -ENODEV;
}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 530547cda8b2..4836286968e8 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2253,6 +2253,10 @@ static void acpi_default_enumeration(struct acpi_device *device)
ACPI_RECONFIG_DEVICE_ADD, device);
return;
}
+
+ if (acpi_dt_hybrid)
+ return;
+
if (match_string(acpi_system_dev_ids, -1, acpi_device_hid(device)) >= 0) {
struct acpi_scan_system_dev *sd;
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index 4286e4af1092..6aec547c0872 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -247,7 +247,7 @@ int __init_or_acpilib acpi_table_parse_entries_array(
int count;
u32 instance = 0;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_dt_hybrid)
return -ENODEV;
if (!id)
@@ -330,7 +330,7 @@ int __init acpi_table_parse(char *id, acpi_tbl_table_handler handler)
{
struct acpi_table_header *table = NULL;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_dt_hybrid)
return -ENODEV;
if (!id || !handler)
diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c
index 1da451582812..dc69342cdb38 100644
--- a/drivers/firmware/efi/efi-bgrt.c
+++ b/drivers/firmware/efi/efi-bgrt.c
@@ -31,7 +31,7 @@ void __init efi_bgrt_init(struct acpi_table_header *table)
struct acpi_table_bgrt *bgrt = &bgrt_tab;
int mem_type;
- if (acpi_disabled)
+ if (acpi_disabled && !acpi_dt_hybrid)
return;
if (!efi_enabled(EFI_MEMMAP) && !efi_enabled(EFI_PARAVIRT))
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 02/12] arm64: acpi: Cleanup acpi=[on|off|force] handling
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
2026-06-23 14:52 ` [RFC 01/12] ACPI: Introduce DT-ACPI " Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 03/12] arm64: acpi: add acpi=hybrid support Hans de Goede
` (12 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
acpi=[on|off|force] are 3 values of a single "should ACPI be enabled"
setting. Instead of storing these in 3 separate booleans, introduce a new
acpi_mode_t enum for this and store these in a single param varialable.
This is a preparation patch for adding acpi=hybrid support to enable the
new DT-ACPI hybrid mode, which makes the kernel booting in DT mode still
load and parse the ACPI tables, without taking any further actions.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
arch/arm64/kernel/acpi.c | 33 +++++++++++++++++++--------------
1 file changed, 19 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 5891f92c2035..06ab3a9da64b 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -43,9 +43,14 @@ EXPORT_SYMBOL(acpi_disabled);
int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);
-static bool param_acpi_off __initdata;
-static bool param_acpi_on __initdata;
-static bool param_acpi_force __initdata;
+enum acpi_mode_t {
+ acpi_mode_unset,
+ acpi_mode_off,
+ acpi_mode_on,
+ acpi_mode_force,
+};
+
+static enum acpi_mode_t param_acpi_mode __initdata;
static bool param_acpi_nospcr __initdata;
static int __init parse_acpi(char *arg)
@@ -55,11 +60,11 @@ static int __init parse_acpi(char *arg)
/* "acpi=off" disables both ACPI table parsing and interpreter */
if (strcmp(arg, "off") == 0)
- param_acpi_off = true;
+ param_acpi_mode = acpi_mode_off;
else if (strcmp(arg, "on") == 0) /* prefer ACPI over DT */
- param_acpi_on = true;
+ param_acpi_mode = acpi_mode_on;
else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
- param_acpi_force = true;
+ param_acpi_mode = acpi_mode_force;
else if (strcmp(arg, "nospcr") == 0) /* disable SPCR as default console */
param_acpi_nospcr = true;
else
@@ -198,14 +203,14 @@ static int __init acpi_fadt_sanity_check(void)
void __init acpi_boot_table_init(void)
{
/*
- * Enable ACPI instead of device tree unless
- * - ACPI has been disabled explicitly (acpi=off), or
- * - the device tree is not empty (it has more than just a /chosen node,
- * and a /hypervisor node when running on Xen)
- * and ACPI has not been [force] enabled (acpi=on|force)
+ * When no ACPI mode (acpi=off|on|force) has been specified,
+ * enable ACPI if the device tree is empty (it only has a /chosen
+ * node, and a /hypervisor node when running on Xen).
*/
- if (param_acpi_off ||
- (!param_acpi_on && !param_acpi_force && !dt_is_stub()))
+ if (!param_acpi_mode)
+ param_acpi_mode = dt_is_stub() ? acpi_mode_on : acpi_mode_off;
+
+ if (param_acpi_mode == acpi_mode_off)
goto done;
/*
@@ -223,7 +228,7 @@ void __init acpi_boot_table_init(void)
*/
if (acpi_table_init() || acpi_fadt_sanity_check()) {
pr_err("Failed to init ACPI tables\n");
- if (!param_acpi_force)
+ if (param_acpi_mode != acpi_mode_force)
disable_acpi();
}
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 03/12] arm64: acpi: add acpi=hybrid support
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
2026-06-23 14:52 ` [RFC 01/12] ACPI: Introduce DT-ACPI " Hans de Goede
2026-06-23 14:52 ` [RFC 02/12] arm64: acpi: Cleanup acpi=[on|off|force] handling Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 04/12] ACPI: Add helpers for dealing with ACPI fwnode as secondary fwnode Hans de Goede
` (11 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
By default when a non stub DT is given to the kernel when booting, ACPI
gets fully disabled.
Add support for selecting a new APCI-DT hybrid mode where the kernel
booting in DT mode still loads and parses the ACPI tables, without taking
any further actions like creating devices.
This will parse ACPI tables and populate /sys/firmware/acpi and
/sys/bus/acpi/devices, which can be useful to e.g. get an acpidump .
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
arch/arm64/include/asm/acpi.h | 2 +-
arch/arm64/kernel/acpi.c | 16 ++++++++++++----
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
index 3116bb872f47..8f8a0b9e49c3 100644
--- a/arch/arm64/include/asm/acpi.h
+++ b/arch/arm64/include/asm/acpi.h
@@ -76,10 +76,10 @@ typedef u64 phys_cpuid_t;
#define PHYS_CPUID_INVALID INVALID_HWID
#define acpi_strict 1 /* No out-of-spec workarounds on ARM64 */
-#define acpi_dt_hybrid 0 /* No DT-ACPI hybrid mode on ARM64 */
extern int acpi_disabled;
extern int acpi_noirq;
extern int acpi_pci_disabled;
+extern int acpi_dt_hybrid;
static inline void disable_acpi(void)
{
diff --git a/arch/arm64/kernel/acpi.c b/arch/arm64/kernel/acpi.c
index 06ab3a9da64b..9d2c42375afe 100644
--- a/arch/arm64/kernel/acpi.c
+++ b/arch/arm64/kernel/acpi.c
@@ -43,11 +43,14 @@ EXPORT_SYMBOL(acpi_disabled);
int acpi_pci_disabled = 1; /* skip ACPI PCI scan and IRQ initialization */
EXPORT_SYMBOL(acpi_pci_disabled);
+int acpi_dt_hybrid; /* disable ACPI-DT hybrid mode */
+
enum acpi_mode_t {
acpi_mode_unset,
acpi_mode_off,
acpi_mode_on,
acpi_mode_force,
+ acpi_mode_hybrid,
};
static enum acpi_mode_t param_acpi_mode __initdata;
@@ -65,6 +68,8 @@ static int __init parse_acpi(char *arg)
param_acpi_mode = acpi_mode_on;
else if (strcmp(arg, "force") == 0) /* force ACPI to be enabled */
param_acpi_mode = acpi_mode_force;
+ else if (strcmp(arg, "hybrid") == 0) /* ACPI-DT hybrid mode */
+ param_acpi_mode = acpi_mode_hybrid;
else if (strcmp(arg, "nospcr") == 0) /* disable SPCR as default console */
param_acpi_nospcr = true;
else
@@ -203,7 +208,7 @@ static int __init acpi_fadt_sanity_check(void)
void __init acpi_boot_table_init(void)
{
/*
- * When no ACPI mode (acpi=off|on|force) has been specified,
+ * When no ACPI mode (acpi=off|on|force|hybrid) has been specified,
* enable ACPI if the device tree is empty (it only has a /chosen
* node, and a /hypervisor node when running on Xen).
*/
@@ -230,6 +235,9 @@ void __init acpi_boot_table_init(void)
pr_err("Failed to init ACPI tables\n");
if (param_acpi_mode != acpi_mode_force)
disable_acpi();
+ } else if (param_acpi_mode == acpi_mode_hybrid) {
+ acpi_dt_hybrid = 1;
+ disable_acpi();
}
done:
@@ -257,10 +265,10 @@ void __init acpi_boot_table_init(void)
*/
acpi_parse_spcr(earlycon_acpi_spcr_enable,
!param_acpi_nospcr);
-
- if (IS_ENABLED(CONFIG_ACPI_BGRT))
- acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
}
+
+ if ((!acpi_disabled || acpi_dt_hybrid) && IS_ENABLED(CONFIG_ACPI_BGRT))
+ acpi_table_parse(ACPI_SIG_BGRT, acpi_parse_bgrt);
}
static pgprot_t __acpi_get_writethrough_mem_attribute(void)
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 04/12] ACPI: Add helpers for dealing with ACPI fwnode as secondary fwnode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (2 preceding siblings ...)
2026-06-23 14:52 ` [RFC 03/12] arm64: acpi: add acpi=hybrid support Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 05/12] ACPI: glue: Implement setting secondary-fwnode for DT-ACPI hybrid mode Hans de Goede
` (10 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
In DT-ACPI hybrid mode in some cases code calling is_acpi_device_node() or
to_acpi_device_node() may also want the secondary fwnode to be used.
DT-ACPI hybrid mode is something which subsystems / drivers should opt-in
to, so it is undesirable to make existing helpers use the secondary fwnode.
Add new is_acpi_device_node_any() and to_acpi_device_node_any() helpers
which also use the secondary fwnode.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/acpi/property.c | 13 +++++++++++++
include/acpi/acpi_bus.h | 16 ++++++++++++++++
include/linux/acpi.h | 10 ++++++++++
3 files changed, 39 insertions(+)
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 8ee5a1f0eb48..fb555e50ce83 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -1775,6 +1775,19 @@ bool is_acpi_device_node(const struct fwnode_handle *fwnode)
}
EXPORT_SYMBOL(is_acpi_device_node);
+/* Like is_acpi_device_node() but also check the secondary fwnode */
+bool is_acpi_device_node_any(const struct fwnode_handle *fwnode)
+{
+ if (IS_ERR_OR_NULL(fwnode))
+ return false;
+
+ if (fwnode->ops == &acpi_device_fwnode_ops)
+ return true;
+
+ return is_acpi_device_node(fwnode->secondary);
+}
+EXPORT_SYMBOL(is_acpi_device_node_any);
+
bool is_acpi_data_node(const struct fwnode_handle *fwnode)
{
return !IS_ERR_OR_NULL(fwnode) && fwnode->ops == &acpi_data_fwnode_ops;
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index c41d9a7565cf..bfc3883765f1 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -515,6 +515,7 @@ extern const struct fwnode_operations acpi_data_fwnode_ops;
extern const struct fwnode_operations acpi_static_fwnode_ops;
bool is_acpi_device_node(const struct fwnode_handle *fwnode);
+bool is_acpi_device_node_any(const struct fwnode_handle *fwnode);
bool is_acpi_data_node(const struct fwnode_handle *fwnode);
static inline bool is_acpi_node(const struct fwnode_handle *fwnode)
@@ -532,6 +533,21 @@ static inline bool is_acpi_node(const struct fwnode_handle *fwnode)
NULL; \
})
+/* Like to_acpi_device_node() but also check the secondary fwnode */
+#define to_acpi_device_node_any(__fwnode) \
+ ({ \
+ typeof(__fwnode) __to_acpi_device_node_fwnode = __fwnode; \
+ \
+ IS_ERR_OR_NULL(__to_acpi_device_node_fwnode) ? NULL : \
+ is_acpi_device_node(__to_acpi_device_node_fwnode) ? \
+ container_of(__to_acpi_device_node_fwnode, \
+ struct acpi_device, fwnode) : \
+ is_acpi_device_node(__to_acpi_device_node_fwnode->secondary) ? \
+ container_of(__to_acpi_device_node_fwnode->secondary, \
+ struct acpi_device, fwnode) : \
+ NULL; \
+ })
+
#define to_acpi_data_node(__fwnode) \
({ \
typeof(__fwnode) __to_acpi_data_node_fwnode = __fwnode; \
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 67effb91fa98..bb84774f61df 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -865,11 +865,21 @@ static inline bool is_acpi_device_node(const struct fwnode_handle *fwnode)
return false;
}
+static inline bool is_acpi_device_node_any(const struct fwnode_handle *fwnode)
+{
+ return false;
+}
+
static inline struct acpi_device *to_acpi_device_node(const struct fwnode_handle *fwnode)
{
return NULL;
}
+static inline struct acpi_device *to_acpi_device_node_any(const struct fwnode_handle *fwnode)
+{
+ return NULL;
+}
+
static inline bool is_acpi_data_node(const struct fwnode_handle *fwnode)
{
return false;
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 05/12] ACPI: glue: Implement setting secondary-fwnode for DT-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (3 preceding siblings ...)
2026-06-23 14:52 ` [RFC 04/12] ACPI: Add helpers for dealing with ACPI fwnode as secondary fwnode Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 06/12] ACPI: scan: Retry acpi_device_notify() in " Hans de Goede
` (9 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
In DT-ACPI hybrid mode devices are instantiated from device tree and
a device's primary firmware-node will normally be an of fwnode.
Add support to the ACPI glue code to check the new "acpi-path" DT property
and when this is set lookup the handle for the specified path and set
the secondary-fwnode of the device to point to the handle's ACPI fwnode.
Also create a firmware_node and physical_node links/entries as normal.
When e.g. an "acpi-path" property is specified for e.g. an i2c-adapter
then the normal ACPI i2c-client enumeration will happen for any ACPI
fwnodes with an I2CSerialBus resource pointing to the i2c-adapter, these
ACPI instantiated i2c_clients will have an ACPI fwnode as primary fwnode.
The normal acpi_device_notify[_remove]() paths will be used for these.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/acpi/glue.c | 70 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 60 insertions(+), 10 deletions(-)
diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index b1776809279d..fef450d5cb7d 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -225,6 +225,15 @@ static void acpi_physnode_link_name(char *buf, unsigned int node_id)
strcpy(buf, PHYSICAL_NODE_STRING);
}
+/* Like ACPI_COMPANION_SET() but set secondary fwnode if primary is an of_node */
+static void acpi_companion_set_any(struct device *dev, struct acpi_device *adev)
+{
+ if (is_of_node(dev->fwnode))
+ set_secondary_fwnode(dev, adev ? acpi_fwnode_handle(adev) : NULL);
+ else
+ set_primary_fwnode(dev, adev ? acpi_fwnode_handle(adev) : NULL);
+}
+
int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
{
struct acpi_device_physical_node *physical_node, *pn;
@@ -233,12 +242,12 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
unsigned int node_id;
int retval = -EINVAL;
- if (has_acpi_companion(dev)) {
+ if (is_acpi_device_node_any(dev->fwnode)) {
if (acpi_dev) {
dev_warn(dev, "ACPI companion already set\n");
return -EINVAL;
} else {
- acpi_dev = ACPI_COMPANION(dev);
+ acpi_dev = to_acpi_device_node_any(dev->fwnode);
}
}
if (!acpi_dev)
@@ -267,7 +276,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
dev_warn(dev, "Already associated with ACPI node\n");
kfree(physical_node);
- if (ACPI_COMPANION(dev) != acpi_dev)
+ if (to_acpi_device_node_any(dev->fwnode) != acpi_dev)
goto err;
put_device(dev);
@@ -285,8 +294,8 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
list_add(&physical_node->node, physnode_list);
acpi_dev->physical_node_count++;
- if (!has_acpi_companion(dev))
- ACPI_COMPANION_SET(dev, acpi_dev);
+ if (!is_acpi_device_node_any(dev->fwnode))
+ acpi_companion_set_any(dev, acpi_dev);
acpi_physnode_link_name(physical_node_name, node_id);
retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
@@ -303,13 +312,14 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
mutex_unlock(&acpi_dev->physical_node_lock);
- if (acpi_dev->wakeup.flags.valid)
+ /* Don't set wakeup flag for devices where ACPI fwnode is secondary */
+ if (acpi_dev->wakeup.flags.valid && has_acpi_companion(dev))
device_set_wakeup_capable(dev, true);
return 0;
err:
- ACPI_COMPANION_SET(dev, NULL);
+ acpi_companion_set_any(dev, NULL);
put_device(dev);
acpi_dev_put(acpi_dev);
return retval;
@@ -318,7 +328,7 @@ EXPORT_SYMBOL_GPL(acpi_bind_one);
int acpi_unbind_one(struct device *dev)
{
- struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
+ struct acpi_device *acpi_dev = to_acpi_device_node_any(dev->fwnode);
struct acpi_device_physical_node *entry;
if (!acpi_dev)
@@ -336,7 +346,7 @@ int acpi_unbind_one(struct device *dev)
acpi_physnode_link_name(physnode_name, entry->node_id);
sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
sysfs_remove_link(&dev->kobj, "firmware_node");
- ACPI_COMPANION_SET(dev, NULL);
+ acpi_companion_set_any(dev, NULL);
/* Drop references taken by acpi_bind_one(). */
put_device(dev);
acpi_dev_put(acpi_dev);
@@ -354,6 +364,40 @@ void acpi_device_notify(struct device *dev)
struct acpi_device *adev;
int ret;
+ if (acpi_dt_hybrid && is_of_node(dev->fwnode)) {
+ const char *acpi_path;
+ acpi_status status;
+ acpi_handle handle;
+
+ ret = of_property_read_string(dev->of_node, "acpi-path", &acpi_path);
+ if (ret)
+ return;
+
+ status = acpi_get_handle(NULL, acpi_path, &handle);
+ if (ACPI_FAILURE(status))
+ goto err_hybrid;
+
+ adev = acpi_fetch_acpi_dev(handle);
+ if (!adev)
+ goto err_hybrid;
+
+ /*
+ * set_secondary_fwnode() + pass NULL to make this work for
+ * child devices which share the fwnode with their parent.
+ */
+ set_secondary_fwnode(dev, acpi_fwnode_handle(adev));
+ ret = acpi_bind_one(dev, NULL);
+ if (ret)
+ goto err_hybrid;
+
+ /* TODO change to dev_dbg() when DT-ACPI hybrid support is stable */
+ dev_info(dev, "Set secondary fwnode to acpi-path '%s'\n", acpi_path);
+ return;
+err_hybrid:
+ dev_warn(dev, "Failed to set ACPI fwnode for acpi-path '%s'\n", acpi_path);
+ return;
+ }
+
ret = acpi_bind_one(dev, NULL);
if (ret) {
struct acpi_bus_type *type = acpi_get_bus_type(dev);
@@ -400,8 +444,14 @@ void acpi_device_notify(struct device *dev)
void acpi_device_notify_remove(struct device *dev)
{
- struct acpi_device *adev = ACPI_COMPANION(dev);
+ struct acpi_device *adev;
+ if (acpi_dt_hybrid && is_of_node(dev->fwnode)) {
+ acpi_unbind_one(dev);
+ return;
+ }
+
+ adev = ACPI_COMPANION(dev);
if (!adev)
return;
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 06/12] ACPI: scan: Retry acpi_device_notify() in DT-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (4 preceding siblings ...)
2026-06-23 14:52 ` [RFC 05/12] ACPI: glue: Implement setting secondary-fwnode for DT-ACPI hybrid mode Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 07/12] ACPI: Make device_match_acpi_handle() also check the secondary fwnode Hans de Goede
` (8 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
of_platform_default_populate_init creates DT platform devices from
an arch_initcall(), so before acpi_scan_init() runs this causing
acpi_device_notify() to be unable to honor "acpi-path" properties
in DT-ACPI hybrid mode.
Re-call acpi_device_notify() at the end of scanning ACPI devices to
fix this up.
Note this relies on the driver which may use the bound ACPI fwnode
to only register after subsys_initcall(acpi_init) has run.
TODO: It is probably better to add an acpi_platform_device_pre_probe()
function and call that from drivers/base/platform.c:platform_probe() and
dot the setting of the secondary fwnode for platform-devices with
an "acpi-path" property there. Basically moving the "if (acpi_dt_hybrid)"
block in acpi_device_notify() to a new acpi_platform_device_pre_probe().
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/acpi/scan.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 4836286968e8..b305c03e8504 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -21,6 +21,7 @@
#include <linux/dmi.h>
#include <linux/dma-map-ops.h>
#include <linux/platform_data/x86/apple.h>
+#include <linux/platform_device.h>
#include <linux/pgtable.h>
#include <linux/crc32.h>
#include <linux/dma-direct.h>
@@ -2818,6 +2819,22 @@ static void __init acpi_get_spcr_uart_addr(void)
acpi_put_table((struct acpi_table_header *)spcr_ptr);
}
+static int acpi_scan_retry_of_acpi_binding(struct device *dev, void *data)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ const char *acpi_path;
+
+ /* Check primary fwnode is OF and secondary fwnode is not yet ACPI */
+ if (!is_of_node(fwnode) || is_acpi_device_node(fwnode->secondary))
+ return 0;
+
+ /* If there is an "acpi-path" property retry binding ACPI fwnode */
+ if (of_property_read_string(dev->of_node, "acpi-path", &acpi_path) == 0)
+ acpi_device_notify(dev);
+
+ return 0;
+}
+
static bool acpi_scan_initialized;
void __init acpi_scan_init(void)
@@ -2881,6 +2898,18 @@ void __init acpi_scan_init(void)
if (!acpi_gbl_reduced_hardware)
acpi_bus_scan_fixed();
+ /*
+ * of_platform_default_populate_init creates DT platform devices from
+ * an arch_initcall(), so before acpi_scan_init() runs this causing
+ * acpi_device_notify() to be unable to honor "acpi-path" properties
+ * in DT-ACPI hybrid mode. Re-call acpi_device_notify() to fix this up.
+ * Note this relies on the driver which may use the bound ACPI fwnode
+ * to only register after subsys_initcall(acpi_init) has run.
+ */
+ if (acpi_dt_hybrid)
+ bus_for_each_dev(&platform_bus_type, NULL, NULL,
+ acpi_scan_retry_of_acpi_binding);
+
acpi_turn_off_unused_power_resources();
acpi_scan_initialized = true;
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 07/12] ACPI: Make device_match_acpi_handle() also check the secondary fwnode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (5 preceding siblings ...)
2026-06-23 14:52 ` [RFC 06/12] ACPI: scan: Retry acpi_device_notify() in " Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 08/12] irqchip/gic-v3: Always call acpi_set_irq_model() Hans de Goede
` (7 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
In DT-ACPI hybrid mode the secondary fwnode of a device may point to
an ACPI fwnode instead of the primary.
device_match_acpi_handle() is used to find parent GPIO/I2C/SPI controllers
when resolving ACPI device resources from (_CRS) in this case we always
want to also check the secondary fwnode to help find the parent.
Modify the existing device_match_acpi_handle() function to also check
the secondary fwnode.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/base/core.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index bd2ddf2aab50..2573253f5815 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -5357,7 +5357,9 @@ EXPORT_SYMBOL(device_match_acpi_dev);
int device_match_acpi_handle(struct device *dev, const void *handle)
{
- return handle && ACPI_HANDLE(dev) == handle;
+ struct acpi_device *adev = to_acpi_device_node_any(dev->fwnode);
+
+ return handle && adev && adev->handle == handle;
}
EXPORT_SYMBOL(device_match_acpi_handle);
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 08/12] irqchip/gic-v3: Always call acpi_set_irq_model()
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (6 preceding siblings ...)
2026-06-23 14:52 ` [RFC 07/12] ACPI: Make device_match_acpi_handle() also check the secondary fwnode Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 09/12] pinctrl: qcom: Add support for WoA ACPI tables virtual TLMM pin numbers Hans de Goede
` (6 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
The gic-v3 code used to only call acpi_set_irq_model() when the GIC is
enumerated through ACPI.
This causes problems for DT-ACPI hybrid mode, where the GIC might be
described in DT but then a driver using info from the ACPI tables might
call acpi_dev_get_irqresource(), which results in a NULL pointer deref
when acpi_set_irq_model() has not been called.
Always call acpi_set_irq_model() when CONFIG_ACPI is enabled to fix this.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/irqchip/irq-gic-v3.c | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 99444a1b2ffa..2ccd04689a04 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -1974,6 +1974,23 @@ static void gic_enable_nmi_support(void)
gic_chip.flags |= IRQCHIP_SUPPORTS_NMI;
}
+#ifdef CONFIG_ACPI
+static struct fwnode_handle *gsi_domain_handle;
+
+static struct fwnode_handle *gic_v3_get_gsi_domain_id(u32 gsi)
+{
+ return gsi_domain_handle;
+}
+
+static void gic_set_acpi_irq_model(struct fwnode_handle *handle)
+{
+ gsi_domain_handle = handle;
+ acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
+}
+#else
+static void gic_set_acpi_irq_model(struct fwnode_handle *handle) { }
+#endif
+
static int __init gic_init_bases(phys_addr_t dist_phys_base,
void __iomem *dist_base,
struct redist_region *rdist_regs,
@@ -2063,6 +2080,9 @@ static int __init gic_init_bases(phys_addr_t dist_phys_base,
gicv2m_init(handle, gic_data.domain);
}
+ /* Also do this when enumerated through DT for DT-ACPI hybrid mode */
+ gic_set_acpi_irq_model(handle);
+
return 0;
out_free:
@@ -2534,13 +2554,6 @@ static void __init gic_acpi_setup_kvm_info(void)
vgic_set_kvm_info(&gic_v3_kvm_info);
}
-static struct fwnode_handle *gsi_domain_handle;
-
-static struct fwnode_handle *gic_v3_get_gsi_domain_id(u32 gsi)
-{
- return gsi_domain_handle;
-}
-
static int __init
gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
{
@@ -2588,8 +2601,6 @@ gic_acpi_init(union acpi_subtable_headers *header, const unsigned long end)
if (err)
goto out_fwhandle_free;
- acpi_set_irq_model(ACPI_IRQ_MODEL_GIC, gic_v3_get_gsi_domain_id);
-
if (static_branch_likely(&supports_deactivate_key))
gic_acpi_setup_kvm_info();
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 09/12] pinctrl: qcom: Add support for WoA ACPI tables virtual TLMM pin numbers
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (7 preceding siblings ...)
2026-06-23 14:52 ` [RFC 08/12] irqchip/gic-v3: Always call acpi_set_irq_model() Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 10/12] i2c: acpi: Also register ACPI i2c_clients for adapters with a secondary ACPI fwnode Hans de Goede
` (5 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
The ACPI tabled on Windows on ARM laptops use TLMM pin numbers outside of
the actual TLMM pin number range. These are a rather convoluted way to let
the Windows Qualcomm GPIO driver now to use the PDC for some pins because
these are wakeup sources.
This adds support for translating the magic Windows virtual GPIOs for these
back to a regular TLMM GPIO so that ACPI described devices using these
virtual GPIOs can work with Linux.
For now this code only tries to do this mapping when booting in DT-ACPI
hybrid mode which is only used on some WoA devices so this should not
impact any other use-cases.
The new functions use woa_acpi in their name to make clear that these
are for dealing with the ACPI tables found on WoA devices, rather then
ACPI tables found on other devices, like ARM system ready devices which
also use ACPI.
Note that simply mapping these virtual GPIOs back to TLMM pin numbers can
safely be done on Linux, because Linux always uses the PDC for GPIO IRQs
where possible.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/pinctrl/qcom/Makefile | 4 +-
drivers/pinctrl/qcom/pinctrl-msm-acpi.c | 196 ++++++++++++++++++++++++
drivers/pinctrl/qcom/pinctrl-msm.c | 47 +++++-
drivers/pinctrl/qcom/pinctrl-msm.h | 35 +++++
4 files changed, 278 insertions(+), 4 deletions(-)
create mode 100644 drivers/pinctrl/qcom/pinctrl-msm-acpi.c
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 84bda3ada874..9029d99190d2 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0
# Qualcomm pin control drivers
-obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm.o
+obj-$(CONFIG_PINCTRL_MSM) += pinctrl-msm-core.o
+pinctrl-msm-core-y := pinctrl-msm.o
+pinctrl-msm-core-$(CONFIG_ACPI) += pinctrl-msm-acpi.o
obj-$(CONFIG_PINCTRL_APQ8064) += pinctrl-apq8064.o
obj-$(CONFIG_PINCTRL_APQ8084) += pinctrl-apq8084.o
obj-$(CONFIG_PINCTRL_ELIZA) += pinctrl-eliza.o
diff --git a/drivers/pinctrl/qcom/pinctrl-msm-acpi.c b/drivers/pinctrl/qcom/pinctrl-msm-acpi.c
new file mode 100644
index 000000000000..df180fd04749
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-msm-acpi.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ACPI GPIO lookup handling for WoA (Windows on ARM) laptop ACPI tables.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/gpio/driver.h>
+#include <linux/list.h>
+#include <linux/math.h>
+#include "pinctrl-msm.h"
+
+#define MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK 64
+#define MSM_GPIO_WOA_ACPI_IRQ_OFFSET 32
+#define MSM_GPIO_WOA_ACPI_INVALID_GPIO ~0U
+#define MSM_GPIO_WOA_ACPI_MAX_PDC_RANGES 16
+
+#define PDC_RANGE_PIN_BASE 0
+#define PDC_RANGE_GIC_BASE 1
+#define PDC_RANGE_COUNT 2
+#define PDC_RANGE_ELEMENTS 3
+
+/**
+ * struct msm_gpio_woa_acpi_parse_data - Data for parsing WoA ACPI GPIO ctl resources
+ * @chip: gpiochip handle
+ * @data: Data for mapping virtual WoA ACPI PDC IRQ GPIOs
+ * @soc_data: Reference to soc_data of platform specific data
+ * @pdc_range: PDC GIC to PDC map ranges
+ * @pdc_range_count: PDC GIC to PDC map range-count
+ */
+struct msm_gpio_woa_acpi_parse_data {
+ struct gpio_chip *chip;
+ struct msm_gpio_woa_acpi_data *data;
+ const struct msm_pinctrl_soc_data *soc_data;
+ u32 pdc_range[MSM_GPIO_WOA_ACPI_MAX_PDC_RANGES][PDC_RANGE_ELEMENTS];
+ unsigned int pdc_range_count;
+};
+
+/*
+ * Mapping does not need translating the acpi_resource in to a regular resoure
+ * and adding it to the resource list. Always return 1 to disable this.
+ */
+static int msm_gpio_woa_acpi_resource(struct acpi_resource *ares, void *_parse)
+{
+ struct msm_gpio_woa_acpi_parse_data *parse = _parse;
+ const struct msm_pinctrl_soc_data *soc_data = parse->soc_data;
+ struct msm_gpio_woa_acpi_data *data = parse->data;
+ struct gpio_chip *chip = parse->chip;
+ u32 gic_irq, pdc_pin;
+
+ if (ares->type != ACPI_RESOURCE_TYPE_EXTENDED_IRQ ||
+ ares->data.extended_irq.interrupt_count != 1)
+ return 1;
+
+ if (data->nmap == MSM_GPIO_WOA_ACPI_MAX_VIRT_GPIOS) {
+ dev_err(chip->parent, "ACPI resources contain more than %d IRQs\n",
+ MSM_GPIO_WOA_ACPI_MAX_VIRT_GPIOS);
+ return 1;
+ }
+
+ /*
+ * Windows ACPI tables divide GPIOs into banks of 64 pins with one IRQ
+ * per bank. The resources start with listing the real TLMM IRQ for
+ * as many banks as are necessary to cover the real GPIOs. The Windows
+ * virtual GPIO indexes skip these banks, mark them as unavailable.
+ */
+ if (data->nmap < DIV_ROUND_UP(chip->ngpio, MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK)) {
+ data->map[data->nmap++] = MSM_GPIO_WOA_ACPI_INVALID_GPIO;
+ return 1;
+ }
+
+ /*
+ * Use the "pdc-ranges" property on the PDC to translate the GIC IRQ
+ * from the acpi_resource to a PDC pin.
+ */
+ gic_irq = ares->data.extended_irq.interrupts[0] - MSM_GPIO_WOA_ACPI_IRQ_OFFSET;
+ pdc_pin = MSM_GPIO_WOA_ACPI_INVALID_GPIO;
+ for (unsigned int i = 0; i < parse->pdc_range_count; i++) {
+ u32 gic_base = parse->pdc_range[i][PDC_RANGE_GIC_BASE];
+ u32 count = parse->pdc_range[i][PDC_RANGE_COUNT];
+ if (gic_irq >= gic_base && gic_irq < (gic_base + count)) {
+ pdc_pin = parse->pdc_range[i][PDC_RANGE_PIN_BASE] +
+ gic_irq - gic_base;
+ break;
+ }
+ }
+ if (pdc_pin == MSM_GPIO_WOA_ACPI_INVALID_GPIO)
+ goto no_map;
+
+ /* Use wakeirq-map to map PDC pin to TLMM pin */
+ for (unsigned int i = 0; i < soc_data->nwakeirq_map; i++) {
+ if (soc_data->wakeirq_map[i].wakeirq == pdc_pin) {
+ data->map[data->nmap++] = soc_data->wakeirq_map[i].gpio;
+ return 1;
+ }
+ }
+
+no_map:
+ dev_warn(chip->parent, "Cannot map GIC IRQ %u to TLMM pin\n", gic_irq);
+ data->map[data->nmap++] = MSM_GPIO_WOA_ACPI_INVALID_GPIO;
+ return 1;
+}
+
+int msm_gpio_woa_acpi_init(struct gpio_chip *chip, struct msm_gpio_woa_acpi_data *data,
+ const struct msm_pinctrl_soc_data *soc_data)
+{
+ struct msm_gpio_woa_acpi_parse_data parse;
+ struct fwnode_handle *fwnode;
+ struct device_node *pdc_np;
+ LIST_HEAD(resources);
+ unsigned int ngpio;
+ int ret;
+
+ /* WoA ACPI tables are only used in DT-ACPI hybrid mode */
+ fwnode = chip->parent->fwnode;
+ if (!is_of_node(fwnode) || !is_acpi_device_node(fwnode->secondary))
+ return 0;
+
+ parse.chip = chip;
+ parse.data = data;
+ parse.soc_data = soc_data;
+
+ /* Get PDC ranges, the PDC is the TLMM's wakeup-parent. */
+ pdc_np = of_parse_phandle(chip->parent->of_node, "wakeup-parent", 0);
+ if (!pdc_np)
+ return 0;
+
+ ret = of_property_count_elems_of_size(pdc_np, "qcom,pdc-ranges", sizeof(u32));
+ if (ret <= 0 || ret % PDC_RANGE_ELEMENTS ||
+ ret > (MSM_GPIO_WOA_ACPI_MAX_PDC_RANGES * PDC_RANGE_ELEMENTS))
+ goto err_pdc_ranges;
+
+ parse.pdc_range_count = ret / PDC_RANGE_ELEMENTS;
+ ret = of_property_read_u32_array(pdc_np, "qcom,pdc-ranges",
+ &parse.pdc_range[0][0], ret);
+ if (ret)
+ goto err_pdc_ranges;
+
+ ret = acpi_dev_get_resources(to_acpi_device_node(fwnode->secondary), &resources,
+ msm_gpio_woa_acpi_resource, &parse);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&resources);
+
+ ngpio = data->nmap * MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK + 1;
+ chip->ngpio = max(chip->ngpio, ngpio);
+
+ for (unsigned int i = 0; i < data->nmap; i++) {
+ if (data->map[i] != MSM_GPIO_WOA_ACPI_INVALID_GPIO) {
+ /* TODO lower log level to dev_dbg() */
+ dev_info(chip->parent, "mapped GPIO 0x%03x to TLMM pin %u\n",
+ i * MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK, data->map[i]);
+ }
+ }
+
+ return 0;
+
+err_pdc_ranges:
+ dev_err(chip->parent, "Error invalid pdc-ranges\n");
+ return 0; /* Continue without mapping */
+}
+
+void msm_gpio_woa_acpi_valid_mask(struct gpio_chip *chip,
+ struct msm_gpio_woa_acpi_data *data,
+ unsigned long *valid_mask,
+ unsigned int soc_ngpio)
+{
+ /* First mark all virtual ACPI PDC GPIOs (if any) as invalid */
+ bitmap_clear(valid_mask, soc_ngpio, chip->ngpio - soc_ngpio);
+
+ /*
+ * WoA ACPI tables list 1 Interrupt resource per PDC pin and use
+ * a 1 interrupt per bank model. So each PDC pin gets its own bank,
+ * with only pin 0 of that bank used for that PDC pin.
+ */
+ for (unsigned int i = 0; i < data->nmap; i++) {
+ if (data->map[i] != MSM_GPIO_WOA_ACPI_INVALID_GPIO)
+ set_bit(i * MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK, valid_mask);
+ }
+}
+
+unsigned int msm_gpio_woa_acpi_map(struct msm_gpio_woa_acpi_data *data, unsigned int offset)
+{
+ unsigned int bank = offset / MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK;
+
+ /* msm_gpio_woa_acpi_valid_mask() should ensure this never happens */
+ if (WARN_ON(offset % MSM_GPIO_WOA_ACPI_GPIOS_PER_BANK || bank >= data->nmap ||
+ data->map[bank] == MSM_GPIO_WOA_ACPI_INVALID_GPIO))
+ return 0;
+
+ return data->map[bank];
+}
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c
index f780bab51d9c..60ba0da95634 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.c
+++ b/drivers/pinctrl/qcom/pinctrl-msm.c
@@ -45,6 +45,7 @@
* @pctrl: pinctrl handle.
* @chip: gpiochip handle.
* @desc: pin controller descriptor
+ * @woa_acpi: data for mapping virtual WoA ACPI PDC IRQ GPIOs
* @irq: parent irq for the TLMM irq_chip.
* @intr_target_use_scm: route irq to application cpu using scm calls
* @lock: Spinlock to protect register resources as well
@@ -64,6 +65,7 @@ struct msm_pinctrl {
struct pinctrl_dev *pctrl;
struct gpio_chip chip;
struct pinctrl_desc desc;
+ struct msm_gpio_woa_acpi_data woa_acpi;
int irq;
@@ -553,6 +555,16 @@ static const struct pinconf_ops msm_pinconf_ops = {
.pin_config_group_set = msm_config_group_set,
};
+static unsigned int msm_gpio_map(struct gpio_chip *chip, unsigned int offset)
+{
+ struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
+
+ if (offset < pctrl->soc->ngpios)
+ return offset;
+
+ return msm_gpio_woa_acpi_map(&pctrl->woa_acpi, offset);
+}
+
static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
const struct msm_pingroup *g;
@@ -560,6 +572,8 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
unsigned long flags;
u32 val;
+ offset = msm_gpio_map(chip, offset);
+
g = &pctrl->soc->groups[offset];
raw_spin_lock_irqsave(&pctrl->lock, flags);
@@ -580,6 +594,8 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in
unsigned long flags;
u32 val;
+ offset = msm_gpio_map(chip, offset);
+
g = &pctrl->soc->groups[offset];
raw_spin_lock_irqsave(&pctrl->lock, flags);
@@ -606,6 +622,8 @@ static int msm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
const struct msm_pingroup *g;
u32 val;
+ offset = msm_gpio_map(chip, offset);
+
g = &pctrl->soc->groups[offset];
val = msm_readl_ctl(pctrl, g);
@@ -620,6 +638,8 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset)
struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
u32 val;
+ offset = msm_gpio_map(chip, offset);
+
g = &pctrl->soc->groups[offset];
val = msm_readl_io(pctrl, g);
@@ -633,6 +653,8 @@ static int msm_gpio_set(struct gpio_chip *chip, unsigned int offset, int value)
unsigned long flags;
u32 val;
+ offset = msm_gpio_map(chip, offset);
+
g = &pctrl->soc->groups[offset];
raw_spin_lock_irqsave(&pctrl->lock, flags);
@@ -717,10 +739,11 @@ static void msm_gpio_dbg_show_one(struct seq_file *s,
static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
+ struct msm_pinctrl *pctrl = gpiochip_get_data(chip);
unsigned gpio = chip->base;
unsigned i;
- for (i = 0; i < chip->ngpio; i++, gpio++)
+ for (i = 0; i < pctrl->soc->ngpios; i++, gpio++)
msm_gpio_dbg_show_one(s, NULL, chip, i, gpio);
}
@@ -738,6 +761,10 @@ static int msm_gpio_init_valid_mask(struct gpio_chip *gc,
const int *reserved = pctrl->soc->reserved_gpios;
u16 *tmp;
+ /* WOA ACPI handles virtual PDC GPIO range, only handle real GPIOs here */
+ ngpios = pctrl->soc->ngpios;
+ msm_gpio_woa_acpi_valid_mask(gc, &pctrl->woa_acpi, valid_mask, ngpios);
+
/* Remove driver-provided reserved GPIOs from valid_mask */
if (reserved) {
for (i = 0; reserved[i] >= 0; i++) {
@@ -1046,6 +1073,10 @@ static void msm_gpio_irq_init_valid_mask(struct gpio_chip *gc,
const struct msm_pingroup *g;
int i;
+ /* WOA ACPI handles virtual PDC GPIO range, only handle real GPIOs here */
+ ngpios = pctrl->soc->ngpios;
+ msm_gpio_woa_acpi_valid_mask(gc, &pctrl->woa_acpi, valid_mask, ngpios);
+
for (i = 0; i < ngpios; i++) {
g = &pctrl->soc->groups[i];
@@ -1387,6 +1418,9 @@ static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl)
if (pctrl->soc->reserved_gpios)
return true;
+ if (pctrl->chip.ngpio != pctrl->soc->ngpios)
+ return true;
+
return device_property_count_u16(pctrl->dev, "gpios") > 0;
}
@@ -1428,8 +1462,6 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
chip->label = dev_name(pctrl->dev);
chip->parent = pctrl->dev;
chip->owner = THIS_MODULE;
- if (msm_gpio_needs_valid_mask(pctrl))
- chip->init_valid_mask = msm_gpio_init_valid_mask;
np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0);
if (np) {
@@ -1450,6 +1482,13 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
}
}
+ ret = msm_gpio_woa_acpi_init(chip, &pctrl->woa_acpi, pctrl->soc);
+ if (ret)
+ return ret;
+
+ if (msm_gpio_needs_valid_mask(pctrl))
+ chip->init_valid_mask = msm_gpio_init_valid_mask;
+
girq = &chip->irq;
gpio_irq_chip_set_chip(girq, &msm_gpio_irq_chip);
girq->parent_handler = msm_gpio_irq_handler;
@@ -1463,6 +1502,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl)
girq->handler = handle_bad_irq;
girq->parents[0] = pctrl->irq;
girq->init_valid_mask = msm_gpio_irq_init_valid_mask;
+ if (pctrl->chip.ngpio != pctrl->soc->ngpios)
+ girq->child_offset_to_irq = msm_gpio_map;
ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl);
if (ret) {
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h
index 4fbff61de6bb..3a8a55a6d8c1 100644
--- a/drivers/pinctrl/qcom/pinctrl-msm.h
+++ b/drivers/pinctrl/qcom/pinctrl-msm.h
@@ -10,6 +10,7 @@
#include <linux/pinctrl/pinctrl.h>
+struct gpio_chip;
struct platform_device;
struct pinctrl_pin_desc;
@@ -181,9 +182,43 @@ struct msm_pinctrl_soc_data {
unsigned int egpio_func;
};
+#define MSM_GPIO_WOA_ACPI_MAX_VIRT_GPIOS 32
+
+/**
+ * struct msm_gpio_woa_acpi_data - Data for mapping virtual WoA ACPI PDC IRQ GPIOs
+ * @map: Map to translate virtual ACPI GPIO offsets to TLMM pin offsets
+ * @nmap: Number of valid entried in @map
+ */
+struct msm_gpio_woa_acpi_data {
+ unsigned int map[MSM_GPIO_WOA_ACPI_MAX_VIRT_GPIOS];
+ unsigned int nmap;
+};
+
extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
int msm_pinctrl_probe(struct platform_device *pdev,
const struct msm_pinctrl_soc_data *soc_data);
+#ifdef CONFIG_ACPI
+int msm_gpio_woa_acpi_init(struct gpio_chip *chip, struct msm_gpio_woa_acpi_data *data,
+ const struct msm_pinctrl_soc_data *soc_data);
+void msm_gpio_woa_acpi_valid_mask(struct gpio_chip *chip,
+ struct msm_gpio_woa_acpi_data *data,
+ unsigned long *valid_mask,
+ unsigned int soc_ngpio);
+unsigned int msm_gpio_woa_acpi_map(struct msm_gpio_woa_acpi_data *data, unsigned int offset);
+#else
+static inline int
+msm_gpio_woa_acpi_init(struct gpio_chip *chip, struct msm_gpio_woa_acpi_data *data,
+ const struct msm_pinctrl_soc_data *soc_data)
+{ return 0; }
+static inline void msm_gpio_woa_acpi_valid_mask(struct gpio_chip *chip,
+ struct msm_gpio_woa_acpi_data *data,
+ unsigned long *valid_mask,
+ unsigned int soc_ngpio) { }
+static inline unsigned int
+msm_gpio_woa_acpi_map(struct msm_gpio_woa_acpi_data *data, unsigned int offset)
+{ return 0; }
#endif
+
+#endif /* ifndef __PINCTRL_MSM_H__ */
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 10/12] i2c: acpi: Also register ACPI i2c_clients for adapters with a secondary ACPI fwnode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (8 preceding siblings ...)
2026-06-23 14:52 ` [RFC 09/12] pinctrl: qcom: Add support for WoA ACPI tables virtual TLMM pin numbers Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 11/12] i2c: qcom-geni: Fall back to i2c_acpi_find_bus_speed() Hans de Goede
` (4 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
In DT-ACPI hybrid mode the primary fwnode of an i2c_adapter will be a DT/of
node and the secondary fwnode may point to an ACPI fwnode.
i2c_acpi_register_devices() exits early if has_acpi_companion() returns
false and has_acpi_companion() only checks the primary fwnode. Switch to
is_acpi_device_node_any() to also check the secondary.
The rest of the code to register ACPI i2c_clients uses
device_match_acpi_handle() which already handles this correctly.
Also adjust i2c_acpi_find_bus_speed() to use to_acpi_device_node_any()
to als make it work in DT-ACPI hybrid mode.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/i2c/i2c-core-acpi.c | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/i2c/i2c-core-acpi.c b/drivers/i2c/i2c-core-acpi.c
index 28c0e4884a7f..995bd88ad09f 100644
--- a/drivers/i2c/i2c-core-acpi.c
+++ b/drivers/i2c/i2c-core-acpi.c
@@ -322,7 +322,7 @@ void i2c_acpi_register_devices(struct i2c_adapter *adap)
struct acpi_device *adev;
acpi_status status;
- if (!has_acpi_companion(&adap->dev))
+ if (!is_acpi_device_node_any(adap->dev.fwnode))
return;
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
@@ -413,13 +413,15 @@ u32 i2c_acpi_find_bus_speed(struct device *dev)
{
struct i2c_acpi_lookup lookup;
struct i2c_board_info dummy;
+ struct acpi_device *adev;
acpi_status status;
- if (!has_acpi_companion(dev))
+ adev = to_acpi_device_node_any(dev->fwnode);
+ if (!adev)
return 0;
memset(&lookup, 0, sizeof(lookup));
- lookup.search_handle = ACPI_HANDLE(dev);
+ lookup.search_handle = adev->handle;
lookup.min_speed = UINT_MAX;
lookup.info = &dummy;
lookup.index = -1;
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 11/12] i2c: qcom-geni: Fall back to i2c_acpi_find_bus_speed()
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (9 preceding siblings ...)
2026-06-23 14:52 ` [RFC 10/12] i2c: acpi: Also register ACPI i2c_clients for adapters with a secondary ACPI fwnode Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-23 14:52 ` [RFC 12/12] arm64: dts: qcom: x1e78100-thinkpad-t14s: Move keyb and touchpad to ACPI enumeration Hans de Goede
` (3 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
Fall back to i2c_acpi_find_bus_speed() to get the bus-frequency if there
is no "clock-frequency" property to get the bus-frequency from ACPI if
available there.
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
drivers/i2c/busses/i2c-qcom-geni.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 2138fcffdaf3..97a538cd9455 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -1024,8 +1024,14 @@ static int geni_i2c_probe(struct platform_device *pdev)
ret = device_property_read_u32(dev, "clock-frequency",
&gi2c->clk_freq_out);
if (ret) {
- dev_info(dev, "Bus frequency not specified, default to 100kHz.\n");
- gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
+ ret = i2c_acpi_find_bus_speed(dev);
+ if (ret) {
+ dev_info(dev, "Using ACPI Bus frequency: %d\n", ret);
+ gi2c->clk_freq_out = ret;
+ } else {
+ dev_info(dev, "Bus frequency not specified, default to 100kHz.\n");
+ gi2c->clk_freq_out = I2C_MAX_STANDARD_MODE_FREQ;
+ }
}
gi2c->irq = platform_get_irq(pdev, 0);
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* [RFC 12/12] arm64: dts: qcom: x1e78100-thinkpad-t14s: Move keyb and touchpad to ACPI enumeration
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (10 preceding siblings ...)
2026-06-23 14:52 ` [RFC 11/12] i2c: qcom-geni: Fall back to i2c_acpi_find_bus_speed() Hans de Goede
@ 2026-06-23 14:52 ` Hans de Goede
2026-06-25 10:18 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Konrad Dybcio
` (2 subsequent siblings)
14 siblings, 0 replies; 19+ messages in thread
From: Hans de Goede @ 2026-06-23 14:52 UTC (permalink / raw)
To: Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Hans de Goede, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
Add acpi-path properties for DT-ACPI hybrid mode and remove the keyboard
and touchpad description switching to relying on ACPI to enumerate these.
Also drop the clock-frequency this is also provided by ACPI now.
FIXME: Needs DT-bindings patch as pre-req
Note this depends on these 2 patch-series for working PDC support on Hamoa:
https://lore.kernel.org/linux-arm-msm/20260410184124.1068210-1-mukesh.ojha@oss.qualcomm.com/
https://lore.kernel.org/linux-arm-msm/20260616-hamoa_pdc_v3-v3-0-4d8e1504ea75@oss.qualcomm.com/
Signed-off-by: Hans de Goede <johannes.goede@oss.qualcomm.com>
---
.../qcom/x1e78100-lenovo-thinkpad-t14s.dtsi | 59 +------------------
1 file changed, 3 insertions(+), 56 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi
index 2fc01e8e8c04..a73576ec238d 100644
--- a/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi
+++ b/arch/arm64/boot/dts/qcom/x1e78100-lenovo-thinkpad-t14s.dtsi
@@ -1017,57 +1017,8 @@ &gpu_zap_shader {
};
&i2c0 {
- clock-frequency = <400000>;
-
- pinctrl-0 = <&qup_i2c0_data_clk>, <&tpad_default>;
- pinctrl-names = "default";
-
+ acpi-path = "\\_SB.I2C1";
status = "okay";
-
- /* ELAN06E2 or ELAN06E3 */
- touchpad@15 {
- compatible = "hid-over-i2c";
- reg = <0x15>;
-
- hid-descr-addr = <0x1>;
- interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>;
-
- vdd-supply = <&vreg_misc_3p3>;
- vddl-supply = <&vreg_l12b_1p2>;
-
- wakeup-source;
- };
-
- /* SYNA8022 or SYNA8024 */
- touchpad@2c {
- compatible = "hid-over-i2c";
- reg = <0x2c>;
-
- hid-descr-addr = <0x20>;
- interrupts-extended = <&tlmm 3 IRQ_TYPE_LEVEL_LOW>;
-
- vdd-supply = <&vreg_misc_3p3>;
- vddl-supply = <&vreg_l12b_1p2>;
-
- wakeup-source;
- };
-
- /* ELAN06F1 or SYNA06F2 */
- keyboard@3a {
- compatible = "hid-over-i2c";
- reg = <0x3a>;
-
- hid-descr-addr = <0x1>;
- interrupts-extended = <&tlmm 67 IRQ_TYPE_LEVEL_LOW>;
-
- vdd-supply = <&vreg_misc_3p3>;
- vddl-supply = <&vreg_l15b_1p8>;
-
- pinctrl-0 = <&kybd_default>;
- pinctrl-names = "default";
-
- wakeup-source;
- };
};
&i2c3 {
@@ -1598,6 +1549,8 @@ wcd_tx: codec@0,3 {
};
&tlmm {
+ acpi-path = "\\_SB.GIO0";
+
gpio-reserved-ranges = <34 2>, /* Unused */
<44 4>, /* SPI (TPM) */
<72 2>, /* Secure EC I2C connection (?) */
@@ -1655,12 +1608,6 @@ hdmi_hpd_default: hdmi-hpd-default-state {
bias-disable;
};
- tpad_default: tpad-default-state {
- pins = "gpio3";
- function = "gpio";
- bias-pull-up;
- };
-
nvme_reg_en: nvme-reg-en-state {
pins = "gpio18";
function = "gpio";
--
2.54.0
^ permalink raw reply related [flat|nested] 19+ messages in thread* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (11 preceding siblings ...)
2026-06-23 14:52 ` [RFC 12/12] arm64: dts: qcom: x1e78100-thinkpad-t14s: Move keyb and touchpad to ACPI enumeration Hans de Goede
@ 2026-06-25 10:18 ` Konrad Dybcio
2026-06-26 14:33 ` Bryan O'Donoghue
2026-06-26 15:52 ` Sudeep Holla
14 siblings, 0 replies; 19+ messages in thread
From: Konrad Dybcio @ 2026-06-25 10:18 UTC (permalink / raw)
To: Hans de Goede, Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Srinivas Kandagatla, Krzysztof Kozlowski, Dmitry Baryshkov,
Bartosz Golaszewski, Abel Vesa, linux-arm-msm, devicetree,
linux-acpi
On 6/23/26 4:52 PM, Hans de Goede wrote:
> Hi All,
>
> Currently as soon as the kernel boots with a populated DT provided then
> the arch/arm64 code sets acpi_disabled=1 and the complete ACPI subsystem
> gets disabled. On WoA Snapdragon laptops where the factory Windows OS
> actually boots using these tables this is not necessarily desirable.
>
> It might still be interesting to at least parse the ACPI tables and make
> the ACPI fwnodes available for device-drivers to use. I call this DT-ACPI
> hybrid mode.
>
> This mainly is an experiment for now and possibly a method for accelerating
> the ongoing effort to run Linux on currently available Snapdragon laptops.
I like the part where the ACPI tables are parsed and are visible to
Linux (so that the user can e.g. do acpidump even though DT is in use,
since the tables are in memory anyway)
I think the 'hybrid enumeration path' is a cool technical experiment,
which lets the user skip _some parts_ of writing a device tree, but
the benefits are too minor - we can only make use of this for devices
that already have ACPI support in Linux and whose nodes fit within the
"describe the actual hw and how it's wired up, not the functions it
exposes" model (because on Windows that all ends up being hijacked by
the PEP driver or something similar in the chain).
This is natural for DT, but not so much for ACPI
The hid-over-i2c example you gave looks useful on the surface, but
if the user already has to have the knowledge and skills to grab and
decompile the tables, assess the right path for the device and still
have to put it in the DT (which again, they would have had to create
at this point) after all, they may as well take the 40 extra seconds
as a single-time cost to fill in the i2c address and interrupt source
to create a valid/full DT node.
The path lookup mechanisms relies on the paths remaining stable,
which I would assume should be the case, but I wouldn't bet money
on it _always_ being the case.
To put it simply, I don't think we should spend time on trying to
forcefully make the incomplete (actually incomplete, since Windows
today loads ACPI fragments sourced from Windows Update at runtime
and relies on many drivers having hardcoded data) DSDTs be any
useful, because in their current form, we would need to rely on
quasi-boardfiles for most things anyway.
If we need to provide the firmware description (DT) externally
anyway, let's just stick to that alone.
Konrad
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (12 preceding siblings ...)
2026-06-25 10:18 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Konrad Dybcio
@ 2026-06-26 14:33 ` Bryan O'Donoghue
2026-06-26 14:43 ` Bryan O'Donoghue
2026-06-26 15:52 ` Sudeep Holla
14 siblings, 1 reply; 19+ messages in thread
From: Bryan O'Donoghue @ 2026-06-26 14:33 UTC (permalink / raw)
To: Hans de Goede, Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Srinivas Kandagatla, Krzysztof Kozlowski, Dmitry Baryshkov,
Bartosz Golaszewski, Abel Vesa, linux-arm-msm, devicetree,
linux-acpi
On 23/06/2026 15:52, Hans de Goede wrote:
> Comments, thoughts ?
Throw out DT and just do this...
One thing I like about this approach TBH is that you don't do the easy
thing of presuming to push the hard work into the bootloader - thus
creating a dependency on bootloader.
We've had _alot_ of problems doing DT selectivity to get OSes installed
on arm64 laptops. You mentioned I2C-HID devices and EC controllers which
I agree are a good and obvious targets.
I don't think this can replace a full and complete DT but, then I don't
think that should be the objective.
Much like installing cursed OSes like Windows on "normal" laptops or x86
machines, you'd expect to boot in ACPI mode have enough of the OS
running to install more of the OS - which I think _can_ be a viable
objective with an ACPI-DT translator.
Sadly OpenBSD could boot all the way to console on the Qcom laptops
where Linux could not - because ACPI support was better there.
And, we have Nvidia laptops coming too, Windows laptops which will parse
ACPI tables to boot.
There's almost no upside in having ACPI data and not trying to make
maximal use of it, especially if you don't have a DT supplied by
antecedent boot stages.
---
bod
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-26 14:33 ` Bryan O'Donoghue
@ 2026-06-26 14:43 ` Bryan O'Donoghue
0 siblings, 0 replies; 19+ messages in thread
From: Bryan O'Donoghue @ 2026-06-26 14:43 UTC (permalink / raw)
To: Hans de Goede, Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio
Cc: Srinivas Kandagatla, Krzysztof Kozlowski, Dmitry Baryshkov,
Bartosz Golaszewski, Abel Vesa, linux-arm-msm, devicetree,
linux-acpi
On 26/06/2026 15:33, Bryan O'Donoghue wrote:
> On 23/06/2026 15:52, Hans de Goede wrote:
>> Comments, thoughts ?
>
> Throw out DT and just do this...
>
> One thing I like about this approach TBH is that you don't do the easy
> thing of presuming to push the hard work into the bootloader - thus
> creating a dependency on bootloader.
>
> We've had _alot_ of problems doing DT selectivity to get OSes installed
> on arm64 laptops. You mentioned I2C-HID devices and EC controllers which
> I agree are a good and obvious targets.
>
> I don't think this can replace a full and complete DT but, then I don't
> think that should be the objective.
>
> Much like installing cursed OSes like Windows on "normal" laptops or x86
> machines, you'd expect to boot in ACPI mode have enough of the OS
> running to install more of the OS - which I think _can_ be a viable
> objective with an ACPI-DT translator.
>
> Sadly OpenBSD could boot all the way to console on the Qcom laptops
> where Linux could not - because ACPI support was better there.
>
> And, we have Nvidia laptops coming too, Windows laptops which will parse
> ACPI tables to boot.
>
> There's almost no upside in having ACPI data and not trying to make
> maximal use of it, especially if you don't have a DT supplied by
> antecedent boot stages.
>
> ---
> bod
>
I'm going to agree with myself some more on the boot story.
If you can boot Linux _at_all_ and dump out ACPI tables from the booted
system you are way further along than not being able to boot without a
"real" DT.
Again, bootloaders have had to be educated on how to make that DT
selection - a problem that isn't well solved or converged on - and even
if such an agreed method were present, exactly 100% useless to you
without the DT to go with it.
As a Linux user I don't expect everything to work, especially so on
aarch64 but, if I can get to a boot console with a screen and keyboard -
I have scope to play in a way I otherwise don't - parsing DSDT from
Windows and walking backwards to DT.
DT _should_ be the landing zone of course but, ACPI-DT hybrid to "just
boot" seems like an obvious yes to me.
---
bod
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-23 14:52 ` [RFC 00/12] RFC: Devicetree-ACPI hybrid mode Hans de Goede
` (13 preceding siblings ...)
2026-06-26 14:33 ` Bryan O'Donoghue
@ 2026-06-26 15:52 ` Sudeep Holla
2026-06-26 20:57 ` Dmitry Baryshkov
14 siblings, 1 reply; 19+ messages in thread
From: Sudeep Holla @ 2026-06-26 15:52 UTC (permalink / raw)
To: Hans de Goede
Cc: Rafael J . Wysocki, Sudeep Holla, Bjorn Andersson, Konrad Dybcio,
Srinivas Kandagatla, Krzysztof Kozlowski, Dmitry Baryshkov,
Bartosz Golaszewski, Abel Vesa, linux-arm-msm, devicetree,
linux-acpi
On Tue, Jun 23, 2026 at 04:52:13PM +0200, Hans de Goede wrote:
> Hi All,
>
> Currently as soon as the kernel boots with a populated DT provided then
> the arch/arm64 code sets acpi_disabled=1 and the complete ACPI subsystem
> gets disabled. On WoA Snapdragon laptops where the factory Windows OS
> actually boots using these tables this is not necessarily desirable.
>
I am bit lost reading the very first statement here.
Who is populating DT and why ? It seems that is the source of the problem.
If windows can boot with ACPI tables, why is it causing issues for the
Linux kernel, any specifics?
IOW why is DT populated which creates the problem you are trying to address
here.
--
Regards,
Sudeep
^ permalink raw reply [flat|nested] 19+ messages in thread* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-26 15:52 ` Sudeep Holla
@ 2026-06-26 20:57 ` Dmitry Baryshkov
2026-06-27 14:12 ` Sudeep Holla
0 siblings, 1 reply; 19+ messages in thread
From: Dmitry Baryshkov @ 2026-06-26 20:57 UTC (permalink / raw)
To: Sudeep Holla
Cc: Hans de Goede, Rafael J . Wysocki, Bjorn Andersson, Konrad Dybcio,
Srinivas Kandagatla, Krzysztof Kozlowski, Dmitry Baryshkov,
Bartosz Golaszewski, Abel Vesa, linux-arm-msm, devicetree,
linux-acpi
On Fri, Jun 26, 2026 at 04:52:41PM +0100, Sudeep Holla wrote:
> On Tue, Jun 23, 2026 at 04:52:13PM +0200, Hans de Goede wrote:
> > Hi All,
> >
> > Currently as soon as the kernel boots with a populated DT provided then
> > the arch/arm64 code sets acpi_disabled=1 and the complete ACPI subsystem
> > gets disabled. On WoA Snapdragon laptops where the factory Windows OS
> > actually boots using these tables this is not necessarily desirable.
> >
>
> I am bit lost reading the very first statement here.
>
> Who is populating DT and why ? It seems that is the source of the problem.
>
> If windows can boot with ACPI tables, why is it causing issues for the
> Linux kernel, any specifics?
Windows uses a separate beast called PEP, which nobody wanted to
implement for these platforms up to now. You can find a lot of ACPI
dumps for these devices at [1].
> IOW why is DT populated which creates the problem you are trying to address
> here.
Most of the laptops resemble other Qualcomm platforms, which use DT for
hardware description. In some cases, it is the same platform being used
for both mobile, IoT and laptops. It was more or less natural to reuse
existing support.
[1] https://github.com/aarch64-laptops/build/tree/master/misc
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [RFC 00/12] RFC: Devicetree-ACPI hybrid mode
2026-06-26 20:57 ` Dmitry Baryshkov
@ 2026-06-27 14:12 ` Sudeep Holla
0 siblings, 0 replies; 19+ messages in thread
From: Sudeep Holla @ 2026-06-27 14:12 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Hans de Goede, Sudeep Holla, Rafael J . Wysocki, Bjorn Andersson,
Konrad Dybcio, Srinivas Kandagatla, Krzysztof Kozlowski,
Dmitry Baryshkov, Bartosz Golaszewski, Abel Vesa, linux-arm-msm,
devicetree, linux-acpi
On Fri, Jun 26, 2026 at 11:57:47PM +0300, Dmitry Baryshkov wrote:
> On Fri, Jun 26, 2026 at 04:52:41PM +0100, Sudeep Holla wrote:
> > On Tue, Jun 23, 2026 at 04:52:13PM +0200, Hans de Goede wrote:
> > > Hi All,
> > >
> > > Currently as soon as the kernel boots with a populated DT provided then
> > > the arch/arm64 code sets acpi_disabled=1 and the complete ACPI subsystem
> > > gets disabled. On WoA Snapdragon laptops where the factory Windows OS
> > > actually boots using these tables this is not necessarily desirable.
> > >
> >
> > I am bit lost reading the very first statement here.
> >
> > Who is populating DT and why ? It seems that is the source of the problem.
> >
> > If windows can boot with ACPI tables, why is it causing issues for the
> > Linux kernel, any specifics?
>
> Windows uses a separate beast called PEP, which nobody wanted to
> implement for these platforms up to now. You can find a lot of ACPI
> dumps for these devices at [1].
>
Agreed and I assume that is the reason why we want DT.
> > IOW why is DT populated which creates the problem you are trying to address
> > here.
>
> Most of the laptops resemble other Qualcomm platforms, which use DT for
> hardware description. In some cases, it is the same platform being used
> for both mobile, IoT and laptops. It was more or less natural to reuse
> existing support.
>
Sure, just use DT then, why are we even talking about mixed/hybrid mode.
It gives no incentive to OEMs using PEP to get away from it and use
standard ACPI or improve if anything is missing.
So I don't like this idea of mixing at all. Use ACPI or DT, make up you
mind. You can't expect to get the best of both worlds if you are not improving
the missing parts in either of those. This is just a shortcut.
--
Regards,
Sudeep
^ permalink raw reply [flat|nested] 19+ messages in thread