From 9f82bbcac0a0d7727d7cff84ef517bf015752c9d Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 10 Apr 2026 11:16:34 +0200 Subject: [PATCH 001/115] platform/x86: use u8 * for raw byte buffers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffer parameters in populate_{security,string}_buffer() and call_{biosattributes,password}_interface() are raw byte buffers, not character strings - use 'u8 *' instead of 'char *' to reflect this. Update the local buffer variables at the call sites and in populate_security_buffer() accordingly. Suggested-by: Ilpo Järvinen Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260410091633.2822-4-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/dell/dell-wmi-sysman/biosattr-interface.c | 8 ++++---- .../platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h | 4 ++-- .../x86/dell/dell-wmi-sysman/passwordattr-interface.c | 4 ++-- drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 6 +++--- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c index c2dd2de6bc20..db278ff4cc4d 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/biosattr-interface.c @@ -13,8 +13,8 @@ #define SETBIOSDEFAULTS_METHOD_ID 0x03 #define SETATTRIBUTE_METHOD_ID 0x04 -static int call_biosattributes_interface(struct wmi_device *wdev, char *in_args, size_t size, - int method_id) +static int call_biosattributes_interface(struct wmi_device *wdev, u8 *in_args, + size_t size, int method_id) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer input; @@ -51,7 +51,7 @@ int set_attribute(const char *a_name, const char *a_value) { size_t security_area_size, buffer_size; size_t a_name_size, a_value_size; - char *buffer = NULL, *start; + u8 *buffer = NULL, *start; int ret; mutex_lock(&wmi_priv.mutex); @@ -109,7 +109,7 @@ int set_bios_defaults(u8 deftype) { size_t security_area_size, buffer_size; size_t integer_area_size = sizeof(u8); - char *buffer = NULL; + u8 *buffer = NULL; u8 *defaultType; int ret; diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h index 5278a93fdaf7..3bddedad5eba 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h +++ b/drivers/platform/x86/dell/dell-wmi-sysman/dell-wmi-sysman.h @@ -190,8 +190,8 @@ int init_bios_attr_set_interface(void); int map_wmi_error(int error_code); size_t calculate_string_buffer(const char *str); size_t calculate_security_buffer(const char *authentication); -void populate_security_buffer(char *buffer, const char *authentication); -ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str); +void populate_security_buffer(u8 *buffer, const char *authentication); +ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str); int set_new_password(const char *password_type, const char *new); int init_bios_attr_pass_interface(void); void exit_bios_attr_pass_interface(void); diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c b/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c index e586f7957946..026c134b3f97 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/passwordattr-interface.c @@ -8,7 +8,7 @@ #include #include "dell-wmi-sysman.h" -static int call_password_interface(struct wmi_device *wdev, char *in_args, size_t size) +static int call_password_interface(struct wmi_device *wdev, u8 *in_args, size_t size) { struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; struct acpi_buffer input; @@ -42,7 +42,7 @@ int set_new_password(const char *password_type, const char *new) { size_t password_type_size, current_password_size, new_size; size_t security_area_size, buffer_size; - char *buffer = NULL, *start; + u8 *buffer = NULL, *start; char *current_password; int ret; diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 51d25fdc1389..ab46a023cc34 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -36,7 +36,7 @@ static int reset_option = -1; * @buffer_len: length of the destination buffer * @str: the string to insert into buffer */ -ssize_t populate_string_buffer(char *buffer, size_t buffer_len, const char *str) +ssize_t populate_string_buffer(u8 *buffer, size_t buffer_len, const char *str) { u16 *length = (u16 *)buffer; u16 *target = length + 1; @@ -87,10 +87,10 @@ size_t calculate_security_buffer(const char *authentication) * * Currently only supported type is PLAIN TEXT */ -void populate_security_buffer(char *buffer, const char *authentication) +void populate_security_buffer(u8 *buffer, const char *authentication) { size_t seclen = strlen(authentication); - char *auth = buffer + sizeof(u32) * 2; + u8 *auth = buffer + sizeof(u32) * 2; u32 *sectype = (u32 *) buffer; u32 *seclenp = sectype + 1; From 0b9f109c318a88d40e8a450d56f9bdcece94a327 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 10 Apr 2026 11:16:36 +0200 Subject: [PATCH 002/115] platform/x86: dell_rbu: use strscpy in image_type_write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strcpy() has been deprecated [1] because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. Link: https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy [1] Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260410091633.2822-6-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell_rbu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index 3fa9de9aa47b..768b15f406d3 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -562,9 +562,9 @@ static ssize_t image_type_write(struct file *filp, struct kobject *kobj, buffer[count] = '\0'; if (strstr(buffer, "mono")) - strcpy(image_type, "mono"); + strscpy(image_type, "mono"); else if (strstr(buffer, "packet")) - strcpy(image_type, "packet"); + strscpy(image_type, "packet"); else if (strstr(buffer, "init")) { /* * If due to the user error the driver gets in a bad From b54a4676b3069c9a8cc68e07122e6438491a3353 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Fri, 24 Apr 2026 09:57:57 +0200 Subject: [PATCH 003/115] sonypi: use strscpy() in sonypi_acpi_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit strcpy() has been deprecated¹ because it performs no bounds checking on the destination buffer, which can lead to buffer overflows. While the current code works correctly, replace strcpy() with the safer strscpy() to follow secure coding best practices. ¹ https://www.kernel.org/doc/html/latest/process/deprecated.html#strcpy Signed-off-by: Thorsten Blum Acked-by: Arnd Bergmann Link: https://patch.msgid.link/20260424075755.305770-3-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/char/sonypi.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index ccda997a9098..959949f04f7d 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -1120,8 +1121,8 @@ static int sonypi_acpi_probe(struct platform_device *pdev) struct acpi_device *device = ACPI_COMPANION(&pdev->dev); sonypi_acpi_device = device; - strcpy(acpi_device_name(device), "Sony laptop hotkeys"); - strcpy(acpi_device_class(device), "sony/hotkey"); + strscpy(acpi_device_name(device), "Sony laptop hotkeys"); + strscpy(acpi_device_class(device), "sony/hotkey"); return 0; } From 0f4a265107902d133b70b1ced12aaea83e3cdeb1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Apr 2026 11:30:18 +0200 Subject: [PATCH 004/115] platform/x86: meraki-mx100: use real software node references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The lpc_ich MFD driver now exposes the software node associated with the its GPIO controller cell. Remove the dummy software node from the meraki-mx100 driver and reference the real one instead. Acked-by: Andy Shevchenko Acked-by: Ilpo Järvinen Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260427-meraki-swnodes-v5-1-ad91cd306472@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/meraki-mx100.c | 41 +++++++++++++---------------- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2ffa4ecf65b0..2f3e554264a4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -345,6 +345,7 @@ config AYANEO_EC config MERAKI_MX100 tristate "Cisco Meraki MX100 Platform Driver" depends on GPIOLIB + depends on LPC_ICH depends on GPIO_ICH depends on LEDS_CLASS select LEDS_GPIO diff --git a/drivers/platform/x86/meraki-mx100.c b/drivers/platform/x86/meraki-mx100.c index 8c5276d98512..9f4caa1f3a92 100644 --- a/drivers/platform/x86/meraki-mx100.c +++ b/drivers/platform/x86/meraki-mx100.c @@ -20,16 +20,11 @@ #include #include #include +#include #include #include #include -#define TINK_GPIO_DRIVER_NAME "gpio_ich" - -static const struct software_node gpio_ich_node = { - .name = TINK_GPIO_DRIVER_NAME, -}; - /* LEDs */ static const struct software_node tink_gpio_leds_node = { .name = "meraki-mx100-leds", @@ -38,7 +33,7 @@ static const struct software_node tink_gpio_leds_node = { static const struct property_entry tink_internet_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:internet"), PROPERTY_ENTRY_STRING("linux,default-trigger", "default-on"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 11, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 11, GPIO_ACTIVE_LOW), { } }; @@ -50,7 +45,7 @@ static const struct software_node tink_internet_led_node = { static const struct property_entry tink_lan2_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan2"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 18, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 18, GPIO_ACTIVE_HIGH), { } }; @@ -62,7 +57,7 @@ static const struct software_node tink_lan2_led_node = { static const struct property_entry tink_lan3_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan3"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 20, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 20, GPIO_ACTIVE_HIGH), { } }; @@ -74,7 +69,7 @@ static const struct software_node tink_lan3_led_node = { static const struct property_entry tink_lan4_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan4"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 22, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 22, GPIO_ACTIVE_HIGH), { } }; @@ -86,7 +81,7 @@ static const struct software_node tink_lan4_led_node = { static const struct property_entry tink_lan5_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan5"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 23, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 23, GPIO_ACTIVE_HIGH), { } }; @@ -98,7 +93,7 @@ static const struct software_node tink_lan5_led_node = { static const struct property_entry tink_lan6_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan6"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 32, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 32, GPIO_ACTIVE_HIGH), { } }; @@ -110,7 +105,7 @@ static const struct software_node tink_lan6_led_node = { static const struct property_entry tink_lan7_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan7"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 34, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 34, GPIO_ACTIVE_HIGH), { } }; @@ -122,7 +117,7 @@ static const struct software_node tink_lan7_led_node = { static const struct property_entry tink_lan8_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan8"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 35, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 35, GPIO_ACTIVE_HIGH), { } }; @@ -134,7 +129,7 @@ static const struct software_node tink_lan8_led_node = { static const struct property_entry tink_lan9_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan9"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 36, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 36, GPIO_ACTIVE_HIGH), { } }; @@ -146,7 +141,7 @@ static const struct software_node tink_lan9_led_node = { static const struct property_entry tink_lan10_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan10"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 37, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 37, GPIO_ACTIVE_HIGH), { } }; @@ -158,7 +153,7 @@ static const struct software_node tink_lan10_led_node = { static const struct property_entry tink_lan11_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:lan11"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 48, GPIO_ACTIVE_HIGH), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 48, GPIO_ACTIVE_HIGH), { } }; @@ -170,7 +165,7 @@ static const struct software_node tink_lan11_led_node = { static const struct property_entry tink_ha_green_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:ha"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 16, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 16, GPIO_ACTIVE_LOW), { } }; @@ -182,7 +177,7 @@ static const struct software_node tink_ha_green_led_node = { static const struct property_entry tink_ha_orange_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:orange:ha"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 7, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 7, GPIO_ACTIVE_LOW), { } }; @@ -194,7 +189,7 @@ static const struct software_node tink_ha_orange_led_node = { static const struct property_entry tink_usb_green_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:green:usb"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 21, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 21, GPIO_ACTIVE_LOW), { } }; @@ -206,7 +201,7 @@ static const struct software_node tink_usb_green_led_node = { static const struct property_entry tink_usb_orange_led_props[] = { PROPERTY_ENTRY_STRING("label", "mx100:orange:usb"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 19, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 19, GPIO_ACTIVE_LOW), { } }; @@ -230,7 +225,7 @@ static const struct software_node tink_gpio_keys_node = { static const struct property_entry tink_reset_key_props[] = { PROPERTY_ENTRY_U32("linux,code", KEY_RESTART), PROPERTY_ENTRY_STRING("label", "Reset"), - PROPERTY_ENTRY_GPIO("gpios", &gpio_ich_node, 60, GPIO_ACTIVE_LOW), + PROPERTY_ENTRY_GPIO("gpios", &lpc_ich_gpio_swnode, 60, GPIO_ACTIVE_LOW), PROPERTY_ENTRY_U32("linux,input-type", EV_KEY), PROPERTY_ENTRY_U32("debounce-interval", 100), { } @@ -243,7 +238,6 @@ static const struct software_node tink_reset_key_node = { }; static const struct software_node *tink_swnodes[] = { - &gpio_ich_node, /* LEDs nodes */ &tink_gpio_leds_node, &tink_internet_led_node, @@ -348,3 +342,4 @@ MODULE_AUTHOR("Chris Blake "); MODULE_DESCRIPTION("Cisco Meraki MX100 Platform Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:meraki-mx100"); +MODULE_IMPORT_NS("LPC_ICH"); From 74ab7c4d73d526bfebc6f3fd7382c6f672d8b912 Mon Sep 17 00:00:00 2001 From: Eduardo Vasconcelos Date: Sat, 25 Apr 2026 03:39:34 -0300 Subject: [PATCH 005/115] platform/x86: thinkpad_acpi: Remove unneeded goto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove an unneeded goto statement in hotkey_kthread(). Since the function has a single exit location with no cleanup code, the jump provides no benefit. Per the kernel coding style, returning directly is preferred over goto in such case [1]. [1] https://www.kernel.org/doc/html/latest/process/coding-style.html Signed-off-by: Eduardo Vasconcelos Tested-by: Eduardo Vasconcelos Reviewed-by: Mark Pearson Link: https://patch.msgid.link/20260425063936.9360-1-eduardo@eduardovasconcelos.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index e1cee42a1683..d4ed6f1216f2 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -2469,7 +2469,7 @@ static int hotkey_kthread(void *data) bool was_frozen; if (tpacpi_lifecycle == TPACPI_LIFE_EXITING) - goto exit; + return 0; set_freezable(); @@ -2526,7 +2526,6 @@ static int hotkey_kthread(void *data) si ^= 1; } -exit: return 0; } From d8e484a452ca195b7c099373f3c7901bd405b623 Mon Sep 17 00:00:00 2001 From: Maciej Wieczor-Retman Date: Wed, 8 Apr 2026 16:27:47 +0000 Subject: [PATCH 006/115] platform/x86/intel-uncore-freq: Rename instance_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "instance" word has a specific meaning in TPMI. It is a physical index related to compute dies and IO dies present on a single TPMI partition (which is also a single TPMI device). It's used for mapping MMIO blocks for direct TPMI register access. The currently used "instance_id" uncore_data struct field is a sequentially generated value that's used for appending to uncore directories inside the /sys/devices/system/cpu/intel_uncore_frequency directory. It has no relation to the physical TPMI elements. Signed-off-by: Maciej Wieczor-Retman Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/4d983157199cf0e163597df254e2dc629878b818.1775665057.git.m.wieczorretman@pm.me Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-common.c | 6 +++--- .../x86/intel/uncore-frequency/uncore-frequency-common.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 7070c94324e0..25ab511ed8d2 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -268,7 +268,7 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) if (ret < 0) goto uncore_unlock; - data->instance_id = ret; + data->seqnum_id = ret; scnprintf(data->name, sizeof(data->name), "uncore%02d", ret); } else { scnprintf(data->name, sizeof(data->name), "package_%02d_die_%02d", @@ -281,7 +281,7 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) ret = create_attr_group(data, data->name); if (ret) { if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) - ida_free(&intel_uncore_ida, data->instance_id); + ida_free(&intel_uncore_ida, data->seqnum_id); } else { data->control_cpu = cpu; data->valid = true; @@ -301,7 +301,7 @@ void uncore_freq_remove_die_entry(struct uncore_data *data) data->control_cpu = -1; data->valid = false; if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) - ida_free(&intel_uncore_ida, data->instance_id); + ida_free(&intel_uncore_ida, data->seqnum_id); mutex_unlock(&uncore_lock); } diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 0abe850ef54e..0d5fd91ee0aa 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -35,7 +35,7 @@ * @die_id: Die id for this instance * @domain_id: Power domain id for this instance * @cluster_id: cluster id in a domain - * @instance_id: Unique instance id to append to directory name + * @seqnum_id: Unique sequential id to append to directory name * @name: Sysfs entry name for this instance * @agent_type_mask: Bit mask of all hardware agents for this domain * @uncore_attr_group: Attribute group storage @@ -71,7 +71,7 @@ struct uncore_data { int die_id; int domain_id; int cluster_id; - int instance_id; + int seqnum_id; char name[32]; u16 agent_type_mask; From 6cf1c1e9f21ba2e44e05e691d5241290c7d6c41a Mon Sep 17 00:00:00 2001 From: Maciej Wieczor-Retman Date: Wed, 8 Apr 2026 16:27:51 +0000 Subject: [PATCH 007/115] platform/x86/intel-uncore-freq: Expose instance ID in the sysfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Insufficient data is exported to allow direct access to TPMI registers through MMIO. On non-partitioned systems domain_id can be used both for mapping CPUs to their compute die IDs and for mapping die indices to their MMIO memory blocks presented to userspace via TPMI debugfs. However on partitioned systems the debugfs association doesn't work anymore. This is due to how TPMI partitioning influences domain_id calculation. The previous association is lost on partitioned systems in order to keep using domain_id for mapping CPUs to compute dies. Expose the instance ID in sysfs that's unique in the scope of one TPMI partition (and hence one TPMI device). It's a physical index into mapped MMIO blocks and can be used by userspace to figure out how to directly access TPMI registers. Signed-off-by: Maciej Wieczor-Retman Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/b9ae8d5f1ab86bcdb1a8636fa48865a9e49e2e21.1775665057.git.m.wieczorretman@pm.me Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../pm/intel_uncore_frequency_scaling.rst | 7 +++++++ .../uncore-frequency/uncore-frequency-common.c | 10 ++++++++++ .../uncore-frequency/uncore-frequency-common.h | 6 +++++- .../uncore-frequency/uncore-frequency-tpmi.c | 15 ++++++++++++++- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst index d367ba4d744a..b43ad4d5e333 100644 --- a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst +++ b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst @@ -88,8 +88,15 @@ and "fabric_cluster_id" in the directory. Attributes in each directory: +``instance_id`` + This attribute is used to get die indices in userspace mapped MMIO + blocks. Indices are local to a single TPMI partition. Needed for direct + TPMI register access. + ``domain_id`` This attribute is used to get the power domain id of this instance. + Indices are unique in all TPMI partitions on a given CPU package. Can be + used to map compute dies to corresponding CPUs. ``die_id`` This attribute is used to get the Linux die id of this instance. diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 25ab511ed8d2..3b554418a7a3 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -29,6 +29,13 @@ static ssize_t show_domain_id(struct kobject *kobj, struct kobj_attribute *attr, return sysfs_emit(buf, "%u\n", data->domain_id); } +static ssize_t show_instance_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) +{ + struct uncore_data *data = container_of(attr, struct uncore_data, instance_id_kobj_attr); + + return sysfs_emit(buf, "%u\n", data->instance_id); +} + static ssize_t show_fabric_cluster_id(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { struct uncore_data *data = container_of(attr, struct uncore_data, fabric_cluster_id_kobj_attr); @@ -200,6 +207,9 @@ static int create_attr_group(struct uncore_data *data, char *name) if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) { init_attribute_root_ro(domain_id); data->uncore_attrs[index++] = &data->domain_id_kobj_attr.attr; + init_attribute_root_ro(instance_id); + data->uncore_attrs[index++] = &data->instance_id_kobj_attr.attr; + init_attribute_root_ro(fabric_cluster_id); data->uncore_attrs[index++] = &data->fabric_cluster_id_kobj_attr.attr; init_attribute_root_ro(package_id); diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h index 0d5fd91ee0aa..e319448dc1a4 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.h @@ -36,6 +36,7 @@ * @domain_id: Power domain id for this instance * @cluster_id: cluster id in a domain * @seqnum_id: Unique sequential id to append to directory name + * @instance_id: Die indices or feature instances for a single TPMI device * @name: Sysfs entry name for this instance * @agent_type_mask: Bit mask of all hardware agents for this domain * @uncore_attr_group: Attribute group storage @@ -56,6 +57,7 @@ * @elc_floor_freq_khz_kobj_attr: Storage for kobject attribute elc_floor_freq_khz * @agent_types_kobj_attr: Storage for kobject attribute agent_type * @die_id_kobj_attr: Attribute storage for die_id information + * @instance_id_kobj_attr: Attribute storage for instance_id value * @uncore_attrs: Attribute storage for group creation * * This structure is used to encapsulate all data related to uncore sysfs @@ -72,6 +74,7 @@ struct uncore_data { int domain_id; int cluster_id; int seqnum_id; + int instance_id; char name[32]; u16 agent_type_mask; @@ -90,7 +93,8 @@ struct uncore_data { struct kobj_attribute elc_floor_freq_khz_kobj_attr; struct kobj_attribute agent_types_kobj_attr; struct kobj_attribute die_id_kobj_attr; - struct attribute *uncore_attrs[15]; + struct kobj_attribute instance_id_kobj_attr; + struct attribute *uncore_attrs[16]; }; #define UNCORE_DOMAIN_ID_INVALID -1 diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c index 88015a2c6a0d..44e2a90004df 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-tpmi.c @@ -385,7 +385,19 @@ static u8 io_die_index_next; /* Lock to protect io_die_start, io_die_index_next */ static DEFINE_MUTEX(domain_lock); -static void set_domain_id(int id, int num_resources, +static inline void set_instance_id(int id, struct tpmi_uncore_cluster_info *cluster_info) +{ + /* + * On non-partitioned systems domain_id can be used for mapping both + * CPUs to compute die IDs and physical die indexes to MMIO mapped + * memory. However on partitioned systems domain_id loses the second + * association. Therefore instance_id should be used for that instead, + * while domain_id should still be used to match CPUs to compute dies. + */ + cluster_info->uncore_data.instance_id = id; +} + +static void set_domain_id(int id, int num_resources, struct oobmsm_plat_info *plat_info, struct tpmi_uncore_cluster_info *cluster_info) { @@ -690,6 +702,7 @@ static int uncore_probe(struct auxiliary_device *auxdev, const struct auxiliary_ set_cdie_id(i, cluster_info, plat_info); set_domain_id(i, num_resources, plat_info, cluster_info); + set_instance_id(i, cluster_info); cluster_info->uncore_root = tpmi_uncore; From 49133d4ae0c9ceb63fa042c0b46b34e26e1c6676 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 27 Apr 2026 12:24:58 +0200 Subject: [PATCH 008/115] platform/x86: barco-p50-gpio: attach software node to its target GPIO device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The software node representing the GPIO controller to consumers is "dangling": it's not really attached to the device. The GPIO lookup relies on matching the name of the node to the chip's label. Switch to using platform_device_register_full() and use the swnode field of struct platform_device_info to attach the software node to the GPIO device. Signed-off-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260427102459.110332-1-bartosz.golaszewski@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/barco-p50-gpio.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 2a6d8607c402..e1400382465d 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -124,7 +124,6 @@ static const struct software_node vendor_key_node = { }; static const struct software_node *p50_swnodes[] = { - &gpiochip_node, &gpio_leds_node, &identify_led_node, &gpio_keys_node, @@ -424,6 +423,13 @@ MODULE_DEVICE_TABLE(dmi, dmi_ids); static int __init p50_module_init(void) { struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1); + struct platform_device_info pdevinfo = { + .name = DRIVER_NAME, + .id = PLATFORM_DEVID_NONE, + .res = &res, + .num_res = 1, + .swnode = &gpiochip_node, + }; int ret; if (!dmi_first_match(dmi_ids)) @@ -433,7 +439,7 @@ static int __init p50_module_init(void) if (ret) return ret; - gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1); + gpio_pdev = platform_device_register_full(&pdevinfo); if (IS_ERR(gpio_pdev)) { pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev)); platform_driver_unregister(&p50_gpio_driver); From fa84425e53c43c8a17ac2af2c8f99202f4e1ee7e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 29 Apr 2026 11:46:01 +0200 Subject: [PATCH 009/115] platform/x86: pcengines-apuv2: reduce indiraction in swnode assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct platform_device_info now has the 'swnode' field allowing passing software nodes directly to platform_device_register_full() without going through software_node_fwnode(). It supports both registered and unregistered software nodes. Use it to drop one layer of indirection. Signed-off-by: Bartosz Golaszewski Reviewed-by: Dmitry Torokhov Link: https://patch.msgid.link/20260429-pcengines-apuv2-swnodes-v1-1-cd1a6fad856d@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pcengines-apuv2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 3f19589d1ba0..8ac5f719c5a3 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -262,7 +262,7 @@ static struct platform_device * __init apu_create_pdev(const char *name, .id = PLATFORM_DEVID_NONE, .data = data, .size_data = size, - .fwnode = software_node_fwnode(swnode), + .swnode = swnode, }; struct platform_device *pdev; int err; From 083a59e05db9203c346e87d822c0c12a0f7a7c85 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:58:56 +0200 Subject: [PATCH 010/115] ACPI: provide acpi_bus_find_device_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Provide a helper allowing to locate an ACPI device by its name. Signed-off-by: Bartosz Golaszewski Acked-by: Rafael J. Wysocki (Intel) Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260504-baytrail-real-swnode-v5-1-c7878b69e383@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/acpi/bus.c | 15 +++++++++++++++ include/linux/acpi.h | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 2ec095e2009e..357b39db345d 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -1181,6 +1181,21 @@ int acpi_bus_for_each_dev(int (*fn)(struct device *, void *), void *data) } EXPORT_SYMBOL_GPL(acpi_bus_for_each_dev); +/** + * acpi_bus_find_device_by_name() - Locate an ACPI device by its name + * @name: Name of the device to match + * + * The caller is responsible for calling put_device() on the returned object. + * + * Returns: + * New reference to the matched device or NULL if the device can't be found. + */ +struct device *acpi_bus_find_device_by_name(const char *name) +{ + return bus_find_device_by_name(&acpi_bus_type, NULL, name); +} +EXPORT_SYMBOL_GPL(acpi_bus_find_device_by_name); + struct acpi_dev_walk_context { int (*fn)(struct acpi_device *, void *); void *data; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 67effb91fa98..10d6c6c11bdf 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -798,6 +798,8 @@ int acpi_get_local_u64_address(acpi_handle handle, u64 *addr); int acpi_get_local_address(acpi_handle handle, u32 *addr); const char *acpi_get_subsystem_id(acpi_handle handle); +struct device *acpi_bus_find_device_by_name(const char *name); + #ifdef CONFIG_ACPI_MRRM int acpi_mrrm_max_mem_region(void); #endif @@ -1106,6 +1108,11 @@ static inline const char *acpi_get_subsystem_id(acpi_handle handle) return ERR_PTR(-ENODEV); } +static inline struct device *acpi_bus_find_device_by_name(const char *name) +{ + return NULL; +} + static inline int acpi_register_wakeup_handler(int wake_irq, bool (*wakeup)(void *context), void *context) { From 1448c2d2ca5cc7f4ea6694e6bc809946de0a751c Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 4 May 2026 12:58:57 +0200 Subject: [PATCH 011/115] platform/x86: x86-android-tablets: enable fwnode matching of GPIO chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to allow GPIOLIB to match cherryview and baytrail GPIO controllers by their firmware nodes instead of their names, we need to attach the - currently "dangling" - existing software nodes to their target devices dynamically. The driver uses platform_create_bundle() and expects all required providers to be present before it itself is probed. We know the name of the device we're waiting for so look them up and assign the appropriate software node as the secondary firmware node of the underlying ACPI node. Scheduling fine-grained devres actions allows for proper teardown and unsetting of the secondary firmware nodes. Signed-off-by: Bartosz Golaszewski Reviewed-by: Rafael J. Wysocki (Intel) Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/20260504-baytrail-real-swnode-v5-2-c7878b69e383@oss.qualcomm.com [ij: added linux/bug.h include] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/x86-android-tablets/core.c | 64 ++++++++++++++++++- 1 file changed, 61 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/x86-android-tablets/core.c b/drivers/platform/x86/x86-android-tablets/core.c index 021009e9085b..5db794d65eb5 100644 --- a/drivers/platform/x86/x86-android-tablets/core.c +++ b/drivers/platform/x86/x86-android-tablets/core.c @@ -11,8 +11,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include +#include #include #include #include @@ -360,6 +362,61 @@ static const struct software_node *cherryview_gpiochip_node_group[] = { NULL }; +static void gpio_secondary_unset(void *data) +{ + struct device *dev = data; + + set_secondary_fwnode(dev, NULL); +} + +static void gpio_secondary_unregister_node_group(void *data) +{ + const struct software_node **nodes = data; + + software_node_unregister_node_group(nodes); +} + +static int gpio_secondary_fwnode_init(struct device *parent) +{ + const struct software_node *const *swnode; + struct fwnode_handle *fwnode; + int ret; + + if (!gpiochip_node_group) + return 0; + + ret = software_node_register_node_group(gpiochip_node_group); + if (ret) + return ret; + + ret = devm_add_action_or_reset(parent, + gpio_secondary_unregister_node_group, + gpiochip_node_group); + if (ret) + return ret; + + for (swnode = gpiochip_node_group; *swnode; swnode++) { + struct device *dev __free(put_device) = + acpi_bus_find_device_by_name((*swnode)->name); + if (!dev) + return dev_err_probe(parent, + -ENODEV, "Failed to find the required GPIO controller: %s\n", + (*swnode)->name); + + fwnode = software_node_fwnode(*swnode); + if (WARN_ON(!fwnode)) + return -ENOENT; + + set_secondary_fwnode(dev, fwnode); + + ret = devm_add_action_or_reset(parent, gpio_secondary_unset, dev); + if (ret) + return ret; + } + + return 0; +} + static void x86_android_tablet_remove(struct platform_device *pdev) { int i; @@ -391,7 +448,6 @@ static void x86_android_tablet_remove(struct platform_device *pdev) software_node_unregister_node_group(gpio_button_swnodes); software_node_unregister_node_group(swnode_group); - software_node_unregister_node_group(gpiochip_node_group); } static __init int x86_android_tablet_probe(struct platform_device *pdev) @@ -427,9 +483,11 @@ static __init int x86_android_tablet_probe(struct platform_device *pdev) break; } - ret = software_node_register_node_group(gpiochip_node_group); - if (ret) + ret = gpio_secondary_fwnode_init(&pdev->dev); + if (ret) { + x86_android_tablet_remove(pdev); return ret; + } ret = software_node_register_node_group(dev_info->swnode_group); if (ret) { From dfe614f82e445a65cb2afd01859b06b01fce8889 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:32 -0700 Subject: [PATCH 012/115] platform/x86/intel/pmc: Use __free() in pmc_core_punit_pmt_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use scope-based cleanup in pmc_core_punit_pmt_init() instead of manually freeing. This simplifies the code flow by removing the explicit put call, making it less error-prone. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-2-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index d91e1ab842d6..c8a92d623520 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1325,16 +1325,15 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) { struct telem_endpoint *ep; - struct pci_dev *pcidev; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(10, 0)); + struct pci_dev *pcidev __free(pci_dev_put) = pci_get_domain_bus_and_slot(0, 0, + PCI_DEVFN(10, 0)); if (!pcidev) { dev_err(&pmcdev->pdev->dev, "PUNIT PMT device not found."); return; } ep = pmc_core_register_endpoint(pcidev, guids); - pci_dev_put(pcidev); if (IS_ERR(ep)) { dev_err(&pmcdev->pdev->dev, "pmc_core: couldn't get DMU telem endpoint %ld", From 38c79dd63b72e36919ef097d4e5025ca0fa17f34 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:33 -0700 Subject: [PATCH 013/115] platform/x86/intel/pmc: Enable PkgC LTR blocking counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the Package C-state LTR blocking counter in the PMT telemetry region. This counter records how many times any Package C-state entry is blocked for the specified reasons. Add pmc_core_pkgc_counters_show() as a common helper to display package C-state blocking counters from the telemetry region. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-3-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 79 +++++++++++++++++++++++---- drivers/platform/x86/intel/pmc/core.h | 15 ++++- 2 files changed, 82 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index c8a92d623520..7681c444f4bd 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1071,6 +1071,34 @@ static int pmc_core_die_c6_us_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_die_c6_us); +static int pmc_core_pkgc_counters_show(struct seq_file *s, + struct telem_endpoint *ep, + u32 offset, const char **counters) +{ + unsigned int i; + u32 counter; + int ret; + + for (i = 0; counters[i]; i++) { + ret = pmt_telem_read32(ep, offset + i, &counter, 1); + if (ret) + return ret; + seq_printf(s, "%-30s %-30u\n", counters[i], counter); + } + + return 0; +} + +static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + + return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep, + pmcdev->pkgc_ltr_blocker_offset, + pmcdev->pkgc_ltr_blocker_counters); +} +DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker); + static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; @@ -1322,7 +1350,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, return ERR_PTR(-ENODEV); } -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) { struct telem_endpoint *ep; @@ -1333,16 +1361,32 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids) return; } - ep = pmc_core_register_endpoint(pcidev, guids); - if (IS_ERR(ep)) { - dev_err(&pmcdev->pdev->dev, - "pmc_core: couldn't get DMU telem endpoint %ld", - PTR_ERR(ep)); - return; + if (pmc_dev_info->dmu_guids) { + ep = pmc_core_register_endpoint(pcidev, pmc_dev_info->dmu_guids); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get DMU telem endpoint %ld", + PTR_ERR(ep)); + return; + } + + pmcdev->punit_ep = ep; + pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; } - pmcdev->punit_ep = ep; - pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + if (pmc_dev_info->pc_guid) { + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc_dev_info->pc_guid, 0); + if (IS_ERR(ep)) { + dev_err(&pmcdev->pdev->dev, + "pmc_core: couldn't get Package C-state telem endpoint %ld", + PTR_ERR(ep)); + return; + } + + pmcdev->pc_ep = ep; + pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters; + pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset; + } } void pmc_core_set_device_d3(unsigned int device) @@ -1466,6 +1510,13 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info pmcdev->dbgfs_dir, pmcdev, &pmc_core_die_c6_us_fops); } + + if (pmcdev->pc_ep) { + debugfs_create_file("pkgc_ltr_blocker_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_pkgc_ltr_blocker_fops); + } + } /* @@ -1716,8 +1767,8 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) } pmc_core_get_low_power_modes(pmcdev); - if (pmc_dev_info->dmu_guids) - pmc_core_punit_pmt_init(pmcdev, pmc_dev_info->dmu_guids); + if (pmc_dev_info->dmu_guids || pmc_dev_info->pc_guid) + pmc_core_punit_pmt_init(pmcdev, pmc_dev_info); if (ssram) { ret = pmc_core_get_telem_info(pmcdev, pmc_dev_info); @@ -1738,6 +1789,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) if (pmcdev->punit_ep) pmt_telem_unregister_endpoint(pmcdev->punit_ep); + if (pmcdev->pc_ep) + pmt_telem_unregister_endpoint(pmcdev->pc_ep); + return ret; } @@ -1834,6 +1888,9 @@ static void pmc_core_clean_structure(struct platform_device *pdev) if (pmcdev->punit_ep) pmt_telem_unregister_endpoint(pmcdev->punit_ep); + if (pmcdev->pc_ep) + pmt_telem_unregister_endpoint(pmcdev->pc_ep); + platform_set_drvdata(pdev, NULL); } diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 118c8740ad3a..a20aab73c140 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -453,6 +453,9 @@ struct pmc { * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * + * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters + * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * * pmc_dev contains info about power management controller device. */ struct pmc_dev { @@ -471,8 +474,12 @@ struct pmc_dev { u8 num_of_pkgc; u32 die_c6_offset; + struct telem_endpoint *pc_ep; struct telem_endpoint *punit_ep; struct pmc_info *regmap_list; + + const char **pkgc_ltr_blocker_counters; + u32 pkgc_ltr_blocker_offset; }; enum pmc_index { @@ -486,12 +493,15 @@ enum pmc_index { * struct pmc_dev_info - Structure to keep PMC device info * @pci_func: Function number of the primary PMC * @dmu_guids: List of Die Management Unit GUID + * @pc_guid: GUID for telemetry region to read PKGC blocker info + * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. * @map: Pointer to a pmc_reg_map struct that contains platform * specific attributes of the primary PMC * @sub_req_show: File operations to show substate requirements + * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action @@ -500,9 +510,12 @@ enum pmc_index { struct pmc_dev_info { u8 pci_func; u32 *dmu_guids; + u32 pc_guid; + u32 pkgc_ltr_blocker_offset; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; + const char **pkgc_ltr_blocker_counters; void (*suspend)(struct pmc_dev *pmcdev); int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); @@ -535,7 +548,7 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); int pmc_core_resume_common(struct pmc_dev *pmcdev); int get_primary_reg_base(struct pmc *pmc); -void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, u32 *guids); +void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); void pmc_core_set_device_d3(unsigned int device); int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); From d727eb1c3ede7c21f885ded1f1ad65b47434a9b9 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:34 -0700 Subject: [PATCH 014/115] platform/x86/intel/pmc: Enable Pkgc blocking residency counter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable the Package C-state blocking counter in the PMT telemetry region. This counter reports the number of 10 µs intervals during which a Package C-state 10.2/3 entry was blocked for the specified reasons. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-4-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 15 +++++++++++++++ drivers/platform/x86/intel/pmc/core.h | 8 ++++++++ 2 files changed, 23 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 7681c444f4bd..94ae098a155a 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1099,6 +1099,16 @@ static int pmc_core_pkgc_ltr_blocker_show(struct seq_file *s, void *unused) } DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_ltr_blocker); +static int pmc_core_pkgc_blocker_residency_show(struct seq_file *s, void *unused) +{ + struct pmc_dev *pmcdev = s->private; + + return pmc_core_pkgc_counters_show(s, pmcdev->pc_ep, + pmcdev->pkgc_blocker_offset, + pmcdev->pkgc_blocker_counters); +} +DEFINE_SHOW_ATTRIBUTE(pmc_core_pkgc_blocker_residency); + static int pmc_core_lpm_latch_mode_show(struct seq_file *s, void *unused) { struct pmc_dev *pmcdev = s->private; @@ -1386,6 +1396,8 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de pmcdev->pc_ep = ep; pmcdev->pkgc_ltr_blocker_counters = pmc_dev_info->pkgc_ltr_blocker_counters; pmcdev->pkgc_ltr_blocker_offset = pmc_dev_info->pkgc_ltr_blocker_offset; + pmcdev->pkgc_blocker_counters = pmc_dev_info->pkgc_blocker_counters; + pmcdev->pkgc_blocker_offset = pmc_dev_info->pkgc_blocker_offset; } } @@ -1515,6 +1527,9 @@ static void pmc_core_dbgfs_register(struct pmc_dev *pmcdev, struct pmc_dev_info debugfs_create_file("pkgc_ltr_blocker_show", 0444, pmcdev->dbgfs_dir, pmcdev, &pmc_core_pkgc_ltr_blocker_fops); + debugfs_create_file("pkgc_blocker_residency_show", 0444, + pmcdev->dbgfs_dir, pmcdev, + &pmc_core_pkgc_blocker_residency_fops); } } diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index a20aab73c140..829b1dee3f63 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -455,6 +455,8 @@ struct pmc { * * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * @pkgc_blocker_counters: Array of PKGC blocker counters + * @pkgc_blocker_offset: Offset to PKGC blocker in telemetry region * * pmc_dev contains info about power management controller device. */ @@ -480,6 +482,8 @@ struct pmc_dev { const char **pkgc_ltr_blocker_counters; u32 pkgc_ltr_blocker_offset; + const char **pkgc_blocker_counters; + u32 pkgc_blocker_offset; }; enum pmc_index { @@ -495,6 +499,7 @@ enum pmc_index { * @dmu_guids: List of Die Management Unit GUID * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region + * @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. @@ -502,6 +507,7 @@ enum pmc_index { * specific attributes of the primary PMC * @sub_req_show: File operations to show substate requirements * @pkgc_ltr_blocker_counters: Array of PKGC LTR blocker counters + * @pkgc_blocker_counters: Array of PKGC blocker counters * @suspend: Function to perform platform specific suspend * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action @@ -512,10 +518,12 @@ struct pmc_dev_info { u32 *dmu_guids; u32 pc_guid; u32 pkgc_ltr_blocker_offset; + u32 pkgc_blocker_offset; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; const char **pkgc_ltr_blocker_counters; + const char **pkgc_blocker_counters; void (*suspend)(struct pmc_dev *pmcdev); int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); From 11de0586ecf40aa747972f1b0bf88bac192d7b06 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:35 -0700 Subject: [PATCH 015/115] platform/x86/intel/pmc: Use PCI DID for PMC SSRAM device discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the PMC SSRAM discovery process to identify the device using its PCI Device ID rather than relying on a fixed PCI bus location. The enumeration of integrated devices on the PCI bus is no longer guaranteed to be consistent across CPUs. On earlier platforms, the IOE and PCH SSRAM devices were hidden from the BIOS, and the SOC SSRAM device is associated to telemetry regions from all available SSRAM devices. Starting with Nova Lake, the IOE and PCH SSRAM devices register their telemetry regions independently, meaning each telemetry region is now linked to its corresponding SSRAM device. A new ssram_hidden attribute has been added to the pmc_dev_info structure to reflect this distinction. Signed-off-by: David E. Box Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-5-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 4 ++-- drivers/platform/x86/intel/pmc/core.c | 17 ++++++++++++----- drivers/platform/x86/intel/pmc/core.h | 6 ++++-- drivers/platform/x86/intel/pmc/lnl.c | 2 +- drivers/platform/x86/intel/pmc/mtl.c | 2 +- drivers/platform/x86/intel/pmc/ptl.c | 2 +- drivers/platform/x86/intel/pmc/wcl.c | 2 +- 7 files changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index eb23bc68340a..95372a0807ac 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -720,7 +720,6 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_pmc_dev = { - .pci_func = 0, .dmu_guids = ARL_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &arl_socs_reg_map, @@ -729,11 +728,11 @@ struct pmc_dev_info arl_pmc_dev = { .resume = arl_resume, .init = arl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_h_pmc_dev = { - .pci_func = 2, .dmu_guids = ARL_H_PMT_DMU_GUIDS, .regmap_list = arl_pmc_info_list, .map = &mtl_socm_reg_map, @@ -742,4 +741,5 @@ struct pmc_dev_info arl_h_pmc_dev = { .resume = arl_h_resume, .init = arl_h_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 94ae098a155a..e0ac329e6723 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1646,17 +1646,13 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc, static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) { - struct pci_dev *pcidev __free(pci_dev_put) = NULL; struct telem_endpoint *ep; unsigned int pmc_idx; int ret; - pcidev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(20, pmc_dev_info->pci_func)); - if (!pcidev) - return -ENODEV; - for (pmc_idx = 0; pmc_idx < ARRAY_SIZE(pmcdev->pmcs); ++pmc_idx) { struct pmc *pmc; + u16 devid; pmc = pmcdev->pmcs[pmc_idx]; if (!pmc) @@ -1665,6 +1661,16 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc->map->lpm_req_guid) return -ENXIO; + if (pmc_dev_info->ssram_hidden) + devid = pmcdev->pmcs[PMC_IDX_MAIN]->devid; + else + devid = pmc->devid; + + struct pci_dev *pcidev __free(pci_dev_put) = + pci_get_device(PCI_VENDOR_ID_INTEL, devid, NULL); + if (!pcidev) + return -ENODEV; + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, pmc->map->lpm_req_guid, 0); if (IS_ERR(ep)) { dev_dbg(&pmcdev->pdev->dev, "couldn't get telem endpoint %pe", ep); @@ -1715,6 +1721,7 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) pmc->map = map; pmc->base_addr = pmc_ssram_telemetry.base_addr; + pmc->devid = pmc_ssram_telemetry.devid; pmc->regbase = ioremap(pmc->base_addr, pmc->map->regmap_length); if (!pmc->regbase) { diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 829b1dee3f63..f385a0eccd2c 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -425,6 +425,7 @@ struct pmc_info { * @ltr_ign: Holds LTR ignore data while suspended * @num_lpm_modes: Count of enabled modes * @lpm_en_modes: Array of enabled modes from lowest to highest priority + * @devid: Device ID of the SSRAM device * * pmc contains info about one power management controller device. */ @@ -436,6 +437,7 @@ struct pmc { u32 ltr_ign; u8 num_lpm_modes; u8 lpm_en_modes[LPM_MAX_NUM_MODES]; + u16 devid; }; /** @@ -495,7 +497,6 @@ enum pmc_index { /** * struct pmc_dev_info - Structure to keep PMC device info - * @pci_func: Function number of the primary PMC * @dmu_guids: List of Die Management Unit GUID * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region @@ -512,9 +513,9 @@ enum pmc_index { * @resume: Function to perform platform specific resume * @init: Function to perform platform specific init action * @sub_req: Function to achieve low power mode substate requirements + * @ssram_hidden: Some SSRAM devices are hidden on this platform */ struct pmc_dev_info { - u8 pci_func; u32 *dmu_guids; u32 pc_guid; u32 pkgc_ltr_blocker_offset; @@ -528,6 +529,7 @@ struct pmc_dev_info { int (*resume)(struct pmc_dev *pmcdev); int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep); + bool ssram_hidden; }; extern const struct pmc_bit_map msr_map[]; diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 1cd81ee54dcf..18f303af328e 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -571,7 +571,6 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info lnl_pmc_dev = { - .pci_func = 2, .regmap_list = lnl_pmc_info_list, .map = &lnl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, @@ -579,4 +578,5 @@ struct pmc_dev_info lnl_pmc_dev = { .resume = lnl_resume, .init = lnl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 57508cbf9cd4..193ebbe58402 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -994,7 +994,6 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info mtl_pmc_dev = { - .pci_func = 2, .dmu_guids = MTL_PMT_DMU_GUIDS, .regmap_list = mtl_pmc_info_list, .map = &mtl_socm_reg_map, @@ -1003,4 +1002,5 @@ struct pmc_dev_info mtl_pmc_dev = { .resume = mtl_resume, .init = mtl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 1f48e2bbc699..6c68772e738c 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -569,7 +569,6 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info ptl_pmc_dev = { - .pci_func = 2, .regmap_list = ptl_pmc_info_list, .map = &ptl_pcdp_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, @@ -577,4 +576,5 @@ struct pmc_dev_info ptl_pmc_dev = { .resume = ptl_resume, .init = ptl_core_init, .sub_req = pmc_core_pmt_get_blk_sub_req, + .ssram_hidden = true, }; diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c index a45707e6364f..b55069945e9e 100644 --- a/drivers/platform/x86/intel/pmc/wcl.c +++ b/drivers/platform/x86/intel/pmc/wcl.c @@ -493,7 +493,6 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info wcl_pmc_dev = { - .pci_func = 2, .regmap_list = wcl_pmc_info_list, .map = &wcl_pcdn_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, @@ -501,4 +500,5 @@ struct pmc_dev_info wcl_pmc_dev = { .resume = wcl_resume, .init = wcl_core_init, .sub_req = pmc_core_pmt_get_blk_sub_req, + .ssram_hidden = true, }; From ebbf33380896cc489e870d88004ad3750e908a6c Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:36 -0700 Subject: [PATCH 016/115] platform/x86/intel/pmc: Add support for variable DMU offsets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for handling different DMU Die C6 offsets across platforms. The previous implementation assumed a uniform DMU Die C6 offset for all platforms, which is no longer valid after an upcoming change. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-6-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 2 ++ drivers/platform/x86/intel/pmc/core.c | 2 +- drivers/platform/x86/intel/pmc/core.h | 2 ++ drivers/platform/x86/intel/pmc/mtl.c | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 95372a0807ac..4d91ee010f6d 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -729,6 +729,7 @@ struct pmc_dev_info arl_pmc_dev = { .init = arl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; @@ -742,4 +743,5 @@ struct pmc_dev_info arl_h_pmc_dev = { .init = arl_h_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index e0ac329e6723..5d2e2681b0eb 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1381,7 +1381,7 @@ void pmc_core_punit_pmt_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_de } pmcdev->punit_ep = ep; - pmcdev->die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET; + pmcdev->die_c6_offset = pmc_dev_info->die_c6_offset; } if (pmc_dev_info->pc_guid) { diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index f385a0eccd2c..ef69de160ffb 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -514,6 +514,7 @@ enum pmc_index { * @init: Function to perform platform specific init action * @sub_req: Function to achieve low power mode substate requirements * @ssram_hidden: Some SSRAM devices are hidden on this platform + * @die_c6_offset: Telemetry offset to read Die C6 residency */ struct pmc_dev_info { u32 *dmu_guids; @@ -530,6 +531,7 @@ struct pmc_dev_info { int (*init)(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info); int (*sub_req)(struct pmc_dev *pmcdev, struct pmc *pmc, struct telem_endpoint *ep); bool ssram_hidden; + u32 die_c6_offset; }; extern const struct pmc_bit_map msr_map[]; diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index 193ebbe58402..b724dd8c34db 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -1003,4 +1003,5 @@ struct pmc_dev_info mtl_pmc_dev = { .init = mtl_core_init, .sub_req = pmc_core_pmt_get_lpm_req, .ssram_hidden = true, + .die_c6_offset = MTL_PMT_DMU_DIE_C6_OFFSET, }; From a7d5916d132300b1ff6ac0c5f6d7a7cb7817a7fc Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:37 -0700 Subject: [PATCH 017/115] platform/x86/intel/pmc: Retrieve PMC info only for available PMCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the Intel PMC Core driver to fetch PMC information only for available PMCs. Previously, the driver attempted to retrieve PMC info even when the corresponding PMC was not present. This change aligns with recent updates to the Intel SSRAM Telemetry driver. Starting with NVL, the SSRAM Telemetry driver is probed for each individual SSRAM device. The prior implementation could not differentiate between an unavailable PMC and one that had not yet completed information retrieval. To resolve this, the PMC Core driver now skips obtaining PMC info for unavailable PMCs. Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-7-xi.pardee@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/arl.c | 7 +++++++ drivers/platform/x86/intel/pmc/core.c | 19 +++++++++++-------- drivers/platform/x86/intel/pmc/core.h | 4 ++++ drivers/platform/x86/intel/pmc/lnl.c | 4 ++++ drivers/platform/x86/intel/pmc/mtl.c | 4 ++++ drivers/platform/x86/intel/pmc/ptl.c | 4 ++++ drivers/platform/x86/intel/pmc/wcl.c | 4 ++++ 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/arl.c b/drivers/platform/x86/intel/pmc/arl.c index 4d91ee010f6d..11609d593383 100644 --- a/drivers/platform/x86/intel/pmc/arl.c +++ b/drivers/platform/x86/intel/pmc/arl.c @@ -672,6 +672,9 @@ static struct pmc_info arl_pmc_info_list[] = { {} }; +static const u8 arl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE, PMC_IDX_PCH}; +static const u8 arl_h_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE}; + #define ARL_NPU_PCI_DEV 0xad1d #define ARL_GNA_PCI_DEV 0xae4c #define ARL_H_NPU_PCI_DEV 0x7d1d @@ -721,6 +724,8 @@ static int arl_h_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_ static u32 ARL_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_pmc_dev = { .dmu_guids = ARL_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(arl_pmc_list), + .pmc_list = arl_pmc_list, .regmap_list = arl_pmc_info_list, .map = &arl_socs_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, @@ -735,6 +740,8 @@ struct pmc_dev_info arl_pmc_dev = { static u32 ARL_H_PMT_DMU_GUIDS[] = {ARL_PMT_DMU_GUID, ARL_H_PMT_DMU_GUID, 0x0}; struct pmc_dev_info arl_h_pmc_dev = { .dmu_guids = ARL_H_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(arl_h_pmc_list), + .pmc_list = arl_h_pmc_list, .regmap_list = arl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 5d2e2681b0eb..9c13a7554af9 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1734,16 +1734,17 @@ static int pmc_core_pmc_add(struct pmc_dev *pmcdev, unsigned int pmc_idx) return 0; } -static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev) +static int pmc_core_ssram_get_reg_base(struct pmc_dev *pmcdev, u8 num_pmcs, const u8 *pmc_list) { + unsigned int i; int ret; - ret = pmc_core_pmc_add(pmcdev, PMC_IDX_MAIN); - if (ret) - return ret; - - pmc_core_pmc_add(pmcdev, PMC_IDX_IOE); - pmc_core_pmc_add(pmcdev, PMC_IDX_PCH); + for (i = 0; i < num_pmcs; ++i) { + /* Non-MAIN PMCs are allowed to fail */ + ret = pmc_core_pmc_add(pmcdev, pmc_list[i]); + if (ret && (pmc_list[i] == PMC_IDX_MAIN)) + return ret; + } return 0; } @@ -1765,7 +1766,9 @@ int generic_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) ssram = pmc_dev_info->regmap_list != NULL; if (ssram) { pmcdev->regmap_list = pmc_dev_info->regmap_list; - ret = pmc_core_ssram_get_reg_base(pmcdev); + ret = pmc_core_ssram_get_reg_base(pmcdev, + pmc_dev_info->num_pmcs, + pmc_dev_info->pmc_list); /* * EAGAIN error code indicates Intel PMC SSRAM Telemetry driver * has not finished probe and PMC info is not available yet. Try diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index ef69de160ffb..428b2e55864a 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -501,6 +501,8 @@ enum pmc_index { * @pc_guid: GUID for telemetry region to read PKGC blocker info * @pkgc_ltr_blocker_offset: Offset to PKGC LTR blockers in telemetry region * @pkgc_blocker_offset:Offset to PKGC blocker in telemetry region + * @num_pmcs: Number of entries in @pmc_list + * @pmc_list: Index list of available PMC * @regmap_list: Pointer to a list of pmc_info structure that could be * available for the platform. When set, this field implies * SSRAM support. @@ -521,6 +523,8 @@ struct pmc_dev_info { u32 pc_guid; u32 pkgc_ltr_blocker_offset; u32 pkgc_blocker_offset; + u8 num_pmcs; + const u8 *pmc_list; struct pmc_info *regmap_list; const struct pmc_reg_map *map; const struct file_operations *sub_req_show; diff --git a/drivers/platform/x86/intel/pmc/lnl.c b/drivers/platform/x86/intel/pmc/lnl.c index 18f303af328e..5ff4922825d9 100644 --- a/drivers/platform/x86/intel/pmc/lnl.c +++ b/drivers/platform/x86/intel/pmc/lnl.c @@ -544,6 +544,8 @@ static struct pmc_info lnl_pmc_info_list[] = { {} }; +static const u8 lnl_pmc_list[] = {PMC_IDX_MAIN}; + #define LNL_NPU_PCI_DEV 0x643e #define LNL_IPU_PCI_DEV 0x645d @@ -571,6 +573,8 @@ static int lnl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info lnl_pmc_dev = { + .num_pmcs = ARRAY_SIZE(lnl_pmc_list), + .pmc_list = lnl_pmc_list, .regmap_list = lnl_pmc_info_list, .map = &lnl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/mtl.c b/drivers/platform/x86/intel/pmc/mtl.c index b724dd8c34db..9abeabd3dbe2 100644 --- a/drivers/platform/x86/intel/pmc/mtl.c +++ b/drivers/platform/x86/intel/pmc/mtl.c @@ -965,6 +965,8 @@ static struct pmc_info mtl_pmc_info_list[] = { {} }; +static const u8 mtl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_IOE}; + #define MTL_GNA_PCI_DEV 0x7e4c #define MTL_IPU_PCI_DEV 0x7d19 #define MTL_VPU_PCI_DEV 0x7d1d @@ -995,6 +997,8 @@ static int mtl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in static u32 MTL_PMT_DMU_GUIDS[] = {MTL_PMT_DMU_GUID, 0x0}; struct pmc_dev_info mtl_pmc_dev = { .dmu_guids = MTL_PMT_DMU_GUIDS, + .num_pmcs = ARRAY_SIZE(mtl_pmc_list), + .pmc_list = mtl_pmc_list, .regmap_list = mtl_pmc_info_list, .map = &mtl_socm_reg_map, .sub_req_show = &pmc_core_substate_req_regs_fops, diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 6c68772e738c..90fcd7900d9f 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -543,6 +543,8 @@ static struct pmc_info ptl_pmc_info_list[] = { {} }; +static const u8 ptl_pmc_list[] = {PMC_IDX_MAIN}; + #define PTL_NPU_PCI_DEV 0xb03e #define PTL_IPU_PCI_DEV 0xb05d @@ -569,6 +571,8 @@ static int ptl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in } struct pmc_dev_info ptl_pmc_dev = { + .num_pmcs = ARRAY_SIZE(ptl_pmc_list), + .pmc_list = ptl_pmc_list, .regmap_list = ptl_pmc_info_list, .map = &ptl_pcdp_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, diff --git a/drivers/platform/x86/intel/pmc/wcl.c b/drivers/platform/x86/intel/pmc/wcl.c index b55069945e9e..d34ac916907d 100644 --- a/drivers/platform/x86/intel/pmc/wcl.c +++ b/drivers/platform/x86/intel/pmc/wcl.c @@ -469,6 +469,8 @@ static struct pmc_info wcl_pmc_info_list[] = { {} }; +static const u8 wcl_pmc_list[] = {PMC_IDX_MAIN}; + #define WCL_NPU_PCI_DEV 0xfd3e /* @@ -494,6 +496,8 @@ static int wcl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_in struct pmc_dev_info wcl_pmc_dev = { .regmap_list = wcl_pmc_info_list, + .num_pmcs = ARRAY_SIZE(wcl_pmc_list), + .pmc_list = wcl_pmc_list, .map = &wcl_pcdn_reg_map, .sub_req_show = &pmc_core_substate_blk_req_fops, .suspend = cnl_suspend, From 41354f4c8a791d3059f4355945e550693ac87ce8 Mon Sep 17 00:00:00 2001 From: Xi Pardee Date: Mon, 4 May 2026 21:33:38 -0700 Subject: [PATCH 018/115] platform/x86/intel/pmc: Add Nova Lake support to intel_pmc_core driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Nova Lake support in intel_pmc_core driver Signed-off-by: Xi Pardee Link: https://patch.msgid.link/20260505043342.2573556-8-xi.pardee@linux.intel.com [ij: added missing statics to a few structs] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Makefile | 3 +- drivers/platform/x86/intel/pmc/core.c | 2 + drivers/platform/x86/intel/pmc/core.h | 33 +- drivers/platform/x86/intel/pmc/nvl.c | 1539 +++++++++++++++++++++++ drivers/platform/x86/intel/pmc/ptl.c | 2 +- 5 files changed, 1576 insertions(+), 3 deletions(-) create mode 100644 drivers/platform/x86/intel/pmc/nvl.c diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index bb960c8721d7..23853e867c91 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -4,7 +4,8 @@ # intel_pmc_core-y := core.o spt.o cnp.o icl.o \ - tgl.o adl.o mtl.o arl.o lnl.o ptl.o wcl.o + tgl.o adl.o mtl.o arl.o \ + lnl.o ptl.o wcl.o nvl.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv-y := pltdrv.o obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 9c13a7554af9..3adae8fe8b30 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1849,6 +1849,8 @@ static const struct x86_cpu_id intel_pmc_core_ids[] = { X86_MATCH_VFM(INTEL_LUNARLAKE_M, &lnl_pmc_dev), X86_MATCH_VFM(INTEL_PANTHERLAKE_L, &ptl_pmc_dev), X86_MATCH_VFM(INTEL_WILDCATLAKE_L, &wcl_pmc_dev), + X86_MATCH_VFM(INTEL_NOVALAKE, &nvl_s_pmc_dev), + X86_MATCH_VFM(INTEL_NOVALAKE_L, &nvl_h_pmc_dev), {} }; diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 428b2e55864a..55cf567febe4 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -46,7 +46,7 @@ struct telem_endpoint; #define SPT_PMC_SLP_S0_RES_COUNTER_STEP 0x68 #define PMC_BASE_ADDR_MASK ~(SPT_PMC_MMIO_REG_LEN - 1) #define MTPMC_MASK 0xffff0000 -#define PPFEAR_MAX_NUM_ENTRIES 12 +#define PPFEAR_MAX_NUM_ENTRIES 13 #define SPT_PPFEAR_NUM_ENTRIES 5 #define SPT_PMC_READ_DISABLE_BIT 0x16 #define SPT_PMC_MSG_FULL_STS_BIT 0x18 @@ -307,6 +307,29 @@ enum ppfear_regs { #define WCL_NUM_S0IX_BLOCKER 94 #define WCL_BLK_REQ_OFFSET 50 +/* Nova Lake */ +#define NVL_PCDH_PPFEAR_NUM_ENTRIES 13 +#define NVL_PCDH_PMC_MMIO_REG_LEN 0x363c +#define NVL_PCDS_PMC_MMIO_REG_LEN 0x3118 +#define NVL_PCHS_PMC_MMIO_REG_LEN 0x30d8 +#define NVL_LPM_PRI_OFFSET 0x17a4 +#define NVL_LPM_EN_OFFSET 0x17a0 +#define NVL_LPM_RESIDENCY_OFFSET 0x17a8 +#define NVL_LPM_LIVE_STATUS_OFFSET 0x1760 +#define NVL_LPM_NUM_MAPS 15 +#define NVL_PCDH_NUM_S0IX_BLOCKER 107 +#define NVL_PCDS_NUM_S0IX_BLOCKER 71 +#define NVL_PCHS_NUM_S0IX_BLOCKER 54 +#define NVL_PCDS_PMC_LTR_RESERVED 0x1bac +#define NVL_PCDH_BLK_REQ_OFFSET 53 +#define NVL_PCDS_BLK_REQ_OFFSET 18 +#define NVL_PCHS_BLK_REQ_OFFSET 46 +#define NVL_PMT_PC_GUID 0x13000101 +#define NVL_PMT_DMU_GUID 0x1a000101 +#define NVL_LTR_BLK_OFFSET 64 +#define NVL_PKGC_BLK_OFFSET 4 +#define NVL_PMT_DMU_DIE_C6_OFFSET 25 + /* SSRAM PMC Device ID */ /* LNL */ #define PMC_DEVID_LNL_SOCM 0xa87f @@ -329,6 +352,11 @@ enum ppfear_regs { #define PMC_DEVID_MTL_IOEP 0x7ecf #define PMC_DEVID_MTL_IOEM 0x7ebf +/* NVL */ +#define PMC_DEVID_NVL_PCDH 0xd37e +#define PMC_DEVID_NVL_PCDS 0xd47e +#define PMC_DEVID_NVL_PCHS 0x6e27 + extern const char *pmc_lpm_modes[]; struct pmc_bit_map { @@ -558,6 +586,7 @@ extern const struct pmc_reg_map mtl_ioep_reg_map; extern const struct pmc_bit_map ptl_pcdp_clocksource_status_map[]; extern const struct pmc_bit_map ptl_pcdp_vnn_req_status_3_map[]; extern const struct pmc_bit_map ptl_pcdp_signal_status_map[]; +extern const struct pmc_bit_map ptl_pcdp_ltr_show_map[]; void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); @@ -581,6 +610,8 @@ extern struct pmc_dev_info arl_h_pmc_dev; extern struct pmc_dev_info lnl_pmc_dev; extern struct pmc_dev_info ptl_pmc_dev; extern struct pmc_dev_info wcl_pmc_dev; +extern struct pmc_dev_info nvl_s_pmc_dev; +extern struct pmc_dev_info nvl_h_pmc_dev; void cnl_suspend(struct pmc_dev *pmcdev); int cnl_resume(struct pmc_dev *pmcdev); diff --git a/drivers/platform/x86/intel/pmc/nvl.c b/drivers/platform/x86/intel/pmc/nvl.c new file mode 100644 index 000000000000..8dabf2511dd9 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/nvl.c @@ -0,0 +1,1539 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file contains platform specific structure definitions + * and init function used by Nova Lake PCH. + * + * Copyright (c) 2026, Intel Corporation. + */ + +#include +#include + +#include "core.h" + +/* PMC SSRAM PMT Telemetry GUIDS */ +#define PCDH_LPM_REQ_GUID 0x01093101 +#define PCHS_LPM_REQ_GUID 0x01092101 +#define PCDS_LPM_REQ_GUID 0x01091102 + +/* + * Die Mapping to Product. + * Product PCDDie PCHDie + * NVL-H PCD-H None + * NVL-S PCD-S PCH-S + */ + +static const struct pmc_bit_map nvl_pcdh_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FUSE_OSSE_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"MPFPW2_PGD0", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"SBR16B20_PGD0", BIT(0)}, + {"DBG_SBR_PGD0", BIT(1)}, + {"SBR16B7_PGD0", BIT(2)}, + {"STRC_PGD0", BIT(3)}, + {"SBR16B8_PGD0", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"SBR16B2_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"DBG_SBR16B_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"SBR16B0_PGD0", BIT(7)}, + + {"P2SB0_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"SBR16B21_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR16B22_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"MPFPW1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"ESE_PGD0", BIT(1)}, + {"SBR16B6_PGD0", BIT(2)}, + {"P2SB1_PGD0", BIT(3)}, + {"SBR16B3_PGD0", BIT(4)}, + {"OSSE_SMT1_PGD0", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"SNPS_USB2_A_PGD0", BIT(7)}, + + {"U3FPW1_PGD0", BIT(0)}, + {"FIA_X_PGD0", BIT(1)}, + {"PSF4_PGD0", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"UFSX2_PGD0", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"FIA_PG_PGD0", BIT(7)}, + + {"D2D_IPU_PGD0", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"FIACPCB_X_PGD0", BIT(2)}, + {"SBR8B4_PGD0", BIT(3)}, + {"DBG_PSF_PGD0", BIT(4)}, + {"PSF6_PGD0", BIT(5)}, + {"UFSPW1_PGD0", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"PSF8_PGD0", BIT(0)}, + {"SBR16B9_PGD0", BIT(1)}, + {"PSF0_PGD0", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"SBR8B2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"DISP_PGA1_PGD0", BIT(2)}, + {"TCSS_PGD0", BIT(3)}, + {"DISP_PGA_PGD0", BIT(4)}, + {"SBR16B1_PGD0", BIT(5)}, + {"SBRG_PGD0", BIT(6)}, + {"PSF5_PGD0", BIT(7)}, + + {"SBR8B3_PGD0", BIT(0)}, + {"ACE_PGD0", BIT(1)}, + {"ACE_PGD1", BIT(2)}, + {"ACE_PGD2", BIT(3)}, + {"ACE_PGD3", BIT(4)}, + {"ACE_PGD4", BIT(5)}, + {"ACE_PGD5", BIT(6)}, + {"ACE_PGD6", BIT(7)}, + + {"ACE_PGD7", BIT(0)}, + {"ACE_PGD8", BIT(1)}, + {"ACE_PGD9", BIT(2)}, + {"ACE_PGD10", BIT(3)}, + {"FIACPCB_PG_PGD0", BIT(4)}, + {"SNPS_USB2_B_PGD0", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"SBR8B0_PGD0", BIT(7)}, + + {"SBR16B4_PGD0", BIT(0)}, + {"CSME_PTIO_PGD0", BIT(1)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pcdh_pfear_map[] = { + nvl_pcdh_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcdh_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 1}, + {"AON5_OFF_STS", BIT(3), 1}, + {"AON1_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"MPFPW1_0_PLL_OFF_STS", BIT(6), 1}, + {"D2D_PLL_OFF_STS", BIT(7), 1}, + {"USB3_PLL_OFF_STS", BIT(8), 1}, + {"AON3_SPL_OFF_STS", BIT(9), 1}, + {"MPFPW2_0_PLL_OFF_STS", BIT(12), 1}, + {"XTAL_AGGR_OFF_STS", BIT(17), 1}, + {"USB2_PLL_OFF_STS", BIT(18), 0}, + {"DDI2_PLL_OFF_STS", BIT(19), 1}, + {"SE_TCSS_PLL_OFF_STS", BIT(20), 1}, + {"DDI_PLL_OFF_STS", BIT(21), 1}, + {"FILTER_PLL_OFF_STS", BIT(22), 1}, + {"ACE_PLL_OFF_STS", BIT(24), 0}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"SOC_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"IMG_PLL_OFF_STS", BIT(29), 1}, + {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1}, + {"RTC_PLL_OFF_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 1}, + {"SPA_PGD0_PG_STS", BIT(4), 1}, + {"SPB_PGD0_PG_STS", BIT(5), 1}, + {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 1}, + {"SBR16B20_PGD0_PG_STS", BIT(8), 0}, + {"DBG_PGD0_PG_STS", BIT(9), 0}, + {"SBR16B7_PGD0_PG_STS", BIT(10), 0}, + {"STRC_PGD0_PG_STS", BIT(11), 0}, + {"SBR16B8_PGD0_PG_STS", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 1}, + {"LPSS_PGD0_PG_STS", BIT(14), 1}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"SBR16B2_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 1}, + {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"SBR16B0_PGD0_PG_STS", BIT(23), 0}, + {"P2SB0_PGD0_PG_STS", BIT(24), 1}, + {"XDCI_PGD0_PG_STS", BIT(25), 1}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 1}, + {"PMT_PGD0_PG_STS", BIT(29), 1}, + {"CLINK_PGD0_PG_STS", BIT(30), 1}, + {"SBR16B21_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 1}, + {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 1}, + {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, + {"SMS2_PGD0_PG_STS", BIT(4), 1}, + {"SMS1_PGD0_PG_STS", BIT(5), 1}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"ESE_PGD0_PG_STS", BIT(9), 1}, + {"SBR16B6_PGD0_PG_STS", BIT(10), 0}, + {"P2SB1_PGD0_PG_STS", BIT(11), 1}, + {"SBR16B3_PGD0_PG_STS", BIT(12), 0}, + {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 1}, + {"SNPA_USB2_A_PGD0_PG_STS", BIT(15), 0}, + {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, + {"FIA_X_PGD0_PG_STS", BIT(17), 0}, + {"PSF4_PGD0_PG_STS", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"UFSX2_PGD0_PG_STS", BIT(20), 1}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"FIA_PG_PGD0_PG_STS", BIT(23), 0}, + {"D2D_IPU_PGD0_PG_STS", BIT(24), 1}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"FIACPCB_X_PGD0_PG_STS", BIT(26), 0}, + {"SBR8B4_PGD0_PG_STS", BIT(27), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(28), 0}, + {"PSF6_PGD0_PG_STS", BIT(29), 0}, + {"UFSPW1_PGD0_PG_STS", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0), 0}, + {"SBR16B9_PGD0_PG_STS", BIT(1), 0}, + {"PSF0_PGD0_PG_STS", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 1}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 1}, + {"SBR8B2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 1}, + {"THC1_PGD0_PG_STS", BIT(8), 1}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"DISP_PGA1_PGD0_PG_STS", BIT(10), 0}, + {"TCSS_PGD0_PG_STS", BIT(11), 0}, + {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, + {"SBR16B1_PGD0_PG_STS", BIT(13), 0}, + {"SBRG_PGD0_PG_STS", BIT(14), 0}, + {"PSF5_PGD0_PG_STS", BIT(15), 0}, + {"SBR8B3_PGD0_PG_STS", BIT(16), 0}, + {"ACE_PGD0_PG_STS", BIT(17), 0}, + {"ACE_PGD1_PG_STS", BIT(18), 0}, + {"ACE_PGD2_PG_STS", BIT(19), 0}, + {"ACE_PGD3_PG_STS", BIT(20), 0}, + {"ACE_PGD4_PG_STS", BIT(21), 0}, + {"ACE_PGD5_PG_STS", BIT(22), 0}, + {"ACE_PGD6_PG_STS", BIT(23), 0}, + {"ACE_PGD7_PG_STS", BIT(24), 0}, + {"ACE_PGD8_PG_STS", BIT(25), 0}, + {"ACE_PGD9_PG_STS", BIT(26), 0}, + {"ACE_PGD10_PG_STS", BIT(27), 0}, + {"FIACPCB_PG_PGD0_PG_STS", BIT(28), 0}, + {"SNPS_USB2_B_PGD0_PG_STS", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 1}, + {"SBR8B0_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_power_gating_status_3_map[] = { + {"SBR16B4_PGD0_PG_STS", BIT(0), 0}, + {"PTIO_PGD0_PG_STS", BIT(1), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 1}, + {"OSSE_D3_STS", BIT(6), 0}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"PSTH_D3_STS", BIT(21), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_1_map[] = { + {"OSSE_SMT1_D3_STS", BIT(0), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {"UFSX2_D3_STS", BIT(28), 0}, + {"ESE_D3_STS", BIT(29), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"OSSE_SMT2_D3_STS", BIT(11), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14), 1}, + {"THC1_D3_STS", BIT(15), 1}, + {"OSSE_SMT3_D3_STS", BIT(16), 0}, + {"ACE_D3_STS", BIT(23), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_0_map[] = { + {"LPSS_VNN_REQ_STS", BIT(3), 1}, + {"OSSE_VNN_REQ_STS", BIT(6), 1}, + {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_1_map[] = { + {"OSSE_SMT1_VNN_REQ_STS", BIT(0), 1}, + {"NPK_VNN_REQ_STS", BIT(4), 1}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 1}, + {"P2D_VNN_REQ_STS", BIT(18), 1}, + {"GBE_VNN_REQ_STS", BIT(19), 1}, + {"SMB_VNN_REQ_STS", BIT(25), 1}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {"ESE_VNN_REQ_STS", BIT(29), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 1}, + {"CSE_VNN_REQ_STS", BIT(4), 1}, + {"ISH_VNN_REQ_STS", BIT(7), 1}, + {"SMT1_VNN_REQ_STS", BIT(8), 1}, + {"CLINK_VNN_REQ_STS", BIT(14), 1}, + {"SMS1_VNN_REQ_STS", BIT(18), 1}, + {"SMS2_VNN_REQ_STS", BIT(19), 1}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 1}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, + {"DISP_SHIM_VNN_REQ_STS", BIT(22), 1}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_req_status_3_map[] = { + {"DTS0_VNN_REQ_STS", BIT(7), 0}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"VNN_SOC_REQ_STS", BIT(6), 1}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, + {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, + {"D2D_IPU_QACTIVE_REQ_STS", BIT(10), 1}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_REQ_STS", BIT(22), 1}, + {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, + {"XDCI_ATTACHED_REQ_STS", BIT(24), 1}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_REQ_STS", BIT(29), 1}, + {"PG5_PMA2_GVNN", BIT(30), 1}, + {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcdh_rsc_status_map[] = { + {"CORE", 0, 1}, + {"Memory", 0, 1}, + {"PRIM_D2D", 0, 1}, + {"PSF0", 0, 1}, + {"PSF4", 0, 1}, + {"PSF6", 0, 1}, + {"PSF8", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pcdh_lpm_maps[] = { + nvl_pcdh_clocksource_status_map, + nvl_pcdh_power_gating_status_0_map, + nvl_pcdh_power_gating_status_1_map, + nvl_pcdh_power_gating_status_2_map, + nvl_pcdh_power_gating_status_3_map, + nvl_pcdh_d3_status_0_map, + nvl_pcdh_d3_status_1_map, + nvl_pcdh_d3_status_2_map, + nvl_pcdh_d3_status_3_map, + nvl_pcdh_vnn_req_status_0_map, + nvl_pcdh_vnn_req_status_1_map, + nvl_pcdh_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pcdh_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pcdh_blk_maps[] = { + nvl_pcdh_power_gating_status_0_map, + nvl_pcdh_power_gating_status_1_map, + nvl_pcdh_power_gating_status_2_map, + nvl_pcdh_power_gating_status_3_map, + nvl_pcdh_rsc_status_map, + nvl_pcdh_vnn_req_status_0_map, + nvl_pcdh_vnn_req_status_1_map, + nvl_pcdh_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pcdh_d3_status_0_map, + nvl_pcdh_d3_status_1_map, + nvl_pcdh_d3_status_2_map, + nvl_pcdh_d3_status_3_map, + nvl_pcdh_clocksource_status_map, + nvl_pcdh_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcds_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FUSE_OSSE_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"RSVD6", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"RSVD8", BIT(0)}, + {"RSVD9", BIT(1)}, + {"SBR16B7_PGD0", BIT(2)}, + {"SBR16B21_PGD0", BIT(3)}, + {"RSVD12", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"SBR16B1_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"DBG_SBR16B_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"RSVD23", BIT(7)}, + + {"P2SB0_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"CSME_PTIO_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR16B22_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"P2SB1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"RSVD41", BIT(1)}, + {"RSVD42", BIT(2)}, + {"RSVD43", BIT(3)}, + {"SBR16B2_PGD0", BIT(4)}, + {"OSSE_SMT1_PGD0", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"RSVD47_PGD0", BIT(7)}, + + {"RSVD48", BIT(0)}, + {"DBG_PSF_PGD0", BIT(1)}, + {"RSVD50", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"UFSX2_PGD0", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"SBR16B4_PGD0", BIT(7)}, + + {"RSVD56", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"RSVD58", BIT(2)}, + {"SBR16B20_PGD0", BIT(3)}, + {"RSVD60", BIT(4)}, + {"SBR8B20_PGD0", BIT(5)}, + {"RSVD62", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"PSF8_PGD0", BIT(0)}, + {"RSVD65", BIT(1)}, + {"RSVD66", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"SBR8B2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"SBR16B3_PGD0", BIT(2)}, + {"TCSS_PGD0", BIT(3)}, + {"DISP_PGA_PGD0", BIT(4)}, + {"RSVD77", BIT(5)}, + {"RSVD78", BIT(6)}, + {"RSVD79", BIT(7)}, + + {"SBRG_PGD0", BIT(0)}, + {"RSVD81", BIT(1)}, + {"SBR16B0_PGD0", BIT(2)}, + {"SBR8B0_PGD0", BIT(3)}, + {"PSF7_PGD0", BIT(4)}, + {"RSVD85", BIT(5)}, + {"RSVD86", BIT(6)}, + {"RSVD87", BIT(7)}, + + {"SBR16B6_PGD0", BIT(0)}, + {"PSD0_PGD0", BIT(1)}, + {"STRC_PGD0", BIT(2)}, + {"RSVD91", BIT(3)}, + {"DBG_SBR_PGD0", BIT(4)}, + {"RSVD93", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"DISP_PGA1_PGD0", BIT(7)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pcds_pfear_map[] = { + nvl_pcds_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pcds_ltr_show_map[] = { + {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, + {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, + {"SATA", CNP_PMC_LTR_SATA}, + {"GIGABIT_ETHERNET", CNP_PMC_LTR_GBE}, + {"XHCI", CNP_PMC_LTR_XHCI}, + {"SOUTHPORT_F", ADL_PMC_LTR_SPF}, + {"ME", CNP_PMC_LTR_ME}, + {"SATA1", CNP_PMC_LTR_EVA}, + {"SOUTHPORT_C", CNP_PMC_LTR_SPC}, + {"HD_AUDIO", CNP_PMC_LTR_AZ}, + {"CNV", CNP_PMC_LTR_CNV}, + {"LPSS", CNP_PMC_LTR_LPSS}, + {"SOUTHPORT_D", CNP_PMC_LTR_SPD}, + {"SOUTHPORT_E", CNP_PMC_LTR_SPE}, + {"SATA2", PTL_PMC_LTR_SATA2}, + {"ESPI", CNP_PMC_LTR_ESPI}, + {"SCC", CNP_PMC_LTR_SCC}, + {"ISH", CNP_PMC_LTR_ISH}, + {"UFSX2", CNP_PMC_LTR_UFSX2}, + {"EMMC", CNP_PMC_LTR_EMMC}, + {"WIGIG", ICL_PMC_LTR_WIGIG}, + {"THC0", TGL_PMC_LTR_THC0}, + {"THC1", TGL_PMC_LTR_THC1}, + {"SOUTHPORT_G", MTL_PMC_LTR_SPG}, + {"RSVD", NVL_PCDS_PMC_LTR_RESERVED}, + {"IOE_PMC", MTL_PMC_LTR_IOE_PMC}, + {"DMI3", ARL_PMC_LTR_DMI3}, + {"OSSE", LNL_PMC_LTR_OSSE}, + + /* Below two cannot be used for LTR_IGNORE */ + {"CURRENT_PLATFORM", PTL_PMC_LTR_CUR_PLT}, + {"AGGREGATED_SYSTEM", PTL_PMC_LTR_CUR_ASLT}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 1}, + {"AON5_OFF_STS", BIT(3), 1}, + {"AON1_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"D2D_OFF_STS", BIT(8), 1}, + {"AON3_SPL_OFF_STS", BIT(9), 1}, + {"XTAL_AGGR_OFF_STS", BIT(17), 1}, + {"BCLK_EXT_INJ_OFF_STS", BIT(18), 1}, + {"DDI2_PLL_OFF_STS", BIT(19), 1}, + {"SE_TCSS_PLL_OFF_STS", BIT(20), 1}, + {"DDI_PLL_OFF_STS", BIT(21), 1}, + {"FILTER_PLL_OFF_STS", BIT(22), 1}, + {"PHY_OC_EXT_INJ_OFF_STS", BIT(23), 1}, + {"ACE_PLL_OFF_STS", BIT(24), 0}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"SOC_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"GENLOCK_FILTER_PLL_OFF_STS", BIT(30), 1}, + {"RTC_PLL_OFF_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FUSE_OSSE_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 0}, + {"SPA_PGD0_PG_STS", BIT(4), 0}, + {"SPB_PGD0_PG_STS", BIT(5), 0}, + {"RSVD_6", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 0}, + {"RSVD_8", BIT(8), 0}, + {"RSVD_9", BIT(9), 0}, + {"SBR16B7_PGD0_PG_STS", BIT(10), 0}, + {"SBR16B21_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 1}, + {"LPSS_PGD0_PG_STS", BIT(14), 0}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"SBR16B1_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 1}, + {"DBG_SBR16B_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"RSVD_23", BIT(23), 0}, + {"P2SB0_PGD0_PG_STS", BIT(24), 1}, + {"XDCI_PGD0_PG_STS", BIT(25), 0}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 0}, + {"PMT_PGD0_PG_STS", BIT(29), 0}, + {"CLINK_PGD0_PG_STS", BIT(30), 0}, + {"PTIO_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 0}, + {"SBR16B22_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 0}, + {"P2SB1_PGD0_PG_STS", BIT(3), 1}, + {"SMS2_PGD0_PG_STS", BIT(4), 0}, + {"SMS1_PGD0_PG_STS", BIT(5), 0}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"RSVD_9", BIT(9), 0}, + {"RSVD_10", BIT(10), 0}, + {"RSVD_11", BIT(11), 0}, + {"SBR16B2_PGD0_PG_STS", BIT(12), 0}, + {"OSSE_SMT1_PGD0_PG_STS", BIT(13), 1}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 1}, + {"RSVD_15", BIT(15), 0}, + {"RSVD_16", BIT(16), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(17), 0}, + {"RSVD_18", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"UFSX2_PGD0_PG_STS", BIT(20), 0}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"SBR16B4_PGD0_PG_STS", BIT(23), 0}, + {"RSVD_24", BIT(24), 0}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"RSVD_26", BIT(26), 0}, + {"SBR16B20_PGD0_PG_STS", BIT(27), 0}, + {"RSVD_28", BIT(28), 0}, + {"SBR8B20_PGD0_PG_STS", BIT(29), 0}, + {"RSVD_30", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_power_gating_status_2_map[] = { + {"PSF8_PGD0_PG_STS", BIT(0), 0}, + {"RSVD_1", BIT(1), 0}, + {"RSVD_2", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 1}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 1}, + {"SBR8B2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 0}, + {"THC1_PGD0_PG_STS", BIT(8), 0}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"SBR16B3_PGD0_PG_STS", BIT(10), 0}, + {"TCSS_PGD0_PG_STS", BIT(11), 0}, + {"DISP_PGA_PGD0_PG_STS", BIT(12), 0}, + {"RSVD_13", BIT(13), 0}, + {"RSVD_14", BIT(14), 0}, + {"RSVD_15", BIT(15), 0}, + {"SBRG_PGD0_PG_STS", BIT(16), 0}, + {"RSVD_17", BIT(17), 0}, + {"SBR16B0_PGD0_PG_STS", BIT(18), 0}, + {"SBR8B0_PGD0_PG_STS", BIT(19), 0}, + {"PSF7_PGD0_PG_STS", BIT(20), 0}, + {"RSVD_21", BIT(21), 0}, + {"RSVD_22", BIT(22), 0}, + {"RSVD_23", BIT(23), 0}, + {"SBR16B6_PGD0_PG_STS", BIT(24), 0}, + {"PSF0_PGD0_PG_STS", BIT(25), 0}, + {"STRC_PGD0_PG_STS", BIT(26), 0}, + {"RSVD_27", BIT(27), 0}, + {"DBG_SBR_PGD0_PG_STS", BIT(28), 0}, + {"RSVD_29", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 1}, + {"DISP_PGA1_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 1}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"PSTH_D3_STS", BIT(21), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_1_map[] = { + {"OSSE_D3_STS", BIT(14), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {"UFSX2_D3_STS", BIT(28), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"OSSE_SMT1_D3_STS", BIT(12), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_d3_status_3_map[] = { + {"OSSE_SMT2_D3_STS", BIT(0), 0}, + {"THC0_D3_STS", BIT(14), 1}, + {"THC1_D3_STS", BIT(15), 1}, + {"OSSE_SMT3_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_0_map[] = { + {"LPSS_VNN_REQ_STS", BIT(3), 0}, + {"ESPISPI_VNN_REQ_STS", BIT(18), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4), 1}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 1}, + {"OSSE_VNN_REQ_STS", BIT(14), 1}, + {"P2D_VNN_REQ_STS", BIT(18), 1}, + {"GBE_VNN_REQ_STS", BIT(19), 0}, + {"SMB_VNN_REQ_STS", BIT(25), 1}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 0}, + {"CSE_VNN_REQ_STS", BIT(4), 1}, + {"ISH_VNN_REQ_STS", BIT(7), 0}, + {"SMT1_VNN_REQ_STS", BIT(8), 0}, + {"OSSE_SMT1_VNN_REQ_STS", BIT(12), 1}, + {"CLINK_VNN_REQ_STS", BIT(14), 0}, + {"SMS1_VNN_REQ_STS", BIT(18), 0}, + {"SMS2_VNN_REQ_STS", BIT(19), 0}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 1}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 1}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_req_status_3_map[] = { + {"DISP_SHIM_VNN_REQ_STS", BIT(4), 1}, + {"DTS0_VNN_REQ_STS", BIT(7), 0}, + {"GPIOCOM5_VNN_REQ_STS", BIT(11), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"VNN_SOC_REQ_STS", BIT(6), 1}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"D2D_NOC_CFI_QACTIVE_REQ_STS", BIT(8), 1}, + {"D2D_NOC_GPSB_QACTIVE_REQ_STS", BIT(9), 1}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"MPHY_CORE_OFF_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_REQ_STS", BIT(22), 1}, + {"DG5_PMA0_REQ_STS", BIT(23), 1}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"D2D_DISP_DDI_QACTIVE_REQ_STS", BIT(26), 1}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_REQ_STS", BIT(29), 1}, + {"D2D_DISP_EDP_QACTIVE_REQ_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_rsc_status_map[] = { + {"CORE", 0, 1}, + {"Memory", 0, 1}, + {"PRIM_D2D", 0, 1}, + {"PSF0", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map nvl_pcds_signal_status_map[] = { + {"LSX_Wake0_STS", BIT(0), 0}, + {"LSX_Wake1_STS", BIT(1), 0}, + {"LSX_Wake2_STS", BIT(2), 0}, + {"LSX_Wake3_STS", BIT(3), 0}, + {"LSX_Wake4_STS", BIT(4), 0}, + {"LSX_Wake5_STS", BIT(5), 0}, + {"LSX_Wake6_STS", BIT(6), 0}, + {"LSX_Wake7_STS", BIT(7), 0}, + {"LPSS_Wake0_STS", BIT(8), 1}, + {"LPSS_Wake1_STS", BIT(9), 1}, + {"Int_Timer_SS_Wake0_STS", BIT(10), 1}, + {"Int_Timer_SS_Wake1_STS", BIT(11), 1}, + {"Int_Timer_SS_Wake2_STS", BIT(12), 1}, + {"Int_Timer_SS_Wake3_STS", BIT(13), 1}, + {"Int_Timer_SS_Wake4_STS", BIT(14), 1}, + {"Int_Timer_SS_Wake5_STS", BIT(15), 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pcds_lpm_maps[] = { + nvl_pcds_clocksource_status_map, + nvl_pcds_power_gating_status_0_map, + nvl_pcds_power_gating_status_1_map, + nvl_pcds_power_gating_status_2_map, + nvl_pcds_d3_status_0_map, + nvl_pcds_d3_status_1_map, + nvl_pcds_d3_status_2_map, + nvl_pcds_d3_status_3_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pcds_vnn_req_status_1_map, + nvl_pcds_vnn_req_status_2_map, + nvl_pcds_vnn_req_status_3_map, + nvl_pcds_vnn_misc_status_map, + nvl_pcds_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pcds_blk_maps[] = { + nvl_pcds_power_gating_status_0_map, + nvl_pcds_power_gating_status_1_map, + nvl_pcds_power_gating_status_2_map, + nvl_pcds_rsc_status_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pcds_vnn_req_status_1_map, + nvl_pcds_vnn_req_status_2_map, + nvl_pcds_vnn_req_status_3_map, + nvl_pcds_d3_status_0_map, + nvl_pcds_d3_status_1_map, + nvl_pcds_d3_status_2_map, + nvl_pcds_d3_status_3_map, + nvl_pcds_clocksource_status_map, + nvl_pcds_vnn_misc_status_map, + nvl_pcds_signal_status_map, + NULL +}; + +static const struct pmc_bit_map nvl_pchs_pfear_map[] = { + {"PMC_PGD0", BIT(0)}, + {"FIA_D_PGD0", BIT(1)}, + {"SPI_PGD0", BIT(2)}, + {"XHCI_PGD0", BIT(3)}, + {"SPA_PGD0", BIT(4)}, + {"SPB_PGD0", BIT(5)}, + {"MPFPW2_PGD0", BIT(6)}, + {"GBE_PGD0", BIT(7)}, + + {"RSVD8", BIT(0)}, + {"PSF3_PGD0", BIT(1)}, + {"SBR5_PGD0", BIT(2)}, + {"SBR0_PGD0", BIT(3)}, + {"RSVD12", BIT(4)}, + {"D2D_DISP_PGD1", BIT(5)}, + {"LPSS_PGD0", BIT(6)}, + {"LPC_PGD0", BIT(7)}, + + {"SMB_PGD0", BIT(0)}, + {"ISH_PGD0", BIT(1)}, + {"P2SB_PGD0", BIT(2)}, + {"NPK_PGD0", BIT(3)}, + {"D2D_NOC_PGD1", BIT(4)}, + {"EAH_PGD0", BIT(5)}, + {"FUSE_PGD0", BIT(6)}, + {"SBR8_PGD0", BIT(7)}, + + {"PSF7_PGD0", BIT(0)}, + {"OTG_PGD0", BIT(1)}, + {"EXI_PGD0", BIT(2)}, + {"CSE_PGD0", BIT(3)}, + {"CSME_KVM_PGD0", BIT(4)}, + {"CSME_PMT_PGD0", BIT(5)}, + {"CSME_CLINK_PGD0", BIT(6)}, + {"CSME_PTIO_PGD0", BIT(7)}, + + {"CSME_USBR_PGD0", BIT(0)}, + {"SBR1_PGD0", BIT(1)}, + {"CSME_SMT1_PGD0", BIT(2)}, + {"MPFPW1_PGD0", BIT(3)}, + {"CSME_SMS2_PGD0", BIT(4)}, + {"CSME_SMS_PGD0", BIT(5)}, + {"CSME_RTC_PGD0", BIT(6)}, + {"CSMEPSF_PGD0", BIT(7)}, + + {"D2D_NOC_PGD0", BIT(0)}, + {"ESE_PGD0", BIT(1)}, + {"SBR2_PGD0", BIT(2)}, + {"SBR3_PGD0", BIT(3)}, + {"SBR4_PGD0", BIT(4)}, + {"RSVD45", BIT(5)}, + {"D2D_DISP_PGD0", BIT(6)}, + {"PSF1_PGD0", BIT(7)}, + + {"U3FPW1_PGD0", BIT(0)}, + {"DMI3FPW_PGD0", BIT(1)}, + {"PSF4_PGD0", BIT(2)}, + {"CNVI_PGD0", BIT(3)}, + {"RSVD52", BIT(4)}, + {"ENDBG_PGD0", BIT(5)}, + {"DBC_PGD0", BIT(6)}, + {"SMT4_PGD0", BIT(7)}, + + {"RSVD56", BIT(0)}, + {"NPK_PGD1", BIT(1)}, + {"RSVD58", BIT(2)}, + {"DMI3_PGD0", BIT(3)}, + {"RSVD60", BIT(4)}, + {"FIACPCB_D_PGD0", BIT(5)}, + {"RSVD62", BIT(6)}, + {"FIA_U_PGD0", BIT(7)}, + + {"FIACPCB_PGS_PGD0", BIT(0)}, + {"FIA_PGS_PGD0", BIT(1)}, + {"RSVD66", BIT(2)}, + {"FIACPCB_U_PGD0", BIT(3)}, + {"TAM_PGD0", BIT(4)}, + {"D2D_NOC_PGD2", BIT(5)}, + {"PSF2_PGD0", BIT(6)}, + {"THC0_PGD0", BIT(7)}, + + {"THC1_PGD0", BIT(0)}, + {"PMC_PGD1", BIT(1)}, + {"SBR9_PGD0", BIT(2)}, + {"U3FPW2_PGD0", BIT(3)}, + {"RSVD76", BIT(4)}, + {"DBG_PSF_PGD0", BIT(5)}, + {"DBG_SBR_PGD0", BIT(6)}, + {"SBR6_PGD0", BIT(7)}, + + {"SPC_PGD0", BIT(0)}, + {"ACE_PGD0", BIT(1)}, + {"ACE_PGD1", BIT(2)}, + {"ACE_PGD2", BIT(3)}, + {"ACE_PGD3", BIT(4)}, + {"ACE_PGD4", BIT(5)}, + {"ACE_PGD5", BIT(6)}, + {"ACE_PGD6", BIT(7)}, + + {"ACE_PGD7", BIT(0)}, + {"ACE_PGD8", BIT(1)}, + {"ACE_PGD9", BIT(2)}, + {"ACE_PGD10", BIT(3)}, + {"U3FPW3_PGD0", BIT(4)}, + {"SBR7_PGD0", BIT(5)}, + {"OSSE_PGD0", BIT(6)}, + {"ST_PGD0", BIT(7)}, + {} +}; + +static const struct pmc_bit_map *ext_nvl_pchs_pfear_map[] = { + nvl_pchs_pfear_map, + NULL +}; + +static const struct pmc_bit_map nvl_pchs_clocksource_status_map[] = { + {"AON2_OFF_STS", BIT(0), 1}, + {"AON3_OFF_STS", BIT(1), 0}, + {"AON4_OFF_STS", BIT(2), 0}, + {"AON2_SPL_OFF_STS", BIT(3), 0}, + {"AONL_OFF_STS", BIT(4), 0}, + {"XTAL_LVM_OFF_STS", BIT(5), 0}, + {"AON5_OFF_STS", BIT(6), 0}, + {"USB3_PLL_OFF_STS", BIT(8), 1}, + {"MAIN_CRO_OFF_STS", BIT(11), 0}, + {"MAIN_DIVIDER_OFF_STS", BIT(12), 1}, + {"REF_PLL_NON_OC_OFF_STS", BIT(13), 1}, + {"DMI_PLL_OFF_STS", BIT(14), 1}, + {"PHY_EXT_INJ_OFF_STS", BIT(15), 1}, + {"AON6_MCRO_OFF_STS", BIT(16), 0}, + {"XTAL_AGGR_OFF_STS", BIT(17), 0}, + {"USB2_PLL_OFF_STS", BIT(18), 1}, + {"GBE_PLL_OFF_STS", BIT(21), 1}, + {"SATA_PLL_OFF_STS", BIT(22), 1}, + {"PCIE0_PLL_OFF_STS", BIT(23), 1}, + {"PCIE1_PLL_OFF_STS", BIT(24), 1}, + {"FABRIC_PLL_OFF_STS", BIT(25), 1}, + {"PCIE2_PLL_OFF_STS", BIT(26), 1}, + {"REF_PLL_OFF_STS", BIT(28), 1}, + {"REF38P4_PLL_OFF_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_0_map[] = { + {"PMC_PGD0_PG_STS", BIT(0), 0}, + {"FIA_D_PGD0_PG_STS", BIT(1), 0}, + {"ESPISPI_PGD0_PG_STS", BIT(2), 0}, + {"XHCI_PGD0_PG_STS", BIT(3), 0}, + {"SPA_PGD0_PG_STS", BIT(4), 1}, + {"SPB_PGD0_PG_STS", BIT(5), 1}, + {"MPFPW2_PGD0_PG_STS", BIT(6), 0}, + {"GBE_PGD0_PG_STS", BIT(7), 1}, + {"RSVD_8", BIT(8), 0}, + {"PSF3_PGD0_PG_STS", BIT(9), 0}, + {"SBR5_PGD0_PG_STS", BIT(10), 0}, + {"SBR0_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"D2D_DISP_PGD1_PG_STS", BIT(13), 0}, + {"LPSS_PGD0_PG_STS", BIT(14), 1}, + {"LPC_PGD0_PG_STS", BIT(15), 0}, + {"SMB_PGD0_PG_STS", BIT(16), 0}, + {"ISH_PGD0_PG_STS", BIT(17), 0}, + {"P2S_PGD0_PG_STS", BIT(18), 0}, + {"NPK_PGD0_PG_STS", BIT(19), 0}, + {"D2D_NOC_PGD1_PG_STS", BIT(20), 0}, + {"EAH_PGD0_PG_STS", BIT(21), 0}, + {"FUSE_PGD0_PG_STS", BIT(22), 0}, + {"SBR8_PGD0_PG_STS", BIT(23), 0}, + {"PSF7_PGD0_PG_STS", BIT(24), 0}, + {"XDCI_PGD0_PG_STS", BIT(25), 1}, + {"EXI_PGD0_PG_STS", BIT(26), 0}, + {"CSE_PGD0_PG_STS", BIT(27), 1}, + {"KVMCC_PGD0_PG_STS", BIT(28), 1}, + {"PMT_PGD0_PG_STS", BIT(29), 1}, + {"CLINK_PGD0_PG_STS", BIT(30), 1}, + {"PTIO_PGD0_PG_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_1_map[] = { + {"USBR0_PGD0_PG_STS", BIT(0), 1}, + {"SBR1_PGD0_PG_STS", BIT(1), 0}, + {"SMT1_PGD0_PG_STS", BIT(2), 1}, + {"MPFPW1_PGD0_PG_STS", BIT(3), 0}, + {"SMS2_PGD0_PG_STS", BIT(4), 1}, + {"SMS1_PGD0_PG_STS", BIT(5), 1}, + {"CSMERTC_PGD0_PG_STS", BIT(6), 0}, + {"CSMEPSF_PGD0_PG_STS", BIT(7), 0}, + {"D2D_NOC_PGD0_PG_STS", BIT(8), 0}, + {"ESE_PGD0_PG_STS", BIT(9), 1}, + {"SBR2_PGD0_PG_STS", BIT(10), 0}, + {"SBR3_PGD0_PG_STS", BIT(11), 0}, + {"SBR4_PGD0_PG_STS", BIT(12), 0}, + {"RSVD_13", BIT(13), 0}, + {"D2D_DISP_PGD0_PG_STS", BIT(14), 0}, + {"PSF1_PGD0_PG_STS", BIT(15), 0}, + {"U3FPW1_PGD0_PG_STS", BIT(16), 0}, + {"DMI3FPW_PGD0_PG_STS", BIT(17), 0}, + {"PSF4_PGD0_PG_STS", BIT(18), 0}, + {"CNVI_PGD0_PG_STS", BIT(19), 0}, + {"RSVD_20", BIT(20), 0}, + {"ENDBG_PGD0_PG_STS", BIT(21), 0}, + {"DBC_PGD0_PG_STS", BIT(22), 0}, + {"SMT4_PGD0_PG_STS", BIT(23), 1}, + {"RSVD_24", BIT(24), 0}, + {"NPK_PGD1_PG_STS", BIT(25), 0}, + {"RSVD_26", BIT(26), 0}, + {"DMI3_PGD0_PG_STS", BIT(27), 1}, + {"RSVD_28", BIT(28), 0}, + {"FIACPCB_D_PGD0_PG_STS", BIT(29), 0}, + {"RSVD_30", BIT(30), 0}, + {"FIA_U_PGD0_PG_STS", BIT(31), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_power_gating_status_2_map[] = { + {"FIACPCB_PGS_PGD0_PG_STS", BIT(0), 0}, + {"FIA_PGS_PGD0_PG_STS", BIT(1), 0}, + {"RSVD_2", BIT(2), 0}, + {"FIACPCB_U_PGD0_PG_STS", BIT(3), 0}, + {"TAM_PGD0_PG_STS", BIT(4), 0}, + {"D2D_NOC_PGD2_PG_STS", BIT(5), 0}, + {"PSF2_PGD0_PG_STS", BIT(6), 0}, + {"THC0_PGD0_PG_STS", BIT(7), 1}, + {"THC1_PGD0_PG_STS", BIT(8), 1}, + {"PMC_PGD1_PG_STS", BIT(9), 0}, + {"SBR9_PGA0_PGD0_PG_STS", BIT(10), 0}, + {"U3FPW2_PGD0_PG_STS", BIT(11), 0}, + {"RSVD_12", BIT(12), 0}, + {"DBG_PSF_PGD0_PG_STS", BIT(13), 0}, + {"DBG_SBR_PGD0_PG_STS", BIT(14), 0}, + {"SBR6_PGD0_PG_STS", BIT(15), 0}, + {"SPC_PGD0_PG_STS", BIT(16), 1}, + {"ACE_PGD0_PG_STS", BIT(17), 0}, + {"ACE_PGD1_PG_STS", BIT(18), 0}, + {"ACE_PGD2_PG_STS", BIT(19), 0}, + {"ACE_PGD3_PG_STS", BIT(20), 0}, + {"ACE_PGD4_PG_STS", BIT(21), 0}, + {"ACE_PGD5_PG_STS", BIT(22), 0}, + {"ACE_PGD6_PG_STS", BIT(23), 0}, + {"ACE_PGD7_PG_STS", BIT(24), 0}, + {"ACE_PGD8_PG_STS", BIT(25), 0}, + {"ACE_PGD9_PG_STS", BIT(26), 0}, + {"ACE_PGD10_PG_STS", BIT(27), 0}, + {"U3FPW3_PGD0_PG_STS", BIT(28), 0}, + {"SBR7_PGD0_PG_STS", BIT(29), 0}, + {"OSSE_PGD0_PG_STS", BIT(30), 0}, + {"SATA_PGD0_PG_STS", BIT(31), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_0_map[] = { + {"LPSS_D3_STS", BIT(3), 1}, + {"XDCI_D3_STS", BIT(4), 1}, + {"XHCI_D3_STS", BIT(5), 0}, + {"SPA_D3_STS", BIT(12), 0}, + {"SPB_D3_STS", BIT(13), 0}, + {"SPC_D3_STS", BIT(14), 0}, + {"ESPISPI_D3_STS", BIT(18), 0}, + {"SATA_D3_STS", BIT(20), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_1_map[] = { + {"OSSE_D3_STS", BIT(6), 0}, + {"GBE_D3_STS", BIT(19), 0}, + {"ITSS_D3_STS", BIT(23), 0}, + {"P2S_D3_STS", BIT(24), 0}, + {"CNVI_D3_STS", BIT(27), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_2_map[] = { + {"CSMERTC_D3_STS", BIT(1), 0}, + {"CSE_D3_STS", BIT(4), 0}, + {"KVMCC_D3_STS", BIT(5), 0}, + {"USBR0_D3_STS", BIT(6), 0}, + {"ISH_D3_STS", BIT(7), 0}, + {"SMT1_D3_STS", BIT(8), 0}, + {"SMT2_D3_STS", BIT(9), 0}, + {"SMT3_D3_STS", BIT(10), 0}, + {"SMT4_D3_STS", BIT(11), 0}, + {"SMT5_D3_STS", BIT(12), 0}, + {"SMT6_D3_STS", BIT(13), 0}, + {"CLINK_D3_STS", BIT(14), 0}, + {"PTIO_D3_STS", BIT(16), 0}, + {"PMT_D3_STS", BIT(17), 0}, + {"SMS1_D3_STS", BIT(18), 0}, + {"SMS2_D3_STS", BIT(19), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_d3_status_3_map[] = { + {"THC0_D3_STS", BIT(14), 0}, + {"THC1_D3_STS", BIT(15), 0}, + {"ACE_D3_STS", BIT(23), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_req_status_1_map[] = { + {"NPK_VNN_REQ_STS", BIT(4), 0}, + {"OSSE_VNN_REQ_STS", BIT(6), 0}, + {"DFXAGG_VNN_REQ_STS", BIT(8), 0}, + {"EXI_VNN_REQ_STS", BIT(9), 0}, + {"GBE_VNN_REQ_STS", BIT(19), 0}, + {"SMB_VNN_REQ_STS", BIT(25), 0}, + {"LPC_VNN_REQ_STS", BIT(26), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_req_status_2_map[] = { + {"CSMERTC_VNN_REQ_STS", BIT(1), 0}, + {"CSE_VNN_REQ_STS", BIT(4), 0}, + {"ISH_VNN_REQ_STS", BIT(7), 0}, + {"SMT1_VNN_REQ_STS", BIT(8), 0}, + {"SMT4_VNN_REQ_STS", BIT(11), 0}, + {"CLINK_VNN_REQ_STS", BIT(14), 0}, + {"SMS1_VNN_REQ_STS", BIT(18), 0}, + {"SMS2_VNN_REQ_STS", BIT(19), 0}, + {"GPIOCOM4_VNN_REQ_STS", BIT(20), 0}, + {"GPIOCOM3_VNN_REQ_STS", BIT(21), 0}, + {"GPIOCOM2_VNN_REQ_STS", BIT(22), 0}, + {"GPIOCOM1_VNN_REQ_STS", BIT(23), 0}, + {"GPIOCOM0_VNN_REQ_STS", BIT(24), 0}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_vnn_misc_status_map[] = { + {"CPU_C10_REQ_STS", BIT(0), 0}, + {"TS_OFF_REQ_STS", BIT(1), 0}, + {"PNDE_MET_REQ_STS", BIT(2), 1}, + {"PG5_PMA0_GVNN_REQ_STS", BIT(3), 1}, + {"FW_THROTTLE_ALLOWED_REQ_STS", BIT(4), 0}, + {"DMI_IN_L1_REQ_STS", BIT(6), 0}, + {"ISH_VNNAON_REQ_STS", BIT(7), 0}, + {"PLT_GREATER_REQ_STS", BIT(11), 1}, + {"ALL_SBR_IDLE_REQ_STS", BIT(12), 0}, + {"PMC_IDLE_FB_OCP_REQ_STS", BIT(13), 0}, + {"PM_SYNC_STATES_REQ_STS", BIT(14), 0}, + {"EA_REQ_STS", BIT(15), 0}, + {"DMI_CLKREQ_B_REQ_STS", BIT(16), 0}, + {"BRK_EV_EN_REQ_STS", BIT(17), 0}, + {"AUTO_DEMO_EN_REQ_STS", BIT(18), 0}, + {"ITSS_CLK_SRC_REQ_STS", BIT(19), 1}, + {"ARC_IDLE_REQ_STS", BIT(21), 0}, + {"PG5_PMA1_GVNN_REQ_STS", BIT(22), 1}, + {"FIA_DEEP_PM_REQ_STS", BIT(23), 0}, + {"XDCI_ATTACHED_REQ_STS", BIT(24), 0}, + {"ARC_INTERRUPT_WAKE_REQ_STS", BIT(25), 0}, + {"PRE_WAKE0_REQ_STS", BIT(27), 1}, + {"PRE_WAKE1_REQ_STS", BIT(28), 1}, + {"PRE_WAKE2_EN_REQ_STS", BIT(29), 0}, + {"PG5_PMA2_GVNN_REQ_STS", BIT(30), 1}, + {} +}; + +static const struct pmc_bit_map nvl_pchs_rsc_status_map[] = { + {"Memory", 0, 1}, + {"Memory_NS", 0, 1}, + {"PSF1", 0, 1}, + {"PSF2", 0, 1}, + {"PSF3", 0, 1}, + {"REF_PLL", 0, 1}, + {"SB", 0, 1}, + {} +}; + +static const struct pmc_bit_map *nvl_pchs_lpm_maps[] = { + nvl_pchs_clocksource_status_map, + nvl_pchs_power_gating_status_0_map, + nvl_pchs_power_gating_status_1_map, + nvl_pchs_power_gating_status_2_map, + nvl_pchs_d3_status_0_map, + nvl_pchs_d3_status_1_map, + nvl_pchs_d3_status_2_map, + nvl_pchs_d3_status_3_map, + nvl_pcds_vnn_req_status_0_map, + nvl_pchs_vnn_req_status_1_map, + nvl_pchs_vnn_req_status_2_map, + nvl_pcdh_vnn_req_status_3_map, + nvl_pchs_vnn_misc_status_map, + ptl_pcdp_signal_status_map, + NULL +}; + +static const struct pmc_bit_map *nvl_pchs_blk_maps[] = { + nvl_pchs_power_gating_status_0_map, + nvl_pchs_power_gating_status_1_map, + nvl_pchs_power_gating_status_2_map, + nvl_pchs_rsc_status_map, + nvl_pchs_d3_status_0_map, + nvl_pchs_clocksource_status_map, + nvl_pchs_vnn_misc_status_map, + NULL +}; + +static const struct pmc_reg_map nvl_pcdh_reg_map = { + .pfear_sts = ext_nvl_pcdh_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = ptl_pcdp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCDH_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = NVL_PCDH_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = NVL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = NVL_LPM_PRI_OFFSET, + .lpm_en_offset = NVL_LPM_EN_OFFSET, + .lpm_residency_offset = NVL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pcdh_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = NVL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pcdh_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCDH_NUM_S0IX_BLOCKER, + .blocker_req_offset = NVL_PCDH_BLK_REQ_OFFSET, + .lpm_req_guid = PCDH_LPM_REQ_GUID, +}; + +static const struct pmc_reg_map nvl_pcds_reg_map = { + .pfear_sts = ext_nvl_pcds_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = nvl_pcds_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCDS_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = PTL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pcds_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pcds_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCDS_NUM_S0IX_BLOCKER, + .lpm_req_guid = PCDS_LPM_REQ_GUID, + .blocker_req_offset = NVL_PCDS_BLK_REQ_OFFSET, +}; + +static const struct pmc_reg_map nvl_pchs_reg_map = { + .pfear_sts = ext_nvl_pchs_pfear_map, + .slp_s0_offset = CNP_PMC_SLP_S0_RES_COUNTER_OFFSET, + .slp_s0_res_counter_step = TGL_PMC_SLP_S0_RES_COUNTER_STEP, + .ltr_show_sts = ptl_pcdp_ltr_show_map, + .msr_sts = msr_map, + .ltr_ignore_offset = CNP_PMC_LTR_IGNORE_OFFSET, + .regmap_length = NVL_PCHS_PMC_MMIO_REG_LEN, + .ppfear0_offset = CNP_PMC_HOST_PPFEAR0A, + .ppfear_buckets = LNL_PPFEAR_NUM_ENTRIES, + .pm_cfg_offset = CNP_PMC_PM_CFG_OFFSET, + .pm_read_disable_bit = CNP_PMC_READ_DISABLE_BIT, + .lpm_num_maps = PTL_LPM_NUM_MAPS, + .ltr_ignore_max = LNL_NUM_IP_IGN_ALLOWED, + .lpm_res_counter_step_x2 = TGL_PMC_LPM_RES_COUNTER_STEP_X2, + .etr3_offset = ETR3_OFFSET, + .lpm_sts_latch_en_offset = MTL_LPM_STATUS_LATCH_EN_OFFSET, + .lpm_priority_offset = MTL_LPM_PRI_OFFSET, + .lpm_en_offset = MTL_LPM_EN_OFFSET, + .lpm_residency_offset = MTL_LPM_RESIDENCY_OFFSET, + .lpm_sts = nvl_pchs_lpm_maps, + .lpm_status_offset = MTL_LPM_STATUS_OFFSET, + .lpm_live_status_offset = MTL_LPM_LIVE_STATUS_OFFSET, + .s0ix_blocker_maps = nvl_pchs_blk_maps, + .s0ix_blocker_offset = LNL_S0IX_BLOCKER_OFFSET, + .num_s0ix_blocker = NVL_PCHS_NUM_S0IX_BLOCKER, + .blocker_req_offset = NVL_PCHS_BLK_REQ_OFFSET, + .lpm_req_guid = PCHS_LPM_REQ_GUID, +}; + +static struct pmc_info nvl_pmc_info_list[] = { + { + .devid = PMC_DEVID_NVL_PCDH, + .map = &nvl_pcdh_reg_map, + }, + { + .devid = PMC_DEVID_NVL_PCDS, + .map = &nvl_pcds_reg_map, + }, + { + .devid = PMC_DEVID_NVL_PCHS, + .map = &nvl_pchs_reg_map, + }, + {} +}; + +static const char *nvl_ltr_block_counter_arr[] = { + "PKGC_PREVENT_LTR_IADOMAIN", + "PKGC_PREVENT_LTR_GDIE", + "PKGC_PREVENT_LTR_PCH", + "PKGC_PREVENT_LTR_DISPLAY", + "PKGC_PREVENT_LTR_IPU", + NULL +}; + +static const char *nvl_pkgc_blocker_residency[] = { + "PKGC_BLOCK_RESIDENCY_INVALID", + "PKGC_BLOCK_RESIDENCY_MISC", + "PKGC_BLOCK_RESIDENCY_CDIE_MISC", + "PKGC_BLOCK_RESIDENCY_MEDIA_MISC", + "PKGC_BLOCK_RESIDENCY_GT_MISC", + "PKGC_BLOCK_RESIDENCY_HUBATOM_MISC", + "PKGC_BLOCK_RESIDENCY_IPU_BUSY", + "PKGC_BLOCK_RESIDENCY_IPU_LTR", + "PKGC_BLOCK_RESIDENCY_IPU_TIMER", + "PKGC_BLOCK_RESIDENCY_DISP_BUSY", + "PKGC_BLOCK_RESIDENCY_DISP_LTR", + "PKGC_BLOCK_RESIDENCY_DISP_TIMER", + "PKGC_BLOCK_RESIDENCY_VPU_BUSY", + "PKGC_BLOCK_RESIDENCY_VPU_TIMER", + "PKGC_BLOCK_RESIDENCY_PMC_BUSY", + "PKGC_BLOCK_RESIDENCY_PMC_LTR", + "PKGC_BLOCK_RESIDENCY_PMC_TIMER", + "PKGC_BLOCK_RESIDENCY_HUBATOM_ARAT", + "PKGC_BLOCK_RESIDENCY_CDIE0_ARAT", + "PKGC_BLOCK_RESIDENCY_CDIE1_ARAT", + "PKGC_BLOCK_RESIDENCY_GT_ARAT", + "PKGC_BLOCK_RESIDENCY_MEDIA_ARAT", + "PKGC_BLOCK_RESIDENCY_DEMOTION", + "PKGC_BLOCK_RESIDENCY_THERMALS", + "PKGC_BLOCK_RESIDENCY_SNCU", + "PKGC_BLOCK_RESIDENCY_SVTU", + "PKGC_BLOCK_RESIDENCY_IAA", + "PKGC_BLOCK_RESIDENCY_IOC", + NULL, +}; + +static const u8 nvl_pmc_list[] = {PMC_IDX_MAIN, PMC_IDX_PCH}; + +#define NVL_NPU_PCI_DEV 0xd71d + +/* + * Set power state of select devices that do not have drivers to D3 + * so that they do not block Package C entry. + */ +static void nvl_d3_fixup(void) +{ + pmc_core_set_device_d3(NVL_NPU_PCI_DEV); +} + +static int nvl_resume(struct pmc_dev *pmcdev) +{ + nvl_d3_fixup(); + return cnl_resume(pmcdev); +} + +static int nvl_core_init(struct pmc_dev *pmcdev, struct pmc_dev_info *pmc_dev_info) +{ + nvl_d3_fixup(); + return generic_core_init(pmcdev, pmc_dev_info); +} + +static u32 nvl_pmt_dmu_guids[] = {NVL_PMT_DMU_GUID, 0x0}; +struct pmc_dev_info nvl_s_pmc_dev = { + .num_pmcs = ARRAY_SIZE(nvl_pmc_list), + .pmc_list = nvl_pmc_list, + .regmap_list = nvl_pmc_info_list, + .map = &nvl_pcds_reg_map, + .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = nvl_resume, + .init = nvl_core_init, + .sub_req = pmc_core_pmt_get_blk_sub_req, + .dmu_guids = nvl_pmt_dmu_guids, + .pc_guid = NVL_PMT_PC_GUID, + .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET, + .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr, + .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET, + .pkgc_blocker_counters = nvl_pkgc_blocker_residency, + .ssram_hidden = false, + .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET, +}; + +struct pmc_dev_info nvl_h_pmc_dev = { + .num_pmcs = ARRAY_SIZE(nvl_pmc_list), + .pmc_list = nvl_pmc_list, + .regmap_list = nvl_pmc_info_list, + .map = &nvl_pcdh_reg_map, + .sub_req_show = &pmc_core_substate_blk_req_fops, + .suspend = cnl_suspend, + .resume = nvl_resume, + .init = nvl_core_init, + .sub_req = pmc_core_pmt_get_blk_sub_req, + .dmu_guids = nvl_pmt_dmu_guids, + .pc_guid = NVL_PMT_PC_GUID, + .pkgc_ltr_blocker_offset = NVL_LTR_BLK_OFFSET, + .pkgc_ltr_blocker_counters = nvl_ltr_block_counter_arr, + .pkgc_blocker_offset = NVL_PKGC_BLK_OFFSET, + .pkgc_blocker_counters = nvl_pkgc_blocker_residency, + .ssram_hidden = false, + .die_c6_offset = NVL_PMT_DMU_DIE_C6_OFFSET, +}; diff --git a/drivers/platform/x86/intel/pmc/ptl.c b/drivers/platform/x86/intel/pmc/ptl.c index 90fcd7900d9f..a7d5d4f77934 100644 --- a/drivers/platform/x86/intel/pmc/ptl.c +++ b/drivers/platform/x86/intel/pmc/ptl.c @@ -137,7 +137,7 @@ static const struct pmc_bit_map *ext_ptl_pcdp_pfear_map[] = { NULL }; -static const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { +const struct pmc_bit_map ptl_pcdp_ltr_show_map[] = { {"SOUTHPORT_A", CNP_PMC_LTR_SPA}, {"SOUTHPORT_B", CNP_PMC_LTR_SPB}, {"SATA", CNP_PMC_LTR_SATA}, From cc966553e6ff0849978b5754531b768b0ff54985 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:00:27 +0200 Subject: [PATCH 019/115] x86/platform/olpc: xo15: Drop wakeup source on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent leaking a wakeup source object after removing the driver by adding appropriate cleanup code to its remove callback function. Fixes: a0f30f592d2d ("x86, olpc: Add XO-1.5 SCI driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2069931.usQuhbGJ8B@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/platform/olpc/olpc-xo15-sci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 82c51b6ec528..276caf756a9c 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -186,6 +186,7 @@ static int xo15_sci_add(struct acpi_device *device) static void xo15_sci_remove(struct acpi_device *device) { + device_init_wakeup(&device->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); From 75c7d3d76b78b568969316224c8ae25c0224cc9a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 20:01:41 +0200 Subject: [PATCH 020/115] x86/platform/olpc: xo15: Convert ACPI driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the olpc-xo15-sci ACPI driver to a platform one. After this change, the wakeup source added by the driver will appear under the platform device used for driver binding, but the sysfs attribute added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting it to be there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1970421.CQOukoFCf9@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- arch/x86/platform/olpc/olpc-xo15-sci.c | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index 276caf756a9c..e486109e88c8 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -136,14 +137,16 @@ static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; } -static int xo15_sci_add(struct acpi_device *device) +static int xo15_sci_probe(struct platform_device *pdev) { + struct acpi_device *device; unsigned long long tmp; acpi_status status; int r; + device = ACPI_COMPANION(&pdev->dev); if (!device) - return -EINVAL; + return -ENODEV; strscpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME); strscpy(acpi_device_class(device), XO15_SCI_CLASS); @@ -160,7 +163,7 @@ static int xo15_sci_add(struct acpi_device *device) if (ACPI_FAILURE(status)) return -ENODEV; - dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); + dev_info(&pdev->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); r = sysfs_create_file(&device->dev.kobj, &lid_wake_on_close_attr.attr); if (r) @@ -174,7 +177,7 @@ static int xo15_sci_add(struct acpi_device *device) /* Enable wake-on-EC */ if (device->wakeup.flags.valid) - device_init_wakeup(&device->dev, true); + device_init_wakeup(&pdev->dev, true); return 0; @@ -184,9 +187,11 @@ static int xo15_sci_add(struct acpi_device *device) return r; } -static void xo15_sci_remove(struct acpi_device *device) +static void xo15_sci_remove(struct platform_device *pdev) { - device_init_wakeup(&device->dev, false); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + + device_init_wakeup(&pdev->dev, false); acpi_disable_gpe(NULL, xo15_sci_gpe); acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); cancel_work_sync(&sci_work); @@ -214,19 +219,18 @@ static const struct acpi_device_id xo15_sci_device_ids[] = { {"", 0}, }; -static struct acpi_driver xo15_sci_drv = { - .name = DRV_NAME, - .class = XO15_SCI_CLASS, - .ids = xo15_sci_device_ids, - .ops = { - .add = xo15_sci_add, - .remove = xo15_sci_remove, +static struct platform_driver xo15_sci_drv = { + .probe = xo15_sci_probe, + .remove = xo15_sci_remove, + .driver = { + .name = DRV_NAME, + .acpi_match_table = xo15_sci_device_ids, + .pm = &xo15_sci_pm, }, - .drv.pm = &xo15_sci_pm, }; static int __init xo15_sci_init(void) { - return acpi_bus_register_driver(&xo15_sci_drv); + return platform_driver_register(&xo15_sci_drv); } device_initcall(xo15_sci_init); From 5c44f48e91deefdd42e567a2779d331937c97cd0 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Mon, 11 May 2026 18:13:20 +0530 Subject: [PATCH 021/115] platform: arm64: Add driver for EC found on Qualcomm reference devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Embedded controller driver support for Hamoa/Purwa/Glymur qualcomm reference boards. It handles fan control, temperature sensors, access to EC state changes and supports reporting suspend entry/exit to the EC. Co-developed-by: Maya Matuszczyk Signed-off-by: Maya Matuszczyk Signed-off-by: Sibi Sankar Reviewed-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Tested-by: Akhil P Oommen Co-developed-by: Anvesh Jain P Signed-off-by: Anvesh Jain P Link: https://patch.msgid.link/20260511-add-driver-for-ec-v9-2-e5437c39b7f8@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- MAINTAINERS | 8 + drivers/platform/arm64/Kconfig | 13 + drivers/platform/arm64/Makefile | 1 + drivers/platform/arm64/qcom-hamoa-ec.c | 451 +++++++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 drivers/platform/arm64/qcom-hamoa-ec.c diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..839e39825455 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21896,6 +21896,14 @@ F: Documentation/devicetree/bindings/misc/qcom,fastrpc.yaml F: drivers/misc/fastrpc.c F: include/uapi/misc/fastrpc.h +QUALCOMM HAMOA EMBEDDED CONTROLLER DRIVER +M: Anvesh Jain P +M: Sibi Sankar +L: linux-arm-msm@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/embedded-controller/qcom,hamoa-crd-ec.yaml +F: drivers/platform/arm64/qcom-hamoa-ec.c + QUALCOMM HEXAGON ARCHITECTURE M: Brian Cain L: linux-hexagon@vger.kernel.org diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig index 10f905d7d6bf..e32e01b2a9bd 100644 --- a/drivers/platform/arm64/Kconfig +++ b/drivers/platform/arm64/Kconfig @@ -90,4 +90,17 @@ config EC_LENOVO_THINKPAD_T14S Say M or Y here to include this support. +config EC_QCOM_HAMOA + tristate "Embedded Controller driver for Qualcomm Hamoa/Glymur reference devices" + depends on ARCH_QCOM || COMPILE_TEST + depends on I2C + depends on THERMAL || THERMAL=n + help + Say M or Y here to enable the Embedded Controller driver for Qualcomm + Snapdragon-based Hamoa/Glymur reference devices. The driver handles fan + control, temperature sensors, access to EC state changes and supports + reporting suspend entry/exit to the EC. + + This driver currently supports Hamoa/Purwa/Glymur reference devices. + endif # ARM64_PLATFORM_DEVICES diff --git a/drivers/platform/arm64/Makefile b/drivers/platform/arm64/Makefile index 60c131cff6a1..7681be4a46e9 100644 --- a/drivers/platform/arm64/Makefile +++ b/drivers/platform/arm64/Makefile @@ -9,3 +9,4 @@ obj-$(CONFIG_EC_ACER_ASPIRE1) += acer-aspire1-ec.o obj-$(CONFIG_EC_HUAWEI_GAOKUN) += huawei-gaokun-ec.o obj-$(CONFIG_EC_LENOVO_YOGA_C630) += lenovo-yoga-c630.o obj-$(CONFIG_EC_LENOVO_THINKPAD_T14S) += lenovo-thinkpad-t14s.o +obj-$(CONFIG_EC_QCOM_HAMOA) += qcom-hamoa-ec.o diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c new file mode 100644 index 000000000000..a018f7bf35d2 --- /dev/null +++ b/drivers/platform/arm64/qcom-hamoa-ec.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2024 Maya Matuszczyk + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EC_SCI_EVT_READ_CMD 0x05 +#define EC_FW_VERSION_CMD 0x0e +#define EC_MODERN_STANDBY_CMD 0x23 +#define EC_FAN_DBG_CONTROL_CMD 0x30 +#define EC_SCI_EVT_CONTROL_CMD 0x35 +#define EC_THERMAL_CAP_CMD 0x42 + +#define EC_FW_VERSION_RESP_LEN 4 +#define EC_THERMAL_CAP_RESP_LEN 3 +#define EC_FAN_DEBUG_CMD_LEN 6 +#define EC_FAN_SPEED_DATA_SIZE 4 + +#define EC_MODERN_STANDBY_ENTER 0x01 +#define EC_MODERN_STANDBY_EXIT 0x00 + +#define EC_FAN_DEBUG_MODE_OFF 0 +#define EC_FAN_DEBUG_MODE_ON BIT(0) +#define EC_FAN_ON BIT(1) +#define EC_FAN_DEBUG_TYPE_PWM BIT(2) +#define EC_MAX_FAN_CNT 2 +#define EC_FAN_NAME_SIZE 20 +#define EC_FAN_MAX_PWM 255 + +enum qcom_ec_sci_events { + EC_FAN1_STATUS_CHANGE_EVT = 0x30, + EC_FAN2_STATUS_CHANGE_EVT, + EC_FAN1_SPEED_CHANGE_EVT, + EC_FAN2_SPEED_CHANGE_EVT, + EC_NEW_LUT_SET_EVT, + EC_FAN_PROFILE_SWITCH_EVT, + EC_THERMISTOR_1_THRESHOLD_CROSS_EVT, + EC_THERMISTOR_2_THRESHOLD_CROSS_EVT, + EC_THERMISTOR_3_THRESHOLD_CROSS_EVT, + /* Reserved: 0x39 - 0x3c/0x3f */ + EC_RECOVERED_FROM_RESET_EVT = 0x3d, +}; + +struct qcom_ec_version { + u8 main_version; + u8 sub_version; + u8 test_version; +}; + +struct qcom_ec_thermal_cap { +#define EC_THERMAL_FAN_CNT(x) (FIELD_GET(GENMASK(1, 0), (x))) +#define EC_THERMAL_FAN_TYPE(x) (FIELD_GET(GENMASK(4, 2), (x))) +#define EC_THERMAL_THERMISTOR_MASK(x) (FIELD_GET(GENMASK(7, 0), (x))) + u8 fan_cnt; + u8 fan_type; + u8 thermistor_mask; +}; + +struct qcom_ec_cooling_dev { + struct thermal_cooling_device *cdev; + struct device *parent_dev; + u8 fan_id; + u8 state; +}; + +struct qcom_ec { + struct qcom_ec_cooling_dev *ec_cdev; + struct qcom_ec_thermal_cap thermal_cap; + struct qcom_ec_version version; + struct i2c_client *client; +}; + +static int qcom_ec_read(struct qcom_ec *ec, u8 cmd, u8 resp_len, u8 *resp) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(ec->client, cmd, resp_len, resp); + if (ret < 0) + return ret; + else if (ret == 0 || ret == 0xff) + return -EOPNOTSUPP; + + if (resp[0] >= resp_len) + return -EINVAL; + + return 0; +} + +/* + * EC Device Firmware Version: + * + * Read Response: + * ---------------------------------------------------------------------- + * | Offset | Name | Description | + * ---------------------------------------------------------------------- + * | 0x00 | Byte count | Number of bytes in response | + * | | | (excluding byte count) | + * ---------------------------------------------------------------------- + * | 0x01 | Test-version | Test-version of EC firmware | + * ---------------------------------------------------------------------- + * | 0x02 | Sub-version | Sub-version of EC firmware | + * ---------------------------------------------------------------------- + * | 0x03 | Main-version | Main-version of EC firmware | + * ---------------------------------------------------------------------- + * + */ +static int qcom_ec_read_fw_version(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qcom_ec *ec = i2c_get_clientdata(client); + struct qcom_ec_version *version = &ec->version; + u8 resp[EC_FW_VERSION_RESP_LEN]; + int ret; + + ret = qcom_ec_read(ec, EC_FW_VERSION_CMD, EC_FW_VERSION_RESP_LEN, resp); + if (ret < 0) + return ret; + + version->main_version = resp[3]; + version->sub_version = resp[2]; + version->test_version = resp[1]; + + dev_dbg(dev, "EC Version %d.%d.%d\n", + version->main_version, version->sub_version, version->test_version); + + return 0; +} + +/* + * EC Device Thermal Capabilities: + * + * Read Response: + * ------------------------------------------------------------------------------ + * | Offset | Name | Description | + * ------------------------------------------------------------------------------ + * | 0x00 | Byte count | Number of bytes in response | + * | | | (excluding byte count) | + * ------------------------------------------------------------------------------ + * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | + * | 0x03 | Capabilities | Bit 2-4: Type of fan | + * | | | Bit 5-6: Reserved | + * | | | Bit 7: Data Valid/Invalid | + * | | | (Valid - 1, Invalid - 0) | + * | | | Bit 8-15: Thermistor 0 - 7 presence | + * | | | (1 present, 0 absent) | + * ------------------------------------------------------------------------------ + * + */ +static int qcom_ec_thermal_capabilities(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct qcom_ec *ec = i2c_get_clientdata(client); + struct qcom_ec_thermal_cap *cap = &ec->thermal_cap; + u8 resp[EC_THERMAL_CAP_RESP_LEN]; + int ret; + + ret = qcom_ec_read(ec, EC_THERMAL_CAP_CMD, EC_THERMAL_CAP_RESP_LEN, resp); + if (ret < 0) + return ret; + + cap->fan_cnt = min(EC_MAX_FAN_CNT, EC_THERMAL_FAN_CNT(resp[1])); + cap->fan_type = EC_THERMAL_FAN_TYPE(resp[1]); + cap->thermistor_mask = EC_THERMAL_THERMISTOR_MASK(resp[2]); + + dev_dbg(dev, "Fan count: %d Fan Type: %d Thermistor Mask: %x\n", + cap->fan_cnt, cap->fan_type, cap->thermistor_mask); + + return 0; +} + +static irqreturn_t qcom_ec_irq(int irq, void *data) +{ + struct qcom_ec *ec = data; + struct device *dev = &ec->client->dev; + int val; + + val = i2c_smbus_read_byte_data(ec->client, EC_SCI_EVT_READ_CMD); + if (val < 0) { + dev_err_ratelimited(dev, "Failed to read EC SCI Event: %d\n", val); + return IRQ_HANDLED; + } + + switch (val) { + case EC_FAN1_STATUS_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan1 status changed\n"); + break; + case EC_FAN2_STATUS_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan2 status changed\n"); + break; + case EC_FAN1_SPEED_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan1 speed crossed low/high trip point\n"); + break; + case EC_FAN2_SPEED_CHANGE_EVT: + dev_dbg_ratelimited(dev, "Fan2 speed crossed low/high trip point\n"); + break; + case EC_NEW_LUT_SET_EVT: + dev_dbg_ratelimited(dev, "New LUT set\n"); + break; + case EC_FAN_PROFILE_SWITCH_EVT: + dev_dbg_ratelimited(dev, "FAN Profile switched\n"); + break; + case EC_THERMISTOR_1_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 1 threshold crossed\n"); + break; + case EC_THERMISTOR_2_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 2 threshold crossed\n"); + break; + case EC_THERMISTOR_3_THRESHOLD_CROSS_EVT: + dev_dbg_ratelimited(dev, "Thermistor 3 threshold crossed\n"); + break; + case EC_RECOVERED_FROM_RESET_EVT: + dev_dbg_ratelimited(dev, "EC recovered from reset\n"); + break; + default: + dev_notice_ratelimited(dev, "Unknown EC event: %d\n", val); + break; + } + + return IRQ_HANDLED; +} + +static int qcom_ec_sci_evt_control(struct device *dev, bool enable) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_SCI_EVT_CONTROL_CMD, enable ? 1 : 0); +} + +static int qcom_ec_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + *state = EC_FAN_MAX_PWM; + + return 0; +} + +static int qcom_ec_fan_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state) +{ + struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata; + + *state = ec_cdev->state; + + return 0; +} + +/* + * Fan Debug control command: + * + * Command Payload: + * -------------------------------------------------------------------------------------- + * | Offset | Name | Description | + * -------------------------------------------------------------------------------------- + * | 0x00 | Command | Fan control command | + * -------------------------------------------------------------------------------------- + * | 0x01 | Fan ID | 0x1 : Fan 1 | + * | | | 0x2 : Fan 2 | + * -------------------------------------------------------------------------------------- + * | 0x02 | Byte count = 4| Size of data to set fan speed | + * -------------------------------------------------------------------------------------- + * | 0x03 | Mode | Bit 0: Debug Mode On/Off (0 - OFF, 1 - ON ) | + * | | | Bit 1: Fan On/Off (0 - Off, 1 - ON) | + * | | | Bit 2: Debug Type (0 - RPM, 1 - PWM) | + * -------------------------------------------------------------------------------------- + * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | + * | 0x05 | | | + * -------------------------------------------------------------------------------------- + * | 0x06 | Speed in PWM | PWM value, if mode selected is PWM (0 - 255) | + * ______________________________________________________________________________________ + * + */ +static int qcom_ec_fan_debug_mode_off(struct qcom_ec_cooling_dev *ec_cdev) +{ + struct device *dev = ec_cdev->parent_dev; + struct i2c_client *client = to_i2c_client(dev); + u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE, + EC_FAN_DEBUG_MODE_OFF, 0, 0, 0 }; + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD, + sizeof(request), request); + if (ret) { + dev_err(dev, "Failed to turn off fan%d debug mode: %d\n", + ec_cdev->fan_id, ret); + } + + return ret; +} + +static int qcom_ec_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct qcom_ec_cooling_dev *ec_cdev = cdev->devdata; + struct device *dev = ec_cdev->parent_dev; + struct i2c_client *client = to_i2c_client(dev); + u8 request[6] = { ec_cdev->fan_id, EC_FAN_SPEED_DATA_SIZE, + EC_FAN_DEBUG_MODE_ON | EC_FAN_ON | EC_FAN_DEBUG_TYPE_PWM, + 0, 0, state }; + int ret; + + ret = i2c_smbus_write_i2c_block_data(client, EC_FAN_DBG_CONTROL_CMD, + sizeof(request), request); + if (ret) { + dev_err(dev, "Failed to set fan pwm: %d\n", ret); + return ret; + } + + ec_cdev->state = state; + + return 0; +} + +static const struct thermal_cooling_device_ops qcom_ec_thermal_ops = { + .get_max_state = qcom_ec_fan_get_max_state, + .get_cur_state = qcom_ec_fan_get_cur_state, + .set_cur_state = qcom_ec_fan_set_cur_state, +}; + +static int qcom_ec_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD, + EC_MODERN_STANDBY_EXIT); +} + +static int qcom_ec_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, EC_MODERN_STANDBY_CMD, + EC_MODERN_STANDBY_ENTER); +} + +static int qcom_ec_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct qcom_ec *ec; + unsigned int i; + int ret; + + ec = devm_kzalloc(dev, sizeof(*ec), GFP_KERNEL); + if (!ec) + return -ENOMEM; + + ec->client = client; + + ret = devm_request_threaded_irq(dev, client->irq, NULL, qcom_ec_irq, + IRQF_ONESHOT, "qcom_ec", ec); + if (ret < 0) + return ret; + + i2c_set_clientdata(client, ec); + + ret = qcom_ec_read_fw_version(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read EC firmware version\n"); + + ret = qcom_ec_sci_evt_control(dev, true); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to enable SCI events\n"); + + ret = qcom_ec_thermal_capabilities(dev); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to read thermal capabilities\n"); + + if (ec->thermal_cap.fan_cnt == 0) { + dev_warn(dev, FW_BUG "Failed to get fan count, firmware update required\n"); + return 0; + } + + ec->ec_cdev = devm_kcalloc(dev, ec->thermal_cap.fan_cnt, sizeof(*ec->ec_cdev), GFP_KERNEL); + if (!ec->ec_cdev) + return -ENOMEM; + + for (i = 0; i < ec->thermal_cap.fan_cnt; i++) { + struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i]; + char name[EC_FAN_NAME_SIZE]; + + scnprintf(name, sizeof(name), "qcom_ec_fan_%u", i); + ec_cdev->fan_id = i + 1; + ec_cdev->parent_dev = dev; + + ec_cdev->cdev = devm_thermal_of_cooling_device_register(dev, NULL, name, ec_cdev, + &qcom_ec_thermal_ops); + if (IS_ERR(ec_cdev->cdev)) { + return dev_err_probe(dev, PTR_ERR(ec_cdev->cdev), + "Failed to register fan%d cooling device\n", i); + } + } + + return 0; +} + +static void qcom_ec_remove(struct i2c_client *client) +{ + struct qcom_ec *ec = i2c_get_clientdata(client); + struct device *dev = &client->dev; + int ret; + + ret = qcom_ec_sci_evt_control(dev, false); + if (ret < 0) + dev_err(dev, "Failed to disable SCI events: %d\n", ret); + + for (int i = 0; i < ec->thermal_cap.fan_cnt; i++) { + struct qcom_ec_cooling_dev *ec_cdev = &ec->ec_cdev[i]; + + qcom_ec_fan_debug_mode_off(ec_cdev); + } +} + +static const struct of_device_id qcom_ec_of_match[] = { + { .compatible = "qcom,hamoa-crd-ec" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_ec_of_match); + +static const struct i2c_device_id qcom_ec_i2c_id_table[] = { + { "qcom-hamoa-ec", }, + {} +}; +MODULE_DEVICE_TABLE(i2c, qcom_ec_i2c_id_table); + +static DEFINE_SIMPLE_DEV_PM_OPS(qcom_ec_pm_ops, + qcom_ec_suspend, + qcom_ec_resume); + +static struct i2c_driver qcom_ec_i2c_driver = { + .driver = { + .name = "qcom-hamoa-ec", + .of_match_table = qcom_ec_of_match, + .pm = &qcom_ec_pm_ops, + }, + .probe = qcom_ec_probe, + .remove = qcom_ec_remove, + .id_table = qcom_ec_i2c_id_table, +}; +module_i2c_driver(qcom_ec_i2c_driver); + +MODULE_DESCRIPTION("QCOM Hamoa Embedded Controller"); +MODULE_LICENSE("GPL"); From 3b95f36464ec161bcf14c5d1e5f9d5d5e9464582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Thu, 7 May 2026 17:50:25 +0200 Subject: [PATCH 022/115] platform/x86: pmc_atom: Use named initializer for pci_device_id array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being more verbose using a named initializer yields easier to understand code and doesn't rely on the two hidden zeros in the PCI_VDEVICE macro. This doesn't introduce any changes to the compiled result of the array, which was confirmed with an ARCH=x86 build. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260507155025.3186956-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pmc_atom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index 48c2a0e59d18..046933f6d1b6 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -570,8 +570,8 @@ static int pmc_setup_dev(struct pci_dev *pdev, const struct pci_device_id *ent) /* Data for PCI driver interface used by pci_match_id() call below */ static const struct pci_device_id pmc_pci_ids[] = { - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), (kernel_ulong_t)&byt_data }, - { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), (kernel_ulong_t)&cht_data }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_VLV_PMC), .driver_data = (kernel_ulong_t)&byt_data }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_CHT_PMC), .driver_data = (kernel_ulong_t)&cht_data }, {} }; From b2fc2c6ebbd2d49935c8960755d8170faead2159 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:40:31 +0200 Subject: [PATCH 023/115] platform/x86: xo15-ebook: Fix wakeup source and GPE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device_set_wakeup_enable() call in ebook_switch_add() doesn't actually do anything because power.can_wakeup is not set for ACPI device objects. Moreover, had it done anything, it would have registered a wakeup source object that wouldn't have been used going forward and that wakeup source would have been leaked after driver removal because ebook_switch_remove() doesn't clean it up. Accordingly, remove that call from ebook_switch_add(). Also prevent leaking an enabled ACPI GPE after removing the driver by adding appropriate cleanup code to ebook_switch_remove(). Fixes: 89ca11771a4b ("OLPC XO-1.5 ebook switch driver") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1966125.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 4d1b1b310cc5..1568169b7872 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -38,6 +38,7 @@ MODULE_DEVICE_TABLE(acpi, ebook_device_ids); struct ebook_switch { struct input_dev *input; char phys[32]; /* for input device */ + bool gpe_enabled; }; static int ebook_send_state(struct acpi_device *device) @@ -128,7 +129,7 @@ static int ebook_switch_add(struct acpi_device *device) /* Button's GPE is run-wake GPE */ acpi_enable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); - device_set_wakeup_enable(&device->dev, true); + button->gpe_enabled = true; } return 0; @@ -144,6 +145,10 @@ static void ebook_switch_remove(struct acpi_device *device) { struct ebook_switch *button = acpi_driver_data(device); + if (button->gpe_enabled) + acpi_disable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number); + input_unregister_device(button->input); kfree(button); } From 826264e0b02dc856979bdd230d96969e93fe41ed Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:41:47 +0200 Subject: [PATCH 024/115] platform/x86: xo15-ebook: Fix formatting of labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix formatting of two labels in ebook_switch_add() to make that function follow the current kernel coding style more closely. No functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/14016199.uLZWGnKmhe@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 1568169b7872..616f4bb3461a 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -134,9 +134,9 @@ static int ebook_switch_add(struct acpi_device *device) return 0; - err_free_input: +err_free_input: input_free_device(input); - err_free_button: +err_free_button: kfree(button); return error; } From 60e68011d8ecdb3071cc7713e05750e6b08aec12 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:43:13 +0200 Subject: [PATCH 025/115] platform/x86: xo15-ebook: Register ACPI notify handler directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to a platform one, make it install an ACPI notify handler directly instead of using a .notify() callback in struct acpi_driver. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3420768.aeNJFYEL58@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 616f4bb3461a..8af1b9078db8 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -57,16 +57,15 @@ static int ebook_send_state(struct acpi_device *device) return 0; } -static void ebook_switch_notify(struct acpi_device *device, u32 event) +static void ebook_switch_notify(acpi_handle handle, u32 event, void *data) { switch (event) { case ACPI_FIXED_HARDWARE_EVENT: case XO15_EBOOK_NOTIFY_STATUS: - ebook_send_state(device); + ebook_send_state(data); break; default: - acpi_handle_debug(device->handle, - "Unsupported event [0x%x]\n", event); + acpi_handle_debug(handle, "Unsupported event [0x%x]\n", event); break; } } @@ -123,6 +122,11 @@ static int ebook_switch_add(struct acpi_device *device) if (error) goto err_free_input; + error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + ebook_switch_notify, device); + if (error) + goto err_unregister_input; + ebook_send_state(device); if (device->wakeup.flags.valid) { @@ -139,6 +143,10 @@ static int ebook_switch_add(struct acpi_device *device) err_free_button: kfree(button); return error; + +err_unregister_input: + input_unregister_device(input); + goto err_free_button; } static void ebook_switch_remove(struct acpi_device *device) @@ -149,6 +157,8 @@ static void ebook_switch_remove(struct acpi_device *device) acpi_disable_gpe(device->wakeup.gpe_device, device->wakeup.gpe_number); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + ebook_switch_notify); input_unregister_device(button->input); kfree(button); } @@ -160,7 +170,6 @@ static struct acpi_driver xo15_ebook_driver = { .ops = { .add = ebook_switch_add, .remove = ebook_switch_remove, - .notify = ebook_switch_notify, }, .drv.pm = &ebook_switch_pm, }; From b82c2e30cf8edca7052d020f278b461a0ef657c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 8 May 2026 19:44:58 +0200 Subject: [PATCH 026/115] platform/x86: xo15-ebook: Convert ACPI driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the OLPC XO-1.5 ebook switch driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8688586.T7Z3S40VBb@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 44 ++++++++++++++++--------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 8af1b9078db8..2ca15f04b2d9 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -15,6 +15,7 @@ #include #include #include +#include #define MODULE_NAME "xo15-ebook" @@ -41,13 +42,13 @@ struct ebook_switch { bool gpe_enabled; }; -static int ebook_send_state(struct acpi_device *device) +static int ebook_send_state(struct device *dev) { - struct ebook_switch *button = acpi_driver_data(device); + struct ebook_switch *button = dev_get_drvdata(dev); unsigned long long state; acpi_status status; - status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state); + status = acpi_evaluate_integer(ACPI_HANDLE(dev), "EBK", NULL, &state); if (ACPI_FAILURE(status)) return -EIO; @@ -73,14 +74,15 @@ static void ebook_switch_notify(acpi_handle handle, u32 event, void *data) #ifdef CONFIG_PM_SLEEP static int ebook_switch_resume(struct device *dev) { - return ebook_send_state(to_acpi_device(dev)); + return ebook_send_state(dev); } #endif static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); -static int ebook_switch_add(struct acpi_device *device) +static int ebook_switch_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); const struct acpi_device_id *id; struct ebook_switch *button; struct input_dev *input; @@ -90,7 +92,7 @@ static int ebook_switch_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); button->input = input = input_allocate_device(); if (!input) { @@ -100,7 +102,7 @@ static int ebook_switch_add(struct acpi_device *device) id = acpi_match_acpi_device(ebook_device_ids, device); if (!id) { - dev_err(&device->dev, "Unsupported hid\n"); + dev_err(&pdev->dev, "Unsupported hid\n"); error = -ENODEV; goto err_free_input; } @@ -113,7 +115,7 @@ static int ebook_switch_add(struct acpi_device *device) input->name = acpi_device_name(device); input->phys = button->phys; input->id.bustype = BUS_HOST; - input->dev.parent = &device->dev; + input->dev.parent = &pdev->dev; input->evbit[0] = BIT_MASK(EV_SW); set_bit(SW_TABLET_MODE, input->swbit); @@ -123,11 +125,11 @@ static int ebook_switch_add(struct acpi_device *device) goto err_free_input; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - ebook_switch_notify, device); + ebook_switch_notify, &pdev->dev); if (error) goto err_unregister_input; - ebook_send_state(device); + ebook_send_state(&pdev->dev); if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ @@ -149,9 +151,10 @@ static int ebook_switch_add(struct acpi_device *device) goto err_free_button; } -static void ebook_switch_remove(struct acpi_device *device) +static void ebook_switch_remove(struct platform_device *pdev) { - struct ebook_switch *button = acpi_driver_data(device); + struct ebook_switch *button = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); if (button->gpe_enabled) acpi_disable_gpe(device->wakeup.gpe_device, @@ -163,14 +166,13 @@ static void ebook_switch_remove(struct acpi_device *device) kfree(button); } -static struct acpi_driver xo15_ebook_driver = { - .name = MODULE_NAME, - .class = XO15_EBOOK_CLASS, - .ids = ebook_device_ids, - .ops = { - .add = ebook_switch_add, - .remove = ebook_switch_remove, +static struct platform_driver xo15_ebook_driver = { + .probe = ebook_switch_probe, + .remove = ebook_switch_remove, + .driver = { + .name = MODULE_NAME, + .acpi_match_table = ebook_device_ids, + .pm = &ebook_switch_pm, }, - .drv.pm = &ebook_switch_pm, }; -module_acpi_driver(xo15_ebook_driver); +module_platform_driver(xo15_ebook_driver); From 18bc6ce6bb618e1dff4473d7dd528d22519abbd7 Mon Sep 17 00:00:00 2001 From: Brodie Abrew Date: Wed, 6 May 2026 17:49:16 -0700 Subject: [PATCH 027/115] platform/x86: sel3350-platform: Retain LED state on load and unload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the platform driver is loaded or unloaded, it overwrites the existing LED states. This can cause a loss of early boot state when the driver loads, and it can cause the ALARM contact to change state or flicker. Explicitly retain the existing LED state to prevent overwriting on driver load and unload. Tested-By: Robert Joslyn Reviewed-by: Robert Joslyn Signed-off-by: Brodie Abrew Link: https://patch.msgid.link/20260507004916.6710-1-brodie_abrew@selinc.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sel3350-platform.c | 136 ++++++++++++++++++------ 1 file changed, 103 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/sel3350-platform.c b/drivers/platform/x86/sel3350-platform.c index 02e2081e2333..f3a314235632 100644 --- a/drivers/platform/x86/sel3350-platform.c +++ b/drivers/platform/x86/sel3350-platform.c @@ -9,6 +9,8 @@ */ #include +#include +#include #include #include #include @@ -30,19 +32,82 @@ #define SEL_PS_B_DETECT "sel_ps_b_detect" #define SEL_PS_B_GOOD "sel_ps_b_good" +#define AUX_LED_GRN1 "sel_aux_led_grn1" +#define AUX_LED_GRN2 "sel_aux_led_grn2" +#define AUX_LED_GRN3 "sel_aux_led_grn3" +#define AUX_LED_GRN4 "sel_aux_led_grn4" +#define ALARM_STATE_USER "sel_alarm_state_user" +#define ENABLE_STATE_USER "sel_enable_state_user" +#define AUX_LED_RED1 "sel_aux_led_red1" +#define AUX_LED_RED2 "sel_aux_led_red2" +#define AUX_LED_RED3 "sel_aux_led_red3" +#define AUX_LED_RED4 "sel_aux_led_red4" + +static const char *const sel3350_leds_gpio_names[] = { + AUX_LED_GRN1, + AUX_LED_GRN2, + AUX_LED_GRN3, + AUX_LED_GRN4, + ALARM_STATE_USER, + ENABLE_STATE_USER, + AUX_LED_RED1, + AUX_LED_RED2, + AUX_LED_RED3, + AUX_LED_RED4, +}; + /* LEDs */ -static const struct gpio_led sel3350_leds[] = { - { .name = "sel:green:aux1" }, - { .name = "sel:green:aux2" }, - { .name = "sel:green:aux3" }, - { .name = "sel:green:aux4" }, - { .name = "sel:red:alarm" }, +static struct gpio_led sel3350_leds[] = { + { .name = "sel:green:aux1", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux2", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux3", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:green:aux4", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:alarm", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, { .name = "sel:green:enabled", - .default_state = LEDS_GPIO_DEFSTATE_ON }, - { .name = "sel:red:aux1" }, - { .name = "sel:red:aux2" }, - { .name = "sel:red:aux3" }, - { .name = "sel:red:aux4" }, + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux1", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux2", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux3", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, + { .name = "sel:red:aux4", + .default_state = LEDS_GPIO_DEFSTATE_KEEP, + .retain_state_suspended = 1, + .retain_state_shutdown = 1, + }, }; static const struct gpio_led_platform_data sel3350_leds_pdata = { @@ -50,25 +115,6 @@ static const struct gpio_led_platform_data sel3350_leds_pdata = { .leds = sel3350_leds, }; -/* Map GPIOs to LEDs */ -static struct gpiod_lookup_table sel3350_leds_table = { - .dev_id = "leds-gpio", - .table = { - GPIO_LOOKUP_IDX(BXT_NW, 49, NULL, 0, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 50, NULL, 1, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 51, NULL, 2, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_NW, 52, NULL, 3, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_W, 20, NULL, 4, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_W, 21, NULL, 5, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 37, NULL, 6, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 38, NULL, 7, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 39, NULL, 8, GPIO_ACTIVE_HIGH), - GPIO_LOOKUP_IDX(BXT_SW, 40, NULL, 9, GPIO_ACTIVE_HIGH), - {}, - } -}; - -/* Map GPIOs to power supplies */ static struct gpiod_lookup_table sel3350_gpios_table = { .dev_id = B2093_GPIO_ACPI_ID ":00", .table = { @@ -76,6 +122,16 @@ static struct gpiod_lookup_table sel3350_gpios_table = { GPIO_LOOKUP(BXT_NW, 45, SEL_PS_A_GOOD, GPIO_ACTIVE_LOW), GPIO_LOOKUP(BXT_NW, 46, SEL_PS_B_DETECT, GPIO_ACTIVE_LOW), GPIO_LOOKUP(BXT_NW, 47, SEL_PS_B_GOOD, GPIO_ACTIVE_LOW), + GPIO_LOOKUP(BXT_NW, 49, AUX_LED_GRN1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 50, AUX_LED_GRN2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 51, AUX_LED_GRN3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_NW, 52, AUX_LED_GRN4, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_W, 20, ALARM_STATE_USER, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_W, 21, ENABLE_STATE_USER, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 37, AUX_LED_RED1, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 38, AUX_LED_RED2, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 39, AUX_LED_RED3, GPIO_ACTIVE_HIGH), + GPIO_LOOKUP(BXT_SW, 40, AUX_LED_RED4, GPIO_ACTIVE_HIGH), {}, } }; @@ -149,6 +205,7 @@ struct sel3350_data { static int sel3350_probe(struct platform_device *pdev) { int rs; + int i; struct sel3350_data *sel3350; struct power_supply_config ps_cfg = {}; @@ -158,9 +215,19 @@ static int sel3350_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sel3350); - gpiod_add_lookup_table(&sel3350_leds_table); gpiod_add_lookup_table(&sel3350_gpios_table); + for (i = 0; i < ARRAY_SIZE(sel3350_leds); ++i) { + sel3350_leds[i].gpiod = devm_gpiod_get(&pdev->dev, + sel3350_leds_gpio_names[i], + GPIOD_ASIS); + if (IS_ERR_OR_NULL(sel3350_leds[i].gpiod)) { + rs = -EPROBE_DEFER; + goto err_gpio_loop; + } + gpiod_set_consumer_name(sel3350_leds[i].gpiod, sel3350_leds[i].name); + } + sel3350->leds_pdev = platform_device_register_data( NULL, "leds-gpio", @@ -209,11 +276,15 @@ static int sel3350_probe(struct platform_device *pdev) return 0; +err_gpio_loop: + while (i--) + devm_gpiod_put(&pdev->dev, sel3350_leds[i].gpiod); + goto err_platform; + err_ps: platform_device_unregister(sel3350->leds_pdev); err_platform: gpiod_remove_lookup_table(&sel3350_gpios_table); - gpiod_remove_lookup_table(&sel3350_leds_table); return rs; } @@ -224,7 +295,6 @@ static void sel3350_remove(struct platform_device *pdev) platform_device_unregister(sel3350->leds_pdev); gpiod_remove_lookup_table(&sel3350_gpios_table); - gpiod_remove_lookup_table(&sel3350_leds_table); } static const struct acpi_device_id sel3350_device_ids[] = { From a3d0dbd18ce908292607bb6cf37c978ece8a33d4 Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:15:55 -0700 Subject: [PATCH 028/115] platform/x86: panasonic-laptop: simplify allocation of sinf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change to a flexible array member to allocate once instead of twice. Use __counted_by for extra runtime analysis. Move the counting variable assignment to right after allocation as done by kzalloc_flex for GCC 15 and above. Remove + 1 to allocation. It's already done in the previous line. Signed-off-by: Rosen Penev Link: https://patch.msgid.link/20260430221555.83351-1-rosenp@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 1337f7c49805..2fe8ff0d32a8 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -246,11 +246,11 @@ struct pcc_acpi { int ac_brightness; int dc_brightness; int current_brightness; - u32 *sinf; struct acpi_device *device; struct input_dev *input_dev; struct backlight_device *backlight; struct platform_device *platform; + u32 sinf[] __counted_by(num_sifr); }; /* @@ -1003,21 +1003,15 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) */ num_sifr++; - pcc = kzalloc_obj(struct pcc_acpi); + pcc = kzalloc_flex(*pcc, sinf, num_sifr); if (!pcc) { pr_err("Couldn't allocate mem for pcc"); return -ENOMEM; } - pcc->sinf = kcalloc(num_sifr + 1, sizeof(u32), GFP_KERNEL); - if (!pcc->sinf) { - result = -ENOMEM; - goto out_hotkey; - } - + pcc->num_sifr = num_sifr; pcc->device = device; pcc->handle = device->handle; - pcc->num_sifr = num_sifr; device->driver_data = pcc; strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_PCC_CLASS); @@ -1025,7 +1019,7 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) result = acpi_pcc_init_input(pcc); if (result) { pr_err("Error installing keyinput handler\n"); - goto out_sinf; + goto out_hotkey; } if (!acpi_pcc_retrieve_biosdata(pcc)) { @@ -1106,10 +1100,8 @@ static int acpi_pcc_hotkey_probe(struct platform_device *pdev) backlight_device_unregister(pcc->backlight); out_input: input_unregister_device(pcc->input_dev); -out_sinf: - device->driver_data = NULL; - kfree(pcc->sinf); out_hotkey: + device->driver_data = NULL; kfree(pcc); return result; @@ -1139,7 +1131,6 @@ static void acpi_pcc_hotkey_remove(struct platform_device *pdev) device->driver_data = NULL; - kfree(pcc->sinf); kfree(pcc); } From 8ef6b01cee44803691c0a0c95b36f8ec710e2afb Mon Sep 17 00:00:00 2001 From: Rosen Penev Date: Thu, 30 Apr 2026 15:43:07 -0700 Subject: [PATCH 029/115] platform/x86/intel/vsec: allocate res with intel_vsec_dev MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a flexible array member to combine allocations. Avoids having to free separately. Add __counted_by for extra runtime analysis. Move counting variable assignment to after allocations as is already done by kzalloc_flex for GCC 15 and above. Signed-off-by: Rosen Penev Tested-by: David E. Box Reviewed-by: David E. Box Link: https://patch.msgid.link/20260430224307.109311-1-rosenp@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 14 ++++---------- drivers/platform/x86/intel/vsec_tpmi.c | 13 ++++--------- include/linux/intel_vsec.h | 6 +++--- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 7d5dbc1c1d05..9ef0f043bbee 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -112,7 +112,6 @@ static void intel_vsec_dev_release(struct device *dev) ida_free(intel_vsec_dev->ida, intel_vsec_dev->auxdev.id); kfree(intel_vsec_dev->acpi_disc); - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); } @@ -225,7 +224,6 @@ int intel_vsec_add_aux(struct device *parent, ret = xa_alloc(&auxdev_array, &intel_vsec_dev->id, intel_vsec_dev, PMT_XA_LIMIT, GFP_KERNEL); if (ret < 0) { - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); return ret; } @@ -233,7 +231,6 @@ int intel_vsec_add_aux(struct device *parent, id = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (id < 0) { xa_erase(&auxdev_array, intel_vsec_dev->id); - kfree(intel_vsec_dev->resource); kfree(intel_vsec_dev); return id; } @@ -282,7 +279,7 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; - struct resource __free(kfree) *res = NULL; + struct resource *res; struct resource *tmp; struct device *parent; unsigned long quirks = info->quirks; @@ -306,13 +303,12 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head return -EINVAL; } - intel_vsec_dev = kzalloc_obj(*intel_vsec_dev); + intel_vsec_dev = kzalloc_flex(*intel_vsec_dev, resource, header->num_entries); if (!intel_vsec_dev) return -ENOMEM; - res = kzalloc_objs(*res, header->num_entries); - if (!res) - return -ENOMEM; + intel_vsec_dev->num_resources = header->num_entries; + res = intel_vsec_dev->resource; if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; @@ -342,8 +338,6 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head } intel_vsec_dev->dev = dev; - intel_vsec_dev->resource = no_free_ptr(res); - intel_vsec_dev->num_resources = header->num_entries; intel_vsec_dev->quirks = info->quirks; intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->priv_data = info->priv_data; diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 7fc6ff8d1040..5e16bc219c5b 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -626,15 +626,12 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, if (!name) return -EOPNOTSUPP; - res = kzalloc_objs(*res, pfs->pfs_header.num_entries); - if (!res) + feature_vsec_dev = kzalloc_flex(*feature_vsec_dev, resource, pfs->pfs_header.num_entries); + if (!feature_vsec_dev) return -ENOMEM; - feature_vsec_dev = kzalloc_obj(*feature_vsec_dev); - if (!feature_vsec_dev) { - kfree(res); - return -ENOMEM; - } + feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; + res = feature_vsec_dev->resource; snprintf(feature_id_name, sizeof(feature_id_name), "tpmi-%s", name); @@ -647,8 +644,6 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, } feature_vsec_dev->dev = vsec_dev->dev; - feature_vsec_dev->resource = res; - feature_vsec_dev->num_resources = pfs->pfs_header.num_entries; feature_vsec_dev->priv_data = &tpmi_info->plat_info; feature_vsec_dev->priv_data_size = sizeof(tpmi_info->plat_info); feature_vsec_dev->ida = &intel_vsec_tpmi_ida; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 1fe5665a9d02..07ea563f524e 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -135,8 +135,6 @@ struct intel_vsec_platform_info { * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access * @dev: struct device associated with the device - * @resource: PCI discovery resources (BAR windows), one per discovery - * instance. Valid only when @src == INTEL_VSEC_DISC_PCI * @acpi_disc: ACPI discovery tables, each entry is two QWORDs * in little-endian format as defined by the PMT ACPI spec. * Valid only when @src == INTEL_VSEC_DISC_ACPI. @@ -149,11 +147,12 @@ struct intel_vsec_platform_info { * @quirks: specified quirks * @base_addr: base address of entries (if specified) * @cap_id: the enumerated id of the vsec feature + * @resource: PCI discovery resources (BAR windows), one per discovery + * instance. Valid only when @src == INTEL_VSEC_DISC_PCI */ struct intel_vsec_device { struct auxiliary_device auxdev; struct device *dev; - struct resource *resource; u32 (*acpi_disc)[4]; enum intel_vsec_disc_source src; struct ida *ida; @@ -164,6 +163,7 @@ struct intel_vsec_device { unsigned long quirks; u64 base_addr; unsigned long cap_id; + struct resource resource[] __counted_by(num_resources); }; /** From 165e81354eefd5551358112773f24027aac59d5a Mon Sep 17 00:00:00 2001 From: Mark Pearson Date: Tue, 28 Apr 2026 21:51:59 -0400 Subject: [PATCH 030/115] platform/x86: thinkpad_acpi: Add debugfs entry to display HWDD raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo diagnostics and support team have requested a mechanism to get the raw data from the HWDD commands to support customer debug situations. Add a debugfs entry to display the HWDD raw data. Signed-off-by: Mark Pearson Link: https://patch.msgid.link/20260429015210.1863316-1-mpearson-lenovo@squebb.ca Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 39 +++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index d4ed6f1216f2..445e1403308e 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -11216,6 +11217,26 @@ static ssize_t hwdd_status_show(struct device *dev, return sysfs_emit(buf, "0\n"); } + +/* sysfs type-c damage detection raw data - accessed via debugfs*/ +static int hwdd_raw_show(struct seq_file *m, void *v) +{ + unsigned int damage_status; + int err; + + if (!ucdd_supported) + return -ENODEV; + + /* Get USB TYPE-C damage status */ + err = hwdd_command(HWDD_GET_DMG_USBC, &damage_status); + if (err) + return err; + + seq_printf(m, "HWDD: 0x%x\n", damage_status); + return 0; +} +DEFINE_SHOW_ATTRIBUTE(hwdd_raw); + static DEVICE_ATTR_RO(hwdd_status); static DEVICE_ATTR_RO(hwdd_detail); @@ -11503,6 +11524,20 @@ static const char * __init str_supported(int is_supported) } #endif /* CONFIG_THINKPAD_ACPI_DEBUG */ +static struct dentry *tpacpi_dbg; +static void tpacpi_debugfs_init(void) +{ + tpacpi_dbg = debugfs_create_dir("tpacpi", NULL); + + /* HWDD raw data */ + debugfs_create_file("hwdd-raw", 0444, tpacpi_dbg, NULL, &hwdd_raw_fops); +} + +static void tpacpi_debugfs_remove(void) +{ + debugfs_remove_recursive(tpacpi_dbg); +} + static void ibm_exit(struct ibm_struct *ibm) { dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name); @@ -12067,6 +12102,8 @@ static void thinkpad_acpi_module_exit(void) remove_proc_entry(TPACPI_PROC_DIR, acpi_root_dir); if (tpacpi_wq) destroy_workqueue(tpacpi_wq); + if (tpacpi_dbg) + tpacpi_debugfs_remove(); kfree(thinkpad_id.bios_version_str); kfree(thinkpad_id.ec_version_str); @@ -12197,6 +12234,8 @@ static int __init thinkpad_acpi_module_init(void) return -ENODEV; } + tpacpi_debugfs_init(); + dmi_id = dmi_first_match(fwbug_list); if (dmi_id) tp_features.quirks = dmi_id->driver_data; From 5809de26ccab13c18d591e923acbab5edebbf283 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Wed, 29 Apr 2026 08:27:02 -0500 Subject: [PATCH 031/115] platform/x86: alienware-wmi-base: Transition to new WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transition to the new wmi_buffer based WMI API. Signed-off-by: Kurt Borja Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260429-aw-new-api-v5-1-7702668d04c6@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/dell/alienware-wmi-base.c | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/dell/alienware-wmi-base.c b/drivers/platform/x86/dell/alienware-wmi-base.c index 64562b92314f..19cadf21203a 100644 --- a/drivers/platform/x86/dell/alienware-wmi-base.c +++ b/drivers/platform/x86/dell/alienware-wmi-base.c @@ -12,8 +12,13 @@ #include #include #include +#include +#include #include #include + +#include + #include "alienware-wmi.h" MODULE_AUTHOR("Mario Limonciello "); @@ -150,22 +155,22 @@ u8 alienware_interface; int alienware_wmi_command(struct wmi_device *wdev, u32 method_id, void *in_args, size_t in_size, u32 *out_data) { - struct acpi_buffer out = {ACPI_ALLOCATE_BUFFER, NULL}; - struct acpi_buffer in = {in_size, in_args}; - acpi_status ret; + struct wmi_buffer out, in = { + .data = in_args, + .length = in_size, + }; + int ret; - ret = wmidev_evaluate_method(wdev, 0, method_id, &in, out_data ? &out : NULL); - if (ACPI_FAILURE(ret)) - return -EIO; + if (!out_data) + return wmidev_invoke_procedure(wdev, 0, method_id, &in); - union acpi_object *obj __free(kfree) = out.pointer; + ret = wmidev_invoke_method(wdev, 0, method_id, &in, &out, + sizeof(*out_data)); + if (ret) + return ret; - if (out_data) { - if (obj && obj->type == ACPI_TYPE_INTEGER) - *out_data = (u32)obj->integer.value; - else - return -ENOMSG; - } + __le32 *data __free(kfree) = out.data; + *out_data = le32_to_cpu(*data); return 0; } From 4339fa4fae057c99cbcfd8127769219feb14964b Mon Sep 17 00:00:00 2001 From: Krishnamoorthi M Date: Fri, 8 May 2026 00:39:26 +0530 Subject: [PATCH 032/115] platform/x86/amd: hfi: Support for ranking table versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add changes to support new ranking table version. Version 2 of the heterogeneous ranking table provides static CPU rankings. Version 3 adds dynamic ranking table support on newer AMD platforms. These changes ensure that platforms still reporting version 2 continue to function with the existing static ranking path, avoiding regressions on older hardware that does not supply a dynamic ranking table. Signed-off-by: Krishnamoorthi M Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Shyam Sundar S K Link: https://patch.msgid.link/20260507190926.1211726-1-krishnamoorthi.m@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hfi/hfi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/hfi/hfi.c b/drivers/platform/x86/amd/hfi/hfi.c index 83863a5e0fbc..e0ebcb0c4acd 100644 --- a/drivers/platform/x86/amd/hfi/hfi.c +++ b/drivers/platform/x86/amd/hfi/hfi.c @@ -33,7 +33,8 @@ #define AMD_HFI_DRIVER "amd_hfi" #define AMD_HFI_MAILBOX_COUNT 1 -#define AMD_HETERO_RANKING_TABLE_VER 2 +#define AMD_HETERO_RANKING_TABLE_MIN_VER 2 +#define AMD_HETERO_RANKING_TABLE_MAX_VER 3 #define AMD_HETERO_CPUID_27 0x80000027 @@ -158,7 +159,8 @@ static int amd_hfi_fill_metadata(struct amd_hfi_data *amd_hfi_data) dev_err(amd_hfi_data->dev, "invalid signature in shared memory\n"); return -EINVAL; } - if (amd_hfi_data->shmem->version_number != AMD_HETERO_RANKING_TABLE_VER) { + if (amd_hfi_data->shmem->version_number < AMD_HETERO_RANKING_TABLE_MIN_VER || + amd_hfi_data->shmem->version_number > AMD_HETERO_RANKING_TABLE_MAX_VER) { dev_err(amd_hfi_data->dev, "invalid version %d\n", amd_hfi_data->shmem->version_number); return -EINVAL; From 5cfb132da0afd3a711e2d5279243100102d2e836 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 15 May 2026 13:47:29 +0200 Subject: [PATCH 033/115] platform/x86: xo15-ebook: Use devres-based resource management MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use devm_kzalloc() and devm_input_allocate_device() in ebook_switch_probe() for allocating the button object and the input device, respectively, to simplify the rollback path in that function and ebook_switch_remove(). Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Link: https://patch.msgid.link/6015220.DvuYhMxLoT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/xo15-ebook.c | 43 ++++++++++--------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 2ca15f04b2d9..e40e385c52bd 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -82,30 +82,28 @@ static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume); static int ebook_switch_probe(struct platform_device *pdev) { - struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct device *dev = &pdev->dev; + struct acpi_device *device = ACPI_COMPANION(dev); const struct acpi_device_id *id; struct ebook_switch *button; struct input_dev *input; int error; - button = kzalloc_obj(struct ebook_switch); + button = devm_kzalloc(dev, sizeof(*button), GFP_KERNEL); if (!button) return -ENOMEM; platform_set_drvdata(pdev, button); - button->input = input = input_allocate_device(); - if (!input) { - error = -ENOMEM; - goto err_free_button; - } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + + button->input = input; id = acpi_match_acpi_device(ebook_device_ids, device); - if (!id) { - dev_err(&pdev->dev, "Unsupported hid\n"); - error = -ENODEV; - goto err_free_input; - } + if (!id) + return dev_err_probe(dev, -ENODEV, "Unsupported hid\n"); strscpy(acpi_device_name(device), XO15_EBOOK_DEVICE_NAME); strscpy(acpi_device_class(device), XO15_EBOOK_CLASS "/" XO15_EBOOK_SUBCLASS); @@ -115,21 +113,20 @@ static int ebook_switch_probe(struct platform_device *pdev) input->name = acpi_device_name(device); input->phys = button->phys; input->id.bustype = BUS_HOST; - input->dev.parent = &pdev->dev; input->evbit[0] = BIT_MASK(EV_SW); set_bit(SW_TABLET_MODE, input->swbit); error = input_register_device(input); if (error) - goto err_free_input; + return error; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - ebook_switch_notify, &pdev->dev); + ebook_switch_notify, dev); if (error) - goto err_unregister_input; + return error; - ebook_send_state(&pdev->dev); + ebook_send_state(dev); if (device->wakeup.flags.valid) { /* Button's GPE is run-wake GPE */ @@ -139,16 +136,6 @@ static int ebook_switch_probe(struct platform_device *pdev) } return 0; - -err_free_input: - input_free_device(input); -err_free_button: - kfree(button); - return error; - -err_unregister_input: - input_unregister_device(input); - goto err_free_button; } static void ebook_switch_remove(struct platform_device *pdev) @@ -162,8 +149,6 @@ static void ebook_switch_remove(struct platform_device *pdev) acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, ebook_switch_notify); - input_unregister_device(button->input); - kfree(button); } static struct platform_driver xo15_ebook_driver = { From a4173887605121f61a5222911b11ac598336618e Mon Sep 17 00:00:00 2001 From: Sakurai Shun Date: Sun, 17 May 2026 11:41:44 +0900 Subject: [PATCH 034/115] docs: fix typo in uniwill-laptop.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace "benifit" with "benefit". Signed-off-by: Sakurai Shun Reviewed-by: Armin Wolf Link: https://patch.msgid.link/20260517024148.9642-1-ssh1326@icloud.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/wmi/devices/uniwill-laptop.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/wmi/devices/uniwill-laptop.rst b/Documentation/wmi/devices/uniwill-laptop.rst index e246bf293450..65583b239706 100644 --- a/Documentation/wmi/devices/uniwill-laptop.rst +++ b/Documentation/wmi/devices/uniwill-laptop.rst @@ -189,7 +189,7 @@ Indexed IO Indexed IO with IO ports with a granularity of a single byte can be performed using the ``RIOP`` (read) and ``WIOP`` (write) ACPI control methods. Those ACPI methods are unused because they -provide no benifit when compared to the native IO port access functions provided by the kernel. +provide no benefit when compared to the native IO port access functions provided by the kernel. Special thanks go to github user `pobrn` which developed the `qc71_laptop `_ driver on which this driver is partly based. From 4baf44b4051940ba1abc68ef5136d25cb1806521 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:03:34 +0200 Subject: [PATCH 035/115] platform/x86: classmate-laptop: Address memory leaks on driver removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch over cmpc_accel_add_v4() and cmpc_accel_add() to using devm_kzalloc() for allocating the accel object which will cause it to be freed automatically on device removal, so it won't be leaked any more. This also simplifies the rollback paths in these functions somewhat. Fixes: 529aa8cb0a59 ("classmate-laptop: add support for Classmate PC ACPI devices") Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/10846403.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index e6eed3d65580..a5fe34211afb 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -400,7 +400,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -411,7 +411,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); if (error) - goto failed_sensitivity; + return error; accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); @@ -434,8 +434,6 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); -failed_sensitivity: - kfree(accel); return error; } @@ -650,7 +648,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) struct input_dev *inputdev; struct cmpc_accel *accel; - accel = kmalloc_obj(*accel); + accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; @@ -659,7 +657,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); if (error) - goto failed_file; + return error; error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", cmpc_accel_idev_init); @@ -673,8 +671,6 @@ static int cmpc_accel_add(struct acpi_device *acpi) failed_input: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); -failed_file: - kfree(accel); return error; } From 87b3892ddc3ac5c62eaa97a1662f6f996d6d4870 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:04:39 +0200 Subject: [PATCH 036/115] platform/x86: classmate-laptop: Unify probe rollback and remove code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent modifications, change code ordering in cmpc_accel_add_v4(), cmpc_accel_add(), and cmpc_accel_remove_v4() so that the ordering of the probe rollback code is the same as the ordering of the corresponding removal code. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/2036641.PYKUYFuaPT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 40 +++++++++++++------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index a5fe34211afb..d1407a28c1de 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -406,12 +406,19 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", + cmpc_accel_idev_init_v4); + if (error) + return error; + + inputdev = dev_get_drvdata(&acpi->dev); + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); if (error) - return error; + goto failed_sensitivity; accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT; cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); @@ -420,27 +427,21 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) if (error) goto failed_g_select; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", - cmpc_accel_idev_init_v4); - if (error) - goto failed_input; - - inputdev = dev_get_drvdata(&acpi->dev); dev_set_drvdata(&inputdev->dev, accel); return 0; -failed_input: - device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); +failed_sensitivity: + cmpc_remove_acpi_notify_device(acpi); return error; } static void cmpc_accel_remove_v4(struct acpi_device *acpi) { - device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); cmpc_remove_acpi_notify_device(acpi); } @@ -652,25 +653,26 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; + error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", + cmpc_accel_idev_init); + if (error) + return error; + + inputdev = dev_get_drvdata(&acpi->dev); + accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); if (error) - return error; + goto failed_file; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", - cmpc_accel_idev_init); - if (error) - goto failed_input; - - inputdev = dev_get_drvdata(&acpi->dev); dev_set_drvdata(&inputdev->dev, accel); return 0; -failed_input: - device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); +failed_file: + cmpc_remove_acpi_notify_device(acpi); return error; } From daca81d9ead06d85b749d6036ea8c4940e7ac128 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:06:59 +0200 Subject: [PATCH 037/115] platform/x86: classmate-laptop: Pass struct device pointer to helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to using struct platform_driver instead of struct acpi_driver, modify two helper functions in it, cmpc_add_acpi_notify_device() and cmpc_remove_acpi_notify_device(), to take a struct device pointer argument instead of a struct acpi_device pointer argument and update their callers accordingly. While at it, change the return type of cmpc_remove_acpi_notify_device() to void because its return value is never checked. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/9615385.CDJkKcVGEf@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 32 ++++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index d1407a28c1de..6521d42f8204 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -38,7 +38,7 @@ struct cmpc_accel { typedef void (*input_device_init)(struct input_dev *dev); -static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, +static int cmpc_add_acpi_notify_device(struct device *dev, char *name, input_device_init idev_init) { struct input_dev *inputdev; @@ -48,22 +48,20 @@ static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, if (!inputdev) return -ENOMEM; inputdev->name = name; - inputdev->dev.parent = &acpi->dev; + inputdev->dev.parent = dev; idev_init(inputdev); error = input_register_device(inputdev); if (error) { input_free_device(inputdev); return error; } - dev_set_drvdata(&acpi->dev, inputdev); + dev_set_drvdata(dev, inputdev); return 0; } -static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) +static void cmpc_remove_acpi_notify_device(struct device *dev) { - struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); - input_unregister_device(inputdev); - return 0; + input_unregister_device(dev_get_drvdata(dev)); } /* @@ -406,7 +404,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4", + error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; @@ -434,7 +432,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); return error; } @@ -442,7 +440,7 @@ static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -653,7 +651,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; - error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", + error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; @@ -672,14 +670,14 @@ static int cmpc_accel_add(struct acpi_device *acpi) return 0; failed_file: - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); return error; } static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -750,13 +748,13 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", + return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); } static void cmpc_tablet_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } #ifdef CONFIG_PM_SLEEP @@ -1074,13 +1072,13 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", + return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); } static void cmpc_keys_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(acpi); + cmpc_remove_acpi_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { From 5658770e6eb5d9cf8d077054120545a98d7316ee Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:07:31 +0200 Subject: [PATCH 038/115] platform/x86: classmate-laptop: Rename two helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since cmpc_add_acpi_notify_device() and cmpc_remove_acpi_notify_device() have been modified to take a plain struct device pointer as the first argument, then have nothing to do with ACPI in principle, so rename them to cmpc_add_notify_device() and cmpc_remove_notify_device(), respectively, and consolidate white space around the call sites of the former. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3338539.5fSG56mABF@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 30 +++++++++++-------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 6521d42f8204..bc229771e7ba 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -38,8 +38,8 @@ struct cmpc_accel { typedef void (*input_device_init)(struct input_dev *dev); -static int cmpc_add_acpi_notify_device(struct device *dev, char *name, - input_device_init idev_init) +static int cmpc_add_notify_device(struct device *dev, char *name, + input_device_init idev_init) { struct input_dev *inputdev; int error; @@ -59,7 +59,7 @@ static int cmpc_add_acpi_notify_device(struct device *dev, char *name, return 0; } -static void cmpc_remove_acpi_notify_device(struct device *dev) +static void cmpc_remove_notify_device(struct device *dev) { input_unregister_device(dev_get_drvdata(dev)); } @@ -404,8 +404,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel_v4", - cmpc_accel_idev_init_v4); + error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; @@ -432,7 +431,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); return error; } @@ -440,7 +439,7 @@ static void cmpc_accel_remove_v4(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -651,8 +650,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (!accel) return -ENOMEM; - error = cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_accel", - cmpc_accel_idev_init); + error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; @@ -670,14 +668,14 @@ static int cmpc_accel_add(struct acpi_device *acpi) return 0; failed_file: - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); return error; } static void cmpc_accel_remove(struct acpi_device *acpi) { device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -748,13 +746,12 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_tablet", - cmpc_tablet_idev_init); + return cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); } static void cmpc_tablet_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } #ifdef CONFIG_PM_SLEEP @@ -1072,13 +1069,12 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_acpi_notify_device(&acpi->dev, "cmpc_keys", - cmpc_keys_idev_init); + return cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); } static void cmpc_keys_remove(struct acpi_device *acpi) { - cmpc_remove_acpi_notify_device(&acpi->dev); + cmpc_remove_notify_device(&acpi->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { From b32123a11dd706c3d0eaa48306a6000666ec11fe Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:08:22 +0200 Subject: [PATCH 039/115] platform/x86: classmate-laptop: Register ACPI notify handlers directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To facilitate subsequent conversion of the driver to using struct platform_driver instead of struct acpi_driver, make it install its ACPI notify handlers directly instead of using struct acpi_driver .notify() callbacks. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/1856277.VLH7GnMWUR@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 66 +++++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index bc229771e7ba..8507483b23f9 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -177,8 +177,10 @@ static acpi_status cmpc_get_accel_v4(acpi_handle handle, return status; } -static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event) +static void cmpc_accel_handler_v4(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; + if (event == 0x81) { int16_t x, y, z; acpi_status status; @@ -424,10 +426,17 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) if (error) goto failed_g_select; + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler_v4, acpi); + if (error) + goto failed_notify_handler; + dev_set_drvdata(&inputdev->dev, accel); return 0; +failed_notify_handler: + device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: @@ -437,6 +446,8 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) static void cmpc_accel_remove_v4(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); cmpc_remove_notify_device(&acpi->dev); @@ -457,7 +468,6 @@ static struct acpi_driver cmpc_accel_acpi_driver_v4 = { .ops = { .add = cmpc_accel_add_v4, .remove = cmpc_accel_remove_v4, - .notify = cmpc_accel_handler_v4, }, .drv.pm = &cmpc_accel_pm, }; @@ -539,8 +549,10 @@ static acpi_status cmpc_get_accel(acpi_handle handle, return status; } -static void cmpc_accel_handler(struct acpi_device *dev, u32 event) +static void cmpc_accel_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; + if (event == 0x81) { unsigned char x, y, z; acpi_status status; @@ -663,10 +675,17 @@ static int cmpc_accel_add(struct acpi_device *acpi) if (error) goto failed_file; + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler, acpi); + if (error) + goto failed_notify_handler; + dev_set_drvdata(&inputdev->dev, accel); return 0; +failed_notify_handler: + device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); failed_file: cmpc_remove_notify_device(&acpi->dev); return error; @@ -674,6 +693,8 @@ static int cmpc_accel_add(struct acpi_device *acpi) static void cmpc_accel_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_accel_handler); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); cmpc_remove_notify_device(&acpi->dev); } @@ -690,7 +711,6 @@ static struct acpi_driver cmpc_accel_acpi_driver = { .ops = { .add = cmpc_accel_add, .remove = cmpc_accel_remove, - .notify = cmpc_accel_handler, } }; @@ -716,8 +736,9 @@ static acpi_status cmpc_get_tablet(acpi_handle handle, return status; } -static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) +static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; unsigned long long val = 0; struct input_dev *inputdev = dev_get_drvdata(&dev->dev); @@ -746,11 +767,24 @@ static void cmpc_tablet_idev_init(struct input_dev *inputdev) static int cmpc_tablet_add(struct acpi_device *acpi) { - return cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + int error; + + error = cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + if (error) + return error; + + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_tablet_handler, acpi); + if (error) + cmpc_remove_notify_device(&acpi->dev); + + return error; } static void cmpc_tablet_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_tablet_handler); cmpc_remove_notify_device(&acpi->dev); } @@ -782,7 +816,6 @@ static struct acpi_driver cmpc_tablet_acpi_driver = { .ops = { .add = cmpc_tablet_add, .remove = cmpc_tablet_remove, - .notify = cmpc_tablet_handler, }, .drv.pm = &cmpc_tablet_pm, }; @@ -1046,8 +1079,9 @@ static int cmpc_keys_codes[] = { KEY_MAX }; -static void cmpc_keys_handler(struct acpi_device *dev, u32 event) +static void cmpc_keys_handler(acpi_handle handle, u32 event, void *data) { + struct acpi_device *dev = data; struct input_dev *inputdev; int code = KEY_MAX; @@ -1069,11 +1103,24 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) static int cmpc_keys_add(struct acpi_device *acpi) { - return cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + int error; + + error = cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + if (error) + return error; + + error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_keys_handler, acpi); + if (error) + cmpc_remove_notify_device(&acpi->dev); + + return error; } static void cmpc_keys_remove(struct acpi_device *acpi) { + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, + cmpc_keys_handler); cmpc_remove_notify_device(&acpi->dev); } @@ -1089,7 +1136,6 @@ static struct acpi_driver cmpc_keys_acpi_driver = { .ops = { .add = cmpc_keys_add, .remove = cmpc_keys_remove, - .notify = cmpc_keys_handler, } }; From 1588b83ad9b95dfa0bc3b9c22eb2608b64e1a3c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:10:32 +0200 Subject: [PATCH 040/115] platform/x86: classmate-laptop: Convert v4 accel driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_accel_acpi_driver_v4 in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding, but the sysfs attributes added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting them to be present there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3609276.QJadu78ljV@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 77 ++++++++++++++----------- 1 file changed, 42 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 8507483b23f9..454ba2823b51 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -13,6 +13,7 @@ #include #include #include +#include struct cmpc_accel { int sensitivity; @@ -179,15 +180,15 @@ static acpi_status cmpc_get_accel_v4(acpi_handle handle, static void cmpc_accel_handler_v4(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; if (event == 0x81) { int16_t x, y, z; acpi_status status; - status = cmpc_get_accel_v4(dev->handle, &x, &y, &z); + status = cmpc_get_accel_v4(ACPI_HANDLE(dev), &x, &y, &z); if (ACPI_SUCCESS(status)) { - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); input_report_abs(inputdev, ABS_X, x); input_report_abs(inputdev, ABS_Y, y); @@ -317,18 +318,17 @@ static struct device_attribute cmpc_accel_g_select_attr_v4 = { static int cmpc_accel_open_v4(struct input_dev *input) { - struct acpi_device *acpi; + acpi_handle handle = ACPI_HANDLE(input->dev.parent); struct cmpc_accel *accel; - acpi = to_acpi_device(input->dev.parent); accel = dev_get_drvdata(&input->dev); if (!accel) return -ENXIO; - cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); - cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select); + cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(handle, accel->g_select); - if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) { + if (ACPI_SUCCESS(cmpc_start_accel_v4(handle))) { accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN; return 0; } @@ -337,13 +337,11 @@ static int cmpc_accel_open_v4(struct input_dev *input) static void cmpc_accel_close_v4(struct input_dev *input) { - struct acpi_device *acpi; struct cmpc_accel *accel; - acpi = to_acpi_device(input->dev.parent); accel = dev_get_drvdata(&input->dev); - cmpc_stop_accel_v4(acpi->handle); + cmpc_stop_accel_v4(ACPI_HANDLE(input->dev.parent)); accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; } @@ -367,7 +365,7 @@ static int cmpc_accel_suspend_v4(struct device *dev) accel = dev_get_drvdata(&inputdev->dev); if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) - return cmpc_stop_accel_v4(to_acpi_device(dev)->handle); + return cmpc_stop_accel_v4(ACPI_HANDLE(dev)); return 0; } @@ -381,12 +379,12 @@ static int cmpc_accel_resume_v4(struct device *dev) accel = dev_get_drvdata(&inputdev->dev); if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) { - cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle, - accel->sensitivity); - cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle, - accel->g_select); + acpi_handle handle = ACPI_HANDLE(dev); - if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle))) + cmpc_accel_set_sensitivity_v4(handle, accel->sensitivity); + cmpc_accel_set_g_select_v4(handle, accel->g_select); + + if (ACPI_FAILURE(cmpc_start_accel_v4(handle))) return -EIO; } @@ -394,23 +392,29 @@ static int cmpc_accel_resume_v4(struct device *dev) } #endif -static int cmpc_accel_add_v4(struct acpi_device *acpi) +static int cmpc_accel_probe_v4(struct platform_device *pdev) { int error; struct input_dev *inputdev; struct cmpc_accel *accel; + struct acpi_device *acpi; - accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); + error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel_v4", cmpc_accel_idev_init_v4); if (error) return error; - inputdev = dev_get_drvdata(&acpi->dev); + inputdev = dev_get_drvdata(&pdev->dev); + dev_set_drvdata(&acpi->dev, inputdev); accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity); @@ -427,7 +431,7 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) goto failed_g_select; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_accel_handler_v4, acpi); + cmpc_accel_handler_v4, &pdev->dev); if (error) goto failed_notify_handler; @@ -440,17 +444,21 @@ static int cmpc_accel_add_v4(struct acpi_device *acpi) failed_g_select: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); failed_sensitivity: - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_accel_remove_v4(struct acpi_device *acpi) +static void cmpc_accel_remove_v4(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, cmpc_accel_handler_v4); device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4); - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); } static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4, @@ -461,15 +469,14 @@ static const struct acpi_device_id cmpc_accel_device_ids_v4[] = { {"", 0} }; -static struct acpi_driver cmpc_accel_acpi_driver_v4 = { - .name = "cmpc_accel_v4", - .class = "cmpc_accel_v4", - .ids = cmpc_accel_device_ids_v4, - .ops = { - .add = cmpc_accel_add_v4, - .remove = cmpc_accel_remove_v4, +static struct platform_driver cmpc_accel_acpi_driver_v4 = { + .probe = cmpc_accel_probe_v4, + .remove = cmpc_accel_remove_v4, + .driver = { + .name = "cmpc_accel_v4", + .acpi_match_table = cmpc_accel_device_ids_v4, + .pm = &cmpc_accel_pm, }, - .drv.pm = &cmpc_accel_pm, }; @@ -1164,7 +1171,7 @@ static int cmpc_init(void) if (r) goto failed_accel; - r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4); + r = platform_driver_register(&cmpc_accel_acpi_driver_v4); if (r) goto failed_accel_v4; @@ -1188,7 +1195,7 @@ static int cmpc_init(void) static void cmpc_exit(void) { - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4); + platform_driver_unregister(&cmpc_accel_acpi_driver_v4); acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); From e51effb3cefe1bffa1afdee6ee5e93ded1746606 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:11:39 +0200 Subject: [PATCH 041/115] platform/x86: classmate-laptop: Convert accel driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_accel_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding, but the sysfs attribute added by the driver under the ACPI companion of that device will stay there in case there are utilities in user space expecting it to be present there. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/24365138.6Emhk5qWAg@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 63 +++++++++++++------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 454ba2823b51..0d5ad89d0f24 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -558,15 +558,15 @@ static acpi_status cmpc_get_accel(acpi_handle handle, static void cmpc_accel_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; if (event == 0x81) { unsigned char x, y, z; acpi_status status; - status = cmpc_get_accel(dev->handle, &x, &y, &z); + status = cmpc_get_accel(ACPI_HANDLE(dev), &x, &y, &z); if (ACPI_SUCCESS(status)) { - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); input_report_abs(inputdev, ABS_X, x); input_report_abs(inputdev, ABS_Y, y); @@ -633,20 +633,14 @@ static struct device_attribute cmpc_accel_sensitivity_attr = { static int cmpc_accel_open(struct input_dev *input) { - struct acpi_device *acpi; - - acpi = to_acpi_device(input->dev.parent); - if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) + if (ACPI_SUCCESS(cmpc_start_accel(ACPI_HANDLE(input->dev.parent)))) return 0; return -EIO; } static void cmpc_accel_close(struct input_dev *input) { - struct acpi_device *acpi; - - acpi = to_acpi_device(input->dev.parent); - cmpc_stop_accel(acpi->handle); + cmpc_stop_accel(ACPI_HANDLE(input->dev.parent)); } static void cmpc_accel_idev_init(struct input_dev *inputdev) @@ -659,21 +653,27 @@ static void cmpc_accel_idev_init(struct input_dev *inputdev) inputdev->close = cmpc_accel_close; } -static int cmpc_accel_add(struct acpi_device *acpi) +static int cmpc_accel_probe(struct platform_device *pdev) { int error; struct input_dev *inputdev; struct cmpc_accel *accel; + struct acpi_device *acpi; - accel = devm_kzalloc(&acpi->dev, sizeof(*accel), GFP_KERNEL); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + accel = devm_kzalloc(&pdev->dev, sizeof(*accel), GFP_KERNEL); if (!accel) return -ENOMEM; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_accel", cmpc_accel_idev_init); + error = cmpc_add_notify_device(&pdev->dev, "cmpc_accel", cmpc_accel_idev_init); if (error) return error; - inputdev = dev_get_drvdata(&acpi->dev); + inputdev = dev_get_drvdata(&pdev->dev); + dev_set_drvdata(&acpi->dev, inputdev); accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); @@ -683,7 +683,7 @@ static int cmpc_accel_add(struct acpi_device *acpi) goto failed_file; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_accel_handler, acpi); + cmpc_accel_handler, &pdev->dev); if (error) goto failed_notify_handler; @@ -694,16 +694,20 @@ static int cmpc_accel_add(struct acpi_device *acpi) failed_notify_handler: device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); failed_file: - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_accel_remove(struct acpi_device *acpi) +static void cmpc_accel_remove(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, cmpc_accel_handler); device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); - cmpc_remove_notify_device(&acpi->dev); + dev_set_drvdata(&acpi->dev, NULL); + cmpc_remove_notify_device(&pdev->dev); } static const struct acpi_device_id cmpc_accel_device_ids[] = { @@ -711,14 +715,13 @@ static const struct acpi_device_id cmpc_accel_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_accel_acpi_driver = { - .name = "cmpc_accel", - .class = "cmpc_accel", - .ids = cmpc_accel_device_ids, - .ops = { - .add = cmpc_accel_add, - .remove = cmpc_accel_remove, - } +static struct platform_driver cmpc_accel_acpi_driver = { + .probe = cmpc_accel_probe, + .remove = cmpc_accel_remove, + .driver = { + .name = "cmpc_accel", + .acpi_match_table = cmpc_accel_device_ids, + }, }; @@ -1167,7 +1170,7 @@ static int cmpc_init(void) if (r) goto failed_tablet; - r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); + r = platform_driver_register(&cmpc_accel_acpi_driver); if (r) goto failed_accel; @@ -1178,7 +1181,7 @@ static int cmpc_init(void) return r; failed_accel_v4: - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); + platform_driver_unregister(&cmpc_accel_acpi_driver); failed_accel: acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); @@ -1196,7 +1199,7 @@ static int cmpc_init(void) static void cmpc_exit(void) { platform_driver_unregister(&cmpc_accel_acpi_driver_v4); - acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); + platform_driver_unregister(&cmpc_accel_acpi_driver); acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); From 0fd6639706b79c0fbb5c0205aebfbbfd40ec44b0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:12:55 +0200 Subject: [PATCH 042/115] platform/x86: classmate-laptop: Convert tablet driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_tablet_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/4504318.ejJDZkT8p0@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 55 +++++++++++++------------ 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 0d5ad89d0f24..464ab852b492 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -748,12 +748,12 @@ static acpi_status cmpc_get_tablet(acpi_handle handle, static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; unsigned long long val = 0; - struct input_dev *inputdev = dev_get_drvdata(&dev->dev); + struct input_dev *inputdev = dev_get_drvdata(dev); if (event == 0x81) { - if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } @@ -762,40 +762,44 @@ static void cmpc_tablet_handler(acpi_handle handle, u32 event, void *data) static void cmpc_tablet_idev_init(struct input_dev *inputdev) { + acpi_handle handle = ACPI_HANDLE(inputdev->dev.parent); unsigned long long val = 0; - struct acpi_device *acpi; set_bit(EV_SW, inputdev->evbit); set_bit(SW_TABLET_MODE, inputdev->swbit); - acpi = to_acpi_device(inputdev->dev.parent); - if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(handle, &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } } -static int cmpc_tablet_add(struct acpi_device *acpi) +static int cmpc_tablet_probe(struct platform_device *pdev) { + struct acpi_device *acpi; int error; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_tablet", cmpc_tablet_idev_init); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + error = cmpc_add_notify_device(&pdev->dev, "cmpc_tablet", cmpc_tablet_idev_init); if (error) return error; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_tablet_handler, acpi); + cmpc_tablet_handler, &pdev->dev); if (error) - cmpc_remove_notify_device(&acpi->dev); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_tablet_remove(struct acpi_device *acpi) +static void cmpc_tablet_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_tablet_handler); - cmpc_remove_notify_device(&acpi->dev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, cmpc_tablet_handler); + cmpc_remove_notify_device(&pdev->dev); } #ifdef CONFIG_PM_SLEEP @@ -804,7 +808,7 @@ static int cmpc_tablet_resume(struct device *dev) struct input_dev *inputdev = dev_get_drvdata(dev); unsigned long long val = 0; - if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) { + if (ACPI_SUCCESS(cmpc_get_tablet(ACPI_HANDLE(dev), &val))) { input_report_switch(inputdev, SW_TABLET_MODE, !val); input_sync(inputdev); } @@ -819,15 +823,14 @@ static const struct acpi_device_id cmpc_tablet_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_tablet_acpi_driver = { - .name = "cmpc_tablet", - .class = "cmpc_tablet", - .ids = cmpc_tablet_device_ids, - .ops = { - .add = cmpc_tablet_add, - .remove = cmpc_tablet_remove, +static struct platform_driver cmpc_tablet_acpi_driver = { + .probe = cmpc_tablet_probe, + .remove = cmpc_tablet_remove, + .driver = { + .name = "cmpc_tablet", + .acpi_match_table = cmpc_tablet_device_ids, + .pm = &cmpc_tablet_pm, }, - .drv.pm = &cmpc_tablet_pm, }; @@ -1166,7 +1169,7 @@ static int cmpc_init(void) if (r) goto failed_bl; - r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); + r = platform_driver_register(&cmpc_tablet_acpi_driver); if (r) goto failed_tablet; @@ -1184,7 +1187,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_accel_acpi_driver); failed_accel: - acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); + platform_driver_unregister(&cmpc_tablet_acpi_driver); failed_tablet: acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); @@ -1200,7 +1203,7 @@ static void cmpc_exit(void) { platform_driver_unregister(&cmpc_accel_acpi_driver_v4); platform_driver_unregister(&cmpc_accel_acpi_driver); - acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); + platform_driver_unregister(&cmpc_tablet_acpi_driver); acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); } From 069b06f8dfc9821fa54f0c5109ebbde891ea363a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:14:13 +0200 Subject: [PATCH 043/115] platform/x86: classmate-laptop: Convert ipml driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_ipml_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the backlight and rfkill devices registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/3753883.R56niFO833@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 42 ++++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 464ab852b492..08ff4549e956 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -997,11 +997,16 @@ struct ipml200_dev { struct rfkill *rf; }; -static int cmpc_ipml_add(struct acpi_device *acpi) +static int cmpc_ipml_probe(struct platform_device *pdev) { int retval; struct ipml200_dev *ipml; struct backlight_properties props; + acpi_handle handle; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; ipml = kmalloc_obj(*ipml); if (ipml == NULL) @@ -1010,16 +1015,16 @@ static int cmpc_ipml_add(struct acpi_device *acpi) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; props.max_brightness = 7; - ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev, - acpi->handle, &cmpc_bl_ops, + ipml->bd = backlight_device_register("cmpc_bl", &pdev->dev, + handle, &cmpc_bl_ops, &props); if (IS_ERR(ipml->bd)) { retval = PTR_ERR(ipml->bd); goto out_bd; } - ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN, - &cmpc_rfkill_ops, acpi->handle); + ipml->rf = rfkill_alloc("cmpc_rfkill", &pdev->dev, RFKILL_TYPE_WLAN, + &cmpc_rfkill_ops, handle); /* * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV). * This is OK, however, since all other uses of the device will not @@ -1033,7 +1038,7 @@ static int cmpc_ipml_add(struct acpi_device *acpi) } } - dev_set_drvdata(&acpi->dev, ipml); + platform_set_drvdata(pdev, ipml); return 0; out_bd: @@ -1041,11 +1046,11 @@ static int cmpc_ipml_add(struct acpi_device *acpi) return retval; } -static void cmpc_ipml_remove(struct acpi_device *acpi) +static void cmpc_ipml_remove(struct platform_device *pdev) { struct ipml200_dev *ipml; - ipml = dev_get_drvdata(&acpi->dev); + ipml = platform_get_drvdata(pdev); backlight_device_unregister(ipml->bd); @@ -1062,14 +1067,13 @@ static const struct acpi_device_id cmpc_ipml_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_ipml_acpi_driver = { - .name = "cmpc", - .class = "cmpc", - .ids = cmpc_ipml_device_ids, - .ops = { - .add = cmpc_ipml_add, - .remove = cmpc_ipml_remove - } +static struct platform_driver cmpc_ipml_acpi_driver = { + .probe = cmpc_ipml_probe, + .remove = cmpc_ipml_remove, + .driver = { + .name = "cmpc", + .acpi_match_table = cmpc_ipml_device_ids, + }, }; @@ -1165,7 +1169,7 @@ static int cmpc_init(void) if (r) goto failed_keys; - r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver); + r = platform_driver_register(&cmpc_ipml_acpi_driver); if (r) goto failed_bl; @@ -1190,7 +1194,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_tablet_acpi_driver); failed_tablet: - acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); + platform_driver_unregister(&cmpc_ipml_acpi_driver); failed_bl: acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); @@ -1204,7 +1208,7 @@ static void cmpc_exit(void) platform_driver_unregister(&cmpc_accel_acpi_driver_v4); platform_driver_unregister(&cmpc_accel_acpi_driver); platform_driver_unregister(&cmpc_tablet_acpi_driver); - acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver); + platform_driver_unregister(&cmpc_ipml_acpi_driver); acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); } From 5fdac9983681f743cfaa89414ea154a2c5fd39c4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 11 May 2026 22:15:18 +0200 Subject: [PATCH 044/115] platform/x86: classmate-laptop: Convert keys driver to a platform one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In all cases in which a struct acpi_driver is used for binding a driver to an ACPI device object, a corresponding platform device is created by the ACPI core and that device is regarded as a proper representation of underlying hardware. Accordingly, a struct platform_driver should be used by driver code to bind to that device. There are multiple reasons why drivers should not bind directly to ACPI device objects [1]. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert cmpc_keys_acpi_driver in the Classmate laptop driver from an ACPI driver to a platform one. After this change, the input device registered by the driver will appear under the platform device used for driver binding. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Acked-by: Thadeu Lima de Souza Cascardo Link: https://patch.msgid.link/2002306.taCxCBeP46@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/classmate-laptop.c | 46 ++++++++++++++----------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index 08ff4549e956..05890815795d 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -1098,13 +1098,13 @@ static int cmpc_keys_codes[] = { static void cmpc_keys_handler(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev = data; + struct device *dev = data; struct input_dev *inputdev; int code = KEY_MAX; if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) code = cmpc_keys_codes[event & 0x0F]; - inputdev = dev_get_drvdata(&dev->dev); + inputdev = dev_get_drvdata(dev); input_report_key(inputdev, code, !(event & 0x10)); input_sync(inputdev); } @@ -1118,27 +1118,32 @@ static void cmpc_keys_idev_init(struct input_dev *inputdev) set_bit(cmpc_keys_codes[i], inputdev->keybit); } -static int cmpc_keys_add(struct acpi_device *acpi) +static int cmpc_keys_probe(struct platform_device *pdev) { + struct acpi_device *acpi; int error; - error = cmpc_add_notify_device(&acpi->dev, "cmpc_keys", cmpc_keys_idev_init); + acpi = ACPI_COMPANION(&pdev->dev); + if (!acpi) + return -ENODEV; + + error = cmpc_add_notify_device(&pdev->dev, "cmpc_keys", cmpc_keys_idev_init); if (error) return error; error = acpi_dev_install_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_keys_handler, acpi); + cmpc_keys_handler, &pdev->dev); if (error) - cmpc_remove_notify_device(&acpi->dev); + cmpc_remove_notify_device(&pdev->dev); return error; } -static void cmpc_keys_remove(struct acpi_device *acpi) +static void cmpc_keys_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(acpi, ACPI_DEVICE_NOTIFY, - cmpc_keys_handler); - cmpc_remove_notify_device(&acpi->dev); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, cmpc_keys_handler); + cmpc_remove_notify_device(&pdev->dev); } static const struct acpi_device_id cmpc_keys_device_ids[] = { @@ -1146,14 +1151,13 @@ static const struct acpi_device_id cmpc_keys_device_ids[] = { {"", 0} }; -static struct acpi_driver cmpc_keys_acpi_driver = { - .name = "cmpc_keys", - .class = "cmpc_keys", - .ids = cmpc_keys_device_ids, - .ops = { - .add = cmpc_keys_add, - .remove = cmpc_keys_remove, - } +static struct platform_driver cmpc_keys_acpi_driver = { + .probe = cmpc_keys_probe, + .remove = cmpc_keys_remove, + .driver = { + .name = "cmpc_keys", + .acpi_match_table = cmpc_keys_device_ids, + }, }; @@ -1165,7 +1169,7 @@ static int cmpc_init(void) { int r; - r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); + r = platform_driver_register(&cmpc_keys_acpi_driver); if (r) goto failed_keys; @@ -1197,7 +1201,7 @@ static int cmpc_init(void) platform_driver_unregister(&cmpc_ipml_acpi_driver); failed_bl: - acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); + platform_driver_unregister(&cmpc_keys_acpi_driver); failed_keys: return r; @@ -1209,7 +1213,7 @@ static void cmpc_exit(void) platform_driver_unregister(&cmpc_accel_acpi_driver); platform_driver_unregister(&cmpc_tablet_acpi_driver); platform_driver_unregister(&cmpc_ipml_acpi_driver); - acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); + platform_driver_unregister(&cmpc_keys_acpi_driver); } module_init(cmpc_init); From 4d86e384c164abeee17da09fe6cd24a37973e628 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:42 +0200 Subject: [PATCH 045/115] platform/x86: uniwill-laptop: Rework FN lock/super key suspend handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the suspend handling for the FN lock and super key enable features saves the whole values of the affected registers instead of the individual feature state. This duplicates the register access logic from the associated sysfs attributes. Rework the suspend handling to reuse said register access logic and only store the individual feature state as a boolean value. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-6-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 119 ++++++++++++-------- 1 file changed, 74 insertions(+), 45 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 8cc01bec77b9..c73e280a58bd 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -341,8 +341,8 @@ struct uniwill_data { struct acpi_battery_hook hook; unsigned int last_charge_ctrl; struct mutex battery_lock; /* Protects the list of currently registered batteries */ - unsigned int last_status; - unsigned int last_switch_status; + bool last_fn_lock_state; + bool last_super_key_enable_state; struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ struct list_head batteries; struct mutex led_lock; /* Protects writes to the lightbar registers */ @@ -619,11 +619,22 @@ static const struct regmap_config uniwill_ec_config = { .use_single_write = true, }; +static int uniwill_write_fn_lock(struct uniwill_data *data, bool status) +{ + unsigned int value; + + if (status) + value = FN_LOCK_STATUS; + else + value = 0; + + return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value); +} + static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct uniwill_data *data = dev_get_drvdata(dev); - unsigned int value; bool enable; int ret; @@ -631,21 +642,15 @@ static ssize_t fn_lock_store(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - if (enable) - value = FN_LOCK_STATUS; - else - value = 0; - - ret = regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, value); + ret = uniwill_write_fn_lock(data, enable); if (ret < 0) return ret; return count; } -static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf) +static int uniwill_read_fn_lock(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -653,23 +658,31 @@ static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, c if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !!(value & FN_LOCK_STATUS)); + *status = !!(value & FN_LOCK_STATUS); + + return 0; +} + +static ssize_t fn_lock_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_fn_lock(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(fn_lock); -static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int uniwill_write_super_key_enable(struct uniwill_data *data, bool status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; - bool enable; int ret; - ret = kstrtobool(buf, &enable); - if (ret < 0) - return ret; - guard(mutex)(&data->super_key_lock); ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); @@ -680,20 +693,33 @@ static ssize_t super_key_enable_store(struct device *dev, struct device_attribut * We can only toggle the super key lock, so we return early if the setting * is already in the correct state. */ - if (enable == !(value & SUPER_KEY_LOCK_STATUS)) - return count; + if (status == !(value & SUPER_KEY_LOCK_STATUS)) + return 0; - ret = regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, - TRIGGER_SUPER_KEY_LOCK); + return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, + TRIGGER_SUPER_KEY_LOCK); +} + +static ssize_t super_key_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool enable; + int ret; + + ret = kstrtobool(buf, &enable); + if (ret < 0) + return ret; + + ret = uniwill_write_super_key_enable(data, enable); if (ret < 0) return ret; return count; } -static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +static int uniwill_read_super_key_enable(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -701,7 +727,22 @@ static ssize_t super_key_enable_show(struct device *dev, struct device_attribute if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !(value & SUPER_KEY_LOCK_STATUS)); + *status = !(value & SUPER_KEY_LOCK_STATUS); + + return 0; +} + +static ssize_t super_key_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_super_key_enable(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(super_key_enable); @@ -1723,10 +1764,10 @@ static int uniwill_suspend_fn_lock(struct uniwill_data *data) return 0; /* - * The EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it + * EC_ADDR_BIOS_OEM is marked as volatile, so we have to restore it * ourselves. */ - return regmap_read(data->regmap, EC_ADDR_BIOS_OEM, &data->last_status); + return uniwill_read_fn_lock(data, &data->last_fn_lock_state); } static int uniwill_suspend_super_key(struct uniwill_data *data) @@ -1735,10 +1776,10 @@ static int uniwill_suspend_super_key(struct uniwill_data *data) return 0; /* - * The EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it + * EC_ADDR_SWITCH_STATUS is marked as volatile, so we have to restore it * ourselves. */ - return regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &data->last_switch_status); + return uniwill_read_super_key_enable(data, &data->last_super_key_enable_state); } static int uniwill_suspend_battery(struct uniwill_data *data) @@ -1795,27 +1836,15 @@ static int uniwill_resume_fn_lock(struct uniwill_data *data) if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return 0; - return regmap_update_bits(data->regmap, EC_ADDR_BIOS_OEM, FN_LOCK_STATUS, - data->last_status); + return uniwill_write_fn_lock(data, data->last_fn_lock_state); } static int uniwill_resume_super_key(struct uniwill_data *data) { - unsigned int value; - int ret; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_SUPER_KEY)) return 0; - ret = regmap_read(data->regmap, EC_ADDR_SWITCH_STATUS, &value); - if (ret < 0) - return ret; - - if ((data->last_switch_status & SUPER_KEY_LOCK_STATUS) == (value & SUPER_KEY_LOCK_STATUS)) - return 0; - - return regmap_write_bits(data->regmap, EC_ADDR_TRIGGER, TRIGGER_SUPER_KEY_LOCK, - TRIGGER_SUPER_KEY_LOCK); + return uniwill_write_super_key_enable(data, data->last_super_key_enable_state); } static int uniwill_resume_battery(struct uniwill_data *data) From f7bcba269143230923e40cd88b9169ed27f178ae Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:43 +0200 Subject: [PATCH 046/115] platform/x86: uniwill-laptop: Mark EC_ADDR_OEM_4 as volatile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turned out that EC_ADDR_OEM_4 also contains bits with a volatile nature. Mark the whole register as volatile to prepare for the usage of said bits. This also means that we now have to save/restore the touchpad toggle state ourself. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-7-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 72 +++++++++++++++++---- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index c73e280a58bd..b08aa3a3f89d 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -343,6 +343,7 @@ struct uniwill_data { struct mutex battery_lock; /* Protects the list of currently registered batteries */ bool last_fn_lock_state; bool last_super_key_enable_state; + bool last_touchpad_toggle_enable_state; struct mutex super_key_lock; /* Protects the toggling of the super key lock state */ struct list_head batteries; struct mutex led_lock; /* Protects writes to the lightbar registers */ @@ -598,6 +599,7 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg) case EC_ADDR_PWM_2: case EC_ADDR_TRIGGER: case EC_ADDR_SWITCH_STATUS: + case EC_ADDR_OEM_4: case EC_ADDR_CHARGE_CTRL: case EC_ADDR_USB_C_POWER_PRIORITY: return true; @@ -747,11 +749,22 @@ static ssize_t super_key_enable_show(struct device *dev, struct device_attribute static DEVICE_ATTR_RW(super_key_enable); +static int uniwill_write_touchpad_toggle_enable(struct uniwill_data *data, bool status) +{ + unsigned int value; + + if (status) + value = 0; + else + value = TOUCHPAD_TOGGLE_OFF; + + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value); +} + static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct uniwill_data *data = dev_get_drvdata(dev); - unsigned int value; bool enable; int ret; @@ -759,22 +772,15 @@ static ssize_t touchpad_toggle_enable_store(struct device *dev, struct device_at if (ret < 0) return ret; - if (enable) - value = 0; - else - value = TOUCHPAD_TOGGLE_OFF; - - ret = regmap_update_bits(data->regmap, EC_ADDR_OEM_4, TOUCHPAD_TOGGLE_OFF, value); + ret = uniwill_write_touchpad_toggle_enable(data, enable); if (ret < 0) return ret; return count; } -static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr, - char *buf) +static int uniwill_read_touchpad_toggle_enable(struct uniwill_data *data, bool *status) { - struct uniwill_data *data = dev_get_drvdata(dev); unsigned int value; int ret; @@ -782,7 +788,23 @@ static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_att if (ret < 0) return ret; - return sysfs_emit(buf, "%d\n", !(value & TOUCHPAD_TOGGLE_OFF)); + *status = !(value & TOUCHPAD_TOGGLE_OFF); + + return 0; +} + +static ssize_t touchpad_toggle_enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + bool status; + int ret; + + ret = uniwill_read_touchpad_toggle_enable(data, &status); + if (ret < 0) + return ret; + + return sysfs_emit(buf, "%d\n", status); } static DEVICE_ATTR_RW(touchpad_toggle_enable); @@ -1782,6 +1804,18 @@ static int uniwill_suspend_super_key(struct uniwill_data *data) return uniwill_read_super_key_enable(data, &data->last_super_key_enable_state); } +static int uniwill_suspend_touchpad_toggle(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE)) + return 0; + + /* + * EC_ADDR_OEM_4 is marked as volatile, so we have to restore it + * ourselves. + */ + return uniwill_read_touchpad_toggle_enable(data, &data->last_touchpad_toggle_enable_state); +} + static int uniwill_suspend_battery(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) @@ -1817,6 +1851,10 @@ static int uniwill_suspend(struct device *dev) if (ret < 0) return ret; + ret = uniwill_suspend_touchpad_toggle(data); + if (ret < 0) + return ret; + ret = uniwill_suspend_battery(data); if (ret < 0) return ret; @@ -1847,6 +1885,14 @@ static int uniwill_resume_super_key(struct uniwill_data *data) return uniwill_write_super_key_enable(data, data->last_super_key_enable_state); } +static int uniwill_resume_touchpad_toggle(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_TOUCHPAD_TOGGLE)) + return 0; + + return uniwill_write_touchpad_toggle_enable(data, data->last_touchpad_toggle_enable_state); +} + static int uniwill_resume_battery(struct uniwill_data *data) { if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) @@ -1892,6 +1938,10 @@ static int uniwill_resume(struct device *dev) if (ret < 0) return ret; + ret = uniwill_resume_touchpad_toggle(data); + if (ret < 0) + return ret; + ret = uniwill_resume_battery(data); if (ret < 0) return ret; From 73fa957a05e9fd43f204225497d4c98d7c0d0490 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 13 May 2026 01:21:44 +0200 Subject: [PATCH 047/115] platform/x86: uniwill-laptop: Add support for battery charge modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Many Uniwill-based devices do not supports the already existing charge limit functionality, but instead support an alternative interface for controlling the battery charge algorithm. Add support for this interface and update the documentation. Reviewed-by: Werner Sembach Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260512232145.329260-8-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- .../admin-guide/laptops/uniwill-laptop.rst | 17 +- drivers/platform/x86/uniwill/uniwill-acpi.c | 248 ++++++++++++++---- drivers/platform/x86/uniwill/uniwill-wmi.c | 5 +- 3 files changed, 216 insertions(+), 54 deletions(-) diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst index 1f3ca84c7d88..24b41dbab886 100644 --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst @@ -46,11 +46,20 @@ Battery Charging Control .. warning:: Some devices do not properly implement the charging threshold interface. Forcing the driver to enable access to said interface on such devices might damage the battery [1]_. Because of this the driver will not enable said feature even when - using the ``force`` module parameter. + using the ``force`` module parameter. The charging profile interface will be + available instead. -The ``uniwill-laptop`` driver supports controlling the battery charge limit. This happens over -the standard ``charge_control_end_threshold`` power supply sysfs attribute. All values -between 1 and 100 percent are supported. +The ``uniwill-laptop`` driver supports controlling the battery charge limit. This either happens +over the standard ``charge_control_end_threshold`` or ``charge_types`` power supply sysfs attribute, +depending on the device. When using the ``charge_control_end_threshold`` sysfs attribute, all values +between 1 and 100 percent are supported. When using the ``charge_types`` sysfs attribute, the driver +supports switching between the ``Standard``, ``Trickle`` and ``Long Life`` profiles. + +Keep in mind that when using the ``charge_types`` sysfs attribute, the EC firmware will hide the +true charging status of the battery from the operating system, potentially misleading users into +thinking that the charging profile does not work. Checking the ``current_now`` sysfs attribute +tells you the true charging status of the battery even when using the ``charge_types`` sysfs +attribute (0 means that the battery is currently not charging). Additionally the driver signals the presence of battery charging issues through the standard ``health`` power supply sysfs attribute. diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index b08aa3a3f89d..53a05a05c594 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -254,6 +254,10 @@ #define EC_ADDR_OEM_4 0x07A6 #define OVERBOOST_DYN_TEMP_OFF BIT(1) +#define CHARGING_PROFILE_MASK GENMASK(5, 4) +#define CHARGING_PROFILE_HIGH_CAPACITY 0x00 +#define CHARGING_PROFILE_BALANCED 0x01 +#define CHARGING_PROFILE_STATIONARY 0x02 #define TOUCHPAD_TOGGLE_OFF BIT(6) #define EC_ADDR_CHARGE_CTRL 0x07B9 @@ -320,13 +324,15 @@ #define UNIWILL_FEATURE_SUPER_KEY BIT(1) #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) #define UNIWILL_FEATURE_LIGHTBAR BIT(3) -#define UNIWILL_FEATURE_BATTERY BIT(4) -#define UNIWILL_FEATURE_CPU_TEMP BIT(5) -#define UNIWILL_FEATURE_GPU_TEMP BIT(6) -#define UNIWILL_FEATURE_PRIMARY_FAN BIT(7) -#define UNIWILL_FEATURE_SECONDARY_FAN BIT(8) -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(9) -#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(10) +#define UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT BIT(4) +/* Mutually exclusive with the charge limit feature */ +#define UNIWILL_FEATURE_BATTERY_CHARGE_MODES BIT(5) +#define UNIWILL_FEATURE_CPU_TEMP BIT(6) +#define UNIWILL_FEATURE_GPU_TEMP BIT(7) +#define UNIWILL_FEATURE_PRIMARY_FAN BIT(8) +#define UNIWILL_FEATURE_SECONDARY_FAN BIT(9) +#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(10) +#define UNIWILL_FEATURE_USB_C_POWER_PRIORITY BIT(11) enum usb_c_power_priority_options { USB_C_POWER_PRIORITY_CHARGING = 0, @@ -339,8 +345,15 @@ struct uniwill_data { struct regmap *regmap; unsigned int features; struct acpi_battery_hook hook; - unsigned int last_charge_ctrl; struct mutex battery_lock; /* Protects the list of currently registered batteries */ + union { + struct { + /* Protects writes to last_charge_type */ + struct mutex charge_type_lock; + enum power_supply_charge_type last_charge_type; + }; + unsigned int last_charge_ctrl; + }; bool last_fn_lock_state; bool last_super_key_enable_state; bool last_touchpad_toggle_enable_state; @@ -447,6 +460,12 @@ static inline bool uniwill_device_supports(const struct uniwill_data *data, return (data->features & features) == features; } +static inline bool uniwill_device_supports_any(const struct uniwill_data *data, + unsigned int features) +{ + return data->features & features; +} + static int uniwill_ec_reg_write(void *context, unsigned int reg, unsigned int val) { union acpi_object params[2] = { @@ -1432,6 +1451,30 @@ static unsigned int uniwill_sanitize_battery_threshold(unsigned int value) return min(value, 100); } +static int uniwill_read_charge_type(struct uniwill_data *data, enum power_supply_charge_type *type) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_OEM_4, &value); + if (ret < 0) + return ret; + + switch (FIELD_GET(CHARGING_PROFILE_MASK, value)) { + case CHARGING_PROFILE_HIGH_CAPACITY: + *type = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + return 0; + case CHARGING_PROFILE_BALANCED: + *type = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + return 0; + case CHARGING_PROFILE_STATIONARY: + *type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + return 0; + default: + return -EPROTO; + } +} + static int uniwill_get_property(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp, union power_supply_propval *val) @@ -1442,6 +1485,16 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup int ret; switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + /* + * We need to use the cached value here because the charging mode + * reported by the EC might temporarily change when a external power + * source has been connected. + */ + mutex_lock(&data->charge_type_lock); + val->intval = data->last_charge_type; + mutex_unlock(&data->charge_type_lock); + return 0; case POWER_SUPPLY_PROP_HEALTH: ret = power_supply_get_property_direct(psy, POWER_SUPPLY_PROP_PRESENT, &prop); if (ret < 0) @@ -1486,13 +1539,52 @@ static int uniwill_get_property(struct power_supply *psy, const struct power_sup } } +static int uniwill_write_charge_type(struct uniwill_data *data, enum power_supply_charge_type type) +{ + unsigned int value; + + switch (type) { + case POWER_SUPPLY_CHARGE_TYPE_TRICKLE: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_STATIONARY); + break; + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_HIGH_CAPACITY); + break; + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + value = FIELD_PREP(CHARGING_PROFILE_MASK, CHARGING_PROFILE_BALANCED); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(data->regmap, EC_ADDR_OEM_4, CHARGING_PROFILE_MASK, value); +} + +static int uniwill_restore_charge_type(struct uniwill_data *data) +{ + guard(mutex)(&data->charge_type_lock); + + return uniwill_write_charge_type(data, data->last_charge_type); +} + static int uniwill_set_property(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp, const union power_supply_propval *val) { struct uniwill_data *data = drvdata; + int ret; switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + mutex_lock(&data->charge_type_lock); + + ret = uniwill_write_charge_type(data, val->intval); + if (ret >= 0) + data->last_charge_type = val->intval; + + mutex_unlock(&data->charge_type_lock); + + return ret; case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: if (val->intval < 0 || val->intval > 100) return -EINVAL; @@ -1508,21 +1600,41 @@ static int uniwill_property_is_writeable(struct power_supply *psy, const struct power_supply_ext *ext, void *drvdata, enum power_supply_property psp) { - if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) + switch (psp) { + case POWER_SUPPLY_PROP_CHARGE_TYPES: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD: return true; - - return false; + default: + return false; + } } -static const enum power_supply_property uniwill_properties[] = { +static const enum power_supply_property uniwill_charge_limit_properties[] = { POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD, }; -static const struct power_supply_ext uniwill_extension = { +static const struct power_supply_ext uniwill_charge_limit_extension = { .name = DRIVER_NAME, - .properties = uniwill_properties, - .num_properties = ARRAY_SIZE(uniwill_properties), + .properties = uniwill_charge_limit_properties, + .num_properties = ARRAY_SIZE(uniwill_charge_limit_properties), + .get_property = uniwill_get_property, + .set_property = uniwill_set_property, + .property_is_writeable = uniwill_property_is_writeable, +}; + +static const enum power_supply_property uniwill_charge_modes_properties[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, + POWER_SUPPLY_PROP_HEALTH, +}; + +static const struct power_supply_ext uniwill_charge_modes_extension = { + .name = DRIVER_NAME, + .charge_types = BIT(POWER_SUPPLY_CHARGE_TYPE_TRICKLE) | + BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE), + .properties = uniwill_charge_modes_properties, + .num_properties = ARRAY_SIZE(uniwill_charge_modes_properties), .get_property = uniwill_get_property, .set_property = uniwill_set_property, .property_is_writeable = uniwill_property_is_writeable, @@ -1538,7 +1650,13 @@ static int uniwill_add_battery(struct power_supply *battery, struct acpi_battery if (!entry) return -ENOMEM; - ret = power_supply_register_extension(battery, &uniwill_extension, data->dev, data); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + ret = power_supply_register_extension(battery, &uniwill_charge_limit_extension, + data->dev, data); + else + ret = power_supply_register_extension(battery, &uniwill_charge_modes_extension, + data->dev, data); + if (ret < 0) { kfree(entry); return ret; @@ -1567,7 +1685,10 @@ static int uniwill_remove_battery(struct power_supply *battery, struct acpi_batt } } - power_supply_unregister_extension(battery, &uniwill_extension); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + power_supply_unregister_extension(battery, &uniwill_charge_limit_extension); + else + power_supply_unregister_extension(battery, &uniwill_charge_modes_extension); return 0; } @@ -1577,28 +1698,37 @@ static int uniwill_battery_init(struct uniwill_data *data) unsigned int value, threshold, sanitized; int ret; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) - return 0; - - ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); - if (ret < 0) - return ret; - - /* - * The charge control threshold might be initialized with 0 by - * the EC to signal that said threshold is uninitialized. We thus - * need to replace this placeholder value with a valid one (100) - * to signal that we want to take control of battery charging. - * For the sake of completeness we also apply this to other - * invalid threshold values. - */ - threshold = FIELD_GET(CHARGE_CTRL_MASK, value); - sanitized = uniwill_sanitize_battery_threshold(threshold); - if (threshold != sanitized) { - FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized); - ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) { + ret = regmap_read(data->regmap, EC_ADDR_CHARGE_CTRL, &value); if (ret < 0) return ret; + + /* + * The charge control threshold might be initialized with 0 by + * the EC to signal that said threshold is uninitialized. We thus + * need to replace this placeholder value with a valid one (100) + * to signal that we want to take control of battery charging. + * For the sake of completeness we also apply this to other + * invalid threshold values. + */ + threshold = FIELD_GET(CHARGE_CTRL_MASK, value); + sanitized = uniwill_sanitize_battery_threshold(threshold); + if (threshold != sanitized) { + FIELD_MODIFY(CHARGE_CTRL_MASK, &value, sanitized); + ret = regmap_write(data->regmap, EC_ADDR_CHARGE_CTRL, value); + if (ret < 0) + return ret; + } + } else if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { + ret = devm_mutex_init(data->dev, &data->charge_type_lock); + if (ret < 0) + return ret; + + ret = uniwill_read_charge_type(data, &data->last_charge_type); + if (ret < 0) + return ret; + } else { + return 0; } ret = devm_mutex_init(data->dev, &data->battery_lock); @@ -1617,10 +1747,13 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action { struct uniwill_data *data = container_of(nb, struct uniwill_data, nb); struct uniwill_battery_entry *entry; + int ret; switch (action) { case UNIWILL_OSD_BATTERY_ALERT: - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) + if (!uniwill_device_supports_any(data, + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) return NOTIFY_DONE; mutex_lock(&data->battery_lock); @@ -1631,10 +1764,24 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action return NOTIFY_OK; case UNIWILL_OSD_DC_ADAPTER_CHANGED: - if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + if (!uniwill_device_supports_any(data, + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) return NOTIFY_DONE; - return notifier_from_errno(usb_c_power_priority_restore(data)); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) { + ret = uniwill_restore_charge_type(data); + if (ret < 0) + return notifier_from_errno(ret); + } + + if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) { + ret = usb_c_power_priority_restore(data); + if (ret < 0) + return notifier_from_errno(ret); + } + + return NOTIFY_OK; case UNIWILL_OSD_FN_LOCK: if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return NOTIFY_DONE; @@ -1818,7 +1965,7 @@ static int uniwill_suspend_touchpad_toggle(struct uniwill_data *data) static int uniwill_suspend_battery(struct uniwill_data *data) { - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) + if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) return 0; /* @@ -1895,11 +2042,14 @@ static int uniwill_resume_touchpad_toggle(struct uniwill_data *data) static int uniwill_resume_battery(struct uniwill_data *data) { - if (!uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY)) - return 0; + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_MODES)) + return uniwill_restore_charge_type(data); - return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, - data->last_charge_ctrl); + if (uniwill_device_supports(data, UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT)) + return regmap_update_bits(data->regmap, EC_ADDR_CHARGE_CTRL, CHARGE_CTRL_MASK, + data->last_charge_ctrl); + + return 0; } static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) @@ -1978,7 +2128,7 @@ static struct platform_driver uniwill_driver = { static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = { .features = UNIWILL_FEATURE_SUPER_KEY | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -1989,7 +2139,7 @@ static struct uniwill_device_descriptor lapac71h_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2001,7 +2151,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_LIGHTBAR | - UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2587,7 +2737,7 @@ static int __init uniwill_init(void) if (force) { /* Assume that the device supports all features except the charge limit */ - device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY; + device_descriptor.features = UINT_MAX & ~UNIWILL_FEATURE_BATTERY_CHARGE_LIMIT; pr_warn("Enabling potentially unsupported features\n"); } diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c index 097882f10b1e..e97aa988a90c 100644 --- a/drivers/platform/x86/uniwill/uniwill-wmi.c +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c @@ -48,6 +48,7 @@ int devm_uniwill_wmi_register_notifier(struct device *dev, struct notifier_block static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) { u32 value; + int ret; if (obj->type != ACPI_TYPE_INTEGER) return; @@ -56,7 +57,9 @@ static void uniwill_wmi_notify(struct wmi_device *wdev, union acpi_object *obj) dev_dbg(&wdev->dev, "Received WMI event %u\n", value); - blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); + ret = blocking_notifier_call_chain(&uniwill_wmi_chain_head, value, NULL); + if (notifier_to_errno(ret) < 0) + dev_err(&wdev->dev, "Failed to handle event %u\n", value); } /* From 2c5d91f35d0d564c8b4c062b67526e0d2d27fae9 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Wed, 13 May 2026 01:21:45 +0200 Subject: [PATCH 048/115] platform/x86: uniwill-laptop: Enable battery charge modes on supported devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable battery charge modes on supported TUXEDO devices by adding the feature bit to the respective device descriptors. Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260512232145.329260-9-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 53a05a05c594..ab063ead45b9 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -2168,6 +2168,7 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN | @@ -2177,6 +2178,7 @@ static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2187,6 +2189,7 @@ static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __ini static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2198,6 +2201,7 @@ static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __ini static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN, @@ -2206,6 +2210,7 @@ static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = static struct uniwill_device_descriptor tux_featureset_3_nvidia_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_GPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | @@ -2231,6 +2236,7 @@ static int phxtxx1_probe(struct uniwill_data *data) static struct uniwill_device_descriptor phxtxx1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_USB_C_POWER_PRIORITY, @@ -2256,6 +2262,7 @@ static int phxarx1_phxaqf1_probe(struct uniwill_data *data) static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY_CHARGE_MODES | UNIWILL_FEATURE_CPU_TEMP | UNIWILL_FEATURE_PRIMARY_FAN | UNIWILL_FEATURE_SECONDARY_FAN | From 1ab843135a7795b0ef37bd7eaf01056276873fd4 Mon Sep 17 00:00:00 2001 From: Jack Wu Date: Tue, 26 May 2026 18:36:37 +0800 Subject: [PATCH 049/115] platform/x86: dell-dw5826e: Add reset driver for DW5826e MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the DW5826e is in a frozen state and unable to receive USB commands, this driver provides a method for the user to reset the DW5826e via ACPI. E.g: echo 1 > /sys/bus/platform/devices/PALC0001\:00/wwan_reset Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Armin Wolf Signed-off-by: Jack Wu Link: https://patch.msgid.link/20260526-dell-reset-v8-v8-1-d3a29cb4cf2f@compal.com [ij: removed default m] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../testing/sysfs-driver-dell-dw5826e-reset | 9 ++ drivers/platform/x86/dell/Kconfig | 5 + drivers/platform/x86/dell/Makefile | 1 + .../platform/x86/dell/dell-dw5826e-reset.c | 93 +++++++++++++++++++ 4 files changed, 108 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset create mode 100644 drivers/platform/x86/dell/dell-dw5826e-reset.c diff --git a/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset b/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset new file mode 100644 index 000000000000..a665e265633f --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-dell-dw5826e-reset @@ -0,0 +1,9 @@ +What: /sys/bus/platform/devices//wwan_reset +Date: April 2026 +KernelVersion: 7.2 +Contact: Jackbb Wu +Description: + Writing to this file triggers a Platform Level Device Reset + (PLDR) of the Dell DW5826e WWAN module via an ACPI _DSM + method. This can be used to recover the modem when it is in + a frozen state and unable to respond to USB commands. diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index 738c108c2163..9f0016056d7e 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -276,4 +276,9 @@ config DELL_WMI_SYSMAN To compile this driver as a module, choose M here: the module will be called dell-wmi-sysman. +config DELL_DW5826E_RESET + tristate "Dell DW5826e PLDR reset support" + depends on ACPI + help + This adds support for the Dell DW5826e PLDR reset via ACPI endif # X86_PLATFORM_DRIVERS_DELL diff --git a/drivers/platform/x86/dell/Makefile b/drivers/platform/x86/dell/Makefile index c7501c25e627..470874c6e6e6 100644 --- a/drivers/platform/x86/dell/Makefile +++ b/drivers/platform/x86/dell/Makefile @@ -28,3 +28,4 @@ obj-$(CONFIG_DELL_WMI_DESCRIPTOR) += dell-wmi-descriptor.o obj-$(CONFIG_DELL_WMI_DDV) += dell-wmi-ddv.o obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o obj-$(CONFIG_DELL_WMI_SYSMAN) += dell-wmi-sysman/ +obj-$(CONFIG_DELL_DW5826E_RESET) += dell-dw5826e-reset.o diff --git a/drivers/platform/x86/dell/dell-dw5826e-reset.c b/drivers/platform/x86/dell/dell-dw5826e-reset.c new file mode 100644 index 000000000000..1ca7c3421bb5 --- /dev/null +++ b/drivers/platform/x86/dell/dell-dw5826e-reset.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * dell-dw5826e-reset.c - Dell DW5826e reset driver + * + * Copyright (C) 2026 Jackbb Wu + */ + +#include +#include +#include +#include +#include +#include +#include + +#define PALC_DSM_FN_TRIGGER_PLDR BIT(1) + +static guid_t palc_dsm_guid = + GUID_INIT(0x5a1a4bba, 0x8006, 0x487e, 0xbe, 0x0a, 0xac, 0xf5, 0xd8, 0xfd, 0xfe, 0x59); + +static int trigger_palc_pldr(struct device *dev, acpi_handle handle) +{ + union acpi_object *obj; + int ret = 0; + + obj = acpi_evaluate_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR, NULL); + if (!obj) { + dev_err(dev, "Failed to evaluate _DSM\n"); + return -EIO; + } + + if (obj->type != ACPI_TYPE_BUFFER) { + dev_err(dev, "Unexpected _DSM return type: %d\n", obj->type); + ret = -EINVAL; + } + + ACPI_FREE(obj); + return ret; +} + +static ssize_t wwan_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + acpi_handle handle = ACPI_HANDLE(dev); + int ret; + + ret = trigger_palc_pldr(dev, handle); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(wwan_reset); + +static struct attribute *palc_attrs[] = { + &dev_attr_wwan_reset.attr, + NULL +}; +ATTRIBUTE_GROUPS(palc); + +static int palc_probe(struct platform_device *pdev) +{ + acpi_handle handle; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + if (!acpi_check_dsm(handle, &palc_dsm_guid, 1, PALC_DSM_FN_TRIGGER_PLDR)) + return -ENODEV; + + return 0; +} + +static const struct acpi_device_id palc_acpi_ids[] = { + { "PALC0001", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, palc_acpi_ids); + +static struct platform_driver palc_driver = { + .driver = { + .name = "dell-dw5826e-reset", + .acpi_match_table = palc_acpi_ids, + .dev_groups = palc_groups, + }, + .probe = palc_probe, +}; +module_platform_driver(palc_driver); + +MODULE_DESCRIPTION("Dell DW5826e reset driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("JackBB Wu"); From a2a893d5757a0d43c0a2ed351eb1e34ab718f2fe Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:34 +0000 Subject: [PATCH 050/115] platform/x86: lenovo-wmi-other: Add missing CPU tunable attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use an enum for all device ID's and CPU attribute feature ID's, add missing CPU attributes. Reviewed-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-2-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/lenovo-wmi-other.rst | 9 ++ drivers/platform/x86/lenovo/wmi-capdata.h | 5 +- drivers/platform/x86/lenovo/wmi-other.c | 99 ++++++++++++++++++- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst index 01d471156738..189dd4d31926 100644 --- a/Documentation/wmi/devices/lenovo-wmi-other.rst +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -68,9 +68,18 @@ Each attribute has the following properties: - type The following firmware-attributes are implemented: + - cpu_temp: CPU Thermal Load Limit + - ppt_cpu_cl: CPU Cross Loading Power Limit + - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit + - ppt_pl1_spl_cl: Platform Profile Tracking Cross Loading Sustained Power Limit + - ppt_pl1_tau: Exceed Duration for Platform Profile Tracking Sustained Power Limit - ppt_pl2_sppt: Platform Profile Tracking Slow Package Power Tracking + - ppt_pl2_sppt_cl: Platform Profile Tracking Cross Loading Slow Package Tracking - ppt_pl3_fppt: Platform Profile Tracking Fast Package Power Tracking + - ppt_pl3_fppt_cl: Platform Profile Tracking Cross Loading Fast Package Power Tracking + - ppt_pl4_ipl: Platform Profile Tracking Instantaneous Power Limit + - ppt_pl4_ipl_cl: Platform Profile Tracking Cross Loading Instantaneous Power Limit LENOVO_FAN_TEST_DATA ------------------------- diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index c3e760b8c3c3..c74a0e5294e7 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -18,7 +18,10 @@ #define LWMI_ATTR_MODE_ID_MASK GENMASK(15, 8) #define LWMI_ATTR_TYPE_ID_MASK GENMASK(7, 0) -#define LWMI_DEVICE_ID_FAN 0x04 +enum lwmi_device_id { + LWMI_DEVICE_ID_CPU = 0x01, + LWMI_DEVICE_ID_FAN = 0x04, +}; #define LWMI_TYPE_ID_NONE 0x00 diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index d318ba432fdc..fe4cd7da017e 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -51,14 +51,21 @@ #define LENOVO_OTHER_MODE_GUID "DC2A8805-3A8C-41BA-A6F7-092E0089CD3B" -#define LWMI_DEVICE_ID_CPU 0x01 - -#define LWMI_FEATURE_ID_CPU_SPPT 0x01 -#define LWMI_FEATURE_ID_CPU_SPL 0x02 -#define LWMI_FEATURE_ID_CPU_FPPT 0x03 +enum lwmi_feature_id_cpu { + LWMI_FEATURE_ID_CPU_SPPT = 0x01, + LWMI_FEATURE_ID_CPU_SPL = 0x02, + LWMI_FEATURE_ID_CPU_FPPT = 0x03, + LWMI_FEATURE_ID_CPU_TEMP = 0x04, + LWMI_FEATURE_ID_CPU_APU = 0x05, + LWMI_FEATURE_ID_CPU_CL = 0x06, + LWMI_FEATURE_ID_CPU_TAU = 0x07, + LWMI_FEATURE_ID_CPU_IPL = 0x09, +}; #define LWMI_FEATURE_ID_FAN_RPM 0x03 +#define LWMI_TYPE_ID_CROSSLOAD 0x01 + #define LWMI_FEATURE_VALUE_GET 17 #define LWMI_FEATURE_VALUE_SET 18 @@ -566,18 +573,72 @@ static struct tunable_attr_01 ppt_pl1_spl = { .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl1_spl_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPL, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + static struct tunable_attr_01 ppt_pl2_sppt = { .device_id = LWMI_DEVICE_ID_CPU, .feature_id = LWMI_FEATURE_ID_CPU_SPPT, .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl2_sppt_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_SPPT, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + static struct tunable_attr_01 ppt_pl3_fppt = { .device_id = LWMI_DEVICE_ID_CPU, .feature_id = LWMI_FEATURE_ID_CPU_FPPT, .type_id = LWMI_TYPE_ID_NONE, }; +static struct tunable_attr_01 ppt_pl3_fppt_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_FPPT, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + +static struct tunable_attr_01 cpu_temp = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_TEMP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl1_apu_spl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_APU, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_cpu_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_CL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl1_tau = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_TAU, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl4_ipl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_IPL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 ppt_pl4_ipl_cl = { + .device_id = LWMI_DEVICE_ID_CPU, + .feature_id = LWMI_FEATURE_ID_CPU_IPL, + .type_id = LWMI_TYPE_ID_CROSSLOAD, +}; + struct capdata01_attr_group { const struct attribute_group *attr_group; struct tunable_attr_01 *tunable_attr; @@ -913,17 +974,45 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) .name = _fsname, .attrs = _attrname##_attrs \ } +LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp", + "Set the CPU thermal load limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl", + "Set the CPU cross loading power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_apu_spl, "ppt_pl1_apu_spl", + "Set the APU sustained power limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl, "ppt_pl1_spl", "Set the CPU sustained power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_spl_cl, "ppt_pl1_spl_cl", + "Set the CPU cross loading sustained power limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt, "ppt_pl2_sppt", "Set the CPU slow package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl2_sppt_cl, "ppt_pl2_sppt_cl", + "Set the CPU cross loading slow package power tracking limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt, "ppt_pl3_fppt", "Set the CPU fast package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl3_fppt_cl, "ppt_pl3_fppt_cl", + "Set the CPU cross loading fast package power tracking limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl1_tau, "ppt_pl1_tau", + "Set the CPU sustained power limit exceed duration"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl", + "Set the CPU instantaneous power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl", + "Set the CPU cross loading instantaneous power limit"); + static struct capdata01_attr_group cd01_attr_groups[] = { + { &cpu_temp_attr_group, &cpu_temp }, + { &ppt_cpu_cl_attr_group, &ppt_cpu_cl }, + { &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl }, { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, + { &ppt_pl1_spl_cl_attr_group, &ppt_pl1_spl_cl }, + { &ppt_pl1_tau_attr_group, &ppt_pl1_tau }, { &ppt_pl2_sppt_attr_group, &ppt_pl2_sppt }, + { &ppt_pl2_sppt_cl_attr_group, &ppt_pl2_sppt_cl }, { &ppt_pl3_fppt_attr_group, &ppt_pl3_fppt }, + { &ppt_pl3_fppt_cl_attr_group, &ppt_pl3_fppt_cl }, + { &ppt_pl4_ipl_attr_group, &ppt_pl4_ipl }, + { &ppt_pl4_ipl_cl_attr_group, &ppt_pl4_ipl_cl }, {}, }; From b51fa3b00e0339bebeac7d0c1beb13f494f6fe41 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:35 +0000 Subject: [PATCH 051/115] platform/x86: lenovo-wmi-other: Add GPU tunable attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use an enum for all GPU attribute feature ID's and add GPU attributes. Reviewed-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-3-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/lenovo-wmi-other.rst | 10 ++ drivers/platform/x86/lenovo/wmi-capdata.h | 1 + drivers/platform/x86/lenovo/wmi-other.c | 105 ++++++++++++++++++ 3 files changed, 116 insertions(+) diff --git a/Documentation/wmi/devices/lenovo-wmi-other.rst b/Documentation/wmi/devices/lenovo-wmi-other.rst index 189dd4d31926..011054d64eac 100644 --- a/Documentation/wmi/devices/lenovo-wmi-other.rst +++ b/Documentation/wmi/devices/lenovo-wmi-other.rst @@ -69,6 +69,16 @@ Each attribute has the following properties: The following firmware-attributes are implemented: - cpu_temp: CPU Thermal Load Limit + - dgpu_boost_clk: Dedicated GPU Boost Clock + - dgpu_didvid: Dedicated GPU Device Identifier and Vendor Identifier + - dgpu_enable: Dedicated GPU Enabled Status + - gpu_mode: GPU Mode by Power Limit + - gpu_nv_ac_offset: Nvidia GPU AC Total Processing Power Baseline Offset + - gpu_nv_bpl: Nvidia GPU Base Power Limit + - gpu_nv_cpu_boost: Nvidia GPU to CPU Dynamic Boost Limit + - gpu_nv_ctgp: Nvidia GPU Configurable Total Graphics Power + - gpu_nv_ppab: Nvidia GPU Power Performance Aware Boost Limit + - gpu_temp: GPU Thermal Load Limit - ppt_cpu_cl: CPU Cross Loading Power Limit - ppt_pl1_apu_spl: Platform Profile Tracking APU Sustained Power Limit - ppt_pl1_spl: Platform Profile Tracking Sustained Power Limit diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index c74a0e5294e7..a7cfdeaa58f7 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -20,6 +20,7 @@ enum lwmi_device_id { LWMI_DEVICE_ID_CPU = 0x01, + LWMI_DEVICE_ID_GPU = 0x02, LWMI_DEVICE_ID_FAN = 0x04, }; diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index fe4cd7da017e..9a6183bbcc92 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -62,6 +62,19 @@ enum lwmi_feature_id_cpu { LWMI_FEATURE_ID_CPU_IPL = 0x09, }; +enum lwmi_feature_id_gpu { + LWMI_FEATURE_ID_GPU_NV_PPAB = 0x01, + LWMI_FEATURE_ID_GPU_NV_CTGP = 0x02, + LWMI_FEATURE_ID_GPU_TEMP = 0x03, + LWMI_FEATURE_ID_GPU_AC_OFFSET = 0x04, + LWMI_FEATURE_ID_DGPU_BOOST_CLK = 0x06, + LWMI_FEATURE_ID_DGPU_EN = 0x07, + LWMI_FEATURE_ID_GPU_MODE = 0x08, + LWMI_FEATURE_ID_DGPU_DIDVID = 0x09, + LWMI_FEATURE_ID_GPU_NV_BPL = 0x0a, + LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b, +}; + #define LWMI_FEATURE_ID_FAN_RPM 0x03 #define LWMI_TYPE_ID_CROSSLOAD 0x01 @@ -639,6 +652,66 @@ static struct tunable_attr_01 ppt_pl4_ipl_cl = { .type_id = LWMI_TYPE_ID_CROSSLOAD, }; +static struct tunable_attr_01 gpu_nv_ppab = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_PPAB, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_ctgp = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_CTGP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_temp = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_TEMP, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_ac_offset = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_AC_OFFSET, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_boost_clk = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_BOOST_CLK, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_enable = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_EN, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_mode = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_MODE, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 dgpu_didvid = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_DGPU_DIDVID, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_bpl = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_BPL, + .type_id = LWMI_TYPE_ID_NONE, +}; + +static struct tunable_attr_01 gpu_nv_cpu_boost = { + .device_id = LWMI_DEVICE_ID_GPU, + .feature_id = LWMI_FEATURE_ID_GPU_NV_CPU_BOOST, + .type_id = LWMI_TYPE_ID_NONE, +}; + struct capdata01_attr_group { const struct attribute_group *attr_group; struct tunable_attr_01 *tunable_attr; @@ -974,6 +1047,7 @@ static bool lwmi_attr_01_is_supported(struct tunable_attr_01 *tunable_attr) .name = _fsname, .attrs = _attrname##_attrs \ } +/* CPU tunable attributes */ LWMI_ATTR_GROUP_TUNABLE_CAP01(cpu_temp, "cpu_temp", "Set the CPU thermal load limit"); LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_cpu_cl, "ppt_cpu_cl", @@ -999,9 +1073,40 @@ LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl, "ppt_pl4_ipl", LWMI_ATTR_GROUP_TUNABLE_CAP01(ppt_pl4_ipl_cl, "ppt_pl4_ipl_cl", "Set the CPU cross loading instantaneous power limit"); +/* GPU tunable attributes */ +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_boost_clk, "dgpu_boost_clk", + "Set the dedicated GPU boost clock"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_didvid, "dgpu_didvid", + "Get the GPU device identifier and vendor identifier"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(dgpu_enable, "dgpu_enable", + "Set the dedicated Nvidia GPU enabled status"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_mode, "gpu_mode", + "Set the GPU mode by power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ac_offset, "gpu_nv_ac_offset", + "Set the Nvidia GPU AC total processing power baseline offset"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_bpl, "gpu_nv_bpl", + "Set the Nvidia GPU base power limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_cpu_boost, "gpu_nv_cpu_boost", + "Set the Nvidia GPU to CPU dynamic boost limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ctgp, "gpu_nv_ctgp", + "Set the GPU configurable total graphics power"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_nv_ppab, "gpu_nv_ppab", + "Set the Nvidia GPU power performance aware boost limit"); +LWMI_ATTR_GROUP_TUNABLE_CAP01(gpu_temp, "gpu_temp", + "Set the GPU thermal load limit"); static struct capdata01_attr_group cd01_attr_groups[] = { { &cpu_temp_attr_group, &cpu_temp }, + { &dgpu_boost_clk_attr_group, &dgpu_boost_clk }, + { &dgpu_didvid_attr_group, &dgpu_didvid }, + { &dgpu_enable_attr_group, &dgpu_enable }, + { &gpu_mode_attr_group, &gpu_mode }, + { &gpu_nv_ac_offset_attr_group, &gpu_nv_ac_offset }, + { &gpu_nv_bpl_attr_group, &gpu_nv_bpl }, + { &gpu_nv_cpu_boost_attr_group, &gpu_nv_cpu_boost }, + { &gpu_nv_ctgp_attr_group, &gpu_nv_ctgp }, + { &gpu_nv_ppab_attr_group, &gpu_nv_ppab }, + { &gpu_temp_attr_group, &gpu_temp }, { &ppt_cpu_cl_attr_group, &ppt_cpu_cl }, { &ppt_pl1_apu_spl_attr_group, &ppt_pl1_apu_spl }, { &ppt_pl1_spl_attr_group, &ppt_pl1_spl }, From 891d43749445fed6725acf9ee1c37e052dba8288 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:36 +0000 Subject: [PATCH 052/115] platform/x86: lenovo-wmi-other: Rename LWMI_OM_FW_ATTR_BASE_PATH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the next patch a power supply extension is added which requires a name attribute. Instead of creating another const macro with the same information, rename LWMI_OM_FW_ATTR_BASE_PATH to LWMI_OM_SYSFS_NAME. Reviewed-by: Rong Zhang Tested-by: Rong Zhang Reviewed-by: Mark Pearson Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-4-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-other.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index 9a6183bbcc92..beb92c7cff25 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -92,7 +92,7 @@ enum lwmi_feature_id_gpu { lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) -#define LWMI_OM_FW_ATTR_BASE_PATH "lenovo-wmi-other" +#define LWMI_OM_SYSFS_NAME "lenovo-wmi-other" #define LWMI_OM_HWMON_NAME "lenovo_wmi_other" static DEFINE_IDA(lwmi_om_ida); @@ -1138,8 +1138,7 @@ static void lwmi_om_fw_attr_add(struct lwmi_om_priv *priv) priv->fw_attr_dev = device_create(&firmware_attributes_class, NULL, MKDEV(0, 0), NULL, "%s-%u", - LWMI_OM_FW_ATTR_BASE_PATH, - priv->ida_id); + LWMI_OM_SYSFS_NAME, priv->ida_id); if (IS_ERR(priv->fw_attr_dev)) { err = PTR_ERR(priv->fw_attr_dev); goto err_free_ida; From 9ca8fc065b88b327acbfdc33454efea391639716 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:37 +0000 Subject: [PATCH 053/115] platform/x86: lenovo-wmi-other: Add WMI battery charge limiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add charge_behaviour and charge_types attributes through a power supply extension for devices that support WMI based charge enable & disable. Lenovo Legion devices that implement WMI function and capdata ID 0x03010001 in their BIOS are able to enable or disable charging at 80% through the lenovo-wmi-other interface. Add a charge_types attribute for BATX devices to expose this capability with Standard and Long_Life types enabled. Additionally, devices that support WMI function and capdata ID 0x03020000 are able to force discharge of the battery. Expose this capability with a charge_behaviour attribute in the power supply extension, with the auto and force-discharge behaviors enabled. The GET method for this attribute is bugged. After analyzing the DSDT, and some testing, it appears the method grabs bit(3) instead of bit(4) from the EC register that stores the current status, and will only report if charging has been inhibited or not. To work around this, store and report the last setting written to the attribute. As some devices only expose one attribute or the other, a bitmask is added with a lookup table and some helper macros to select the correct configuration for the hardware at runtime. The ideapad_laptop driver provides the charge_types attribute to provide similar functionality. When the WMI method is set this can corrupt the ACPI method return and cause hardware and driver errors. To avoid conflicts between the drivers, we get the acpi_handle and do the same check that ideapad_laptop does when it enables the feature. If the feature is supported in ideapad_laptop, abort adding the extension from lenovo-wmi-other. The ACPI method is more reliable when both are present from my testing, so we can prefer that implementation and do not need to worry about de-conflicting from inside that driver. Reviewed-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-5-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 1 + drivers/platform/x86/lenovo/wmi-capdata.h | 1 + drivers/platform/x86/lenovo/wmi-other.c | 391 ++++++++++++++++++++++ 3 files changed, 393 insertions(+) diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index 09b1b055d2e0..b9a5d18caa1e 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -262,6 +262,7 @@ config LENOVO_WMI_GAMEZONE config LENOVO_WMI_TUNING tristate "Lenovo Other Mode WMI Driver" depends on ACPI_WMI + depends on ACPI_BATTERY select HWMON select FW_ATTR_CLASS select LENOVO_WMI_CAPDATA diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index a7cfdeaa58f7..a49229cec245 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -21,6 +21,7 @@ enum lwmi_device_id { LWMI_DEVICE_ID_CPU = 0x01, LWMI_DEVICE_ID_GPU = 0x02, + LWMI_DEVICE_ID_PSU = 0x03, LWMI_DEVICE_ID_FAN = 0x04, }; diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index beb92c7cff25..8331da864beb 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -41,9 +41,12 @@ #include #include #include +#include #include #include +#include + #include "wmi-capdata.h" #include "wmi-events.h" #include "wmi-helpers.h" @@ -75,9 +78,15 @@ enum lwmi_feature_id_gpu { LWMI_FEATURE_ID_GPU_NV_CPU_BOOST = 0x0b, }; +enum lwmi_feature_id_psu { + LWMI_FEATURE_ID_PSU_CHARGE_TYPES = 0x01, + LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR = 0x02, +}; + #define LWMI_FEATURE_ID_FAN_RPM 0x03 #define LWMI_TYPE_ID_CROSSLOAD 0x01 +#define LWMI_TYPE_ID_PSU_AC 0x01 #define LWMI_FEATURE_VALUE_GET 17 #define LWMI_FEATURE_VALUE_SET 18 @@ -88,10 +97,19 @@ enum lwmi_feature_id_gpu { #define LWMI_FAN_DIV 100 +#define LWMI_CHARGE_BEHAVIOR_DISCHARGE 0x00 +#define LWMI_CHARGE_BEHAVIOR_AUTO 0x01 +#define LWMI_CHARGE_TYPE_STANDARD 0x00 +#define LWMI_CHARGE_TYPE_LONGLIFE 0x01 + #define LWMI_ATTR_ID_FAN_RPM(x) \ lwmi_attr_id(LWMI_DEVICE_ID_FAN, LWMI_FEATURE_ID_FAN_RPM, \ LWMI_GZ_THERMAL_MODE_NONE, LWMI_FAN_ID(x)) +#define LWMI_ATTR_ID_PSU(feat, type) \ + lwmi_attr_id(LWMI_DEVICE_ID_PSU, feat, \ + LWMI_GZ_THERMAL_MODE_NONE, type) + #define LWMI_OM_SYSFS_NAME "lenovo-wmi-other" #define LWMI_OM_HWMON_NAME "lenovo_wmi_other" @@ -131,6 +149,11 @@ struct lwmi_om_priv { bool capdata00_collected : 1; bool capdata_fan_collected : 1; } fan_flags; + + enum power_supply_charge_behaviour charge_behaviour; + const struct power_supply_ext *battery_ext; + struct acpi_battery_hook battery_hook; + bool bh_registered; }; /* @@ -557,6 +580,371 @@ static void lwmi_om_fan_info_collect_cd_fan(struct device *dev, struct cd_list * lwmi_om_hwmon_add(priv); } +/* ======== Power Supply Extension (component: lenovo-wmi-capdata 00) ======== */ + +/** + * lwmi_psy_ext_get_prop() - Get a power_supply_ext property + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer to the lwmi_om_priv drvdata + * @prop: The property to read + * @val: The value to return + * + * Reads the given value from the power_supply_ext property + * + * Return: 0 on success, or an error + */ +static int lwmi_psy_ext_get_prop(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct lwmi_om_priv *priv = ext_data; + struct wmi_method_args_32 args = {}; + u32 retval; + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + /* Reading from BIOS reads the wrong bit. Use cached value */ + val->intval = priv->charge_behaviour; + return 0; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + break; + default: + return -EINVAL; + } + + ret = lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_GET, + (u8 *)&args, sizeof(args), + &retval); + if (ret) + return ret; + + dev_dbg(&priv->wdev->dev, "Got return value %#x for property %#x\n", retval, prop); + + switch (retval) { + case LWMI_CHARGE_TYPE_LONGLIFE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_LONGLIFE; + break; + case LWMI_CHARGE_TYPE_STANDARD: + val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n", retval); + return -EINVAL; + } + + return 0; +} + +/** + * lwmi_psy_ext_set_prop() - Set a power_supply_ext property + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer to the lwmi_om_priv drvdata + * @prop: The property to write + * @val: The value to write + * + * Writes the given value to the power_supply_ext property + * + * Return: 0 on success, or an error + */ +static int lwmi_psy_ext_set_prop(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct lwmi_om_priv *priv = ext_data; + struct wmi_method_args_32 args = {}; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR, + LWMI_TYPE_ID_NONE); + switch (val->intval) { + case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO: + args.arg1 = LWMI_CHARGE_BEHAVIOR_AUTO; + break; + case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE: + args.arg1 = LWMI_CHARGE_BEHAVIOR_DISCHARGE; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge behavior value: %#x\n", + val->intval); + return -EINVAL; + } + priv->charge_behaviour = val->intval; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + args.arg0 = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_LONGLIFE: + args.arg1 = LWMI_CHARGE_TYPE_LONGLIFE; + break; + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + args.arg1 = LWMI_CHARGE_TYPE_STANDARD; + break; + default: + dev_err(&priv->wdev->dev, "Got invalid charge types value: %#x\n", + val->intval); + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + dev_dbg(&priv->wdev->dev, "Attempting to set %#010x for property %#x to %#x\n", + args.arg0, prop, args.arg1); + + return lwmi_dev_evaluate_int(priv->wdev, 0x0, LWMI_FEATURE_VALUE_SET, + (u8 *)&args, sizeof(args), NULL); +} + +/** lwmi_psy_prop_get_supported() - Gets the support level from capdata for a given property + * @priv: Pointer to the lwmi_om_priv drvdata + * @prop: The power supply property to be evaluated + * + * Return: bitmapped capability data support level + */ +static u32 lwmi_psy_prop_get_supported(struct lwmi_om_priv *priv, enum power_supply_property prop) +{ + struct capdata00 capdata; + u32 attribute_id; + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR: + attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_BEHAVIOUR, + LWMI_TYPE_ID_NONE); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPES: + attribute_id = LWMI_ATTR_ID_PSU(LWMI_FEATURE_ID_PSU_CHARGE_TYPES, + LWMI_TYPE_ID_PSU_AC); + break; + default: + return 0; + } + + ret = lwmi_cd00_get_data(priv->cd00_list, attribute_id, &capdata); + if (ret) + return 0; + + dev_dbg(&priv->wdev->dev, "Battery charge feature (%#010x) support level: %#x\n", + attribute_id, capdata.supported); + + return capdata.supported; +} + +/** + * lwmi_psy_prop_is_supported() - Determine if the property is supported + * @priv: Pointer to the lwmi_om_priv drvdata + * @prop: The power supply property to be evaluated + * + * Checks capdata 00 to determine if the property is supported. + * + * Return: true if readable, or false + */ +static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv, enum power_supply_property prop) +{ + int ret; + + ret = lwmi_psy_prop_get_supported(priv, prop); + if (ret < 0) + return false; + + return (ret & LWMI_SUPP_VALID) && (ret & LWMI_SUPP_GET); +} + +/** + * lwmi_psy_prop_is_writeable() - Determine if the property is writeable + * @ps: The battery that was extended + * @ext: The extension + * @ext_data: Pointer the lwmi_om_priv drvdata + * @prop: The property to check + * + * Checks capdata 00 to determine if the property is writable. + * + * Return: true if writable, or false + */ +static int lwmi_psy_prop_is_writeable(struct power_supply *ps, + const struct power_supply_ext *ext, + void *ext_data, + enum power_supply_property prop) +{ + struct lwmi_om_priv *priv = ext_data; + int ret; + + ret = lwmi_psy_prop_get_supported(priv, prop); + if (ret < 0) + return false; + + return !!(ret & LWMI_SUPP_SET); +} + +#define DEFINE_LWMI_POWER_SUPPLY_EXTENSION(_name, _props, _behaviours, _types) \ + static const struct power_supply_ext _name = { \ + .name = LWMI_OM_SYSFS_NAME, \ + .properties = _props, \ + .num_properties = ARRAY_SIZE(_props), \ + .charge_behaviours = _behaviours, \ + .charge_types = _types, \ + .get_property = lwmi_psy_ext_get_prop, \ + .set_property = lwmi_psy_ext_set_prop, \ + .property_is_writeable = lwmi_psy_prop_is_writeable, \ + } + +static const enum power_supply_property lwmi_psy_ext_props_all[] = { + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const enum power_supply_property lwmi_psy_ext_props_types[] = { + POWER_SUPPLY_PROP_CHARGE_TYPES, +}; + +static const enum power_supply_property lwmi_psy_ext_props_behaviour[] = { + POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR, +}; + +#define LWMI_CHARGE_BEHAVIOURS (BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO) | \ + BIT(POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE)) + +#define LWMI_CHARGE_TYPES (BIT(POWER_SUPPLY_CHARGE_TYPE_STANDARD) | \ + BIT(POWER_SUPPLY_CHARGE_TYPE_LONGLIFE)) + +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_all, lwmi_psy_ext_props_all, + LWMI_CHARGE_BEHAVIOURS, LWMI_CHARGE_TYPES); +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_types, lwmi_psy_ext_props_types, + 0, LWMI_CHARGE_TYPES); +DEFINE_LWMI_POWER_SUPPLY_EXTENSION(lwmi_psy_ext_behaviour, lwmi_psy_ext_props_behaviour, + LWMI_CHARGE_BEHAVIOURS, 0); + +#define LWMI_PSY_PROP_BEHAVIOUR BIT(0) +#define LWMI_PSY_PROP_TYPES BIT(1) + +static const struct power_supply_ext *lwmi_psy_exts[] = { + [LWMI_PSY_PROP_BEHAVIOUR] = &lwmi_psy_ext_behaviour, + [LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_types, + [LWMI_PSY_PROP_BEHAVIOUR | LWMI_PSY_PROP_TYPES] = &lwmi_psy_ext_all, +}; + +/** + * lwmi_add_battery() - Connect the power_supply_ext + * @battery: The battery to extend + * @hook: The driver hook used to extend the battery + * + * Return: 0 on success, or an error. + */ +static int lwmi_add_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook); + + return power_supply_register_extension(battery, priv->battery_ext, &priv->wdev->dev, priv); +} + +/** + * lwmi_remove_battery() - Disconnect the power_supply_ext + * @battery: The battery that was extended + * @hook: The driver hook used to extend the battery + * + * Return: 0 on success, or an error. + */ +static int lwmi_remove_battery(struct power_supply *battery, struct acpi_battery_hook *hook) +{ + struct lwmi_om_priv *priv = container_of(hook, struct lwmi_om_priv, battery_hook); + + power_supply_unregister_extension(battery, priv->battery_ext); + return 0; +} + +/** + * lwmi_acpi_match() - Attempts to return the ideapad acpi handle + * @handle: The ACPI handle that manages battery charging + * @lvl: Unused + * @context: Void pointer to the acpi_handle object to return + * @retval: Unused + * + * Checks if the ideapad_laptop driver is going to manage charge_type first, + * then if not, hooks the battery to our WMI methods. + * + * Return: AE_CTRL_TERMINATE if found, AE_OK if not found. + */ +static acpi_status lwmi_acpi_match(acpi_handle handle, u32 lvl, + void *context, void **retval) +{ + acpi_handle *ahand = context; + + if (!handle) + return AE_OK; + + *ahand = handle; + + return AE_CTRL_TERMINATE; +} + +/** + * lwmi_om_psy_ext_init() - Hooks power supply extension to device battery + * @priv: Pointer to the lwmi_om_priv drvdata. + * + * Checks if the ideapad_laptop driver is going to manage charge attributes first, + * then if not, hooks the battery to our WMI methods if they are supported. + */ +static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) +{ + static const char * const ideapad_hid = "VPC2004"; + acpi_handle handle = NULL; + unsigned int props = 0; + int ret; + + if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR)) + props |= LWMI_PSY_PROP_BEHAVIOUR; + if (lwmi_psy_prop_is_supported(priv, POWER_SUPPLY_PROP_CHARGE_TYPES)) + props |= LWMI_PSY_PROP_TYPES; + if (!props) + return; + + /* Deconflict ideapad_laptop driver */ + ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL); + if (ret) + return; + + if (handle && acpi_has_method(handle, "GBMD") && acpi_has_method(handle, "SBMC")) { + dev_dbg(&priv->wdev->dev, "ideapad_laptop driver manages battery for device\n"); + return; + } + + /* Add battery hooks */ + priv->battery_ext = lwmi_psy_exts[props]; + priv->battery_hook.add_battery = lwmi_add_battery; + priv->battery_hook.remove_battery = lwmi_remove_battery; + priv->battery_hook.name = "Lenovo WMI Other Battery Extension"; + priv->bh_registered = true; + + battery_hook_register(&priv->battery_hook); +} + +/** + * lwmi_om_psy_remove() - Unregister battery hook + * @priv: Driver private data + * + * Unregisters the battery hook if applicable. + */ +static void lwmi_om_psy_remove(struct lwmi_om_priv *priv) +{ + if (!priv->bh_registered) + return; + + battery_hook_unregister(&priv->battery_hook); + priv->bh_registered = false; +} + /* ======== fw_attributes (component: lenovo-wmi-capdata 01) ======== */ struct tunable_attr_01 { @@ -1243,6 +1631,7 @@ static int lwmi_om_master_bind(struct device *dev) } lwmi_om_fan_info_collect_cd00(priv); + lwmi_om_psy_ext_init(priv); lwmi_om_fw_attr_add(priv); @@ -1265,6 +1654,8 @@ static void lwmi_om_master_unbind(struct device *dev) lwmi_om_hwmon_remove(priv); + lwmi_om_psy_remove(priv); + component_unbind_all(dev, NULL); } From fb8e9629efbab67135f801f5ec5e91307e7693c2 Mon Sep 17 00:00:00 2001 From: "Derek J. Clark" Date: Wed, 20 May 2026 06:07:38 +0000 Subject: [PATCH 054/115] platform/x86: lenovo-wmi-other: Add force_load_psy_ext module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Lenovo BIOS have been shown to have incomplete and/or broken capability data and WMI attribute IDs. In some cases the capability data reports that a feature is not supported when the get/set methods are fully implemented. It is also possible that the ACPI methods from the ideapad_laptop driver we defer to could be bugged while the WMI method is fully working. To aid end users in submitting more complete bug reports in these situations, add an override to skip the ACPI and compatibility checks to force load the power supply extension as if it is fully supported and has no conflicts. Reviewed-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-6-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-other.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/platform/x86/lenovo/wmi-other.c b/drivers/platform/x86/lenovo/wmi-other.c index 8331da864beb..fbb32bf404f2 100644 --- a/drivers/platform/x86/lenovo/wmi-other.c +++ b/drivers/platform/x86/lenovo/wmi-other.c @@ -185,6 +185,16 @@ MODULE_PARM_DESC(relax_fan_constraint, "Enabling this may results in HWMON attributes being out-of-sync, " "and setting a too low RPM stops the fan. Use with caution."); +/* Visibility of power supply extensions */ +static bool force_load_psy_ext; +module_param(force_load_psy_ext, bool, 0444); +MODULE_PARM_DESC(force_load_psy_ext, + "This option will skip checking if the ideapad_laptop driver will conflict " + "with adding an extension to set the battery charge behavior and battery charge " + "control end threshold. It will also skip checking if the BIOS reports that " + "those features are fully supported. It is recommended to blacklist the ideapad " + "driver before using this option."); + /* ======== HWMON (component: lenovo-wmi-capdata 00 & fan) ======== */ /** @@ -755,6 +765,9 @@ static bool lwmi_psy_prop_is_supported(struct lwmi_om_priv *priv, enum power_sup { int ret; + if (force_load_psy_ext) + return true; + ret = lwmi_psy_prop_get_supported(priv, prop); if (ret < 0) return false; @@ -781,6 +794,9 @@ static int lwmi_psy_prop_is_writeable(struct power_supply *ps, struct lwmi_om_priv *priv = ext_data; int ret; + if (force_load_psy_ext) + return true; + ret = lwmi_psy_prop_get_supported(priv, prop); if (ret < 0) return false; @@ -909,6 +925,8 @@ static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) props |= LWMI_PSY_PROP_TYPES; if (!props) return; + if (force_load_psy_ext) + goto load_psy_ext; /* Deconflict ideapad_laptop driver */ ret = acpi_get_devices(ideapad_hid, lwmi_acpi_match, &handle, NULL); @@ -920,6 +938,7 @@ static void lwmi_om_psy_ext_init(struct lwmi_om_priv *priv) return; } +load_psy_ext: /* Add battery hooks */ priv->battery_ext = lwmi_psy_exts[props]; priv->battery_hook.add_battery = lwmi_add_battery; From 229e575451e4e65db47bb1873be0ccab764d9f3d Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 20 May 2026 06:07:39 +0000 Subject: [PATCH 055/115] platform/x86: lenovo-wmi-helpers: Add helper for creating per-device debugfs dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to add debugfs support for lenovo-wmi-capdata. Let's setup a debugfs directory called "lenovo_wmi" for tidiness, so that any lenovo-wmi-* device can put its subdirectory under the directory. Subdirectories will be named after the corresponding WMI devices. Signed-off-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-7-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/wmi-helpers.c | 34 +++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-helpers.h | 2 ++ 2 files changed, 36 insertions(+) diff --git a/drivers/platform/x86/lenovo/wmi-helpers.c b/drivers/platform/x86/lenovo/wmi-helpers.c index 7a198259e393..8f5766c391eb 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.c +++ b/drivers/platform/x86/lenovo/wmi-helpers.c @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include #include @@ -185,6 +187,38 @@ int lwmi_tm_notifier_call(enum thermal_mode *mode) } EXPORT_SYMBOL_NS_GPL(lwmi_tm_notifier_call, "LENOVO_WMI_HELPERS"); +static struct dentry *lwmi_debugfs_dir; + +/** + * lwmi_debugfs_create_dir() - Helper function for creating a debugfs directory + * for a device. + * @wdev: Pointer to the WMI device to be called. + * + * Caller must remove the directory with debugfs_remove_recursive() on device + * removal. + * + * Return: Pointer to the created directory. + */ +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev) +{ + return debugfs_create_dir(dev_name(&wdev->dev), lwmi_debugfs_dir); +} +EXPORT_SYMBOL_NS_GPL(lwmi_debugfs_create_dir, "LENOVO_WMI_HELPERS"); + +static int __init lwmi_helpers_init(void) +{ + lwmi_debugfs_dir = debugfs_create_dir("lenovo_wmi", NULL); + + return 0; +} +subsys_initcall(lwmi_helpers_init) + +static void __exit lwmi_helpers_exit(void) +{ + debugfs_remove_recursive(lwmi_debugfs_dir); +} +module_exit(lwmi_helpers_exit) + MODULE_AUTHOR("Derek J. Clark "); MODULE_DESCRIPTION("Lenovo WMI Helpers Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/lenovo/wmi-helpers.h b/drivers/platform/x86/lenovo/wmi-helpers.h index ed7db3ebba6c..039fe61003ce 100644 --- a/drivers/platform/x86/lenovo/wmi-helpers.h +++ b/drivers/platform/x86/lenovo/wmi-helpers.h @@ -16,6 +16,8 @@ struct wmi_method_args_32 { u32 arg1; }; +struct dentry *lwmi_debugfs_create_dir(struct wmi_device *wdev); + enum lwmi_event_type { LWMI_GZ_GET_THERMAL_MODE = 0x01, }; From 18a166d29aa12d361907a3aa724c8892d834592b Mon Sep 17 00:00:00 2001 From: Rong Zhang Date: Wed, 20 May 2026 06:07:40 +0000 Subject: [PATCH 056/115] platform/x86: lenovo-wmi-capdata: Add debugfs file for dumping capdata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Lenovo GameZone/Other interfaces have some delicate divergences among different devices. When making a bug report or adding support for new devices/interfaces, capdata is the most important information to cross-check with. Add a debugfs file (lenovo_wmi//capdata), so that users can dump capdata and include it in their reports. Since `struct capdata01' is just an extension to `struct capdata00', also convert the former to include the latter anonymously (-fms-extensions, since v6.19). This is declared as a union in the capdata01 struct, with both the anonymous declaration and as a named member to avoid type casting when passing just the capdata00 struct pointer. Tested-by: Kurt Borja Signed-off-by: Rong Zhang Signed-off-by: Derek J. Clark Link: https://patch.msgid.link/20260520060740.119554-8-derekjohn.clark@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/Kconfig | 1 + drivers/platform/x86/lenovo/wmi-capdata.c | 120 ++++++++++++++++++++++ drivers/platform/x86/lenovo/wmi-capdata.h | 7 +- 3 files changed, 125 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/Kconfig b/drivers/platform/x86/lenovo/Kconfig index b9a5d18caa1e..4443f40ef8aa 100644 --- a/drivers/platform/x86/lenovo/Kconfig +++ b/drivers/platform/x86/lenovo/Kconfig @@ -236,6 +236,7 @@ config YT2_1380 config LENOVO_WMI_CAPDATA tristate depends on ACPI_WMI + depends on LENOVO_WMI_HELPERS config LENOVO_WMI_EVENTS tristate diff --git a/drivers/platform/x86/lenovo/wmi-capdata.c b/drivers/platform/x86/lenovo/wmi-capdata.c index 714aa6fd6f1f..d5e961566136 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.c +++ b/drivers/platform/x86/lenovo/wmi-capdata.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +89,7 @@ struct lwmi_cd_priv { struct notifier_block acpi_nb; /* ACPI events */ struct wmi_device *wdev; struct cd_list *list; + struct dentry *debugfs_dir; /* * A capdata device may be a component master of another capdata device. @@ -117,6 +120,8 @@ struct cd_list { static struct wmi_driver lwmi_cd_driver; +/* ======== Device components ======== */ + /** * lwmi_cd_match() - Match rule for the master driver. * @dev: Pointer to the capability data parent device. @@ -470,6 +475,116 @@ EXPORT_SYMBOL_NS_GPL(lwmi_cd01_get_data, "LENOVO_WMI_CAPDATA"); DEF_LWMI_CDXX_GET_DATA(cd_fan, LENOVO_FAN_TEST_DATA, struct capdata_fan); EXPORT_SYMBOL_NS_GPL(lwmi_cd_fan_get_data, "LENOVO_WMI_CAPDATA"); +/* ======== debugfs ======== */ + +/** + * lwmi_cd00_show() - Dump capdata00 + * @s: Pointer to seq_file where the capdata00 is dumped. + * @cd00: Pointer to a capdata00 struct to be dumped. + */ +static void lwmi_cd00_show(struct seq_file *s, struct capdata00 *cd00) +{ + u8 dev = FIELD_GET(LWMI_ATTR_DEV_ID_MASK, cd00->id); + u8 feat = FIELD_GET(LWMI_ATTR_FEAT_ID_MASK, cd00->id); + u8 mode = FIELD_GET(LWMI_ATTR_MODE_ID_MASK, cd00->id); + u8 type = FIELD_GET(LWMI_ATTR_TYPE_ID_MASK, cd00->id); + bool extra = cd00->supported & ~(LWMI_SUPP_GET | LWMI_SUPP_SET | LWMI_SUPP_VALID); + bool get = cd00->supported & LWMI_SUPP_GET; + bool set = cd00->supported & LWMI_SUPP_SET; + bool valid = cd00->supported & LWMI_SUPP_VALID; + + seq_printf(s, " id: 0x%08x [dev: %2u, feat: %2u, mode: %2u, type: %2u]\n", + cd00->id, dev, feat, mode, type); + + seq_printf(s, " supported: 0x%08x [%c%c%c%c]\n", cd00->supported, + extra ? '+' : ' ', + get ? 'R' : ' ', + set ? 'W' : ' ', + valid ? 'V' : ' '); + + seq_printf(s, " default_value: %u\n", cd00->default_value); +} + +/** + * lwmi_cd01_show() - Dump capdata01 + * @s: Pointer to seq_file where the capdata01 is dumped. + * @cd01: Pointer to a capdata01 struct to be dumped. + */ +static void lwmi_cd01_show(struct seq_file *s, struct capdata01 *cd01) +{ + /* capdata01 is an extension to capdata00. */ + lwmi_cd00_show(s, &cd01->cd00); + + seq_printf(s, " step: %u\n", cd01->step); + seq_printf(s, " min_value: %u\n", cd01->min_value); + seq_printf(s, " max_value: %u\n", cd01->max_value); +} + +/** + * lwmi_cd_fan_show() - Dump capdata_fan + * @s: Pointer to seq_file where the capdata_fan is dumped. + * @cd_fan: Pointer to a capdata_fan struct to be dumped. + */ +static void lwmi_cd_fan_show(struct seq_file *s, struct capdata_fan *cd_fan) +{ + seq_printf(s, " id: %u\n", cd_fan->id); + seq_printf(s, " min_rpm: %u\n", cd_fan->min_rpm); + seq_printf(s, " max_rpm: %u\n", cd_fan->max_rpm); +} + +/** + * lwmi_cd_debugfs_show() - Dump capability data to debugfs + * @s: Pointer to seq_file where the capability data is dumped. + * @data: unused. + * + * Return: 0 + */ +static int lwmi_cd_debugfs_show(struct seq_file *s, void *data) +{ + struct lwmi_cd_priv *priv = s->private; + u8 idx; + + guard(mutex)(&priv->list->list_mutex); + + /* lwmi_cd_alloc() ensured priv->list->type must be a valid type. */ + for (idx = 0; idx < priv->list->count; idx++) { + seq_printf(s, "%s[%u]:\n", lwmi_cd_table[priv->list->type].name, idx); + + if (priv->list->type == LENOVO_CAPABILITY_DATA_00) + lwmi_cd00_show(s, &priv->list->cd00[idx]); + else if (priv->list->type == LENOVO_CAPABILITY_DATA_01) + lwmi_cd01_show(s, &priv->list->cd01[idx]); + else if (priv->list->type == LENOVO_FAN_TEST_DATA) + lwmi_cd_fan_show(s, &priv->list->cd_fan[idx]); + } + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(lwmi_cd_debugfs); + +/** + * lwmi_cd_debugfs_add() - Create debugfs directory and files for a device + * @priv: lenovo-wmi-capdata driver data. + */ +static void lwmi_cd_debugfs_add(struct lwmi_cd_priv *priv) +{ + priv->debugfs_dir = lwmi_debugfs_create_dir(priv->wdev); + + debugfs_create_file("capdata", 0444, priv->debugfs_dir, priv, &lwmi_cd_debugfs_fops); +} + +/** + * lwmi_cd_debugfs_remove() - Remove debugfs directory for a device + * @priv: lenovo-wmi-capdata driver data. + */ +static void lwmi_cd_debugfs_remove(struct lwmi_cd_priv *priv) +{ + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; +} + +/* ======== WMI interface ======== */ + /** * lwmi_cd_cache() - Cache all WMI data block information * @priv: lenovo-wmi-capdata driver data. @@ -772,6 +887,8 @@ static int lwmi_cd_probe(struct wmi_device *wdev, const void *context) dev_err(&wdev->dev, "failed to register %s: %d\n", info->name, ret); } else { + lwmi_cd_debugfs_add(priv); + dev_dbg(&wdev->dev, "registered %s with %u items\n", info->name, priv->list->count); } @@ -782,6 +899,8 @@ static void lwmi_cd_remove(struct wmi_device *wdev) { struct lwmi_cd_priv *priv = dev_get_drvdata(&wdev->dev); + lwmi_cd_debugfs_remove(priv); + switch (priv->list->type) { case LENOVO_CAPABILITY_DATA_00: lwmi_cd_sub_master_del(priv); @@ -821,6 +940,7 @@ static struct wmi_driver lwmi_cd_driver = { module_wmi_driver(lwmi_cd_driver); +MODULE_IMPORT_NS("LENOVO_WMI_HELPERS"); MODULE_DEVICE_TABLE(wmi, lwmi_cd_id_table); MODULE_AUTHOR("Derek J. Clark "); MODULE_AUTHOR("Rong Zhang "); diff --git a/drivers/platform/x86/lenovo/wmi-capdata.h b/drivers/platform/x86/lenovo/wmi-capdata.h index a49229cec245..92098aeeee84 100644 --- a/drivers/platform/x86/lenovo/wmi-capdata.h +++ b/drivers/platform/x86/lenovo/wmi-capdata.h @@ -38,9 +38,10 @@ struct capdata00 { }; struct capdata01 { - u32 id; - u32 supported; - u32 default_value; + union { + struct capdata00; + struct capdata00 cd00; + }; u32 step; u32 min_value; u32 max_value; From fbab18baaf9a3fe45c6fd75dab7f5b7f6992356f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 16:43:41 +0200 Subject: [PATCH 057/115] platform: arm64 Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled arrays, only their representation in source form benefits. The former was confirmed with x86 and arm64 builds. While touching all these arrays, unify usage of whitespace in the list terminator. Signed-off-by: Uwe Kleine-König (The Capable Hub) Reviewed-by: Bryan O'Donoghue Link: https://patch.msgid.link/20260519144341.1589034-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/arm64/acer-aspire1-ec.c | 2 +- drivers/platform/arm64/huawei-gaokun-ec.c | 2 +- drivers/platform/arm64/lenovo-thinkpad-t14s.c | 4 ++-- drivers/platform/arm64/lenovo-yoga-c630.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/platform/arm64/acer-aspire1-ec.c b/drivers/platform/arm64/acer-aspire1-ec.c index 438532a047e6..08d0b155a197 100644 --- a/drivers/platform/arm64/acer-aspire1-ec.c +++ b/drivers/platform/arm64/acer-aspire1-ec.c @@ -532,7 +532,7 @@ static int aspire_ec_resume(struct device *dev) } static const struct i2c_device_id aspire_ec_id[] = { - { "aspire1-ec", }, + { .name = "aspire1-ec" }, { } }; MODULE_DEVICE_TABLE(i2c, aspire_ec_id); diff --git a/drivers/platform/arm64/huawei-gaokun-ec.c b/drivers/platform/arm64/huawei-gaokun-ec.c index a83ddc20b5a3..80a8ba8b8dda 100644 --- a/drivers/platform/arm64/huawei-gaokun-ec.c +++ b/drivers/platform/arm64/huawei-gaokun-ec.c @@ -795,7 +795,7 @@ static int gaokun_ec_probe(struct i2c_client *client) } static const struct i2c_device_id gaokun_ec_id[] = { - { "gaokun-ec", }, + { .name = "gaokun-ec" }, { } }; MODULE_DEVICE_TABLE(i2c, gaokun_ec_id); diff --git a/drivers/platform/arm64/lenovo-thinkpad-t14s.c b/drivers/platform/arm64/lenovo-thinkpad-t14s.c index 5590302a5694..e7acb66b77f2 100644 --- a/drivers/platform/arm64/lenovo-thinkpad-t14s.c +++ b/drivers/platform/arm64/lenovo-thinkpad-t14s.c @@ -637,8 +637,8 @@ static const struct of_device_id t14s_ec_of_match[] = { MODULE_DEVICE_TABLE(of, t14s_ec_of_match); static const struct i2c_device_id t14s_ec_i2c_id_table[] = { - { "thinkpad-t14s-ec", }, - {} + { .name = "thinkpad-t14s-ec" }, + { } }; MODULE_DEVICE_TABLE(i2c, t14s_ec_i2c_id_table); diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c index 75060c842b24..a8600a977fbc 100644 --- a/drivers/platform/arm64/lenovo-yoga-c630.c +++ b/drivers/platform/arm64/lenovo-yoga-c630.c @@ -238,8 +238,8 @@ static const struct of_device_id yoga_c630_ec_of_match[] = { MODULE_DEVICE_TABLE(of, yoga_c630_ec_of_match); static const struct i2c_device_id yoga_c630_ec_i2c_id_table[] = { - { "yoga-c630-ec", }, - {} + { .name = "yoga-c630-ec" }, + { } }; MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table); From 84b27a3cdd512f88ef837a913f8bb54c0b9d9290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Tue, 19 May 2026 17:40:40 +0200 Subject: [PATCH 058/115] platform/x86: x86-android-tablets: Use named initializers for struct i2c_device_id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While being less compact, using named initializers allows to more easily see which members of the structs are assigned which value without having to lookup the declaration of the struct. And it's also more robust against changes to the struct definition. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with x86 and arm64 builds. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/20260519154040.1594878-2-u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c index ebbedfe5f4e8..67dba9aa51f6 100644 --- a/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c +++ b/drivers/platform/x86/x86-android-tablets/vexia_atla10_ec.c @@ -242,7 +242,7 @@ static int atla10_ec_probe(struct i2c_client *client) } static const struct i2c_device_id atla10_ec_id_table[] = { - { "vexia_atla10_ec" }, + { .name = "vexia_atla10_ec" }, { } }; MODULE_DEVICE_TABLE(i2c, atla10_ec_id_table); From 5a3feefbcfa652e65b9259708222dd31d180eb33 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 15 May 2026 16:58:51 +0200 Subject: [PATCH 059/115] platform/x86: Move delayed work on system_dfl_wq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the code enqueue work items using {queue|mod}_delayed_work(), using system_wq, which will be deprecated soon and replaced by system_percpu_wq. commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") The function(s) mentioned earlier, end up calling __queue_delayed_work(), which set a global timer that could fire anywhere, enqueuing the work where the timer fired. Unbound works could benefit from scheduler task placement, to optimize performance and power consumption. Since the workqueue work doesn't rely on per-cpu variables, there is no obvious reason that justify the use of a per-cpu workqueue. So change system_wq with system_dfl_wq so that the work may benefit from scheduler task placement. Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20260515145851.318787-1-marco.crivellari@suse.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index f63bc00d9a9b..48292cc02ee2 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -2396,7 +2396,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) ret = hp_wmi_fan_speed_max_set(1); if (ret < 0) return ret; - mod_delayed_work(system_wq, &priv->keep_alive_dwork, + mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork, secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: @@ -2405,7 +2405,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) ret = hp_wmi_fan_speed_set(priv, pwm_to_rpm(priv->pwm, priv)); if (ret < 0) return ret; - mod_delayed_work(system_wq, &priv->keep_alive_dwork, + mod_delayed_work(system_dfl_wq, &priv->keep_alive_dwork, secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_AUTO: From a167ae8eace52dd6c80438b77d92450fe12cd4be Mon Sep 17 00:00:00 2001 From: Harrison Vanderbyl Date: Fri, 15 May 2026 15:41:48 +1000 Subject: [PATCH 060/115] platform/surface: SAM: Add support for Surface Pro 12in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a SAM client device node group and registry entry for the Microsoft Surface Pro, 12-inch with Snapdragon. This set enables the use of the following devices. 1: cover keyboard 2: cover touchpad 3: pen stash events. The battery info and charger info devices have been purposefully omitted as they are also reported by other drivers and cause conflicts. Signed-off-by: Harrison Vanderbyl Link: https://patch.msgid.link/ab458aadea651396d9ea7629419a32dc7510c593.1778822464.git.harrison.vanderbyl@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../surface/surface_aggregator_registry.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index f0881edfb616..d37a7e4c6b02 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -420,6 +420,19 @@ static const struct software_node *ssam_node_group_sp11[] = { NULL, }; +/* Devices for Surface Pro 12" first edition (ARM/QCOM) */ +static const struct software_node *ssam_node_group_sp12in[] = { + &ssam_node_root, + &ssam_node_hub_kip, + &ssam_node_tmp_sensors, + &ssam_node_hid_kip_keyboard, + &ssam_node_hid_sam_penstash, + &ssam_node_hid_kip_touchpad, + &ssam_node_hid_kip_fwupd, + &ssam_node_pos_tablet_switch, + NULL, +}; + /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ static const struct acpi_device_id ssam_platform_hub_acpi_match[] = { @@ -498,6 +511,8 @@ static const struct of_device_id ssam_platform_hub_of_match[] __maybe_unused = { { .compatible = "microsoft,arcata", (void *)ssam_node_group_sp9_5g }, /* Surface Pro 11 (ARM/QCOM) */ { .compatible = "microsoft,denali", (void *)ssam_node_group_sp11 }, + /* Surface Pro 12in First Edition (ARM/QCOM) */ + { .compatible = "microsoft,surface-pro-12in", (void *)ssam_node_group_sp12in }, /* Surface Laptop 7 */ { .compatible = "microsoft,romulus13", (void *)ssam_node_group_sl7 }, { .compatible = "microsoft,romulus15", (void *)ssam_node_group_sl7 }, From 607af438e6430893a822964c841a1994b33acccc Mon Sep 17 00:00:00 2001 From: Ali Ahmet MEMIS Date: Sun, 26 Apr 2026 08:09:28 -0700 Subject: [PATCH 061/115] tools/power/x86/intel-speed-select: Harden daemon pidfile open Avoid symlink-based pidfile clobbering by opening the pidfile with O_NOFOLLOW and validating it with fstat() before locking/writing. The daemon currently uses a fixed pidfile path under /tmp. A local unprivileged user can pre-create a symlink at that path and cause a root-run daemon instance to write into an attacker-chosen file. Fixes: 7fd786dfbd2c ("tools/power/x86/intel-speed-select: OOB daemon mode") Signed-off-by: Ali Ahmet MEMIS Signed-off-by: Srinivas Pandruvada Cc: stable@kernel.org --- tools/power/x86/intel-speed-select/isst-daemon.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 66df21b2b573..acedb7432849 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -148,6 +148,7 @@ static void daemonize(char *rundir, char *pidfile) { int pid, sid, i; char str[10]; + struct stat st; struct sigaction sig_actions; sigset_t sig_set; int ret; @@ -200,11 +201,17 @@ static void daemonize(char *rundir, char *pidfile) if (ret == -1) exit(EXIT_FAILURE); - pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + pid_file_handle = open(pidfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600); if (pid_file_handle == -1) { /* Couldn't open lock file */ exit(1); } + + if (fstat(pid_file_handle, &st) == -1) + exit(1); + + if (!S_ISREG(st.st_mode)) + exit(1); /* Try to lock file */ #ifdef LOCKF_SUPPORT if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { From 0b6573e23acc7bca808e539e3edea49683f106de Mon Sep 17 00:00:00 2001 From: Alexander Egorov Date: Tue, 19 May 2026 18:51:24 +0300 Subject: [PATCH 062/115] platform/x86: oxpec: add support for OneXPlayer Super X MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OneXPlayer Super X identifies itself via DMI as: board vendor: ONE-NETBOOK board name: ONEXPLAYER SUPER X product name: ONEXPLAYER SUPER X Current mainline oxpec does not contain a matching DMI entry for this system, so the in-tree driver is not auto-loaded. The tested Super X fan, PWM, turbo-toggle, and battery charge-control EC layout matches the existing ONEXPLAYER G1 A handling. Add a DMI match for OneXPlayer Super X and reuse the oxp_g1_a board data. Reviewed-by: Derek J Clark Reviewed-by: Antheas Kapenekakis Signed-off-by: Alexander Egorov Link: https://patch.msgid.link/20260519155124.3240359-1-begeebe@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/oxpec.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/oxpec.c b/drivers/platform/x86/oxpec.c index 6d4a53a2ed60..99c0dfcf393b 100644 --- a/drivers/platform/x86/oxpec.c +++ b/drivers/platform/x86/oxpec.c @@ -205,6 +205,13 @@ static const struct dmi_system_id dmi_table[] = { }, .driver_data = (void *)oxp_g1_a, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "ONEXPLAYER SUPER X"), + }, + .driver_data = (void *)oxp_g1_a, + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "ONE-NETBOOK"), From a221557958e3a82d8565729d445a7385963f30b6 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 21 May 2026 21:08:47 +0800 Subject: [PATCH 063/115] platform/x86/intel/tpmi: use cleanup helpers in mem_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In mem_write(), the temporary array returned by parse_int_array_user() must be released on all exit paths. Convert the array variable to use cleanup.h scope-based cleanup so it is freed automatically on return. This also moves the array declaration next to parse_int_array_user() as required by cleanup.h usage guidelines. Fixes: 8e0a2fc68ec3 ("platform/x86/intel/tpmi: Use 32 bit aligned address for debugfs mem write") Cc: stable@vger.kernel.org Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260521130848.2860219-1-zhaojinming@uniontech.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec_tpmi.c | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 8c4e2474b30c..3b76cccb975f 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -473,7 +474,7 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l struct seq_file *m = file->private_data; struct intel_tpmi_pm_feature *pfs = m->private; u32 addr, value, punit, size; - u32 num_elems, *array; + u32 num_elems; void __iomem *mem; int ret; @@ -481,15 +482,14 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!size) return -EIO; + u32 *array __free(kfree) = NULL; ret = parse_int_array_user(userbuf, len, (int **)&array); if (ret < 0) return ret; num_elems = *array; - if (num_elems != 3) { - ret = -EINVAL; - goto exit_write; - } + if (num_elems != 3) + return -EINVAL; punit = array[1]; addr = array[2]; @@ -498,15 +498,11 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (!IS_ALIGNED(addr, sizeof(u32))) return -EINVAL; - if (punit >= pfs->pfs_header.num_entries) { - ret = -EINVAL; - goto exit_write; - } + if (punit >= pfs->pfs_header.num_entries) + return -EINVAL; - if (addr >= size) { - ret = -EINVAL; - goto exit_write; - } + if (addr >= size) + return -EINVAL; mutex_lock(&tpmi_dev_lock); @@ -525,9 +521,6 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l unlock_mem_write: mutex_unlock(&tpmi_dev_lock); -exit_write: - kfree(array); - return ret; } From abefbbfc71f5ee50f9e549a2d143f23694d65fc2 Mon Sep 17 00:00:00 2001 From: yahia ahmed Date: Fri, 22 May 2026 23:34:18 +0300 Subject: [PATCH 064/115] platform/x86: hp-wmi: Add thermal support for board 8B2F MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added thermal support for board ID 8B2F. Signed-off-by: yahia ahmed Link: https://patch.msgid.link/20260522203418.28784-1-yahia.a.abdrabou@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 48292cc02ee2..5042d124dea0 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -205,6 +205,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BAB") }, .driver_data = (void *)&omen_v1_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8B2F") }, + .driver_data = (void *)&victus_s_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8BBE") }, .driver_data = (void *)&victus_s_thermal_params, From ceb8678adfd68b33d5e8a52cb2e96cfa90d81761 Mon Sep 17 00:00:00 2001 From: Mike Bommarito Date: Sat, 23 May 2026 07:45:17 -0400 Subject: [PATCH 065/115] platform/x86/intel/pmc: rate-limit LTR scale-factor warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit convert_ltr_scale() emits an unconditional pr_warn() whenever an LTR row encoded by hardware has a scale-factor field of 6 or 7 (reserved values per the PCIe LTR ECN). The function is called twice per LTR row (snoop + non-snoop) by pmc_core_ltr_show(), which is invoked on every read of /sys/kernel/debug/pmc_core/ltr_show as well as during certain platform driver activity. On a Meteor Lake laptop with an Intel AX210 Wi-Fi card, this produces 4-12 "Invalid LTR scale factor." lines per second in dmesg, with no context to help identify which PMC IP / row carries the bad value. Switch to pr_warn_once() so the warning still flags the spec violation once per boot, and include the offending scale value in the message. Identifying the originating LTR row would require restructuring the caller's loop to perform the validity check itself; that is left as a separate change. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mike Bommarito Link: https://patch.msgid.link/20260523114517.101305-1-michael.bommarito@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 3adae8fe8b30..825ba5fa0bcb 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -623,7 +623,8 @@ static u32 convert_ltr_scale(u32 val) * ---------------------------------------------- */ if (val > 5) { - pr_warn("Invalid LTR scale factor.\n"); + pr_warn_once("Invalid LTR scale factor %u (only 0-5 are valid per PCIe spec)\n", + val); return 0; } From 0aab31d47c2e857ca05028d718d1e0d239e683ad Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 25 May 2026 15:52:26 +0530 Subject: [PATCH 066/115] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8D26) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-ap0xxx (board ID: 8D26) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8D26 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Alberto Escaño Reported-by: Alberto Escaño Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221514 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260525102226.56300-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 5042d124dea0..079db53bf10c 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -253,6 +253,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C9C") }, .driver_data = (void *)&victus_s_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D26") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, From bfe91a80b13f8068f6fa07aa8c468d284150d4ad Mon Sep 17 00:00:00 2001 From: Gleb Sonichev Date: Mon, 25 May 2026 13:00:47 +0300 Subject: [PATCH 067/115] platform/x86: dell-laptop: add Inspiron N5110 to touchpad LED quirk table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Inspiron N5110 needs the touchpad LED quirk (Vostro V130 quirk) to properly control the touchpad LED. Add its DMI identifier to the existing quirk table, next to the similar Inspiron M5110 entry. Tested on Dell Inspiron N5110. The touchpad LED works correctly with this quirk enabled. Signed-off-by: Gleb Sonichev Acked-by: Pali Rohár Link: https://patch.msgid.link/20260525100047.20046-1-sonichev555@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-laptop.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 57748c3ea24f..7fc3bbb8c4a4 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -222,6 +222,15 @@ static const struct dmi_system_id dell_quirks[] __initconst = { }, .driver_data = &quirk_dell_vostro_v130, }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron N5110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N5110"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, { .callback = dmi_matched, .ident = "Dell Vostro 3360", From ab7be7ed913086e076bfd8aba79f614f415cd6dc Mon Sep 17 00:00:00 2001 From: Luis de Carlos Date: Wed, 27 May 2026 15:47:50 +0200 Subject: [PATCH 068/115] platform/x86: msi-ec: Add support for MSI Pulse GL66 12th Gen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the firmware string '1583EMS1.109' to the ALLOWED_FW_10 array. This enables Embedded Controller support, including battery charge thresholds, for the MSI Pulse GL66 12UEK (MS-1583) laptop. Signed-off-by: Luis de Carlos Link: https://patch.msgid.link/20260527134750.25263-1-reskoldo73@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/msi-ec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/msi-ec.c b/drivers/platform/x86/msi-ec.c index f19504dbf164..0157e233e430 100644 --- a/drivers/platform/x86/msi-ec.c +++ b/drivers/platform/x86/msi-ec.c @@ -823,6 +823,7 @@ static struct msi_ec_conf CONF9 __initdata = { static const char * const ALLOWED_FW_10[] __initconst = { "1582EMS1.107", // GF66 11UC + "1583EMS1.109", // Pulse GL66 12UEK NULL }; From 56b7981c6f21670c0a1a62e6d2f9afb380e2596d Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Mon, 8 Jun 2026 19:12:55 +0530 Subject: [PATCH 069/115] platform/x86: hp-wmi: Add support for Omen 16-ap0xxx (8E35) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-ap0xxx (board ID: 8E35) has the same WMI interface as other Victus S boards, but requires quirks for correctly switching thermal profile. Add the DMI board name to victus_s_thermal_profile_boards[] table and map it to omen_v1_legacy_thermal_params. Testing on board 8E35 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Ahmet Öztürk Reported-by: Ahmet Öztürk Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221523 Cc: stable@vger.kernel.org # v6.18+ Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260608134255.36280-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 079db53bf10c..8ba286ed8721 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -265,6 +265,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, .driver_data = (void *)&omen_v1_no_ec_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8E35") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, {}, }; From 3b9f95b5a45786f1ca3feff7a736f30f60af08c7 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 8 Jun 2026 16:23:48 +0800 Subject: [PATCH 070/115] platform: arm64: qcom-hamoa-ec: Fix indentation in comment tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With tab=8 (Linux Kernel coding style), there are a couple of lines misaligned in the comment ASCII tables. Fix indentation for them. Signed-off-by: Shawn Guo Link: https://patch.msgid.link/20260608082348.92575-1-shengchao.guo@oss.qualcomm.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/arm64/qcom-hamoa-ec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/arm64/qcom-hamoa-ec.c b/drivers/platform/arm64/qcom-hamoa-ec.c index a018f7bf35d2..89374ab3e600 100644 --- a/drivers/platform/arm64/qcom-hamoa-ec.c +++ b/drivers/platform/arm64/qcom-hamoa-ec.c @@ -149,7 +149,7 @@ static int qcom_ec_read_fw_version(struct device *dev) * | 0x00 | Byte count | Number of bytes in response | * | | | (excluding byte count) | * ------------------------------------------------------------------------------ - * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | + * | 0x02 (LSB) | EC Thermal | Bit 0-1: Number of fans | * | 0x03 | Capabilities | Bit 2-4: Type of fan | * | | | Bit 5-6: Reserved | * | | | Bit 7: Data Valid/Invalid | @@ -273,7 +273,7 @@ static int qcom_ec_fan_get_cur_state(struct thermal_cooling_device *cdev, unsign * | | | Bit 1: Fan On/Off (0 - Off, 1 - ON) | * | | | Bit 2: Debug Type (0 - RPM, 1 - PWM) | * -------------------------------------------------------------------------------------- - * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | + * | 0x04 (LSB) | Speed in RPM | RPM value, if mode selected is RPM | * | 0x05 | | | * -------------------------------------------------------------------------------------- * | 0x06 | Speed in PWM | PWM value, if mode selected is PWM (0 - 255) | From 6736b1801908acfa64ef2b651c5bb78389a8a4c6 Mon Sep 17 00:00:00 2001 From: ZhaoJinming Date: Thu, 21 May 2026 21:08:48 +0800 Subject: [PATCH 071/115] platform/x86/intel/tpmi: convert mutex in mem_write() to guard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the explicit mutex_lock/mutex_unlock pair in mem_write() into a cleanup.h guard(mutex)() scope-based lock acquisition. This removes the remaining goto-based cleanup path and keeps the lock held until the end of the mem_write() scope. Suggested-by: Ilpo Järvinen Signed-off-by: ZhaoJinming Link: https://patch.msgid.link/20260521130848.2860219-2-zhaojinming@uniontech.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec_tpmi.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 3b76cccb975f..0153dd57838e 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -504,24 +504,17 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l if (addr >= size) return -EINVAL; - mutex_lock(&tpmi_dev_lock); + guard(mutex)(&tpmi_dev_lock); mem = ioremap(pfs->vsec_offset + punit * size, size); - if (!mem) { - ret = -ENOMEM; - goto unlock_mem_write; - } + if (!mem) + return -ENOMEM; writel(value, mem + addr); iounmap(mem); - ret = len; - -unlock_mem_write: - mutex_unlock(&tpmi_dev_lock); - - return ret; + return len; } static int mem_write_show(struct seq_file *s, void *unused) From c39023ca9a447f09c072080efc84d6874c2275c9 Mon Sep 17 00:00:00 2001 From: Nikolay Metchev Date: Tue, 9 Jun 2026 22:33:09 +0100 Subject: [PATCH 072/115] platform/x86: intel-hid: Add HP ProBook x360 440 G1 to button_array_table MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The volume rocker buttons on the HP ProBook x360 440 G1 convertible emit events 0xc4-0xc7 via the intel-hid ACPI device (INT33D5). These codes are only present in intel_array_keymap, which is used when the "5 button array" input device exists. On this machine button_array_present() returns false because the firmware does not advertise the array through the HEBC method, so notify_handler() routes the events to a NULL priv->array and they are dropped as "unknown event 0xc4". As a result the side volume keys do nothing. Add the machine to button_array_table so the array device is created and the volume rocker emits KEY_VOLUMEUP / KEY_VOLUMEDOWN. This is equivalent to booting with the enable_5_button_array=1 module parameter, which was used to confirm the fix on the affected hardware. Signed-off-by: Nikolay Metchev Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260609213309.445019-1-nikolaymetchev@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/hid.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 085093506dda..73874d4369c1 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -156,6 +156,13 @@ static const struct dmi_system_id button_array_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 4"), }, }, + { + .ident = "HP ProBook x360 440 G1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP ProBook x360 440 G1"), + }, + }, { } }; From c3a2521bcc8e4913a246132f276e5ce1251dd1cd Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 9 Jun 2026 20:09:51 +0530 Subject: [PATCH 073/115] platform/x86/amd/pmc: Use per-SoC cpu_info struct for SMU mailbox and IP info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the scattered per-field assignments in amd_pmc_get_ip_info() with a single amd_pmc_cpu_info struct capturing all SoC-specific parameters such as SMU offsets, IP block table, and OS hint. Define static const instances per SoC variant and embed them as driver_data in the PCI ID table via PCI_DEVICE_DATA(). Consolidate pci_match_id() into amd_pmc_set_cpu_info(), which assigns driver_data directly to cpu_info, the switch falls through only for 1Ah M20H/M60H variants requiring boot_cpu_data.x86_model detection to distinguish the M70 sub-variant. Add scratch_reg to amd_pmc_cpu_info and populate it for each SoC variant, allowing amd_pmc_idlemask_read() to drop its cpu_id switch in favour of a single cpu_info->scratch_reg lookup. Move dev->cpu_id assignment into amd_pmc_set_cpu_info() so it is valid before the switch statement. Handle SP/SHP directly in the switch since their NULL driver_data bypasses the early return, and remove the duplicate check from probe. Remove amd_pmc_get_os_hint() and use cpu_info->os_hint directly at call sites and rename AMD_CPU_ID_* to PCI_DEVICE_ID_AMD_CPU_ID_* with backward compatibility aliases. Reviewed-by: Mario Limonciello (AMD) Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://patch.msgid.link/20260609143952.2999707-2-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 231 +++++++++++++++++------------ drivers/platform/x86/amd/pmc/pmc.h | 66 ++++++--- 2 files changed, 177 insertions(+), 120 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index cae3fcafd4d7..92e36a6161a9 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -85,6 +85,89 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {"VPE", BIT(21)}, }; +/* CPU info structures for different SoC variants */ +static const struct amd_pmc_cpu_info amd_pco_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_PCO, +}; + +static const struct amd_pmc_cpu_info amd_czn_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .scratch_reg = AMD_PMC_SCRATCH_REG_CZN, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_vg_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_yc_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 12, + .scratch_reg = AMD_PMC_SCRATCH_REG_YC, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_ps_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MESSAGE, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = 21, + .scratch_reg = AMD_PMC_SCRATCH_REG_YC, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_1ah_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = ARRAY_SIZE(soc15_ip_blk), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct amd_pmc_cpu_info amd_1ah_m70_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_20H, + .smu_arg = AMD_PMC_REGISTER_ARGUMENT, + .smu_rsp = AMD_PMC_REGISTER_RESPONSE, + .num_ips = ARRAY_SIZE(soc15_ip_blk_v2), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk_v2, + .os_hint = MSG_OS_HINT_RN, +}; + +static const struct pci_device_id pmc_pci_ids[] = { + { PCI_DEVICE_DATA(AMD, CPU_ID_PCO, &amd_pco_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_CZN, &amd_czn_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_VG, &amd_vg_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_YC, &amd_yc_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_CB, &amd_yc_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_PS, &amd_ps_cpu_info) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_SP, NULL) }, + { PCI_DEVICE_DATA(AMD, CPU_ID_SHP, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, NULL) }, + { } +}; + static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); @@ -101,35 +184,40 @@ static inline void amd_pmc_reg_write(struct amd_pmc_dev *dev, int reg_offset, u3 iowrite32(val, dev->regbase + reg_offset); } -static void amd_pmc_get_ip_info(struct amd_pmc_dev *dev) +static int amd_pmc_set_cpu_info(struct amd_pmc_dev *dev, struct pci_dev *rdev) { + const struct pci_device_id *id; + + id = pci_match_id(pmc_pci_ids, rdev); + if (!id) + return -ENODEV; + + dev->cpu_id = rdev->device; + + if (id->driver_data) { + dev->cpu_info = (const struct amd_pmc_cpu_info *)id->driver_data; + return 0; + } + + /* Special case: 1Ah M20H/M60H needs x86_model detection */ switch (dev->cpu_id) { - case AMD_CPU_ID_PCO: - case AMD_CPU_ID_RN: - case AMD_CPU_ID_VG: - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - dev->num_ips = 12; - dev->ips_ptr = soc15_ip_blk; - dev->smu_msg = 0x538; - break; - case AMD_CPU_ID_PS: - dev->num_ips = 21; - dev->ips_ptr = soc15_ip_blk; - dev->smu_msg = 0x538; - break; case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - if (boot_cpu_data.x86_model == 0x70) { - dev->num_ips = ARRAY_SIZE(soc15_ip_blk_v2); - dev->ips_ptr = soc15_ip_blk_v2; - } else { - dev->num_ips = ARRAY_SIZE(soc15_ip_blk); - dev->ips_ptr = soc15_ip_blk; - } - dev->smu_msg = 0x938; + if (boot_cpu_data.x86_model == 0x70) + dev->cpu_info = &amd_1ah_m70_cpu_info; + else + dev->cpu_info = &amd_1ah_cpu_info; break; + case AMD_CPU_ID_SP: + case AMD_CPU_ID_SHP: + dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); + return -ENODEV; + default: + dev_err(dev->dev, "Unknown CPU ID: 0x%x\n", dev->cpu_id); + return -ENODEV; } + + return 0; } static int amd_pmc_setup_smu_logging(struct amd_pmc_dev *dev) @@ -296,9 +384,9 @@ static int smu_fw_info_show(struct seq_file *s, void *unused) table.timeto_resume_to_os_lastcapture); seq_puts(s, "\n=== Active time (in us) ===\n"); - for (idx = 0 ; idx < dev->num_ips ; idx++) { - if (dev->ips_ptr[idx].bit_mask & dev->active_ips) - seq_printf(s, "%-8s : %lld\n", dev->ips_ptr[idx].name, + for (idx = 0 ; idx < dev->cpu_info->num_ips ; idx++) { + if (dev->cpu_info->ips_ptr[idx].bit_mask & dev->active_ips) + seq_printf(s, "%-8s : %lld\n", dev->cpu_info->ips_ptr[idx].name, table.timecondition_notmet_lastcapture[idx]); } @@ -347,32 +435,22 @@ static int amd_pmc_idlemask_read(struct amd_pmc_dev *pdev, struct device *dev, u32 val; int rc; - switch (pdev->cpu_id) { - case AMD_CPU_ID_CZN: - /* we haven't yet read SMU version */ + /* we haven't yet read SMU version */ + if (pdev->cpu_id == AMD_CPU_ID_CZN) { if (!pdev->major) { rc = amd_pmc_get_smu_version(pdev); if (rc) return rc; } - if (pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37)) - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_CZN); - else + if (!(pdev->major > 56 || (pdev->major >= 55 && pdev->minor >= 37))) return -EINVAL; - break; - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_YC); - break; - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - val = amd_pmc_reg_read(pdev, AMD_PMC_SCRATCH_REG_1AH); - break; - default: - return -EINVAL; } + if (!pdev->cpu_info->scratch_reg) + return -EINVAL; + + val = amd_pmc_reg_read(pdev, pdev->cpu_info->scratch_reg); + if (dev) pm_pr_dbg("SMU idlemask s0i3: 0x%x\n", val); @@ -425,9 +503,9 @@ static void amd_pmc_dump_registers(struct amd_pmc_dev *dev) argument = dev->stb_arg.arg; response = dev->stb_arg.resp; } else { - message = dev->smu_msg; - argument = AMD_PMC_REGISTER_ARGUMENT; - response = AMD_PMC_REGISTER_RESPONSE; + message = dev->cpu_info->smu_msg; + argument = dev->cpu_info->smu_arg; + response = dev->cpu_info->smu_rsp; } value = amd_pmc_reg_read(dev, response); @@ -452,9 +530,9 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r argument = dev->stb_arg.arg; response = dev->stb_arg.resp; } else { - message = dev->smu_msg; - argument = AMD_PMC_REGISTER_ARGUMENT; - response = AMD_PMC_REGISTER_RESPONSE; + message = dev->cpu_info->smu_msg; + argument = dev->cpu_info->smu_arg; + response = dev->cpu_info->smu_rsp; } /* Wait until we get a valid response */ @@ -512,23 +590,6 @@ int amd_pmc_send_cmd(struct amd_pmc_dev *dev, u32 arg, u32 *data, u8 msg, bool r return rc; } -static int amd_pmc_get_os_hint(struct amd_pmc_dev *dev) -{ - switch (dev->cpu_id) { - case AMD_CPU_ID_PCO: - return MSG_OS_HINT_PCO; - case AMD_CPU_ID_RN: - case AMD_CPU_ID_VG: - case AMD_CPU_ID_YC: - case AMD_CPU_ID_CB: - case AMD_CPU_ID_PS: - case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: - case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: - return MSG_OS_HINT_RN; - } - return -EINVAL; -} - static int amd_pmc_wa_irq1(struct amd_pmc_dev *pdev) { struct device *d; @@ -602,7 +663,6 @@ static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; int rc; - u8 msg; u32 arg = 1; /* Reset and Start SMU logging - to monitor the s0i3 stats */ @@ -617,8 +677,7 @@ static void amd_pmc_s2idle_prepare(void) } } - msg = amd_pmc_get_os_hint(pdev); - rc = amd_pmc_send_cmd(pdev, arg, NULL, msg, false); + rc = amd_pmc_send_cmd(pdev, arg, NULL, pdev->cpu_info->os_hint, false); if (rc) { dev_err(pdev->dev, "suspend failed: %d\n", rc); return; @@ -659,10 +718,8 @@ static void amd_pmc_s2idle_restore(void) { struct amd_pmc_dev *pdev = &pmc; int rc; - u8 msg; - msg = amd_pmc_get_os_hint(pdev); - rc = amd_pmc_send_cmd(pdev, 0, NULL, msg, false); + rc = amd_pmc_send_cmd(pdev, 0, NULL, pdev->cpu_info->os_hint, false); if (rc) dev_err(pdev->dev, "resume failed: %d\n", rc); @@ -709,22 +766,6 @@ static const struct dev_pm_ops amd_pmc_pm = { .suspend = amd_pmc_suspend_handler, }; -static const struct pci_device_id pmc_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CB) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_YC) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_CZN) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RN) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SHP) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_VG) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M60H_ROOT) }, - { } -}; - static int amd_pmc_probe(struct platform_device *pdev) { struct amd_pmc_dev *dev = &pmc; @@ -736,17 +777,14 @@ static int amd_pmc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; rdev = pci_get_domain_bus_and_slot(0, 0, PCI_DEVFN(0, 0)); - if (!rdev || !pci_match_id(pmc_pci_ids, rdev)) { + if (!rdev) { err = -ENODEV; goto err_pci_dev_put; } - dev->cpu_id = rdev->device; - if (dev->cpu_id == AMD_CPU_ID_SP || dev->cpu_id == AMD_CPU_ID_SHP) { - dev_warn_once(dev->dev, "S0i3 is not supported on this hardware\n"); - err = -ENODEV; + err = amd_pmc_set_cpu_info(dev, rdev); + if (err) goto err_pci_dev_put; - } dev->rdev = rdev; err = amd_smn_read(0, AMD_PMC_BASE_ADDR_LO, &val); @@ -778,9 +816,6 @@ static int amd_pmc_probe(struct platform_device *pdev) if (err) goto err_pci_dev_put; - /* Get num of IP blocks within the SoC */ - amd_pmc_get_ip_info(dev); - platform_set_drvdata(pdev, dev); if (IS_ENABLED(CONFIG_SUSPEND)) { err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index fe3f53eb5955..616faddc70b7 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -17,6 +17,10 @@ /* SMU communication registers */ #define AMD_PMC_REGISTER_RESPONSE 0x980 #define AMD_PMC_REGISTER_ARGUMENT 0x9BC +#define AMD_PMC_REGISTER_MESSAGE 0x538 + +/* SMU communication registers for 1Ah 20h SoC */ +#define AMD_PMC_REGISTER_MSG_1AH_20H 0x938 /* PMC Scratch Registers */ #define AMD_PMC_SCRATCH_REG_CZN 0x94 @@ -90,6 +94,22 @@ struct stb_arg { u32 resp; }; +struct amd_pmc_bit_map { + const char *name; + u32 bit_mask; +}; + +/* SoC-specific information */ +struct amd_pmc_cpu_info { + u32 smu_msg; + u32 smu_arg; + u32 smu_rsp; + u32 num_ips; + u32 scratch_reg; + const struct amd_pmc_bit_map *ips_ptr; + u8 os_hint; +}; + struct amd_pmc_dev { void __iomem *regbase; void __iomem *smu_virt_addr; @@ -99,9 +119,6 @@ struct amd_pmc_dev { u32 cpu_id; u32 dram_size; u32 active_ips; - const struct amd_pmc_bit_map *ips_ptr; - u32 num_ips; - u32 smu_msg; /* SMU version information */ u8 smu_program; u8 major; @@ -116,11 +133,7 @@ struct amd_pmc_dev { bool disable_8042_wakeup; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; -}; - -struct amd_pmc_bit_map { - const char *name; - u32 bit_mask; + const struct amd_pmc_cpu_info *cpu_info; }; struct smu_metrics { @@ -151,20 +164,29 @@ void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); -/* List of supported CPU ids */ -#define AMD_CPU_ID_RV 0x15D0 -#define AMD_CPU_ID_RN 0x1630 -#define AMD_CPU_ID_PCO AMD_CPU_ID_RV -#define AMD_CPU_ID_CZN AMD_CPU_ID_RN -#define AMD_CPU_ID_VG 0x1645 -#define AMD_CPU_ID_YC 0x14B5 -#define AMD_CPU_ID_CB 0x14D8 -#define AMD_CPU_ID_PS 0x14E8 -#define AMD_CPU_ID_SP 0x14A4 -#define AMD_CPU_ID_SHP 0x153A -#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 -#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 -#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c +/* List of supported CPU/device IDs */ +#define PCI_DEVICE_ID_AMD_CPU_ID_PCO 0x15D0 +#define PCI_DEVICE_ID_AMD_CPU_ID_CZN 0x1630 +#define PCI_DEVICE_ID_AMD_CPU_ID_VG 0x1645 +#define PCI_DEVICE_ID_AMD_CPU_ID_YC 0x14B5 +#define PCI_DEVICE_ID_AMD_CPU_ID_CB 0x14D8 +#define PCI_DEVICE_ID_AMD_CPU_ID_PS 0x14E8 +#define PCI_DEVICE_ID_AMD_CPU_ID_SP 0x14A4 +#define PCI_DEVICE_ID_AMD_CPU_ID_SHP 0x153A + +/* Backward compatibility aliases */ +#define AMD_CPU_ID_PCO PCI_DEVICE_ID_AMD_CPU_ID_PCO +#define AMD_CPU_ID_CZN PCI_DEVICE_ID_AMD_CPU_ID_CZN +#define AMD_CPU_ID_VG PCI_DEVICE_ID_AMD_CPU_ID_VG +#define AMD_CPU_ID_YC PCI_DEVICE_ID_AMD_CPU_ID_YC +#define AMD_CPU_ID_CB PCI_DEVICE_ID_AMD_CPU_ID_CB +#define AMD_CPU_ID_PS PCI_DEVICE_ID_AMD_CPU_ID_PS +#define AMD_CPU_ID_SP PCI_DEVICE_ID_AMD_CPU_ID_SP +#define AMD_CPU_ID_SHP PCI_DEVICE_ID_AMD_CPU_ID_SHP + +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 +#define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 +#define PCI_DEVICE_ID_AMD_MP2_STB 0x172c int amd_stb_s2d_init(struct amd_pmc_dev *dev); int amd_stb_read(struct amd_pmc_dev *dev, u32 *buf); From 043af31c8d3031bbeb77dfdc6373005ef3b8a23b Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Tue, 9 Jun 2026 20:09:52 +0530 Subject: [PATCH 074/115] platform/x86/amd/pmc: Add PMC driver support for AMD 1Ah M80H SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 1Ah M80H SoC uses a different set of SMU mailbox register offsets compared to the existing 1Ah variants: message at 0xA10, argument at 0xA18, and response at 0xA14. Add amd_1ah_m80_cpu_info with these offsets, wire it into the PCI ID table via PCI_DEVICE_DATA(), populate scratch_reg field with AMD_PMC_SCRATCH_REG_1AH and add the corresponding ACPI ID AMDI000C. Reviewed-by: Mario Limonciello (AMD) Co-developed-by: Sanket Goswami Signed-off-by: Sanket Goswami Signed-off-by: Shyam Sundar S K Link: https://patch.msgid.link/20260609143952.2999707-3-Shyam-sundar.S-k@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 12 ++++++++++++ drivers/platform/x86/amd/pmc/pmc.h | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 92e36a6161a9..ccb37383b337 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -154,6 +154,16 @@ static const struct amd_pmc_cpu_info amd_1ah_m70_cpu_info = { .os_hint = MSG_OS_HINT_RN, }; +static const struct amd_pmc_cpu_info amd_1ah_m80_cpu_info = { + .smu_msg = AMD_PMC_REGISTER_MSG_1AH_80H, + .smu_arg = AMD_PMC_REGISTER_ARG_1AH_80H, + .smu_rsp = AMD_PMC_REGISTER_RSP_1AH_80H, + .num_ips = ARRAY_SIZE(soc15_ip_blk), + .scratch_reg = AMD_PMC_SCRATCH_REG_1AH, + .ips_ptr = soc15_ip_blk, + .os_hint = MSG_OS_HINT_RN, +}; + static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE_DATA(AMD, CPU_ID_PCO, &amd_pco_cpu_info) }, { PCI_DEVICE_DATA(AMD, CPU_ID_CZN, &amd_czn_cpu_info) }, @@ -165,6 +175,7 @@ static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE_DATA(AMD, CPU_ID_SHP, NULL) }, { PCI_DEVICE_DATA(AMD, 1AH_M20H_ROOT, NULL) }, { PCI_DEVICE_DATA(AMD, 1AH_M60H_ROOT, NULL) }, + { PCI_DEVICE_DATA(AMD, 1AH_M80H_ROOT, &amd_1ah_m80_cpu_info) }, { } }; @@ -860,6 +871,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = { {"AMDI0009", 0}, {"AMDI000A", 0}, {"AMDI000B", 0}, + {"AMDI000C", 0}, {"AMD0004", 0}, {"AMD0005", 0}, { } diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 616faddc70b7..36756e25b4bd 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -22,6 +22,11 @@ /* SMU communication registers for 1Ah 20h SoC */ #define AMD_PMC_REGISTER_MSG_1AH_20H 0x938 +/* SMU communication registers for 1Ah 80h SoC */ +#define AMD_PMC_REGISTER_MSG_1AH_80H 0xA10 +#define AMD_PMC_REGISTER_ARG_1AH_80H 0xA18 +#define AMD_PMC_REGISTER_RSP_1AH_80H 0xA14 + /* PMC Scratch Registers */ #define AMD_PMC_SCRATCH_REG_CZN 0x94 #define AMD_PMC_SCRATCH_REG_YC 0xD14 @@ -186,6 +191,7 @@ void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); #define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 #define PCI_DEVICE_ID_AMD_1AH_M60H_ROOT 0x1122 +#define PCI_DEVICE_ID_AMD_1AH_M80H_ROOT 0x115b #define PCI_DEVICE_ID_AMD_MP2_STB 0x172c int amd_stb_s2d_init(struct amd_pmc_dev *dev); From 6e9cab2247e5b243ae2d907ce7c948a8a9c8d61a Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Tue, 9 Jun 2026 16:14:19 +0800 Subject: [PATCH 075/115] platform/x86: dell-laptop: fix missing cleanups in init error path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dell_init() initializes several resources after dell_setup_rfkill(), including the optional touchpad LED, keyboard backlight LED, battery hook, debugfs directory and dell-laptop notifier. If a later LED or backlight registration fails, the error path only tears down the battery hook and rfkill resources. This leaves the notifier, debugfs directory, keyboard backlight LED and optional touchpad LED registered after dell_init() returns an error. Add the missing cleanup calls before tearing down rfkill. Fixes: 9c656b07997f ("platform/x86: dell-*: Call new led hw_changed API on kbd brightness change") Fixes: 037accfa14b2 ("dell-laptop: Add debugfs support") Fixes: 2d8b90be4f1c ("dell-laptop: support Synaptics/Alps touchpad led") Fixes: 6cff8d60aa0a ("platform: x86: dell-laptop: Add support for keyboard backlight") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Link: https://patch.msgid.link/20260609081419.1995169-1-lihaoxiang@isrc.iscas.ac.cn Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-laptop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/dell/dell-laptop.c b/drivers/platform/x86/dell/dell-laptop.c index 7fc3bbb8c4a4..89e85c7f7132 100644 --- a/drivers/platform/x86/dell/dell-laptop.c +++ b/drivers/platform/x86/dell/dell-laptop.c @@ -2560,7 +2560,12 @@ static int __init dell_init(void) if (mute_led_registered) led_classdev_unregister(&mute_led_cdev); fail_led: + dell_laptop_unregister_notifier(&dell_laptop_notifier); + debugfs_remove_recursive(dell_laptop_dir); dell_battery_exit(); + kbd_led_exit(); + if (quirks && quirks->touchpad_led) + touchpad_led_exit(); dell_cleanup_rfkill(); fail_rfkill: platform_device_del(platform_device); From 375bbbbd112af028ee0b45d833a6233c23d19bbf Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 29 May 2026 11:31:49 -0700 Subject: [PATCH 076/115] platform/x86/intel/vsec: Restore BAR fallback for header walk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base_addr refactor changed intel_vsec_walk_header() to pass info->base_addr as the discovery-table base address. For the PCI VSEC driver this info comes from driver_data, but exported callers may provide their own static headers and leave base_addr unset. For xe, this made the discovery-table base address zero instead of the BAR selected by header->tbir, preventing PMT endpoints from being created. Restore the previous behavior for the header-walk path by falling back to pci_resource_start(pdev, header->tbir) when base_addr is not specified. Keep explicit base_addr override behavior unchanged. This preserves the refactor structure while fixing the functional regression in manual-header users. Fixes: 904b333fc51c ("platform/x86/intel/vsec: Refactor base_addr handling") Assisted-by: Claude:claude-sonnet-4-6 Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260529183150.129744-1-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 1657834fd275..3ae4557b32b4 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -482,10 +482,25 @@ static int intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; + u64 base_addr; int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(dev, *header, info, info->base_addr); + if (info->base_addr) { + base_addr = info->base_addr; + } else { + struct pci_dev *pdev; + + if (!dev_is_pci(dev)) { + dev_err(dev, "non-PCI device without a base address\n"); + return -EINVAL; + } + + pdev = to_pci_dev(dev); + base_addr = pci_resource_start(pdev, (*header)->tbir); + } + + ret = intel_vsec_register_device(dev, *header, info, base_addr); if (ret) return ret; } From c085d82613d5618814b84406c8b2d64f1bc305e7 Mon Sep 17 00:00:00 2001 From: HyeongJun An Date: Sat, 6 Jun 2026 02:49:05 +0900 Subject: [PATCH 077/115] platform/x86: intel-hid: Protect ACPI notify handler against recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") ACPI notify handlers like the intel-hid notify_handler() may run on multiple CPU cores racing with themselves. On convertibles and detachables (matched by DMI chassis-type 31 and 32 in dmi_auto_add_switch[]) the SW_TABLET_MODE input device is registered lazily from notify_handler() on the first tablet-mode event, via intel_hid_switches_setup(). When two such events race on different CPUs both can pass the !priv->switches check and register the priv->switches input device twice, resulting in a duplicate sysfs entry and a subsequent NULL pointer dereference. This is the same class of bug fixed by commit e075c3b13a0a ("platform/x86: intel-vbtn: Protect ACPI notify handler against recursion") for the sibling intel-vbtn driver. Protect intel-hid notify_handler() from racing with itself with a mutex to fix this. Fixes: e2ffcda16290 ("ACPI: OSL: Allow Notify () handlers to run on all CPUs") Cc: stable@vger.kernel.org Signed-off-by: HyeongJun An Link: https://patch.msgid.link/20260605174905.131095-1-sammiee5311@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/hid.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c index 73874d4369c1..acd42e639fa4 100644 --- a/drivers/platform/x86/intel/hid.c +++ b/drivers/platform/x86/intel/hid.c @@ -7,11 +7,13 @@ */ #include +#include #include #include #include #include #include +#include #include #include #include @@ -237,6 +239,7 @@ static const struct dmi_system_id dmi_auto_add_switch[] = { }; struct intel_hid_priv { + struct mutex mutex; /* Avoid notify_handler() racing with itself */ struct input_dev *input_dev; struct input_dev *array; struct input_dev *switches; @@ -572,6 +575,8 @@ static void notify_handler(acpi_handle handle, u32 event, void *context) struct key_entry *ke; int err; + guard(mutex)(&priv->mutex); + /* * Some convertible have unreliable VGBS return which could cause incorrect * SW_TABLET_MODE report, in these cases we enable support when receiving @@ -727,6 +732,10 @@ static int intel_hid_probe(struct platform_device *device) return -ENOMEM; dev_set_drvdata(&device->dev, priv); + err = devm_mutex_init(&device->dev, &priv->mutex); + if (err) + return err; + /* See dual_accel_detect.h for more info on the dual_accel check. */ if (enable_sw_tablet_mode == TABLET_SW_AUTO) { if (dmi_check_system(dmi_vgbs_allow_list)) From 6b63520ed14b17bbe9c2103debbd2152dde1fba3 Mon Sep 17 00:00:00 2001 From: Guixiong Wei Date: Tue, 2 Jun 2026 10:07:52 +0800 Subject: [PATCH 078/115] platform/x86/intel-uncore-freq: Fix current_freq_khz after CPU hotplug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the last CPU of a legacy uncore die goes offline, uncore_freq_remove_die_entry() clears control_cpu. During CPU hotplug re-add, uncore_freq_add_entry() still populates sysfs attributes before assigning the new control CPU. As a result, the current frequency read returns -ENXIO and current_freq_khz is omitted from the recreated sysfs group. Assign control_cpu before the initial read paths and before create_attr_group() so sysfs recreation uses the new online CPU. If sysfs creation fails, restore control_cpu to -1 to keep the error path state consistent. Fixes: 4d73c6772ab7 ("platform/x86: intel-uncore-freq: Conditionally create attribute for read frequency") Cc: stable@vger.kernel.org Signed-off-by: Guixiong Wei Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260602020752.3126-1-weiguixiong@bytedance.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/intel/uncore-frequency/uncore-frequency-common.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c index 3b554418a7a3..4bcafedf68ef 100644 --- a/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c +++ b/drivers/platform/x86/intel/uncore-frequency/uncore-frequency-common.c @@ -285,15 +285,20 @@ int uncore_freq_add_entry(struct uncore_data *data, int cpu) data->package_id, data->die_id); } + /* + * Set the control CPU before any read path so entry recreation after CPU + * hotplug can populate read-only attributes from the new online CPU. + */ + data->control_cpu = cpu; uncore_read(data, &data->initial_min_freq_khz, UNCORE_INDEX_MIN_FREQ); uncore_read(data, &data->initial_max_freq_khz, UNCORE_INDEX_MAX_FREQ); ret = create_attr_group(data, data->name); if (ret) { + data->control_cpu = -1; if (data->domain_id != UNCORE_DOMAIN_ID_INVALID) ida_free(&intel_uncore_ida, data->seqnum_id); } else { - data->control_cpu = cpu; data->valid = true; } From 2565a28cdcdcb035e151d285efcba26bccb3726e Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 28 May 2026 13:45:21 -0700 Subject: [PATCH 079/115] platform/x86: ISST: Restore SST-PP control to all domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SST-PP control offset is only restored to power domain 0 after resume. During suspend, control values are read and stored for all power domains. Use pd_info->sst_base instead of power_domain_info->sst_base, which only points to power domain 0 base address. Fixes: dc7901b5a156 ("platform/x86: ISST: Store and restore all domains data") Reported-by: Yi Lai Signed-off-by: Srinivas Pandruvada Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260528204521.3531456-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c index b804cb753f94..24334ae70d82 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_tpmi_core.c @@ -1804,7 +1804,7 @@ void tpmi_sst_dev_resume(struct auxiliary_device *auxdev) if (!(pd_info->sst_header.cap_mask & SST_PP_CAP_PP_ENABLE)) continue; - writeq(pd_info->saved_pp_control, power_domain_info->sst_base + + writeq(pd_info->saved_pp_control, pd_info->sst_base + pd_info->sst_header.pp_offset + SST_PP_CONTROL_OFFSET); } } From 711104ab5ed500f155e4a53c56571b9b24a9a9b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dariusz=20Figza=C5=82?= Date: Wed, 10 Jun 2026 18:49:42 +0200 Subject: [PATCH 080/115] platform/x86: asus-wmi: add keystone dongle support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ASUS Keystone is a physical NFC-like dongle that slots into supported ASUS laptops. The EC fires WMI notify code 0xB4 on insert/remove events. Expose the current insert state via a sysfs attribute by querying WMI device ID 0x00120091 (DSTS). This devid does not follow the standard DSTS convention: PRESENCE_BIT (0x00010000) encodes the insert state rather than feature presence, and STATUS_BIT is never set. Presence of a keystone slot is detected by a successful DSTS call. Reviewed-by: Denis Benato Signed-off-by: Dariusz Figzał Link: https://patch.msgid.link/20260610164942.74956-1-dariuszfigzal@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../ABI/testing/sysfs-platform-asus-wmi | 9 +++ drivers/platform/x86/asus-wmi.c | 65 +++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 7 ++ 3 files changed, 81 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi index 89acb6638df8..f9825c6150b5 100644 --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi @@ -58,6 +58,15 @@ Description: * 1 - overboost, * 2 - silent +What: /sys/devices/platform//keystone +Date: Jun 2026 +KernelVersion: 7.2 +Contact: "Dariusz Figzał" +Description: + Reports the Keystone dongle insert state (read-only): + * 0 - not inserted + * 1 - inserted + What: /sys/devices/platform//gpu_mux_mode Date: Aug 2022 KernelVersion: 6.1 diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 80144c412b90..3c9ef826551d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -70,6 +70,7 @@ module_param(fnlock_default, bool, 0444); #define NOTIFY_KBD_TTP 0xae #define NOTIFY_LID_FLIP 0xfa #define NOTIFY_LID_FLIP_ROG 0xbd +#define NOTIFY_KEYSTONE 0xb4 #define ASUS_WMI_FNLOCK_BIOS_DISABLED BIT(0) @@ -279,6 +280,8 @@ struct asus_wmi { u32 tablet_switch_dev_id; bool tablet_switch_inverted; + bool keystone_available; + enum fan_type fan_type; enum fan_type gpu_fan_type; enum fan_type mid_fan_type; @@ -646,6 +649,55 @@ static bool asus_wmi_dev_is_present(struct asus_wmi *asus, u32 dev_id) return status == 0 && (retval & ASUS_WMI_DSTS_PRESENCE_BIT); } +/* Keystone *******************************************************************/ + +static int keystone_check_present(struct asus_wmi *asus) +{ + u32 retval; + int err; + + asus->keystone_available = false; + + /* + * Use a raw devstate call rather than asus_wmi_dev_is_present(). + * For this devid, PRESENCE_BIT encodes current insert state, not + * feature presence, so asus_wmi_dev_is_present() would return false + * whenever the dongle is absent at boot, even on machines that have + * a keystone slot. + * -ENODEV means the firmware doesn't know this devid at all. + * retval is not examined here, only the return code matters. + */ + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval); + if (err == -ENODEV) + return 0; + if (err) + return err; + + asus->keystone_available = true; + return 0; +} + +static ssize_t keystone_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + u32 retval; + int err; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_KEYSTONE, &retval); + if (err) + return err; + + return sysfs_emit(buf, "%d\n", !!(retval & ASUS_WMI_DSTS_PRESENCE_BIT)); +} + +static DEVICE_ATTR_RO(keystone); + +static void asus_wmi_keystone_notify(struct asus_wmi *asus) +{ + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "keystone"); +} + /* Input **********************************************************************/ static void asus_wmi_tablet_sw_report(struct asus_wmi *asus, bool value) { @@ -4575,6 +4627,12 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + if (code == NOTIFY_KEYSTONE) { + if (asus->keystone_available) + asus_wmi_keystone_notify(asus); + return; + } + if (code == NOTIFY_KBD_FBM || code == NOTIFY_KBD_TTP) { if (asus->fan_boost_mode_available) fan_boost_mode_switch_next(asus); @@ -4698,6 +4756,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, + &dev_attr_keystone.attr, #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) &dev_attr_charge_mode.attr, &dev_attr_egpu_enable.attr, @@ -4741,6 +4800,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_ALS_ENABLE; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; + else if (attr == &dev_attr_keystone.attr) + ok = asus->keystone_available; #if IS_ENABLED(CONFIG_ASUS_WMI_DEPRECATED_ATTRS) if (attr == &dev_attr_charge_mode.attr) @@ -5081,6 +5142,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform_profile_setup; + err = keystone_check_present(asus); + if (err) + dev_warn(&pdev->dev, "Failed to check Keystone presence: %d\n", err); + err = asus_wmi_sysfs_init(asus->platform_device); if (err) goto fail_sysfs; diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 554f41b827e1..c29962d5baac 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -147,6 +147,13 @@ #define ASUS_WMI_DEVID_GPU_MUX 0x00090016 #define ASUS_WMI_DEVID_GPU_MUX_VIVO 0x00090026 +/* Keystone dongle insert/remove state. + * PRESENCE_BIT (0x00010000) encodes insert state: + * 0x00010000 = inserted, 0x00000000 = absent. STATUS_BIT is never set. + * 0xFFFFFFFE means no keystone slot on this machine. + */ +#define ASUS_WMI_DEVID_KEYSTONE 0x00120091 + /* TUF laptop RGB modes/colours */ #define ASUS_WMI_DEVID_TUF_RGB_MODE 0x00100056 #define ASUS_WMI_DEVID_TUF_RGB_MODE2 0x0010005A From 6b3bbe770f4ca0439710b7c42f88b9f6eeebabd0 Mon Sep 17 00:00:00 2001 From: Marco Scardovi Date: Wed, 10 Jun 2026 17:21:30 +0200 Subject: [PATCH 081/115] platform/x86: asus-armoury: add support for G614PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP power limits and fan curve requirements for the ASUS ROG Strix G16 G614PR laptop model. The ASUS ROG Strix G16 G614PR requires specific AC/DC power limits (PPT PL1/PL2/PL3, dynamic boost, and NV TGP targets) to function correctly under various power profiles. Without these limits, the Asus Armoury driver cannot configure the correct power envelopes or enable custom fan curves, leading to suboptimal performance or noise management. This patch adds the corresponding DMI board name matching entry ("G614PR") under the power_limits table in asus-armoury.h, populating the AC and DC limits based on the platform's hardware specification. Assisted-by: Antigravity:gemini-3.5-flash Signed-off-by: Marco Scardovi Reviewed-by: Denis Benato Link: https://patch.msgid.link/20260610152130.25892-1-scardracs@disroot.org Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 692978b61959..5be34b6a92db 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1898,6 +1898,40 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "G614PR"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 30, + .ppt_pl1_spl_max = 90, + .ppt_pl2_sppt_min = 65, + .ppt_pl2_sppt_def = 110, + .ppt_pl2_sppt_max = 125, + .ppt_pl3_fppt_min = 65, + .ppt_pl3_fppt_def = 110, + .ppt_pl3_fppt_max = 125, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_max = 25, + .nv_tgp_min = 65, + .nv_tgp_max = 115, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 65, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 75, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "G615LR"), From 25fbff92dd485eb15eaf1aa7252a99fcaabfef5e Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Wed, 10 Jun 2026 17:12:34 -0700 Subject: [PATCH 082/115] platform/x86: dell-privacy: correct CONFIG_DELL_WMI_PRIVACY macro name in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Comments in drivers/platform/x86/dell/dell-wmi-privacy.h incorrectly refer to CONFIG_DELL_PRIVACY instead of CONFIG_DELL_WMI_PRIVACY. Correct them. Discovered while searching for CONFIG_* symbols referenced in code but not defined in any Kconfig file. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260611001238.391045-1-enelsonmoore@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-privacy.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.h b/drivers/platform/x86/dell/dell-wmi-privacy.h index 50c9b943dd47..ca242e1fe53f 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.h +++ b/drivers/platform/x86/dell/dell-wmi-privacy.h @@ -13,7 +13,7 @@ bool dell_privacy_has_mic_mute(void); bool dell_privacy_process_event(int type, int code, int status); int dell_privacy_register_driver(void); void dell_privacy_unregister_driver(void); -#else /* CONFIG_DELL_PRIVACY */ +#else /* CONFIG_DELL_WMI_PRIVACY */ static inline bool dell_privacy_has_mic_mute(void) { return false; @@ -32,5 +32,5 @@ static inline int dell_privacy_register_driver(void) static inline void dell_privacy_unregister_driver(void) { } -#endif /* CONFIG_DELL_PRIVACY */ +#endif /* CONFIG_DELL_WMI_PRIVACY */ #endif From 01ffffc4d0bd5b44a3e5fc07ec232266a1601c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig=20=28The=20Capable=20Hub=29?= Date: Wed, 10 Jun 2026 16:32:59 +0200 Subject: [PATCH 083/115] platform/x86: apple-gmux: Drop unused assignment of pnp_device_id driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver explicitly sets the .driver_data member of struct pnp_device_id to zero without relying on that value. Drop these unused assignments. While touching this array use a named initializer for .id for improved readability and simplify the list terminator. This patch doesn't modify the compiled array, only its representation in source form benefits. The former was confirmed with an x86 build. Signed-off-by: Uwe Kleine-König (The Capable Hub) Link: https://patch.msgid.link/dd6e70d3075205a1d5c1fa324db7a822f37e2349.1781101905.git.u.kleine-koenig@baylibre.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/apple-gmux.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index fbc30f1f8abd..9c728ac4e045 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -1013,8 +1013,8 @@ static void gmux_remove(struct pnp_dev *pnp) } static const struct pnp_device_id gmux_device_ids[] = { - {GMUX_ACPI_HID, 0}, - {"", 0} + { .id = GMUX_ACPI_HID }, + { } }; static const struct dev_pm_ops gmux_dev_pm_ops = { From 3bdd6fc11fbfa8249483f4b716ead51e43e3a0cd Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:23 +0200 Subject: [PATCH 084/115] platform/x86/amd/pmc: Check for intermediate wakeup in function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor code introduced by commit 9f5595d5f03f ("pmc: Require at least 2.5 seconds between HW sleep cycles") to allow adding different conditions for that delay in an upcoming change. Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-2-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index ccb37383b337..25509099a958 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -670,6 +670,19 @@ static int amd_pmc_verify_czn_rtc(struct amd_pmc_dev *pdev, u32 *arg) return rc; } +static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) +{ + /* + * Starting a new HW sleep cycle right after waking from one + * can cause electrical problems triggering the over voltage protection. + * That is avoided by delaying the next suspend a bit, see also + * https://lore.kernel.org/all/20250414162446.3853194-1-superm1@kernel.org/ + */ + struct smu_metrics table; + + return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -702,11 +715,9 @@ static void amd_pmc_s2idle_prepare(void) static void amd_pmc_s2idle_check(void) { struct amd_pmc_dev *pdev = &pmc; - struct smu_metrics table; int rc; - /* Avoid triggering OVP */ - if (!get_metrics_table(pdev, &table) && table.s0i3_last_entry_status) + if (amd_pmc_intermediate_wakeup_need_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ From 9b9e60dd31da054a37d601e9fcabdfd8a2bff354 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:24 +0200 Subject: [PATCH 085/115] platform/x86/amd/pmc: Delay suspend for some Lenovo Laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some IdeaPad Slim 3 devices and similar with AMD CPUs have a nonfunctional keyboard and lid switch after s2idle. It helps to delay suspend by 2.5 seconds so the EC has some time to do whatever it needs to get done before suspend - unfortunately at least on my 16ABR8 waking it with a timer (wakealarm) still triggers the issue, but at least normal resume via keypress or lid works fine. On the 14ARP10 wakealarm has been reported to also work fine with this patch. This issue has been reported for many different devices, this patch has been tested with the Zen3-based IdeaPad Slim 3 16ABR8 (82XR) and the Zen3+-based IdeaPad Slim 3 14ARP10 (83K6) and IdeaPad Slim 3 15ARP10 (83MM). Reported-by: Sindre Henriksen Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Tested-by: Sindre Henriksen Suggested-by: Mario Limonciello (AMD) Reviewed-by: Mario Limonciello (AMD) Reviewed-by: Ilpo Järvinen Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-3-daniel@gibson.sh Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc-quirks.c | 39 +++++++++++++++++++++++ drivers/platform/x86/amd/pmc/pmc.c | 24 +++++++++++++- drivers/platform/x86/amd/pmc/pmc.h | 1 + 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/pmc/pmc-quirks.c b/drivers/platform/x86/amd/pmc/pmc-quirks.c index 24506e342943..74ddf1d8289a 100644 --- a/drivers/platform/x86/amd/pmc/pmc-quirks.c +++ b/drivers/platform/x86/amd/pmc/pmc-quirks.c @@ -18,6 +18,7 @@ struct quirk_entry { u32 s2idle_bug_mmio; bool spurious_8042; + bool need_suspend_delay; }; static struct quirk_entry quirk_s2idle_bug = { @@ -33,6 +34,10 @@ static struct quirk_entry quirk_s2idle_spurious_8042 = { .spurious_8042 = true, }; +static struct quirk_entry quirk_s2idle_need_suspend_delay = { + .need_suspend_delay = true, +}; + static const struct dmi_system_id fwbug_list[] = { { .ident = "L14 Gen2 AMD", @@ -203,6 +208,35 @@ static const struct dmi_system_id fwbug_list[] = { DMI_MATCH(DMI_PRODUCT_NAME, "82XQ"), } }, + /* https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ + { + .ident = "Zen3-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + /* + * Note: there are also some Zen2-based 82X* devices that + * need different quirks, they're already handled above + */ + DMI_MATCH(DMI_PRODUCT_NAME, "82X"), + } + }, + { + .ident = "Zen3+-based IdeaPad Slim and similar", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83K"), + } + }, + { + .ident = "IdeaPad Slim 3 15ARP10 (83MM)", + .driver_data = &quirk_s2idle_need_suspend_delay, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "83MM"), + } + }, /* https://bugzilla.kernel.org/show_bug.cgi?id=221273 */ { .ident = "Thinkpad L14 Gen3", @@ -356,6 +390,11 @@ void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); } +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev) +{ + return dev->quirks && dev->quirks->need_suspend_delay; +} + void amd_pmc_quirks_init(struct amd_pmc_dev *dev) { const struct dmi_system_id *dmi_id; diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 25509099a958..758abcf9f094 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -683,6 +683,27 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) return get_metrics_table(pdev, &table) == 0 && table.s0i3_last_entry_status; } +static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) +{ + /* + * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some + * me-time before sleeping or they get uncooperative after waking + * up and don't send events for keyboard and lid switch anymore. + * + * Unfortunately this doesn't entirely fix the problem: It can still + * happen when resuming with a timer (wakealarm), but at least the + * more common usecases (wakeup by opening lid or pressing a key) + * work fine with this workaround. + * + * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 + */ + if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + return false; +} + static void amd_pmc_s2idle_prepare(void) { struct amd_pmc_dev *pdev = &pmc; @@ -717,7 +738,8 @@ static void amd_pmc_s2idle_check(void) struct amd_pmc_dev *pdev = &pmc; int rc; - if (amd_pmc_intermediate_wakeup_need_delay(pdev)) + if (amd_pmc_intermediate_wakeup_need_delay(pdev) || + amd_pmc_want_suspend_delay(pdev)) msleep(2500); /* Dump the IdleMask before we add to the STB */ diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 36756e25b4bd..1ef182bb240d 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -165,6 +165,7 @@ enum amd_pmc_def { }; void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); +bool amd_pmc_quirk_need_suspend_delay(struct amd_pmc_dev *dev); void amd_pmc_quirks_init(struct amd_pmc_dev *dev); void amd_mp2_stb_init(struct amd_pmc_dev *dev); void amd_mp2_stb_deinit(struct amd_pmc_dev *dev); From 428b9fd2dce50b4dc5cd9ade10b92efcf57ce7aa Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:25 +0200 Subject: [PATCH 086/115] platform/x86/amd/pmc: Add delay_suspend module parameter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enabling the new delay_suspend module parameter delays suspend for 2.5 seconds which is known to help for some AMD-based Lenovo Laptops that otherwise failed to send/receive events for key presses or the lid switch after s2idle. Apparently the EC needs to do some things in the background before suspend or it gets into a bad state. There are many reports of AMD-based laptops (mostly but not exclusively IdeaPads) about similar issues on the web; this parameter gives affected users an easy way to try out if their issues have the same root cause and to work around them until their specific device is added to the quirks list. The parameter description has a note encouraging users to report their device so it can be added to the quirks list, inspired by a similar request in parameter descriptions of the ideapad-laptop module. The module parameter can be set to "1" to explicitly enable it, "0" to disable it even on devices that are assumed to be affected, or -1 (the default) to enable it if the device is assumed to be affected (according to fwbug_list[]) Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-4-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index 758abcf9f094..ce97c27bc362 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -183,6 +184,11 @@ static bool disable_workarounds; module_param(disable_workarounds, bool, 0644); MODULE_PARM_DESC(disable_workarounds, "Disable workarounds for platform bugs"); +static int delay_suspend = -1; +module_param(delay_suspend, int, 0644); +MODULE_PARM_DESC(delay_suspend, + "Delays s2idle by 2.5 seconds to work around buggy ECs, often causing keyboard issues after suspend. 0: don't delay, 1: do delay, -1 (default): let amd_pmc decide. If you need this please report this to: platform-driver-x86@vger.kernel.org"); + static struct amd_pmc_dev pmc; static inline u32 amd_pmc_reg_read(struct amd_pmc_dev *dev, int reg_offset) @@ -697,8 +703,23 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * * See https://bugzilla.kernel.org/show_bug.cgi?id=221383 */ - if (!disable_workarounds && amd_pmc_quirk_need_suspend_delay(pdev)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (amd_pmc_quirk_need_suspend_delay(pdev)) { + /* + * delay_suspend=1 force-enables this, otherwise it can be + * disabled with disable_workarounds or delay_suspend=0 + */ + if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + return true; + } + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + } else if (delay_suspend == 1) { + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; From 037f0b03c663a247366673a807834389107995b7 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Thu, 11 Jun 2026 17:04:26 +0200 Subject: [PATCH 087/115] platform/x86/amd/pmc: Don't log during intermediate wakeups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ECs in the IdeaPads that need the delay_suspend quirk send lots of messages when charging, which not only causes intermediate wakeups when suspended, but also prevents the device from reaching the deepest suspend state. Because of this amd_pmc_intermediate_wakeup_need_delay() returns false during intermediate wakeups and amd_pmc_want_suspend_delay() is called. So far it always logged its "Delaying suspend by 2.5s ..." messages then, which spams dmesg. This commit makes sure that those messages are only logged once per suspend. Link: https://bugzilla.kernel.org/show_bug.cgi?id=221383 Reviewed-by: Hans de Goede Signed-off-by: Daniel Gibson Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260611150426.3683372-5-daniel@gibson.sh Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmc/pmc.c | 39 ++++++++++++++++++++++++------ drivers/platform/x86/amd/pmc/pmc.h | 1 + 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/amd/pmc/pmc.c b/drivers/platform/x86/amd/pmc/pmc.c index ce97c27bc362..b2eb9909f6a4 100644 --- a/drivers/platform/x86/amd/pmc/pmc.c +++ b/drivers/platform/x86/amd/pmc/pmc.c @@ -691,6 +691,20 @@ static bool amd_pmc_intermediate_wakeup_need_delay(struct amd_pmc_dev *pdev) static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) { + /* + * intermediate_wakeup implies that the machine didn't get to deepest sleep + * state before - otherwise this function isn't called in amd_pmc_s2idle_check() + * because amd_pmc_intermediate_wakeup_need_delay() returns true first. + * On some IdeaPads that happens when charging, because the EC seems + * to send lots of messages then that wake the machine. + * + * But even in that case, the sleep here is necessary (on those IdeaPads), + * otherwise they wake up completely (resume) after a few seconds. + * So this variable is only used to avoid spamming dmesg on each + * intermediate wakeup. + */ + bool intermediate_wakeup = !pdev->is_first_check_after_suspend; + /* * Some Lenovo Laptops (like different IdeaPad 3 Slims) need some * me-time before sleeping or they get uncooperative after waking @@ -709,17 +723,20 @@ static bool amd_pmc_want_suspend_delay(struct amd_pmc_dev *pdev) * disabled with disable_workarounds or delay_suspend=0 */ if (delay_suspend == 1 || (delay_suspend == -1 && !disable_workarounds)) { - dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s to avoid platform bug\n"); return true; } - dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Not delaying suspend because of module parameter, even though your device is assumed to need it!\n"); } else if (delay_suspend == 1) { - dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", - dmi_get_system_info(DMI_SYS_VENDOR), - dmi_get_system_info(DMI_PRODUCT_NAME), - dmi_get_system_info(DMI_PRODUCT_FAMILY), - dmi_get_system_info(DMI_BOARD_VENDOR), - dmi_get_system_info(DMI_BOARD_NAME)); + if (!intermediate_wakeup) + dev_info(pdev->dev, "Delaying suspend by 2.5s because delay_suspend=1. If this solves problems on your machine, please report this whole line to: platform-driver-x86@vger.kernel.org so it can be automatically detected as affected in the future. System Vendor: \"%s\" Product Name: \"%s\" Product Family: \"%s\" Board Vendor: \"%s\" Board Name: \"%s\"\n", + dmi_get_system_info(DMI_SYS_VENDOR), + dmi_get_system_info(DMI_PRODUCT_NAME), + dmi_get_system_info(DMI_PRODUCT_FAMILY), + dmi_get_system_info(DMI_BOARD_VENDOR), + dmi_get_system_info(DMI_BOARD_NAME)); return true; } return false; @@ -731,6 +748,9 @@ static void amd_pmc_s2idle_prepare(void) int rc; u32 arg = 1; + /* Reset this variable because this is a fresh suspend */ + pdev->is_first_check_after_suspend = true; + /* Reset and Start SMU logging - to monitor the s0i3 stats */ amd_pmc_setup_smu_logging(pdev); @@ -769,6 +789,9 @@ static void amd_pmc_s2idle_check(void) rc = amd_stb_write(pdev, AMD_PMC_STB_S2IDLE_CHECK); if (rc) dev_err(pdev->dev, "error writing to STB: %d\n", rc); + + /* remember that first check after suspend is done (until next prepare) */ + pdev->is_first_check_after_suspend = false; } static int amd_pmc_dump_data(struct amd_pmc_dev *pdev) diff --git a/drivers/platform/x86/amd/pmc/pmc.h b/drivers/platform/x86/amd/pmc/pmc.h index 1ef182bb240d..6973a639d7e3 100644 --- a/drivers/platform/x86/amd/pmc/pmc.h +++ b/drivers/platform/x86/amd/pmc/pmc.h @@ -136,6 +136,7 @@ struct amd_pmc_dev { struct dentry *dbgfs_dir; struct quirk_entry *quirks; bool disable_8042_wakeup; + bool is_first_check_after_suspend; struct amd_mp2_dev *mp2; struct stb_arg stb_arg; const struct amd_pmc_cpu_info *cpu_info; From d20457b46eca76b9bb716dd31af591cad21607b5 Mon Sep 17 00:00:00 2001 From: Muralidhara M K Date: Fri, 12 Jun 2026 09:56:08 +0530 Subject: [PATCH 088/115] platform/x86/amd/hsmp: Clamp ioctl/send_message indices (Spectre v1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although validate_message() checks msg_id, a mispredicted branch can still allow speculative indexing into hsmp_msg_desc_table[]. Clamp msg.msg_id with array_index_nospec() at entry to hsmp_ioctl_msg() so downstream dereferences (including via is_get_msg() and hsmp_send_message()) see a bounded index. Similarly, hsmp_send_message() bounds-checks msg->sock_ind before indexing hsmp_pdev.sock[], but a mispredicted branch can still speculatively use the raw index (Spectre v1, CVE-2017-5753). Apply array_index_nospec() after the check so every caller that reaches hsmp_pdev.sock[] through this helper sees a clamped socket index—including hsmp_ioctl_msg() and any other path that hands a user-derived struct hsmp_message to hsmp_send_message(). Reviewed-by: Muthusamy Ramalingam Signed-off-by: Muralidhara M K Link: https://patch.msgid.link/20260612042610.1629037-7-muralidhara.mk@amd.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/hsmp/hsmp.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 631ffc0978d1..6a26937fc2b5 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -202,6 +202,7 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { struct hsmp_socket *sock; + unsigned int sock_ind; int ret; if (!msg) @@ -212,7 +213,15 @@ int hsmp_send_message(struct hsmp_message *msg) if (!hsmp_pdev.sock || msg->sock_ind >= hsmp_pdev.num_sockets) return -ENODEV; - sock = &hsmp_pdev.sock[msg->sock_ind]; + + /* + * Sanitize sock_ind after the bounds check. A mispredicted branch can + * still let the CPU speculatively use msg->sock_ind as an index into + * hsmp_pdev.sock[] (Spectre v1, CVE-2017-5753), including for callers + * other than hsmp_ioctl_msg() that pass a user-derived socket index. + */ + sock_ind = array_index_nospec(msg->sock_ind, hsmp_pdev.num_sockets); + sock = &hsmp_pdev.sock[sock_ind]; ret = down_interruptible(&sock->hsmp_sem); if (ret < 0) @@ -308,6 +317,19 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) if (msg.msg_id < HSMP_TEST || msg.msg_id >= HSMP_MSG_ID_MAX) return -ENOMSG; + /* + * Sanitize the user-controlled msg_id against speculative + * execution. The bounds check above retires the out-of-range + * case with -ENOMSG, but a mispredicted branch can still let the + * CPU speculatively use msg_id as an index into + * hsmp_msg_desc_table[] (here and in validate_message() / + * is_get_msg() called downstream via hsmp_send_message()), and + * pull arbitrary kernel memory into the cache (Spectre v1, + * CVE-2017-5753). Clamp once into msg.msg_id so every downstream + * dereference sees the sanitized value. + */ + msg.msg_id = array_index_nospec(msg.msg_id, HSMP_MSG_ID_MAX); + switch (fp->f_mode & (FMODE_WRITE | FMODE_READ)) { case FMODE_WRITE: /* From 0b38a42bf59fc3ba97dd6f02bfc257dffa0e1d6d Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:06 +0000 Subject: [PATCH 089/115] platform/x86: asus-armoury: add support for GA402NJ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GA402NJ. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-2-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 5be34b6a92db..b53288687659 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -950,6 +950,38 @@ static const struct dmi_system_id power_limits[] = { .dc_data = NULL, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA402NJ"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_def = 35, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_def = 65, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_dynamic_boost_min = 5, + .nv_dynamic_boost_def = 10, + .nv_dynamic_boost_max = 15, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + }, + }, { .matches = { // This model is full AMD. No Nvidia dGPU. From a0bb64f3170adccb5780cf8778e601392d94f244 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:07 +0000 Subject: [PATCH 090/115] platform/x86: asus-armoury: add support for GA403UM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model GA403UM. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-3-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index b53288687659..52c122b01abd 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -1065,6 +1065,38 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "GA403UM"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 80, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 80, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 80, + .nv_dynamic_boost_min = 0, + .nv_dynamic_boost_max = 15, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 85, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 15, + .ppt_pl1_spl_max = 35, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 35, + .ppt_pl3_fppt_min = 35, + .ppt_pl3_fppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GA403UV"), From 72dd918bbe008d64a2c2352bdf14671f1ac068c7 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Fri, 12 Jun 2026 12:10:08 +0000 Subject: [PATCH 091/115] platform/x86: asus-armoury: add support for FX608JPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add TDP data for laptop model FX608JPR. Signed-off-by: Denis Benato Link: https://patch.msgid.link/20260612121008.970269-4-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-armoury.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/platform/x86/asus-armoury.h b/drivers/platform/x86/asus-armoury.h index 52c122b01abd..65166b50a2c3 100644 --- a/drivers/platform/x86/asus-armoury.h +++ b/drivers/platform/x86/asus-armoury.h @@ -936,6 +936,34 @@ static const struct dmi_system_id power_limits[] = { .requires_fan_curve = true, }, }, + { + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "FX608JPR"), + }, + .driver_data = &(struct power_data) { + .ac_data = &(struct power_limits) { + .ppt_pl1_spl_min = 28, + .ppt_pl1_spl_max = 100, + .ppt_pl2_sppt_min = 28, + .ppt_pl2_sppt_max = 135, + .nv_dynamic_boost_min = 10, + .nv_dynamic_boost_max = 15, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + .nv_tgp_min = 55, + .nv_tgp_max = 100, + }, + .dc_data = &(struct power_limits) { + .ppt_pl1_spl_min = 25, + .ppt_pl1_spl_max = 53, + .ppt_pl2_sppt_min = 25, + .ppt_pl2_sppt_max = 65, + .nv_temp_target_min = 75, + .nv_temp_target_max = 87, + }, + .requires_fan_curve = true, + }, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "GA401Q"), From 0f2d6a308210caaa5e0ebf9c085d87f4a2c06bfa Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:45 +0200 Subject: [PATCH 092/115] platform/x86: dell-descriptor: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the descriptor. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/dell/dell-wmi-descriptor.c | 110 ++++++++---------- 1 file changed, 49 insertions(+), 61 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-descriptor.c b/drivers/platform/x86/dell/dell-wmi-descriptor.c index c2a180202719..179d5678b3ad 100644 --- a/drivers/platform/x86/dell/dell-wmi-descriptor.c +++ b/drivers/platform/x86/dell/dell-wmi-descriptor.c @@ -7,14 +7,35 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include +#include #include #include +#include #include #include "dell-wmi-descriptor.h" #define DELL_WMI_DESCRIPTOR_GUID "8D9DDCBC-A997-11DA-B012-B622A1EF5492" +/* + * Descriptor buffer is 128 byte long and contains: + * + * Name Offset Length Value + * Vendor Signature 0 4 "DELL" + * Object Signature 4 4 " WMI" + * WMI Interface Version 8 4 + * WMI buffer length 12 4 + * WMI hotfix number 16 4 + */ +struct descriptor { + /* Both fields are NOT null-terminated */ + char vendor_signature[4] __nonstring; + char object_signature[4] __nonstring; + __le32 interface_version; + __le32 buffer_length; + __le32 hotfix_number; +} __packed; + struct descriptor_priv { struct list_head list; u32 interface_version; @@ -88,77 +109,46 @@ bool dell_wmi_get_hotfix(u32 *hotfix) } EXPORT_SYMBOL_GPL(dell_wmi_get_hotfix); -/* - * Descriptor buffer is 128 byte long and contains: - * - * Name Offset Length Value - * Vendor Signature 0 4 "DELL" - * Object Signature 4 4 " WMI" - * WMI Interface Version 8 4 - * WMI buffer length 12 4 - * WMI hotfix number 16 4 - */ -static int dell_wmi_descriptor_probe(struct wmi_device *wdev, - const void *context) +static int dell_wmi_descriptor_probe(struct wmi_device *wdev, const void *context) { - union acpi_object *obj = NULL; struct descriptor_priv *priv; - u32 *buffer; + struct wmi_buffer buffer; int ret; - obj = wmidev_block_query(wdev, 0); - if (!obj) { - dev_err(&wdev->dev, "failed to read Dell WMI descriptor\n"); - ret = -EIO; - goto out; + ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct descriptor)); + if (ret < 0) { + descriptor_valid = ret; + return ret; } - if (obj->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Dell descriptor has wrong type\n"); - ret = -EINVAL; - descriptor_valid = ret; - goto out; + struct descriptor *desc __free(kfree) = buffer.data; + + if (strncmp(desc->vendor_signature, "DELL", sizeof(desc->vendor_signature))) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid vendor signature (%4ph)\n", + desc->vendor_signature); + descriptor_valid = -ENOMSG; + return -ENOMSG; } - /* Although it's not technically a failure, this would lead to - * unexpected behavior - */ - if (obj->buffer.length != 128) { - dev_err(&wdev->dev, - "Dell descriptor buffer has unexpected length (%d)\n", - obj->buffer.length); - ret = -EINVAL; - descriptor_valid = ret; - goto out; - } - - buffer = (u32 *)obj->buffer.pointer; - - if (strncmp(obj->string.pointer, "DELL WMI", 8) != 0) { - dev_err(&wdev->dev, "Dell descriptor buffer has invalid signature (%8ph)\n", - buffer); - ret = -EINVAL; - descriptor_valid = ret; - goto out; + if (strncmp(desc->object_signature, " WMI", sizeof(desc->object_signature))) { + dev_err(&wdev->dev, "Dell descriptor buffer has invalid object signature (%4ph)\n", + desc->object_signature); + descriptor_valid = -ENOMSG; + return -ENOMSG; } descriptor_valid = 0; - if (buffer[2] != 0 && buffer[2] != 1) - dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%lu)\n", - (unsigned long) buffer[2]); + if (le32_to_cpu(desc->interface_version) > 1) + dev_warn(&wdev->dev, "Dell descriptor buffer has unknown version (%u)\n", + le32_to_cpu(desc->interface_version)); - priv = devm_kzalloc(&wdev->dev, sizeof(struct descriptor_priv), - GFP_KERNEL); + priv = devm_kzalloc(&wdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; - if (!priv) { - ret = -ENOMEM; - goto out; - } - - priv->interface_version = buffer[2]; - priv->size = buffer[3]; - priv->hotfix = buffer[4]; - ret = 0; + priv->interface_version = le32_to_cpu(desc->interface_version); + priv->size = le32_to_cpu(desc->buffer_length); + priv->hotfix = le32_to_cpu(desc->hotfix_number); dev_set_drvdata(&wdev->dev, priv); mutex_lock(&list_mutex); list_add_tail(&priv->list, &wmi_list); @@ -169,8 +159,6 @@ static int dell_wmi_descriptor_probe(struct wmi_device *wdev, (unsigned long) priv->size, (unsigned long) priv->hotfix); -out: - kfree(obj); return ret; } From 1719340487e63da13f2ac25e488c72737a545772 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:46 +0200 Subject: [PATCH 093/115] platform/x86: dell-privacy: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the device state. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-privacy.c | 77 +++++++++----------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-privacy.c b/drivers/platform/x86/dell/dell-wmi-privacy.c index ed099a431ea4..f9d275b2f900 100644 --- a/drivers/platform/x86/dell/dell-wmi-privacy.c +++ b/drivers/platform/x86/dell/dell-wmi-privacy.c @@ -9,11 +9,14 @@ #include #include +#include +#include #include #include #include #include #include +#include #include #include "dell-wmi-privacy.h" @@ -25,6 +28,26 @@ #define DELL_PRIVACY_CAMERA_EVENT 0x2 #define led_to_priv(c) container_of(c, struct privacy_wmi_data, cdev) +/* + * Describes the Device State class exposed by BIOS which can be consumed by + * various applications interested in knowing the Privacy feature capabilities. + * class DeviceState + * { + * [key, read] string InstanceName; + * [read] boolean ReadOnly; + * + * [WmiDataId(1), read] uint32 DevicesSupported; + * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen + * + * [WmiDataId(2), read] uint32 CurrentState; + * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen + * }; + */ +struct device_state { + __le32 devices_supported; + __le32 current_state; +} __packed; + /* * The wmi_list is used to store the privacy_priv struct with mutex protecting */ @@ -185,60 +208,28 @@ static struct attribute *privacy_attrs[] = { }; ATTRIBUTE_GROUPS(privacy); -/* - * Describes the Device State class exposed by BIOS which can be consumed by - * various applications interested in knowing the Privacy feature capabilities. - * class DeviceState - * { - * [key, read] string InstanceName; - * [read] boolean ReadOnly; - * - * [WmiDataId(1), read] uint32 DevicesSupported; - * 0 - None; 0x1 - Microphone; 0x2 - Camera; 0x4 - ePrivacy Screen - * - * [WmiDataId(2), read] uint32 CurrentState; - * 0 - Off; 1 - On; Bit0 - Microphone; Bit1 - Camera; Bit2 - ePrivacyScreen - * }; - */ static int get_current_status(struct wmi_device *wdev) { struct privacy_wmi_data *priv = dev_get_drvdata(&wdev->dev); - union acpi_object *obj_present; - u32 *buffer; - int ret = 0; + struct wmi_buffer buffer; + int ret; if (!priv) { dev_err(&wdev->dev, "dell privacy priv is NULL\n"); return -EINVAL; } + /* check privacy support features and device states */ - obj_present = wmidev_block_query(wdev, 0); - if (!obj_present) { - dev_err(&wdev->dev, "failed to read Binary MOF\n"); - return -EIO; - } + ret = wmidev_query_block(wdev, 0, &buffer, sizeof(struct device_state)); + if (ret < 0) + return ret; - if (obj_present->type != ACPI_TYPE_BUFFER) { - dev_err(&wdev->dev, "Binary MOF is not a buffer!\n"); - ret = -EIO; - goto obj_free; - } - /* Although it's not technically a failure, this would lead to - * unexpected behavior - */ - if (obj_present->buffer.length != 8) { - dev_err(&wdev->dev, "Dell privacy buffer has unexpected length (%d)!\n", - obj_present->buffer.length); - ret = -EINVAL; - goto obj_free; - } - buffer = (u32 *)obj_present->buffer.pointer; - priv->features_present = buffer[0]; - priv->last_status = buffer[1]; + struct device_state *state __free(kfree) = buffer.data; -obj_free: - kfree(obj_present); - return ret; + priv->features_present = le32_to_cpu(state->devices_supported); + priv->last_status = le32_to_cpu(state->current_state); + + return 0; } static int dell_privacy_micmute_led_set(struct led_classdev *led_cdev, From 6f918e3d95c9b0b4e6a6881692a9b5f9bf0f36b0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:47 +0200 Subject: [PATCH 094/115] platform/x86: dell-smbios-wmi: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for returning the results of a SMBIOS call. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-smbios-wmi.c | 40 +++++++++------------ 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/dell/dell-smbios-wmi.c b/drivers/platform/x86/dell/dell-smbios-wmi.c index a7dca8c59d60..64d0871b706e 100644 --- a/drivers/platform/x86/dell/dell-smbios-wmi.c +++ b/drivers/platform/x86/dell/dell-smbios-wmi.c @@ -50,38 +50,32 @@ static inline struct wmi_smbios_priv *get_first_smbios_priv(void) static int run_smbios_call(struct wmi_device *wdev) { - struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; - struct wmi_smbios_priv *priv; - struct acpi_buffer input; - union acpi_object *obj; - acpi_status status; - - priv = dev_get_drvdata(&wdev->dev); - input.length = priv->req_buf_size - sizeof(u64); - input.pointer = &priv->buf->std; + struct wmi_smbios_priv *priv = dev_get_drvdata(&wdev->dev); + const struct wmi_buffer input = { + .length = priv->req_buf_size - sizeof(u64), + .data = &priv->buf->std, + }; + struct wmi_buffer output; + int ret; dev_dbg(&wdev->dev, "evaluating: %u/%u [%x,%x,%x,%x]\n", priv->buf->std.cmd_class, priv->buf->std.cmd_select, priv->buf->std.input[0], priv->buf->std.input[1], priv->buf->std.input[2], priv->buf->std.input[3]); - status = wmidev_evaluate_method(wdev, 0, 1, &input, &output); - if (ACPI_FAILURE(status)) - return -EIO; - obj = (union acpi_object *)output.pointer; - if (obj->type != ACPI_TYPE_BUFFER) { - dev_dbg(&wdev->dev, "received type: %d\n", obj->type); - if (obj->type == ACPI_TYPE_INTEGER) - dev_dbg(&wdev->dev, "SMBIOS call failed: %llu\n", - obj->integer.value); - kfree(output.pointer); - return -EIO; - } - memcpy(input.pointer, obj->buffer.pointer, obj->buffer.length); + /* + * The output buffer returned by the WMI method should have at least the size + * of the input buffer. + */ + ret = wmidev_invoke_method(wdev, 0, 1, &input, &output, input.length); + if (ret < 0) + return ret; + + memcpy(input.data, output.data, input.length); + kfree(output.data); dev_dbg(&wdev->dev, "result: [%08x,%08x,%08x,%08x]\n", priv->buf->std.output[0], priv->buf->std.output[1], priv->buf->std.output[2], priv->buf->std.output[3]); - kfree(output.pointer); return 0; } From 982b0e683aa3b1d20b1512cde53207ba1c80e22b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:48 +0200 Subject: [PATCH 095/115] platform/x86: dell-wmi-base: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for the event data. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-base.c | 60 +++++++++++------------ 1 file changed, 28 insertions(+), 32 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c index 2a5804efd3ea..997383ba1846 100644 --- a/drivers/platform/x86/dell/dell-wmi-base.c +++ b/drivers/platform/x86/dell/dell-wmi-base.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -414,7 +415,8 @@ static void dell_wmi_switch_event(struct input_dev **subdev, input_sync(*subdev); } -static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 *buffer, int remaining) +static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, __le16 *buffer, + int remaining) { struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); const struct key_entry *key; @@ -446,15 +448,15 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 } else if (type == 0x0011 && code == 0xe070 && remaining > 0) { dell_wmi_switch_event(&priv->tabletswitch_dev, "Dell tablet mode switch", - SW_TABLET_MODE, !buffer[0]); + SW_TABLET_MODE, !le16_to_cpu(buffer[0])); return 1; } else if (type == 0x0012 && code == 0x000c && remaining > 0) { /* Eprivacy toggle, switch to "on" key entry for on events */ - if (buffer[0] == 2) + if (le16_to_cpu(buffer[0]) == 2) key++; used = 1; } else if (type == 0x0012 && code == 0x000d && remaining > 0) { - value = (buffer[2] == 2); + value = (le16_to_cpu(buffer[2]) == 2); used = 1; } @@ -463,24 +465,17 @@ static int dell_wmi_process_key(struct wmi_device *wdev, int type, int code, u16 return used; } -static void dell_wmi_notify(struct wmi_device *wdev, - union acpi_object *obj) +static void dell_wmi_notify(struct wmi_device *wdev, const struct wmi_buffer *buffer) { struct dell_wmi_priv *priv = dev_get_drvdata(&wdev->dev); - u16 *buffer_entry, *buffer_end; - acpi_size buffer_size; + __le16 *buffer_entry, *buffer_end; + size_t buffer_size; int len, i; - if (obj->type != ACPI_TYPE_BUFFER) { - pr_warn("bad response type %x\n", obj->type); - return; - } + pr_debug("Received WMI event (%*ph)\n", (int)buffer->length, buffer->data); - pr_debug("Received WMI event (%*ph)\n", - obj->buffer.length, obj->buffer.pointer); - - buffer_entry = (u16 *)obj->buffer.pointer; - buffer_size = obj->buffer.length/2; + buffer_entry = buffer->data; + buffer_size = buffer->length / 2; buffer_end = buffer_entry + buffer_size; /* @@ -496,12 +491,12 @@ static void dell_wmi_notify(struct wmi_device *wdev, * one event on devices with WMI interface version 0. */ if (priv->interface_version == 0 && buffer_entry < buffer_end) - if (buffer_end > buffer_entry + buffer_entry[0] + 1) - buffer_end = buffer_entry + buffer_entry[0] + 1; + if (buffer_end > buffer_entry + le16_to_cpu(buffer_entry[0]) + 1) + buffer_end = buffer_entry + le16_to_cpu(buffer_entry[0]) + 1; while (buffer_entry < buffer_end) { - len = buffer_entry[0]; + len = le16_to_cpu(buffer_entry[0]); if (len == 0) break; @@ -514,11 +509,11 @@ static void dell_wmi_notify(struct wmi_device *wdev, pr_debug("Process buffer (%*ph)\n", len*2, buffer_entry); - switch (buffer_entry[1]) { + switch (le16_to_cpu(buffer_entry[1])) { case 0x0000: /* One key pressed or event occurred */ if (len > 2) - dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[2], + dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[2]), buffer_entry + 3, len - 3); /* Extended data is currently ignored */ @@ -526,22 +521,23 @@ static void dell_wmi_notify(struct wmi_device *wdev, case 0x0010: /* Sequence of keys pressed */ case 0x0011: /* Sequence of events occurred */ for (i = 2; i < len; ++i) - i += dell_wmi_process_key(wdev, buffer_entry[1], - buffer_entry[i], + i += dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[i]), buffer_entry + i, len - i - 1); break; case 0x0012: - if ((len > 4) && dell_privacy_process_event(buffer_entry[1], buffer_entry[3], - buffer_entry[4])) + if ((len > 4) && dell_privacy_process_event(le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[3]), + le16_to_cpu(buffer_entry[4]))) /* dell_privacy_process_event has handled the event */; else if (len > 2) - dell_wmi_process_key(wdev, buffer_entry[1], buffer_entry[2], + dell_wmi_process_key(wdev, le16_to_cpu(buffer_entry[1]), + le16_to_cpu(buffer_entry[2]), buffer_entry + 3, len - 3); break; default: /* Unknown event */ - pr_info("Unknown WMI event type 0x%x\n", - (int)buffer_entry[1]); + pr_info("Unknown WMI event type 0x%x\n", le16_to_cpu(buffer_entry[1])); break; } @@ -825,10 +821,10 @@ static struct wmi_driver dell_wmi_driver = { .name = "dell-wmi", }, .id_table = dell_wmi_id_table, - .min_event_size = sizeof(u16), + .min_event_size = sizeof(__le16), .probe = dell_wmi_probe, .remove = dell_wmi_remove, - .notify = dell_wmi_notify, + .notify_new = dell_wmi_notify, }; static int __init dell_wmi_init(void) From 7fced293bbd00ee8d20eaf4654849ac9ff332973 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:49 +0200 Subject: [PATCH 096/115] platform/x86: dell-ddv: Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI intergers/strings/packages for exchanging data. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-6-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-ddv.c | 179 +++++++++++------------ 1 file changed, 85 insertions(+), 94 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 62e3d060f038..736d9b1fdcfb 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -7,8 +7,9 @@ #define pr_format(fmt) KBUILD_MODNAME ": " fmt -#include #include +#include +#include #include #include #include @@ -99,6 +100,11 @@ enum dell_ddv_method { DELL_DDV_THERMAL_SENSOR_INFORMATION = 0x22, }; +struct dell_wmi_buffer { + __le32 raw_size; + u8 raw_data[]; +} __packed; + struct fan_sensor_entry { u8 type; __le16 rpm; @@ -126,7 +132,7 @@ struct dell_wmi_ddv_sensors { bool active; struct mutex lock; /* protect caching */ unsigned long timestamp; - union acpi_object *obj; + struct dell_wmi_buffer *buffer; u64 entries; }; @@ -158,105 +164,96 @@ static const char * const fan_dock_labels[] = { "Docking Chipset Fan", }; -static int dell_wmi_ddv_query_type(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, - union acpi_object **result, acpi_object_type type) +static int dell_wmi_ddv_query(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, + struct wmi_buffer *output, size_t min_size) { - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; - const struct acpi_buffer in = { - .length = sizeof(arg), - .pointer = &arg, + __le32 arg2 = cpu_to_le32(arg); + const struct wmi_buffer input = { + .length = sizeof(arg2), + .data = &arg2, }; - union acpi_object *obj; - acpi_status ret; - ret = wmidev_evaluate_method(wdev, 0x0, method, &in, &out); - if (ACPI_FAILURE(ret)) - return -EIO; - - obj = out.pointer; - if (!obj) - return -ENODATA; - - if (obj->type != type) { - kfree(obj); - return -ENOMSG; - } - - *result = obj; - - return 0; + return wmidev_invoke_method(wdev, 0x0, method, &input, output, min_size); } static int dell_wmi_ddv_query_integer(struct wmi_device *wdev, enum dell_ddv_method method, u32 arg, u32 *res) { - union acpi_object *obj; + struct wmi_buffer output; int ret; - ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_INTEGER); + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(__le32)); if (ret < 0) return ret; - if (obj->integer.value <= U32_MAX) - *res = (u32)obj->integer.value; - else - ret = -ERANGE; + __le32 *argr __free(kfree) = output.data; - kfree(obj); + *res = le32_to_cpu(*argr); - return ret; + return 0; } static int dell_wmi_ddv_query_buffer(struct wmi_device *wdev, enum dell_ddv_method method, - u32 arg, union acpi_object **result) + u32 arg, struct dell_wmi_buffer **result) { - union acpi_object *obj; - u64 buffer_size; + struct dell_wmi_buffer *buffer; + struct wmi_buffer output; + size_t buffer_size; int ret; - ret = dell_wmi_ddv_query_type(wdev, method, arg, &obj, ACPI_TYPE_PACKAGE); + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(*buffer)); if (ret < 0) return ret; - if (obj->package.count != 2 || - obj->package.elements[0].type != ACPI_TYPE_INTEGER || - obj->package.elements[1].type != ACPI_TYPE_BUFFER) { - ret = -ENOMSG; - - goto err_free; - } - - buffer_size = obj->package.elements[0].integer.value; - - if (!buffer_size) { + buffer = output.data; + if (!le32_to_cpu(buffer->raw_size)) { ret = -ENODATA; goto err_free; } - if (buffer_size > obj->package.elements[1].buffer.length) { + buffer_size = struct_size(buffer, raw_data, le32_to_cpu(buffer->raw_size)); + if (buffer_size > output.length) { dev_warn(&wdev->dev, - FW_WARN "WMI buffer size (%llu) exceeds ACPI buffer size (%d)\n", - buffer_size, obj->package.elements[1].buffer.length); + FW_WARN "Dell WMI buffer size (%zu) exceeds WMI buffer size (%zu)\n", + buffer_size, output.length); ret = -EMSGSIZE; goto err_free; } - *result = obj; + *result = buffer; return 0; err_free: - kfree(obj); + kfree(output.data); return ret; } -static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, - u32 arg, union acpi_object **result) +static ssize_t dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_method method, + u32 arg, char *buf, size_t length) { - return dell_wmi_ddv_query_type(wdev, method, arg, result, ACPI_TYPE_STRING); + struct wmi_buffer output; + size_t str_size; + int ret; + + ret = dell_wmi_ddv_query(wdev, method, arg, &output, sizeof(struct wmi_string)); + if (ret < 0) + return ret; + + struct wmi_string *str __free(kfree) = output.data; + + str_size = sizeof(*str) + le16_to_cpu(str->length); + if (str_size > output.length) { + dev_warn(&wdev->dev, + FW_WARN "WMI string size (%zu) exceeds WMI buffer size (%zu)\n", + str_size, output.length); + return -EMSGSIZE; + } + + return wmi_string_to_utf8s(str, buf, length); } /* @@ -265,28 +262,26 @@ static int dell_wmi_ddv_query_string(struct wmi_device *wdev, enum dell_ddv_meth static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_method method, struct dell_wmi_ddv_sensors *sensors, size_t entry_size) { + struct dell_wmi_buffer *buffer; u64 buffer_size, rem, entries; - union acpi_object *obj; - u8 *buffer; int ret; - if (sensors->obj) { + if (sensors->buffer) { if (time_before(jiffies, sensors->timestamp + HZ)) return 0; - kfree(sensors->obj); - sensors->obj = NULL; + kfree(sensors->buffer); + sensors->buffer = NULL; } - ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &obj); + ret = dell_wmi_ddv_query_buffer(wdev, method, 0, &buffer); if (ret < 0) return ret; /* buffer format sanity check */ - buffer_size = obj->package.elements[0].integer.value; - buffer = obj->package.elements[1].buffer.pointer; + buffer_size = le32_to_cpu(buffer->raw_size); entries = div64_u64_rem(buffer_size, entry_size, &rem); - if (rem != 1 || buffer[buffer_size - 1] != 0xff) { + if (rem != 1 || buffer->raw_data[buffer_size - 1] != 0xff) { ret = -ENOMSG; goto err_free; } @@ -296,14 +291,14 @@ static int dell_wmi_ddv_update_sensors(struct wmi_device *wdev, enum dell_ddv_me goto err_free; } - sensors->obj = obj; + sensors->buffer = buffer; sensors->entries = entries; sensors->timestamp = jiffies; return 0; err_free: - kfree(obj); + kfree(buffer); return ret; } @@ -328,7 +323,7 @@ static int dell_wmi_ddv_fan_read_channel(struct dell_wmi_ddv_data *data, u32 att if (channel >= data->fans.entries) return -ENXIO; - entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer; + entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data; switch (attr) { case hwmon_fan_input: *val = get_unaligned_le16(&entry[channel].rpm); @@ -354,7 +349,7 @@ static int dell_wmi_ddv_temp_read_channel(struct dell_wmi_ddv_data *data, u32 at if (channel >= data->temps.entries) return -ENXIO; - entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer; + entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data; switch (attr) { case hwmon_temp_input: *val = entry[channel].now * 1000; @@ -411,7 +406,7 @@ static int dell_wmi_ddv_fan_read_string(struct dell_wmi_ddv_data *data, int chan if (channel >= data->fans.entries) return -ENXIO; - entry = (struct fan_sensor_entry *)data->fans.obj->package.elements[1].buffer.pointer; + entry = (struct fan_sensor_entry *)data->fans.buffer->raw_data; type = entry[channel].type; switch (type) { case 0x00 ... 0x07: @@ -442,7 +437,7 @@ static int dell_wmi_ddv_temp_read_string(struct dell_wmi_ddv_data *data, int cha if (channel >= data->temps.entries) return -ENXIO; - entry = (struct thermal_sensor_entry *)data->temps.obj->package.elements[1].buffer.pointer; + entry = (struct thermal_sensor_entry *)data->temps.buffer->raw_data; switch (entry[channel].type) { case 0x00: *str = "CPU"; @@ -553,8 +548,8 @@ static void dell_wmi_ddv_hwmon_cache_invalidate(struct dell_wmi_ddv_sensors *sen return; mutex_lock(&sensors->lock); - kfree(sensors->obj); - sensors->obj = NULL; + kfree(sensors->buffer); + sensors->buffer = NULL; mutex_unlock(&sensors->lock); } @@ -564,7 +559,7 @@ static void dell_wmi_ddv_hwmon_cache_destroy(void *data) sensors->active = false; mutex_destroy(&sensors->lock); - kfree(sensors->obj); + kfree(sensors->buffer); } static struct hwmon_channel_info *dell_wmi_ddv_channel_init(struct wmi_device *wdev, @@ -750,7 +745,7 @@ static void dell_wmi_battery_invalidate(struct dell_wmi_ddv_data *data, static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dell_wmi_ddv_data *data = container_of(attr, struct dell_wmi_ddv_data, eppid_attr); - union acpi_object *obj; + ssize_t count; u32 index; int ret; @@ -758,19 +753,19 @@ static ssize_t eppid_show(struct device *dev, struct device_attribute *attr, cha if (ret < 0) return ret; - ret = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, &obj); + count = dell_wmi_ddv_query_string(data->wdev, DELL_DDV_BATTERY_EPPID, index, buf, + PAGE_SIZE); + if (count < 0) + return count; + + if (count != DELL_EPPID_LENGTH && count != DELL_EPPID_EXT_LENGTH) + dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%zd)\n", count); + + ret = sysfs_emit_at(buf, count, "\n"); if (ret < 0) return ret; - if (obj->string.length != DELL_EPPID_LENGTH && obj->string.length != DELL_EPPID_EXT_LENGTH) - dev_info_once(&data->wdev->dev, FW_INFO "Suspicious ePPID length (%d)\n", - obj->string.length); - - ret = sysfs_emit(buf, "%s\n", obj->string.pointer); - - kfree(obj); - - return ret; + return count + ret; } static int dell_wmi_ddv_get_health(struct dell_wmi_ddv_data *data, u32 index, @@ -994,19 +989,15 @@ static int dell_wmi_ddv_buffer_read(struct seq_file *seq, enum dell_ddv_method m { struct device *dev = seq->private; struct dell_wmi_ddv_data *data = dev_get_drvdata(dev); - union acpi_object *obj; - u64 size; - u8 *buf; + struct dell_wmi_buffer *buffer; int ret; - ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &obj); + ret = dell_wmi_ddv_query_buffer(data->wdev, method, 0, &buffer); if (ret < 0) return ret; - size = obj->package.elements[0].integer.value; - buf = obj->package.elements[1].buffer.pointer; - ret = seq_write(seq, buf, size); - kfree(obj); + ret = seq_write(seq, buffer->raw_data, le32_to_cpu(buffer->raw_size)); + kfree(buffer); return ret; } From ab1eb37dd63ff3555e4f98918f1bd3498522f765 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:50 +0200 Subject: [PATCH 097/115] hwmon: (dell-smm) Use new buffer-based WMI API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the new buffer-based WMI API to also support ACPI firmware implementations that do not use ACPI buffers for returning the results of a SMM call. Acked-by: Guenter Roeck Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-7-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/hwmon/dell-smm-hwmon.c | 49 ++++++++++++---------------------- 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/drivers/hwmon/dell-smm-hwmon.c b/drivers/hwmon/dell-smm-hwmon.c index 038edffc1ac7..6ca2ea4bfe7d 100644 --- a/drivers/hwmon/dell-smm-hwmon.c +++ b/drivers/hwmon/dell-smm-hwmon.c @@ -12,8 +12,10 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include +#include #include +#include +#include #include #include #include @@ -36,10 +38,10 @@ #include #include #include +#include #include #include -#include #define I8K_SMM_FN_STATUS 0x0025 #define I8K_SMM_POWER_STATUS 0x0069 @@ -232,7 +234,7 @@ static const struct dell_smm_ops i8k_smm_ops = { /* * Call the System Management Mode BIOS over WMI. */ -static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg) +static ssize_t wmi_parse_register(u8 *buffer, size_t length, unsigned int *reg) { __le32 value; u32 reg_size; @@ -253,7 +255,7 @@ static ssize_t wmi_parse_register(u8 *buffer, u32 length, unsigned int *reg) return reg_size + sizeof(reg_size); } -static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) +static int wmi_parse_response(u8 *buffer, size_t length, struct smm_regs *regs) { unsigned int *registers[] = { ®s->eax, @@ -261,7 +263,7 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) ®s->ecx, ®s->edx }; - u32 offset = 0; + size_t offset = 0; ssize_t ret; int i; @@ -273,19 +275,16 @@ static int wmi_parse_response(u8 *buffer, u32 length, struct smm_regs *regs) if (ret < 0) return ret; - offset += ret; + /* WMI aligns u32 integers on a 4 byte boundary */ + offset = ALIGN(offset + ret, 4); } - if (offset != length) - return -ENOMSG; - return 0; } static int wmi_smm_call(struct device *dev, struct smm_regs *regs) { struct wmi_device *wdev = container_of(dev, struct wmi_device, dev); - struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL }; u32 wmi_payload[] = { sizeof(regs->eax), regs->eax, @@ -296,34 +295,20 @@ static int wmi_smm_call(struct device *dev, struct smm_regs *regs) sizeof(regs->edx), regs->edx }; - const struct acpi_buffer in = { + const struct wmi_buffer in = { .length = sizeof(wmi_payload), - .pointer = &wmi_payload, + .data = &wmi_payload, }; - union acpi_object *obj; - acpi_status status; + struct wmi_buffer out; int ret; - status = wmidev_evaluate_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out); - if (ACPI_FAILURE(status)) - return -EIO; + ret = wmidev_invoke_method(wdev, 0x0, DELL_SMM_LEGACY_EXECUTE, &in, &out, sizeof(__le32)); + if (ret < 0) + return ret; - obj = out.pointer; - if (!obj) - return -ENODATA; + u8 *response __free(kfree) = out.data; - if (obj->type != ACPI_TYPE_BUFFER) { - ret = -ENOMSG; - - goto err_free; - } - - ret = wmi_parse_response(obj->buffer.pointer, obj->buffer.length, regs); - -err_free: - kfree(obj); - - return ret; + return wmi_parse_response(response, out.length, regs); } static int dell_smm_call(const struct dell_smm_ops *ops, struct smm_regs *regs) From b79ad5e8ba5cfda93a83e0cf71d4743829cc9f83 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:51 +0200 Subject: [PATCH 098/115] platform/wmi: Make wmi_bus_class const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions class_register()/_unregister() and device_create() both support taking a const pointer to the class struct. Use this to mark wmi_bus_class as const so that it can be placed into read-only memory for better security. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20260610203453.816254-8-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 5a2ffcbab6af..0782ebc33c00 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -1088,7 +1088,7 @@ static void wmi_dev_shutdown(struct device *dev) } } -static struct class wmi_bus_class = { +static const struct class wmi_bus_class = { .name = "wmi_bus", }; From c3cbac4be03d769571f32e7f27241b2c58f722f5 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:52 +0200 Subject: [PATCH 099/115] platform/wmi: Make sysfs attributes const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The sysfs core supports const attributes. Use this to mark all sysfs attributes as const so that they can be placed into read-only memory for better security. Reviewed-by: Ilpo Järvinen Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260610203453.816254-9-W_Armin@gmx.de Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 0782ebc33c00..529825dcfbfe 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -858,7 +858,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "wmi:%pUL\n", &wblock->gblock.guid); } -static DEVICE_ATTR_RO(modalias); + +static const DEVICE_ATTR_RO(modalias); static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -867,7 +868,8 @@ static ssize_t guid_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%pUL\n", &wblock->gblock.guid); } -static DEVICE_ATTR_RO(guid); + +static const DEVICE_ATTR_RO(guid); static ssize_t instance_count_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -876,7 +878,8 @@ static ssize_t instance_count_show(struct device *dev, return sysfs_emit(buf, "%d\n", (int)wblock->gblock.instance_count); } -static DEVICE_ATTR_RO(instance_count); + +static const DEVICE_ATTR_RO(instance_count); static ssize_t expensive_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -886,9 +889,10 @@ static ssize_t expensive_show(struct device *dev, return sysfs_emit(buf, "%d\n", (wblock->gblock.flags & ACPI_WMI_EXPENSIVE) != 0); } -static DEVICE_ATTR_RO(expensive); -static struct attribute *wmi_attrs[] = { +static const DEVICE_ATTR_RO(expensive); + +static const struct attribute * const wmi_attrs[] = { &dev_attr_modalias.attr, &dev_attr_guid.attr, &dev_attr_instance_count.attr, @@ -904,9 +908,10 @@ static ssize_t notify_id_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%02X\n", (unsigned int)wblock->gblock.notify_id); } -static DEVICE_ATTR_RO(notify_id); -static struct attribute *wmi_event_attrs[] = { +static const DEVICE_ATTR_RO(notify_id); + +static const struct attribute * const wmi_event_attrs[] = { &dev_attr_notify_id.attr, NULL }; @@ -920,7 +925,8 @@ static ssize_t object_id_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%c%c\n", wblock->gblock.object_id[0], wblock->gblock.object_id[1]); } -static DEVICE_ATTR_RO(object_id); + +static const DEVICE_ATTR_RO(object_id); static ssize_t setable_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -929,16 +935,17 @@ static ssize_t setable_show(struct device *dev, struct device_attribute *attr, return sysfs_emit(buf, "%d\n", (int)wdev->setable); } -static DEVICE_ATTR_RO(setable); -static struct attribute *wmi_data_attrs[] = { +static const DEVICE_ATTR_RO(setable); + +static const struct attribute * const wmi_data_attrs[] = { &dev_attr_object_id.attr, &dev_attr_setable.attr, NULL }; ATTRIBUTE_GROUPS(wmi_data); -static struct attribute *wmi_method_attrs[] = { +static const struct attribute * const wmi_method_attrs[] = { &dev_attr_object_id.attr, NULL }; From 3429ae7b2f02a4a6ad40d36ee06641d433d75a1b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Wed, 10 Jun 2026 22:34:53 +0200 Subject: [PATCH 100/115] modpost: Handle malformed WMI GUID strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some WMI GUIDs found inside binary MOF files contain both uppercase and lowercase characters. Blindly copying such GUIDs will prevent the associated WMI driver from loading automatically because the WMI GUID found inside WMI device ids always contains uppercase characters. Avoid this issue by always converting WMI GUID strings to uppercase. Also verify that the WMI GUID string actually looks like a valid GUID. Signed-off-by: Armin Wolf Reviewed-by: Mario Limonciello Link: https://patch.msgid.link/20260610203453.816254-10-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/driver-development-guide.rst | 2 +- scripts/mod/file2alias.c | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst index 387f508d57ad..6290c448f5e7 100644 --- a/Documentation/wmi/driver-development-guide.rst +++ b/Documentation/wmi/driver-development-guide.rst @@ -54,7 +54,7 @@ to matching WMI devices using a struct wmi_device_id table: :: static const struct wmi_device_id foo_id_table[] = { - /* Only use uppercase letters! */ + /* Using only uppercase letters is recommended */ { "936DA01F-9ABD-4D9D-80C7-02AF85C822A8", NULL }, { } }; diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 4e99393a35f1..20e542a888c4 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1253,6 +1253,8 @@ static void do_tee_entry(struct module *mod, void *symval) static void do_wmi_entry(struct module *mod, void *symval) { DEF_FIELD_ADDR(symval, wmi_device_id, guid_string); + char result[sizeof(*guid_string)]; + int i; if (strlen(*guid_string) != UUID_STRING_LEN) { warn("Invalid WMI device id 'wmi:%s' in '%s'\n", @@ -1260,7 +1262,31 @@ static void do_wmi_entry(struct module *mod, void *symval) return; } - module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", *guid_string); + for (i = 0; i < UUID_STRING_LEN; i++) { + char value = (*guid_string)[i]; + bool valid = false; + + if (i == 8 || i == 13 || i == 18 || i == 23) { + if (value == '-') + valid = true; + } else { + if (isxdigit(value)) + valid = true; + } + + if (!valid) { + warn("Invalid character %c inside WMI GUID string '%s' in '%s'\n", + value, *guid_string, mod->name); + return; + } + + /* Some GUIDs from BMOF definitions contain lowercase characters */ + result[i] = toupper(value); + } + + result[i] = '\0'; + + module_alias_printf(mod, false, WMI_MODULE_PREFIX "%s", result); } /* Looks like: mhi:S */ From acd3b94f8604aeee2b62f8fd18e95a0474546288 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:42 -0700 Subject: [PATCH 101/115] platform/x86/intel/pmt: Add pre/post decode hooks around header parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add optional pre- and post-decode callbacks to the PMT class so namespaces can perform setup and cleanup steps around header parsing. - Add pmt_pre_decode() and pmt_post_decode() to struct intel_pmt_namespace. - Update intel_pmt_dev_create() to invoke, in order: pre → header_decode() → post. - Keep the existing pmt_header_decode() callback unchanged. No functional changes. This adds flexibility for upcoming decoders while preserving current behavior. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/b178a341601ca694db99c3b738fe4ed9e0c2bede.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 12 ++++++++++++ drivers/platform/x86/intel/pmt/class.h | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index b4c9964df807..9b315334a69b 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -381,10 +381,22 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); + if (ns->pmt_pre_decode) { + ret = ns->pmt_pre_decode(intel_vsec_dev, entry); + if (ret) + return ret; + } + ret = ns->pmt_header_decode(entry, dev); if (ret) return ret; + if (ns->pmt_post_decode) { + ret = ns->pmt_post_decode(intel_vsec_dev, entry); + if (ret) + return ret; + } + ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 1ae56a5baad2..ff39014b208c 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -62,6 +62,10 @@ struct intel_pmt_namespace { struct xarray *xa; int (*pmt_header_decode)(struct intel_pmt_entry *entry, struct device *dev); + int (*pmt_pre_decode)(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry); + int (*pmt_post_decode)(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry); int (*pmt_add_endpoint)(struct intel_vsec_device *ivdev, struct intel_pmt_entry *entry); }; From b3de79d932bdcee4b2b9c8f9a058516699cf1c50 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:43 -0700 Subject: [PATCH 102/115] platform/x86/intel/pmt/crashlog: Split init into pre-decode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor crashlog initialization to use the PMT namespace pre-decode hook: - Add pmt_crashlog_pre_decode() to parse type/version, select the crashlog_info, initialize the control mutex, and set entry->attr_grp. - Simplify pmt_crashlog_header_decode() to only read header fields from the discovery table. - Wire the namespace with .pmt_pre_decode = pmt_crashlog_pre_decode. This separates structural initialization from header parsing, aligning crashlog with the PMT class pre/post decode flow. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/ed8cda8456c97132cf2d2b4ff6a5cffb1ce3a666.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/crashlog.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/crashlog.c b/drivers/platform/x86/intel/pmt/crashlog.c index b0393c9c5b4b..f936daf99e4d 100644 --- a/drivers/platform/x86/intel/pmt/crashlog.c +++ b/drivers/platform/x86/intel/pmt/crashlog.c @@ -496,11 +496,9 @@ static const struct crashlog_info *select_crashlog_info(u32 type, u32 version) return &crashlog_type1_ver2; } -static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, - struct device *dev) +static int pmt_crashlog_pre_decode(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry) { - void __iomem *disc_table = entry->disc_table; - struct intel_pmt_header *header = &entry->header; struct crashlog_entry *crashlog; u32 version; u32 type; @@ -513,6 +511,16 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, mutex_init(&crashlog->control_mutex); crashlog->info = select_crashlog_info(type, version); + entry->attr_grp = crashlog->info->attr_grp; + + return 0; +} + +static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, + struct device *dev) +{ + void __iomem *disc_table = entry->disc_table; + struct intel_pmt_header *header = &entry->header; header->access_type = GET_ACCESS(readl(disc_table)); header->guid = readl(disc_table + GUID_OFFSET); @@ -521,8 +529,6 @@ static int pmt_crashlog_header_decode(struct intel_pmt_entry *entry, /* Size is measured in DWORDS, but accessor returns bytes */ header->size = GET_SIZE(readl(disc_table + SIZE_OFFSET)); - entry->attr_grp = crashlog->info->attr_grp; - return 0; } @@ -530,6 +536,7 @@ static DEFINE_XARRAY_ALLOC(crashlog_array); static struct intel_pmt_namespace pmt_crashlog_ns = { .name = "crashlog", .xa = &crashlog_array, + .pmt_pre_decode = pmt_crashlog_pre_decode, .pmt_header_decode = pmt_crashlog_header_decode, }; From 521460e6699557f2aa0e4818110e082b76809e98 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:44 -0700 Subject: [PATCH 103/115] platform/x86/intel/pmt/telemetry: Move overlap check to post-decode hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the telemetry namespace to use the new PMT class pre/post decode interface. The overlap check, which previously occurred during header decode, is now performed in the post-decode hook once header fields are populated. This preserves existing behavior while reusing the same header decode logic across PMT drivers. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/2f5e429a38e22eb45fcfaaca4e037fa395d4f199.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.h | 1 + drivers/platform/x86/intel/pmt/telemetry.c | 24 ++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index ff39014b208c..8a0db0ef58c1 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -37,6 +37,7 @@ struct intel_pmt_header { u32 size; u32 guid; u8 access_type; + u8 telem_type; }; struct intel_pmt_entry { diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index bdc7c24a3678..d22f633638be 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -58,14 +58,9 @@ struct pmt_telem_priv { struct intel_pmt_entry entry[]; }; -static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, - struct device *dev) +static bool pmt_telem_region_overlaps(struct device *dev, u32 guid, u32 type) { - u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); - if (intel_pmt_is_early_client_hw(dev)) { - u32 type = TELEM_TYPE(readl(entry->disc_table)); - if ((type == TELEM_TYPE_PUNIT_FIXED) || (guid == TELEM_CLIENT_FIXED_BLOCK_GUID)) return true; @@ -80,15 +75,25 @@ static int pmt_telem_header_decode(struct intel_pmt_entry *entry, void __iomem *disc_table = entry->disc_table; struct intel_pmt_header *header = &entry->header; - if (pmt_telem_region_overlaps(entry, dev)) - return 1; - header->access_type = TELEM_ACCESS(readl(disc_table)); header->guid = readl(disc_table + TELEM_GUID_OFFSET); header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); /* Size is measured in DWORDS, but accessor returns bytes */ header->size = TELEM_SIZE(readl(disc_table)); + header->telem_type = TELEM_TYPE(readl(entry->disc_table)); + + return 0; +} + +static int pmt_telem_post_decode(struct intel_vsec_device *ivdev, + struct intel_pmt_entry *entry) +{ + struct intel_pmt_header *header = &entry->header; + struct device *dev = &ivdev->auxdev.dev; + + if (pmt_telem_region_overlaps(dev, header->guid, header->telem_type)) + return 1; /* * Some devices may expose non-functioning entries that are @@ -131,6 +136,7 @@ static struct intel_pmt_namespace pmt_telem_ns = { .name = "telem", .xa = &telem_array, .pmt_header_decode = pmt_telem_header_decode, + .pmt_post_decode = pmt_telem_post_decode, .pmt_add_endpoint = pmt_telem_add_endpoint, }; From 4a87492cd137d158779923a034d7e742f7358952 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:45 -0700 Subject: [PATCH 104/115] platform/x86/intel/pmt: Pass discovery index instead of resource MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change PMT class code to pass a discovery index rather than a direct struct resource when creating entries. This allows the class to identify the discovery source generically without assuming PCI BAR resources. For PCI devices, the index still resolves to a resource in the intel_vsec_device. Other discovery sources, such as ACPI, can use the same index without needing a struct resource. Signed-off-by: David E. Box Link: https://patch.msgid.link/8e785902c6a3ac1b5a9c3f0f65096553dc5acd4f.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 9b315334a69b..7da8279b54f8 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -206,11 +206,12 @@ EXPORT_SYMBOL_GPL(intel_pmt_class); static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, - struct resource *disc_res) + int idx) { struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; struct intel_pmt_header *header = &entry->header; + struct resource *disc_res; u8 bir; /* @@ -235,6 +236,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, * For access_type LOCAL, the base address is as follows: * base address = end of discovery region + base offset */ + disc_res = &ivdev->resource[idx]; entry->base_addr = disc_res->end + 1 + header->base_offset; /* @@ -397,7 +399,7 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa return ret; } - ret = intel_pmt_populate_entry(entry, intel_vsec_dev, disc_res); + ret = intel_pmt_populate_entry(entry, intel_vsec_dev, idx); if (ret) return ret; From 4dfc7dca6e934ca414d8d3c70a84e79d13d9e750 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:46 -0700 Subject: [PATCH 105/115] platform/x86/intel/pmt: Cache the telemetry discovery header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pmt_telem_header_decode() only needs the discovery header dwords, but it currently decodes them by reading directly from entry->disc_table. Cache the discovery header in intel_pmt_entry when the device is created and have telemetry decode use the cached values instead of performing MMIO reads at decode time. The DVSEC discovery resource for a namespace is sized by its per-entry entry_size (in dwords), which can be less than the 4-dword cache (e.g. telemetry uses entry_size = 3, i.e. 12 bytes). Cap the memcpy_fromio() to resource_size(disc_res) so the new cache does not read past the mapped region. Any unread dwords stay zero from the zero-initialized allocation of the containing struct. This keeps the telemetry header decode path independent of how the discovery data is backed and avoids baking a direct MMIO assumption into the feature-specific decode logic. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/f805e2ada52dc0661761cda7f692e76e6ea2d257.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 11 +++++++++++ drivers/platform/x86/intel/pmt/class.h | 1 + drivers/platform/x86/intel/pmt/telemetry.c | 12 ++++++------ 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 7da8279b54f8..8d27f59d5bff 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -383,6 +384,16 @@ int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespa if (IS_ERR(entry->disc_table)) return PTR_ERR(entry->disc_table); + /* + * The mapped discovery resource may be smaller than disc_header (its + * size is the namespace's DVSEC entry_size in dwords, which can be + * less than 4). Cap the copy to the actual resource size to avoid + * reading past the mapped region; any unread dwords stay zero from + * the zero-initialized allocation of the containing struct. + */ + memcpy_fromio(entry->disc_header, entry->disc_table, + min(sizeof(entry->disc_header), resource_size(disc_res))); + if (ns->pmt_pre_decode) { ret = ns->pmt_pre_decode(intel_vsec_dev, entry); if (ret) diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 8a0db0ef58c1..84202fc7920c 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -44,6 +44,7 @@ struct intel_pmt_entry { struct telem_endpoint *ep; struct pci_dev *pcidev; struct intel_pmt_header header; + u32 disc_header[4]; struct bin_attribute pmt_bin_attr; const struct attribute_group *attr_grp; struct kobject *kobj; diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index d22f633638be..953f35b6daec 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -72,16 +72,16 @@ static bool pmt_telem_region_overlaps(struct device *dev, u32 guid, u32 type) static int pmt_telem_header_decode(struct intel_pmt_entry *entry, struct device *dev) { - void __iomem *disc_table = entry->disc_table; struct intel_pmt_header *header = &entry->header; + u32 *disc_header = entry->disc_header; - header->access_type = TELEM_ACCESS(readl(disc_table)); - header->guid = readl(disc_table + TELEM_GUID_OFFSET); - header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); + header->access_type = TELEM_ACCESS(disc_header[0]); + header->guid = disc_header[1]; + header->base_offset = disc_header[2]; /* Size is measured in DWORDS, but accessor returns bytes */ - header->size = TELEM_SIZE(readl(disc_table)); - header->telem_type = TELEM_TYPE(readl(entry->disc_table)); + header->size = TELEM_SIZE(disc_header[0]); + header->telem_type = TELEM_TYPE(disc_header[0]); return 0; } From 13793c7f9e9ff30042a54bb680662b5cfa0f58fa Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:47 -0700 Subject: [PATCH 106/115] platform/x86/intel/pmt: Unify header fetch and add ACPI source MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the PMT class to read discovery headers from either PCI MMIO or ACPI-provided entries, depending on the discovery source. The new source-aware fetch helper caches the canonical discovery header for both paths, capping PCI MMIO reads to the mapped resource size, while keeping the mapped PCI discovery table available for users such as crashlog. Split intel_pmt_populate_entry() into source-specific resolvers: - pmt_resolve_access_pci(): handles both ACCESS_LOCAL and ACCESS_BARID for PCI-backed devices and sets entry->pcidev. Same existing functionality. - pmt_resolve_access_acpi(): handles only ACCESS_BARID for ACPI-backed devices, rejecting ACCESS_LOCAL which has no valid semantics without a physical discovery resource. This maintains existing PCI behavior and makes no functional changes for PCI devices. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/4b33b04ffaf0943b67d330f48b5d1dfcb6d1be5d.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmt/class.c | 151 +++++++++++++++++++++---- drivers/platform/x86/intel/pmt/class.h | 2 +- include/linux/intel_vsec.h | 5 +- 3 files changed, 136 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 8d27f59d5bff..d0ab8e33c62a 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -205,9 +205,9 @@ struct class intel_pmt_class = { }; EXPORT_SYMBOL_GPL(intel_pmt_class); -static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, - struct intel_vsec_device *ivdev, - int idx) +static int pmt_resolve_access_pci(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev, + int idx) { struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; @@ -287,6 +287,81 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, } entry->pcidev = pci_dev; + + return 0; +} + +static int pmt_resolve_access_acpi(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev) +{ + struct pci_dev *pci_dev = NULL; + struct device *dev = &ivdev->auxdev.dev; + struct intel_pmt_header *header = &entry->header; + u8 bir; + + if (dev_is_pci(ivdev->dev)) + pci_dev = to_pci_dev(ivdev->dev); + + /* + * The base offset should always be 8 byte aligned. + * + * For non-local access types the lower 3 bits of base offset + * contains the index of the base address register where the + * telemetry can be found. + */ + bir = GET_BIR(header->base_offset); + + switch (header->access_type) { + case ACCESS_BARID: + /* ACPI platform drivers use base_addr */ + if (ivdev->base_addr) { + entry->base_addr = ivdev->base_addr + + GET_ADDRESS(header->base_offset); + break; + } + + /* If base_addr is not provided, then this is an ACPI companion device */ + if (!pci_dev) { + dev_err(dev, "ACCESS_BARID requires PCI BAR resources or base_addr\n"); + return -EINVAL; + } + + entry->base_addr = pci_resource_start(pci_dev, bir) + + GET_ADDRESS(header->base_offset); + break; + default: + dev_err(dev, "Unsupported access type %d for ACPI based PMT\n", + header->access_type); + return -EINVAL; + } + + return 0; +} + +static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, + struct intel_vsec_device *ivdev, + int idx) +{ + struct intel_pmt_header *header = &entry->header; + struct device *dev = &ivdev->auxdev.dev; + int ret; + + switch (ivdev->src) { + case INTEL_VSEC_DISC_PCI: + ret = pmt_resolve_access_pci(entry, ivdev, idx); + if (ret) + return ret; + break; + case INTEL_VSEC_DISC_ACPI: + ret = pmt_resolve_access_acpi(entry, ivdev); + if (ret) + return ret; + break; + default: + dev_err(dev, "Unknown discovery source: %d\n", ivdev->src); + return -EINVAL; + } + entry->guid = header->guid; entry->size = header->size; entry->cb = ivdev->priv_data; @@ -371,28 +446,66 @@ static int intel_pmt_dev_register(struct intel_pmt_entry *entry, return ret; } +static int pmt_get_headers(struct intel_vsec_device *ivdev, int idx, + struct intel_pmt_entry *entry) +{ + struct device *dev = &ivdev->auxdev.dev; + size_t header_bytes = sizeof(entry->disc_header); + + switch (ivdev->src) { + case INTEL_VSEC_DISC_PCI: { + struct resource *disc_res = &ivdev->resource[idx]; + void __iomem *disc_table; + + disc_table = devm_ioremap_resource(dev, disc_res); + if (IS_ERR(disc_table)) + return PTR_ERR(disc_table); + + /* + * The mapped resource is sized by the namespace's DVSEC + * entry_size (in dwords), which can be less than the default + * size (e.g. telemetry uses entry_size = 3, 12 bytes). Cap the + * copy to resource_size() to avoid reading past the mapped + * region. + */ + memset(entry->disc_header, 0, header_bytes); + memcpy_fromio(entry->disc_header, disc_table, + min(header_bytes, resource_size(disc_res))); + + /* Used by crashlog driver */ + entry->disc_table = disc_table; + + return 0; + } + case INTEL_VSEC_DISC_ACPI: { + memcpy(entry->disc_header, &ivdev->acpi_disc[idx][0], header_bytes); + /* + * No MMIO mapping exists on the ACPI source path; the cached + * headers are the only view of the discovery record. Consumers + * that dereference disc_table (e.g. crashlog) must therefore + * only be wired to namespaces backed by INTEL_VSEC_DISC_PCI. + */ + entry->disc_table = NULL; + + return 0; + } + default: + dev_err(dev, "Unknown discovery source type: %d\n", ivdev->src); + break; + } + + return -EINVAL; +} + int intel_pmt_dev_create(struct intel_pmt_entry *entry, struct intel_pmt_namespace *ns, struct intel_vsec_device *intel_vsec_dev, int idx) { struct device *dev = &intel_vsec_dev->auxdev.dev; - struct resource *disc_res; int ret; - disc_res = &intel_vsec_dev->resource[idx]; - - entry->disc_table = devm_ioremap_resource(dev, disc_res); - if (IS_ERR(entry->disc_table)) - return PTR_ERR(entry->disc_table); - - /* - * The mapped discovery resource may be smaller than disc_header (its - * size is the namespace's DVSEC entry_size in dwords, which can be - * less than 4). Cap the copy to the actual resource size to avoid - * reading past the mapped region; any unread dwords stay zero from - * the zero-initialized allocation of the containing struct. - */ - memcpy_fromio(entry->disc_header, entry->disc_table, - min(sizeof(entry->disc_header), resource_size(disc_res))); + ret = pmt_get_headers(intel_vsec_dev, idx, entry); + if (ret) + return ret; if (ns->pmt_pre_decode) { ret = ns->pmt_pre_decode(intel_vsec_dev, entry); diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 84202fc7920c..a0ece4fc3837 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -44,7 +44,7 @@ struct intel_pmt_entry { struct telem_endpoint *ep; struct pci_dev *pcidev; struct intel_pmt_header header; - u32 disc_header[4]; + u32 disc_header[PMT_DISC_DWORDS]; struct bin_attribute pmt_bin_attr; const struct attribute_group *attr_grp; struct kobject *kobj; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 07ea563f524e..843cda8f8644 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -28,6 +28,7 @@ #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +#define PMT_DISC_DWORDS 4 struct device; struct pci_dev; @@ -122,7 +123,7 @@ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; const struct vsec_feature_dependency *deps; - u32 (*acpi_disc)[4]; + u32 (*acpi_disc)[PMT_DISC_DWORDS]; enum intel_vsec_disc_source src; void *priv_data; unsigned long caps; @@ -153,7 +154,7 @@ struct intel_vsec_platform_info { struct intel_vsec_device { struct auxiliary_device auxdev; struct device *dev; - u32 (*acpi_disc)[4]; + u32 (*acpi_disc)[PMT_DISC_DWORDS]; enum intel_vsec_disc_source src; struct ida *ida; int num_resources; From 8ba4cf60c5ce4a3073126a6cbb09475010f8fa52 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:48 -0700 Subject: [PATCH 107/115] platform/x86/intel/pmc: Add PMC SSRAM Kconfig description MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a proper description for the intel_pmc_ssram driver. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/bd7ba2f98450751af1de054dac0469acc1138513.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Kconfig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index c6ef0bcf76af..e9012b703918 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -28,3 +28,14 @@ config INTEL_PMC_CORE config INTEL_PMC_SSRAM_TELEMETRY tristate + help + This driver discovers PMC SSRAM telemetry regions through the PMC's + MMIO interface (PCI) or ACPI _DSD properties and registers them with + the Intel VSEC framework as Intel PMT telemetry devices. + + It probes the PMC SSRAM device, extracts DVSEC information from MMIO, + reads device IDs and base addresses for multiple PMCs (main, IOE, PCH), + and exposes the discovered telemetry through Intel PMT interfaces + (including sysfs). + + This option is selected by INTEL_PMC_CORE. From adc5d98d9ff8b5d37e89e11a2ac5512595541329 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:49 -0700 Subject: [PATCH 108/115] platform/x86/intel/pmc: Add ACPI PWRM telemetry driver for Nova Lake S MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an ACPI-based PMC PWRM telemetry driver for Nova Lake S. The driver locates PMT discovery data in _DSD under the Intel VSEC UUID, parses it, and registers telemetry regions with the PMT/VSEC framework so PMC telemetry is exposed via existing PMT interfaces. Export pmc_parse_telem_dsd() and pmc_find_telem_guid() to support ACPI discovery in other PMC drivers (e.g., ssram_telemetry) without duplicating ACPI parsing logic. Also export acpi_disc_t typedef from core.h for callers to properly declare discovery table arrays. Selected by INTEL_PMC_CORE. Existing PCI functionality is preserved. Assisted-by: GitHub-Copilot:claude-opus-4.7 Signed-off-by: David E. Box Link: https://patch.msgid.link/09b8211d8a5a79fa019ee2397137a6a43cf19430.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/Kconfig | 14 ++ drivers/platform/x86/intel/pmc/Makefile | 2 + drivers/platform/x86/intel/pmc/core.h | 16 ++ .../platform/x86/intel/pmc/pwrm_telemetry.c | 216 ++++++++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 drivers/platform/x86/intel/pmc/pwrm_telemetry.c diff --git a/drivers/platform/x86/intel/pmc/Kconfig b/drivers/platform/x86/intel/pmc/Kconfig index e9012b703918..561d46634ab2 100644 --- a/drivers/platform/x86/intel/pmc/Kconfig +++ b/drivers/platform/x86/intel/pmc/Kconfig @@ -9,6 +9,7 @@ config INTEL_PMC_CORE depends on ACPI depends on INTEL_PMT_TELEMETRY select INTEL_PMC_SSRAM_TELEMETRY + select INTEL_PMC_PWRM_TELEMETRY help The Intel Platform Controller Hub for Intel Core SoCs provides access to Power Management Controller registers via various interfaces. This @@ -39,3 +40,16 @@ config INTEL_PMC_SSRAM_TELEMETRY (including sysfs). This option is selected by INTEL_PMC_CORE. + +config INTEL_PMC_PWRM_TELEMETRY + tristate + help + This driver discovers PMC PWRM telemetry regions described in ACPI + _DSD and registers them with the Intel VSEC framework as Intel PMT + telemetry devices. + + It validates the ACPI discovery data and publishes the discovered + regions so they can be accessed through the Intel PMT telemetry + interfaces (including sysfs). + + This option is selected by INTEL_PMC_CORE. diff --git a/drivers/platform/x86/intel/pmc/Makefile b/drivers/platform/x86/intel/pmc/Makefile index 23853e867c91..5b595176f812 100644 --- a/drivers/platform/x86/intel/pmc/Makefile +++ b/drivers/platform/x86/intel/pmc/Makefile @@ -13,3 +13,5 @@ obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core_pltdrv.o # Intel PMC SSRAM driver intel_pmc_ssram_telemetry-y += ssram_telemetry.o obj-$(CONFIG_INTEL_PMC_SSRAM_TELEMETRY) += intel_pmc_ssram_telemetry.o +intel_pmc_pwrm_telemetry-y += pwrm_telemetry.o +obj-$(CONFIG_INTEL_PMC_PWRM_TELEMETRY) += intel_pmc_pwrm_telemetry.o diff --git a/drivers/platform/x86/intel/pmc/core.h b/drivers/platform/x86/intel/pmc/core.h index 55cf567febe4..b4c7399f8369 100644 --- a/drivers/platform/x86/intel/pmc/core.h +++ b/drivers/platform/x86/intel/pmc/core.h @@ -14,10 +14,15 @@ #include #include +#include +#include #include +#include struct telem_endpoint; +DEFINE_FREE(pmc_acpi_free, void *, if (_T) ACPI_FREE(_T)) + #define SLP_S0_RES_COUNTER_MASK GENMASK(31, 0) #define PMC_BASE_ADDR_DEFAULT 0xFE000000 @@ -622,6 +627,8 @@ int pmc_core_pmt_get_blk_sub_req(struct pmc_dev *pmcdev, struct pmc *pmc, extern const struct file_operations pmc_core_substate_req_regs_fops; extern const struct file_operations pmc_core_substate_blk_req_fops; +extern const guid_t intel_vsec_guid; + #define pmc_for_each_mode(mode, pmc) \ for (unsigned int __i = 0, __cond; \ __cond = __i < (pmc)->num_lpm_modes, \ @@ -643,4 +650,13 @@ static const struct file_operations __name ## _fops = { \ .release = single_release, \ } +struct intel_vsec_header; +union acpi_object; + +/* Avoid checkpatch warning */ +typedef u32 (*acpi_disc_t)[PMT_DISC_DWORDS]; + +acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj, + struct intel_vsec_header *header); +union acpi_object *pmc_find_telem_guid(union acpi_object *dsd); #endif /* PMC_CORE_H */ diff --git a/drivers/platform/x86/intel/pmc/pwrm_telemetry.c b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c new file mode 100644 index 000000000000..62b0e9fc7920 --- /dev/null +++ b/drivers/platform/x86/intel/pmc/pwrm_telemetry.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel PMC PWRM ACPI driver + * + * Copyright (C) 2025, Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" + +#define ENTRY_LEN 5 + +/* DWORD2 */ +#define DVSEC_ID_MASK GENMASK(15, 0) +#define NUM_ENTRIES_MASK GENMASK(23, 16) +#define ENTRY_SIZE_MASK GENMASK(31, 24) + +/* DWORD3 */ +#define TBIR_MASK GENMASK(2, 0) +#define DISC_TBL_OFF_MASK GENMASK(31, 3) + +const guid_t intel_vsec_guid = + GUID_INIT(0x294903fb, 0x634d, 0x4fc7, 0xaf, 0x1f, 0x0f, 0xb9, + 0x56, 0xb0, 0x4f, 0xc1); + +static bool is_valid_entry(union acpi_object *pkg) +{ + int i; + + if (!pkg || pkg->type != ACPI_TYPE_PACKAGE || pkg->package.count != ENTRY_LEN) + return false; + + if (pkg->package.elements[0].type != ACPI_TYPE_STRING) + return false; + + for (i = 1; i < ENTRY_LEN; i++) + if (pkg->package.elements[i].type != ACPI_TYPE_INTEGER) + return false; + + return true; +} + +acpi_disc_t pmc_parse_telem_dsd(union acpi_object *obj, + struct intel_vsec_header *header) +{ + union acpi_object *vsec_pkg; + union acpi_object *disc_pkg; + u64 hdr0, hdr1; + int num_regions; + int i; + + if (!header) + return ERR_PTR(-EINVAL); + + if (!obj || obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 2) + return ERR_PTR(-EINVAL); + + /* First Package is DVSEC info */ + vsec_pkg = &obj->package.elements[0]; + if (!is_valid_entry(vsec_pkg)) + return ERR_PTR(-EINVAL); + + hdr0 = vsec_pkg->package.elements[3].integer.value; + hdr1 = vsec_pkg->package.elements[4].integer.value; + + header->id = FIELD_GET(DVSEC_ID_MASK, hdr0); + header->num_entries = FIELD_GET(NUM_ENTRIES_MASK, hdr0); + header->entry_size = FIELD_GET(ENTRY_SIZE_MASK, hdr0); + header->tbir = FIELD_GET(TBIR_MASK, hdr1); + header->offset = hdr1 & DISC_TBL_OFF_MASK; + + /* Second Package contains the discovery tables */ + disc_pkg = &obj->package.elements[1]; + if (disc_pkg->type != ACPI_TYPE_PACKAGE || disc_pkg->package.count < 1) + return ERR_PTR(-EINVAL); + + num_regions = disc_pkg->package.count; + if (header->num_entries != num_regions) + return ERR_PTR(-EINVAL); + + acpi_disc_t disc __free(kfree) = kmalloc_array(num_regions, sizeof(*disc), + GFP_KERNEL); + if (!disc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < num_regions; i++) { + union acpi_object *pkg; + u64 value; + int j; + + pkg = &disc_pkg->package.elements[i]; + if (!is_valid_entry(pkg)) + return ERR_PTR(-EINVAL); + + /* Element 0 is a descriptive string; DWORD values start at index 1. */ + for (j = 1; j < ENTRY_LEN; j++) { + value = pkg->package.elements[j].integer.value; + if (value > U32_MAX) + return ERR_PTR(-ERANGE); + + disc[i][j - 1] = value; + } + } + + return no_free_ptr(disc); +} +EXPORT_SYMBOL_NS_GPL(pmc_parse_telem_dsd, "INTEL_PMC_CORE"); + +union acpi_object *pmc_find_telem_guid(union acpi_object *dsd) +{ + int i; + + if (!dsd || dsd->type != ACPI_TYPE_PACKAGE) + return NULL; + + for (i = 0; i + 1 < dsd->package.count; i += 2) { + union acpi_object *uuid_obj, *data_obj; + guid_t uuid; + + uuid_obj = &dsd->package.elements[i]; + data_obj = &dsd->package.elements[i + 1]; + + if (uuid_obj->type != ACPI_TYPE_BUFFER || + uuid_obj->buffer.length != 16) + continue; + + memcpy(&uuid, uuid_obj->buffer.pointer, 16); + if (guid_equal(&uuid, &intel_vsec_guid)) + return data_obj; + } + + return NULL; +} +EXPORT_SYMBOL_NS_GPL(pmc_find_telem_guid, "INTEL_PMC_CORE"); + +static int pmc_pwrm_acpi_probe(struct platform_device *pdev) +{ + struct intel_vsec_header header; + struct intel_vsec_header *headers[2] = { &header, NULL }; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct intel_vsec_platform_info info = { }; + struct device *dev = &pdev->dev; + union acpi_object *dsd; + struct resource *res; + acpi_handle handle; + acpi_status status; + + handle = ACPI_HANDLE(&pdev->dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_object(handle, "_DSD", NULL, &buf); + if (ACPI_FAILURE(status)) { + return dev_err_probe(dev, -ENODEV, "Could not evaluate _DSD: %s\n", + acpi_format_exception(status)); + } + + void *dsd_buf __free(pmc_acpi_free) = buf.pointer; + + dsd = pmc_find_telem_guid(dsd_buf); + if (!dsd) + return -ENODEV; + + acpi_disc_t acpi_disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header); + if (IS_ERR(acpi_disc)) + return PTR_ERR(acpi_disc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, header.tbir); + if (!res) + return -EINVAL; + + info.headers = headers; + info.caps = VSEC_CAP_TELEMETRY; + info.acpi_disc = acpi_disc; + info.src = INTEL_VSEC_DISC_ACPI; + info.base_addr = res->start; + + return intel_vsec_register(&pdev->dev, &info); +} + +static const struct acpi_device_id pmc_pwrm_acpi_ids[] = { + { "INTC1122", 0 }, /* Nova Lake */ + { "INTC1129", 0 }, /* Nova Lake */ + { } +}; +MODULE_DEVICE_TABLE(acpi, pmc_pwrm_acpi_ids); + +static struct platform_driver pmc_pwrm_acpi_driver = { + .probe = pmc_pwrm_acpi_probe, + .driver = { + .name = "intel_pmc_pwrm_acpi", + .acpi_match_table = ACPI_PTR(pmc_pwrm_acpi_ids), + }, +}; +module_platform_driver(pmc_pwrm_acpi_driver); + +MODULE_AUTHOR("David E. Box "); +MODULE_DESCRIPTION("Intel PMC PWRM ACPI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("INTEL_VSEC"); From 90fd47d0d8109ef1301a60a44a7f85581a1e6efe Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:50 -0700 Subject: [PATCH 109/115] platform/x86/intel/pmc/ssram: Rename probe and PCI ID table for consistency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename intel_pmc_ssram_telemetry_probe() to pmc_ssram_telemetry_probe() and intel_pmc_ssram_telemetry_pci_ids[] to pmc_ssram_telemetry_pci_ids[], updating the MODULE_DEVICE_TABLE() and pci_driver wiring accordingly. This aligns the symbol names with the driver filename and module name, reduces redundant intel_ prefixes, and improves readability. No functional behavior changes are intended. Reviewed-by: Ilpo Järvinen Signed-off-by: David E. Box Link: https://patch.msgid.link/6d3935858214fdd0f530f9a7c08a387fa4e5e8cd.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 6f6e83e70fc5..1deb4d71da3f 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -149,7 +149,7 @@ int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, } EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); -static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) +static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { int ret; @@ -183,7 +183,7 @@ static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct return ret; } -static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { +static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, @@ -193,14 +193,14 @@ static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, { } }; -MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); +MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids); -static struct pci_driver intel_pmc_ssram_telemetry_driver = { +static struct pci_driver pmc_ssram_telemetry_driver = { .name = "intel_pmc_ssram_telemetry", - .id_table = intel_pmc_ssram_telemetry_pci_ids, - .probe = intel_pmc_ssram_telemetry_probe, + .id_table = pmc_ssram_telemetry_pci_ids, + .probe = pmc_ssram_telemetry_probe, }; -module_pci_driver(intel_pmc_ssram_telemetry_driver); +module_pci_driver(pmc_ssram_telemetry_driver); MODULE_IMPORT_NS("INTEL_VSEC"); MODULE_AUTHOR("Xi Pardee "); From d936dd2c605a17ab41826c1e7f738424d3d0bbfd Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:51 -0700 Subject: [PATCH 110/115] platform/x86/intel/pmc/ssram: Add PCI platform data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add per-device platform data for SSRAM telemetry PCI IDs and route probe through a method selector driven by id->driver_data. This is a preparatory refactor for follow-on discovery methods while preserving current behavior: all supported IDs continue to use the PCI initialization path. Signed-off-by: David E. Box Link: https://patch.msgid.link/1c6180097b20dbe1337828bf6d3667854d63583a.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 71 +++++++++++++++---- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 1deb4d71da3f..05f213aa108f 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -24,6 +25,18 @@ DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) +enum resource_method { + RES_METHOD_PCI, +}; + +struct ssram_type { + enum resource_method method; +}; + +static const struct ssram_type pci_main = { + .method = RES_METHOD_PCI, +}; + static struct pmc_ssram_telemetry *pmc_ssram_telems; static bool device_probed; @@ -69,7 +82,7 @@ static inline u64 get_base(void __iomem *addr, u32 offset) } static int -pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) +pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; @@ -109,6 +122,20 @@ pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 of return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); } +static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) +{ + int ret; + + ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_MAIN, 0); + if (ret) + return ret; + + pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); + pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + + return 0; +} + /** * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information * @pmc_idx: Index of the PMC @@ -151,6 +178,8 @@ EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { + const struct ssram_type *ssram_type; + enum resource_method method; int ret; pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, @@ -160,18 +189,25 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de goto probe_finish; } + ssram_type = (const struct ssram_type *)id->driver_data; + if (!ssram_type) { + dev_dbg(&pcidev->dev, "missing driver data\n"); + ret = -EINVAL; + goto probe_finish; + } + + method = ssram_type->method; + ret = pcim_enable_device(pcidev); if (ret) { dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n"); goto probe_finish; } - ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0); - if (ret) - goto probe_finish; - - pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); - pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + if (method == RES_METHOD_PCI) + ret = pmc_ssram_telemetry_pci_init(pcidev); + else + ret = -EINVAL; probe_finish: /* @@ -184,13 +220,20 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de } static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) }, - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP), + .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN), + .driver_data = (kernel_ulong_t)&pci_main }, { } }; MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids); From f79e57e2677c7641e94d5a38d4b2c2165f5c385c Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:52 -0700 Subject: [PATCH 111/115] platform/x86/intel/pmc/ssram: Refactor DEVID/PWRMBASE extraction into helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move DEVID/PWRMBASE extraction into pmc_ssram_get_devid_pwrmbase(). This is a preparatory refactor to place functionality in a common helper for reuse by a subsequent patch. Additionally add missing bits.h include and define SSRAM_BASE_ADDR_MASK for the address extraction mask. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Link: https://patch.msgid.link/75ca738c88729f37f286f342c1fe8ff86f7eafe7.1781294741.git.david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 05f213aa108f..211d842df449 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -22,6 +23,7 @@ #define SSRAM_PCH_OFFSET 0x60 #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 +#define SSRAM_BASE_ADDR_MASK GENMASK_ULL(63, 3) DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) @@ -40,6 +42,23 @@ static const struct ssram_type pci_main = { static struct pmc_ssram_telemetry *pmc_ssram_telems; static bool device_probed; +static inline u64 get_base(void __iomem *addr, u32 offset) +{ + return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK; +} + +static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_idx) +{ + u64 pwrm_base; + u16 devid; + + pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); + devid = readw(ssram + SSRAM_DEVID_OFFSET); + + pmc_ssram_telems[pmc_idx].devid = devid; + pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; +} + static int pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram) { @@ -76,18 +95,12 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem return intel_vsec_register(&pcidev->dev, &info); } -static inline u64 get_base(void __iomem *addr, u32 offset) -{ - return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3); -} - static int pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; - u64 ssram_base, pwrm_base; - u16 devid; + u64 ssram_base; ssram_base = pci_resource_start(pcidev, 0); tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); @@ -112,11 +125,7 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u3 ssram = no_free_ptr(tmp_ssram); } - pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); - devid = readw(ssram + SSRAM_DEVID_OFFSET); - - pmc_ssram_telems[pmc_idx].devid = devid; - pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; + pmc_ssram_get_devid_pwrmbase(ssram, pmc_idx); /* Find and register and PMC telemetry entries */ return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); From e3c9200a9c0e05532c495956883ba11fe23a3df0 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:53 -0700 Subject: [PATCH 112/115] platform/x86/intel/pmc/ssram: Switch to static array with per-index probe state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace devm-allocated pmc_ssram_telems pointer with a fixed-size static array and introduce per-index probe state tracking. This prepares the driver for later per-device probe handling where tying the PMC tracking storage to one probed PCI device is no longer suitable. The previous single global device_probed flag cannot describe the state of individual PMC indices when multiple devices can be probed independently. Replace it with per-index state (UNPROBED, PROBING, PRESENT, ABSENT) and a staging cache that publishes discovered values only after probe completes. This avoids races between probe/unbind and concurrent readers. Use marked state accesses with release/acquire ordering to prevent compiler and CPU reordering issues across concurrent probe/unbind cycles. This patch was substantially rewritten in later revisions. Originally developed from earlier work by Xi Pardee. Signed-off-by: David E. Box Assisted-by: Claude:claude-sonnet-4-5 Link: https://patch.msgid.link/a2cccf532bec04fcc370819890d936212bc1b14c.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 230 ++++++++++++++---- 1 file changed, 182 insertions(+), 48 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 211d842df449..3349f65ae7bd 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -24,6 +25,7 @@ #define SSRAM_IOE_OFFSET 0x68 #define SSRAM_DEVID_OFFSET 0x70 #define SSRAM_BASE_ADDR_MASK GENMASK_ULL(63, 3) +#define SSRAM_PCI_PMC_MASK (BIT(PMC_IDX_MAIN) | BIT(PMC_IDX_IOE) | BIT(PMC_IDX_PCH)) DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) @@ -39,15 +41,37 @@ static const struct ssram_type pci_main = { .method = RES_METHOD_PCI, }; -static struct pmc_ssram_telemetry *pmc_ssram_telems; -static bool device_probed; +enum pmc_ssram_state { + PMC_SSRAM_UNPROBED = 0, + PMC_SSRAM_PROBING, + PMC_SSRAM_PRESENT, + PMC_SSRAM_ABSENT, +}; + +static enum pmc_ssram_state pmc_ssram_state[MAX_NUM_PMC]; +static struct pmc_ssram_telemetry pmc_ssram_telems[MAX_NUM_PMC]; + +struct pmc_ssram_probe_cache { + /* Per-index values staged by probe before publishing globally. */ + struct pmc_ssram_telemetry telems[MAX_NUM_PMC]; + /* PMCs this probe instance is responsible for publishing. */ + unsigned long owned_mask; + /* Subset of owned_mask that was discovered successfully. */ + unsigned long valid_mask; +}; + +struct pmc_ssram_drvdata { + /* PMCs published by this bound device; used to unpublish on remove. */ + unsigned long owned_mask; +}; static inline u64 get_base(void __iomem *addr, u32 offset) { return lo_hi_readq(addr + offset) & SSRAM_BASE_ADDR_MASK; } -static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_idx) +static void pmc_ssram_get_devid_pwrmbase(struct pmc_ssram_probe_cache *probe_cache, + void __iomem *ssram, unsigned int pmc_idx) { u64 pwrm_base; u16 devid; @@ -55,8 +79,45 @@ static void pmc_ssram_get_devid_pwrmbase(void __iomem *ssram, unsigned int pmc_i pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); devid = readw(ssram + SSRAM_DEVID_OFFSET); - pmc_ssram_telems[pmc_idx].devid = devid; - pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; + probe_cache->telems[pmc_idx].base_addr = pwrm_base; + probe_cache->telems[pmc_idx].devid = devid; +} + +static void pmc_ssram_publish_absent(unsigned int pmc_idx) +{ + /* + * Publish only the state without modifying base_addr and devid. This + * lets a reader that already observed PRESENT finish copying the + * previous values even if unbind concurrently publishes ABSENT. Readers + * that observe ABSENT return -ENODEV without accessing data. + */ + smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_ABSENT); +} + +static void pmc_ssram_publish_present(struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx) +{ + /* + * The devid and base_addr fields are read from hardware MMIO registers + * whose values are stable for a given PMC index. A reader that observed + * PRESENT from an earlier probe can safely copy them while a concurrent + * rebind republishes those fields because both probes read the same + * hardware values. + */ + pmc_ssram_telems[pmc_idx] = probe_cache->telems[pmc_idx]; + /* + * Publish base_addr and devid before publishing PRESENT. Pairs with the + * acquire load in the reader that consumes them after observing PRESENT. + */ + smp_store_release(&pmc_ssram_state[pmc_idx], PMC_SSRAM_PRESENT); +} + +static void pmc_ssram_mark_probing(unsigned long mask) +{ + unsigned long bit; + + for_each_set_bit(bit, &mask, MAX_NUM_PMC) + WRITE_ONCE(pmc_ssram_state[bit], PMC_SSRAM_PROBING); } static int @@ -96,11 +157,14 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem } static int -pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) +pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx, u32 offset) { void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; u64 ssram_base; + int ret; ssram_base = pci_resource_start(pcidev, 0); tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); @@ -125,22 +189,38 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, unsigned int pmc_idx, u3 ssram = no_free_ptr(tmp_ssram); } - pmc_ssram_get_devid_pwrmbase(ssram, pmc_idx); + pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx); /* Find and register and PMC telemetry entries */ - return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); -} - -static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) -{ - int ret; - - ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_MAIN, 0); + ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); if (ret) return ret; - pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); - pmc_ssram_telemetry_get_pmc_pci(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); + probe_cache->valid_mask |= BIT(pmc_idx); + + return 0; +} + +static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache) +{ + int ret; + + ret = pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_MAIN, 0); + if (ret) + return ret; + + /* + * If MAIN PMC enumeration is successful but either IOE or PCH fail, + * don't fail probe as the MAIN PMC is still useful as it provides the + * global reset and slp_s0 counter access. Failed or missing secondary + * PMCs are left out of valid_mask and published as absent. + */ + pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_IOE, + SSRAM_IOE_OFFSET); + + pmc_ssram_telemetry_get_pmc_pci(pcidev, probe_cache, PMC_IDX_PCH, + SSRAM_PCH_OFFSET); return 0; } @@ -150,62 +230,106 @@ static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev) * @pmc_idx: Index of the PMC * @pmc_ssram_telemetry: pmc_ssram_telemetry structure to store the PMC information * + * State flow per PMC index: + * - PMC_SSRAM_UNPROBED: Probe has not started for this index. + * - PMC_SSRAM_PROBING: Probe is currently discovering data for this index. + * - PMC_SSRAM_PRESENT: base_addr/devid were published and can be read. + * - PMC_SSRAM_ABSENT: No data is available for this index. + * + * Readers use an acquire load of the state. If state is PRESENT, reads of + * base_addr/devid are ordered after the state observation and pair with the + * writer's release-store when publishing PRESENT. + * * Return: * * 0 - Success * * -EAGAIN - Probe function has not finished yet. Try again. * * -EINVAL - Invalid pmc_idx - * * -ENODEV - PMC device is not available + * * -ENODEV - PMC device is not available (hardware absent or driver failed to initialize) */ int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, struct pmc_ssram_telemetry *pmc_ssram_telemetry) { - /* - * PMCs are discovered in probe function. If this function is called before - * probe function complete, the result would be invalid. Use device_probed - * variable to avoid this case. Return -EAGAIN to inform the consumer to call - * again later. - */ - if (!device_probed) - return -EAGAIN; + enum pmc_ssram_state state; - /* - * Memory barrier is used to ensure the correct read order between - * device_probed variable and PMC info. - */ - smp_rmb(); if (pmc_idx >= MAX_NUM_PMC) return -EINVAL; - if (!pmc_ssram_telems || !pmc_ssram_telems[pmc_idx].devid) + /* + * PMCs are discovered in probe function. If this function is called before + * probe function complete, the result would be invalid. Use per-PMC state + * to inform the consumer to call again later. + */ + state = smp_load_acquire(&pmc_ssram_state[pmc_idx]); + if (state == PMC_SSRAM_UNPROBED || state == PMC_SSRAM_PROBING) + return -EAGAIN; + + if (state == PMC_SSRAM_ABSENT) return -ENODEV; + /* + * Acquire semantics order reads of devid and base_addr after observing + * PRESENT and pair with the writer's release-store. + */ pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid; pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr; return 0; } EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); +static void pmc_ssram_publish_absent_mask(unsigned long mask) +{ + unsigned long bit; + + for_each_set_bit(bit, &mask, MAX_NUM_PMC) + pmc_ssram_publish_absent(bit); +} + +static void pmc_ssram_publish_telems(struct pmc_ssram_probe_cache *probe_cache, int ret) +{ + unsigned long bit; + + /* If probe failed, all owned indexes become absent for readers. */ + if (ret) { + pmc_ssram_publish_absent_mask(probe_cache->owned_mask); + return; + } + + /* Publish each owned index independently based on discovery result. */ + for_each_set_bit(bit, &probe_cache->owned_mask, MAX_NUM_PMC) { + if (probe_cache->valid_mask & BIT(bit)) + pmc_ssram_publish_present(probe_cache, bit); + else + pmc_ssram_publish_absent(bit); + } +} + static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { + struct pmc_ssram_probe_cache probe_cache = {}; + struct pmc_ssram_drvdata *drvdata; const struct ssram_type *ssram_type; enum resource_method method; int ret; - pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, - GFP_KERNEL); - if (!pmc_ssram_telems) { - ret = -ENOMEM; - goto probe_finish; - } - ssram_type = (const struct ssram_type *)id->driver_data; if (!ssram_type) { dev_dbg(&pcidev->dev, "missing driver data\n"); - ret = -EINVAL; - goto probe_finish; + return -EINVAL; } method = ssram_type->method; + if (method == RES_METHOD_PCI) + probe_cache.owned_mask = SSRAM_PCI_PMC_MASK; + else + return -EINVAL; + + pmc_ssram_mark_probing(probe_cache.owned_mask); + + drvdata = devm_kzalloc(&pcidev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) { + ret = -ENOMEM; + goto probe_finish; + } ret = pcim_enable_device(pcidev); if (ret) { @@ -214,20 +338,29 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de } if (method == RES_METHOD_PCI) - ret = pmc_ssram_telemetry_pci_init(pcidev); + ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache); else ret = -EINVAL; + if (!ret) { + drvdata->owned_mask = probe_cache.owned_mask; + pci_set_drvdata(pcidev, drvdata); + } + probe_finish: - /* - * Memory barrier is used to ensure the correct write order between PMC info - * and device_probed variable. - */ - smp_wmb(); - device_probed = true; + pmc_ssram_publish_telems(&probe_cache, ret); + return ret; } +static void pmc_ssram_telemetry_remove(struct pci_dev *pcidev) +{ + struct pmc_ssram_drvdata *drvdata = pci_get_drvdata(pcidev); + + if (drvdata) + pmc_ssram_publish_absent_mask(drvdata->owned_mask); +} + static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM), .driver_data = (kernel_ulong_t)&pci_main }, @@ -251,6 +384,7 @@ static struct pci_driver pmc_ssram_telemetry_driver = { .name = "intel_pmc_ssram_telemetry", .id_table = pmc_ssram_telemetry_pci_ids, .probe = pmc_ssram_telemetry_probe, + .remove = pmc_ssram_telemetry_remove, }; module_pci_driver(pmc_ssram_telemetry_driver); From 08b94937c78aaa144e3afff659c6112bffea6bf0 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:54 -0700 Subject: [PATCH 113/115] platform/x86/intel/pmc/ssram: Add ACPI discovery scaffolding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare the SSRAM telemetry driver for ACPI-based discovery by adding support for reading telemetry regions from ACPI _DSD properties. Add pmc_ssram_telemetry_acpi_init() to parse _DSD for telemetry discovery tables and register them with the Intel VSEC framework. Extend ssram_type with a p_index field to specify which PMC index each ACPI device owns (unlike PCI which discovers all three PMCs from one device). At this stage, no platform IDs are wired to use ACPI discovery - existing devices continue using the PCI path. Follow-on patches will add platform support. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: Xi Pardee Signed-off-by: David E. Box Link: https://patch.msgid.link/54850d175993ee38aef99707f954492d24684dcc.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../platform/x86/intel/pmc/ssram_telemetry.c | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 3349f65ae7bd..10462fa7ebbf 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -5,6 +5,7 @@ * Copyright (c) 2023, Intel Corporation. */ +#include #include #include #include @@ -31,14 +32,17 @@ DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) enum resource_method { RES_METHOD_PCI, + RES_METHOD_ACPI, }; struct ssram_type { enum resource_method method; + enum pmc_index p_index; }; static const struct ssram_type pci_main = { .method = RES_METHOD_PCI, + .p_index = PMC_IDX_MAIN, }; enum pmc_ssram_state { @@ -225,6 +229,73 @@ static int pmc_ssram_telemetry_pci_init(struct pci_dev *pcidev, return 0; } +static int pmc_ssram_telemetry_get_pmc_acpi(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + unsigned int pmc_idx) +{ + u64 ssram_base; + + ssram_base = pci_resource_start(pcidev, 0); + if (!ssram_base) + return -ENODEV; + + void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = + ioremap(ssram_base, SSRAM_HDR_SIZE); + if (!ssram) + return -ENOMEM; + + pmc_ssram_get_devid_pwrmbase(probe_cache, ssram, pmc_idx); + probe_cache->valid_mask |= BIT(pmc_idx); + + return 0; +} + +static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev, + struct pmc_ssram_probe_cache *probe_cache, + enum pmc_index index) +{ + struct intel_vsec_header header; + struct intel_vsec_header *headers[2] = { &header, NULL }; + struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; + struct intel_vsec_platform_info info = { }; + union acpi_object *dsd; + acpi_handle handle; + acpi_status status; + int ret; + + handle = ACPI_HANDLE(&pcidev->dev); + if (!handle) + return -ENODEV; + + status = acpi_evaluate_object(handle, "_DSD", NULL, &buf); + if (ACPI_FAILURE(status)) + return -ENODEV; + + void *dsd_buf __free(pmc_acpi_free) = buf.pointer; + + dsd = pmc_find_telem_guid(dsd_buf); + if (!dsd) + return -ENODEV; + + acpi_disc_t disc __free(kfree) = pmc_parse_telem_dsd(dsd, &header); + if (IS_ERR(disc)) + return PTR_ERR(disc); + + info.headers = headers; + info.caps = VSEC_CAP_TELEMETRY; + info.acpi_disc = disc; + info.src = INTEL_VSEC_DISC_ACPI; + + /* This is an ACPI companion device. PCI BAR will be used for base addr. */ + info.base_addr = 0; + + ret = intel_vsec_register(&pcidev->dev, &info); + if (ret) + return ret; + + return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index); +} + /** * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information * @pmc_idx: Index of the PMC @@ -309,6 +380,7 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de struct pmc_ssram_drvdata *drvdata; const struct ssram_type *ssram_type; enum resource_method method; + enum pmc_index index; int ret; ssram_type = (const struct ssram_type *)id->driver_data; @@ -317,9 +389,12 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de return -EINVAL; } + index = ssram_type->p_index; method = ssram_type->method; if (method == RES_METHOD_PCI) probe_cache.owned_mask = SSRAM_PCI_PMC_MASK; + else if (method == RES_METHOD_ACPI) + probe_cache.owned_mask = BIT(index); else return -EINVAL; @@ -339,6 +414,8 @@ static int pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_de if (method == RES_METHOD_PCI) ret = pmc_ssram_telemetry_pci_init(pcidev, &probe_cache); + else if (method == RES_METHOD_ACPI) + ret = pmc_ssram_telemetry_acpi_init(pcidev, &probe_cache, index); else ret = -EINVAL; @@ -389,6 +466,7 @@ static struct pci_driver pmc_ssram_telemetry_driver = { module_pci_driver(pmc_ssram_telemetry_driver); MODULE_IMPORT_NS("INTEL_VSEC"); +MODULE_IMPORT_NS("INTEL_PMC_CORE"); MODULE_AUTHOR("Xi Pardee "); MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver"); MODULE_LICENSE("GPL"); From 8a3607ece59fcf84cf4129bdf683cb09122e6b51 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:55 -0700 Subject: [PATCH 114/115] platform/x86/intel/pmc/ssram: Make PMT registration optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SSRAM telemetry driver extracts essential PMC device ID and power management base address information that intel_pmc_core depends on for core functionality. If PMT registration failure prevents this critical data from being available, intel_pmc_core operation would break entirely. Therefore, PMT registration failures must not block access to this data. Change the behavior to log a warning when PMT registration fails but continue with successful driver initialization, ensuring the primary telemetry data remains accessible to dependent drivers. Signed-off-by: David E. Box Link: https://patch.msgid.link/4f4c324977951f6082bf2218c8b911e1ae7e0a7b.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 10462fa7ebbf..9a8922045ab7 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -198,7 +198,7 @@ pmc_ssram_telemetry_get_pmc_pci(struct pci_dev *pcidev, /* Find and register and PMC telemetry entries */ ret = pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); if (ret) - return ret; + dev_warn(&pcidev->dev, "could not register PMT\n"); probe_cache->valid_mask |= BIT(pmc_idx); @@ -291,7 +291,7 @@ static int pmc_ssram_telemetry_acpi_init(struct pci_dev *pcidev, ret = intel_vsec_register(&pcidev->dev, &info); if (ret) - return ret; + dev_warn(&pcidev->dev, "could not register PMT\n"); return pmc_ssram_telemetry_get_pmc_acpi(pcidev, probe_cache, index); } From 50022e56dc89fbf1ec22826edf03dc2e5b9076cc Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Fri, 12 Jun 2026 14:01:56 -0700 Subject: [PATCH 115/115] platform/x86/intel/pmc: Add NVL PCI IDs for SSRAM telemetry discovery MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Nova Lake S PMC device IDs to enable binding of the SSRAM telemetry driver on NVL platforms, and map them to the ACPI-based discovery policy. Signed-off-by: David E. Box Link: https://patch.msgid.link/fc0e8bb00e2765fb7d145fef2ed1b0236b935c08.1781294741.git.david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/pmc/ssram_telemetry.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 9a8922045ab7..8e280ec525a6 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -45,6 +45,16 @@ static const struct ssram_type pci_main = { .p_index = PMC_IDX_MAIN, }; +static const struct ssram_type acpi_main = { + .method = RES_METHOD_ACPI, + .p_index = PMC_IDX_MAIN, +}; + +static const struct ssram_type acpi_pch = { + .method = RES_METHOD_ACPI, + .p_index = PMC_IDX_PCH, +}; + enum pmc_ssram_state { PMC_SSRAM_UNPROBED = 0, PMC_SSRAM_PROBING, @@ -453,6 +463,12 @@ static const struct pci_device_id pmc_ssram_telemetry_pci_ids[] = { .driver_data = (kernel_ulong_t)&pci_main }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_WCL_PCDN), .driver_data = (kernel_ulong_t)&pci_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDH), + .driver_data = (kernel_ulong_t)&acpi_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCDS), + .driver_data = (kernel_ulong_t)&acpi_main }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_NVL_PCHS), + .driver_data = (kernel_ulong_t)&acpi_pch }, { } }; MODULE_DEVICE_TABLE(pci, pmc_ssram_telemetry_pci_ids);