From 85cd4fbf5669cf394c8627d91c7b50b9aca8f4af Mon Sep 17 00:00:00 2001 From: Ethan Nelson-Moore Date: Fri, 30 Jan 2026 18:01:17 -0800 Subject: [PATCH 001/106] platform: x86: remove unnecessary module_init/exit() functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two x86 platform drivers have unnecessary module_init() and module_exit() functions that are empty or just print a message. Remove them. Signed-off-by: Ethan Nelson-Moore Link: https://patch.msgid.link/20260131020118.46171-1-enelsonmoore@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 14 -------------- drivers/platform/x86/mxm-wmi.c | 12 ------------ 2 files changed, 26 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 7c0915e097ba..6ba49bd375df 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -5413,17 +5413,3 @@ void asus_wmi_unregister_driver(struct asus_wmi_driver *driver) used = false; } EXPORT_SYMBOL_GPL(asus_wmi_unregister_driver); - -static int __init asus_wmi_init(void) -{ - pr_info("ASUS WMI generic driver loaded\n"); - return 0; -} - -static void __exit asus_wmi_exit(void) -{ - pr_info("ASUS WMI generic driver unloaded\n"); -} - -module_init(asus_wmi_init); -module_exit(asus_wmi_exit); diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c index 9a457956025a..dbc5e35ec38b 100644 --- a/drivers/platform/x86/mxm-wmi.c +++ b/drivers/platform/x86/mxm-wmi.c @@ -80,15 +80,3 @@ bool mxm_wmi_supported(void) return guid_valid; } EXPORT_SYMBOL_GPL(mxm_wmi_supported); - -static int __init mxm_wmi_init(void) -{ - return 0; -} - -static void __exit mxm_wmi_exit(void) -{ -} - -module_init(mxm_wmi_init); -module_exit(mxm_wmi_exit); From 0e59b31dbd3bfca96567e3d22518f4b6a7051a40 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 13 Feb 2026 14:40:32 -0800 Subject: [PATCH 002/106] platform/x86: pcengines-apuv2: attach software node to the gpiochip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPIO subsystem is switching the way it locates GPIO chip instances for GPIO references in software nodes from matching on node names to identity matching, which necessitates assigning firmware nodes (software nodes) to GPIO chips. Attach apu2_gpiochip_node to the platform device representing the GPIO controller. Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/aY-oAVI0TubcaD2K@google.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/pcengines-apuv2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/pcengines-apuv2.c b/drivers/platform/x86/pcengines-apuv2.c index 3b086863c6ac..3f19589d1ba0 100644 --- a/drivers/platform/x86/pcengines-apuv2.c +++ b/drivers/platform/x86/pcengines-apuv2.c @@ -294,7 +294,8 @@ static int __init apu_board_init(void) } apu_gpio_pdev = apu_create_pdev(AMD_FCH_GPIO_DRIVER_NAME, - id->driver_data, sizeof(struct amd_fch_gpio_pdata), NULL); + id->driver_data, sizeof(struct amd_fch_gpio_pdata), + &apu2_gpiochip_node); err = PTR_ERR_OR_ZERO(apu_gpio_pdev); if (err) goto err_unregister_swnodes; From 1b0b124a9c41bf384f609003a97ba35a25fae130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Mon, 23 Feb 2026 21:59:07 +0100 Subject: [PATCH 003/106] platform/x86: dell-wmi-sysman: Use standard kobj_sysfs_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit wmi_sysman_kobj_sysfs_ops are identical to the standard kobj_sysfs_ops. Drop the unnecessary custom copy. Signed-off-by: Thomas Weißschuh Link: https://patch.msgid.link/20260223-sysfs-const-dell-wmi-sysman-v1-1-8a690884044e@weissschuh.net Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../x86/dell/dell-wmi-sysman/sysman.c | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 9dddab6c9397..6241f16fd3da 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -220,35 +220,6 @@ static int create_attributes_level_sysfs_files(void) return 0; } -static ssize_t wmi_sysman_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct kobj_attribute *kattr; - ssize_t ret = -EIO; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->show) - ret = kattr->show(kobj, kattr, buf); - return ret; -} - -static ssize_t wmi_sysman_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct kobj_attribute *kattr; - ssize_t ret = -EIO; - - kattr = container_of(attr, struct kobj_attribute, attr); - if (kattr->store) - ret = kattr->store(kobj, kattr, buf, count); - return ret; -} - -static const struct sysfs_ops wmi_sysman_kobj_sysfs_ops = { - .show = wmi_sysman_attr_show, - .store = wmi_sysman_attr_store, -}; - static void attr_name_release(struct kobject *kobj) { kfree(kobj); @@ -256,7 +227,7 @@ static void attr_name_release(struct kobject *kobj) static const struct kobj_type attr_name_ktype = { .release = attr_name_release, - .sysfs_ops = &wmi_sysman_kobj_sysfs_ops, + .sysfs_ops = &kobj_sysfs_ops, }; /** From c2948247cd1a6f3e476df46a1bd903c79f3228dd Mon Sep 17 00:00:00 2001 From: Coby McKinney Date: Mon, 2 Feb 2026 12:52:11 -0800 Subject: [PATCH 004/106] platform/x86: thinkpad_acpi: use seq_puts() instead of seq_printf() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkpatch.pl reported warnings where seq_printf() was used for simple strings with no format specifiers. Replace these instances with seq_puts() to avoid the overhead of runtime string parsing and to conform to kernel coding standards. Signed-off-by: Coby McKinney Reviewed-by: Mark Pearson Link: https://patch.msgid.link/20260202205214.18898-1-coby@bytemap.space Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 86 ++++++++++----------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index f9c736777908..17590f939060 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -1315,7 +1315,7 @@ static ssize_t tpacpi_rfk_sysfs_enable_store(const enum tpacpi_rfk_id id, static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file *m) { if (id >= TPACPI_RFK_SW_MAX) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { int status; @@ -1330,7 +1330,7 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file * } seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status == TPACPI_RFK_RADIO_ON)); - seq_printf(m, "commands:\tenable, disable\n"); + seq_puts(m, "commands:\tenable, disable\n"); } return 0; @@ -4017,7 +4017,7 @@ static int hotkey_read(struct seq_file *m) int res, status; if (!tp_features.hotkey) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } @@ -4033,10 +4033,10 @@ static int hotkey_read(struct seq_file *m) seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status & BIT(0))); if (hotkey_all_mask) { seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); - seq_printf(m, "commands:\tenable, disable, reset, \n"); + seq_puts(m, "commands:\tenable, disable, reset, \n"); } else { - seq_printf(m, "mask:\t\tnot supported\n"); - seq_printf(m, "commands:\tenable, disable, reset\n"); + seq_puts(m, "mask:\t\tnot supported\n"); + seq_puts(m, "commands:\tenable, disable, reset\n"); } return 0; @@ -4933,7 +4933,7 @@ static int video_read(struct seq_file *m) int status, autosw; if (video_supported == TPACPI_VIDEO_NONE) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } @@ -4949,18 +4949,18 @@ static int video_read(struct seq_file *m) if (autosw < 0) return autosw; - seq_printf(m, "status:\t\tsupported\n"); + seq_puts(m, "status:\t\tsupported\n"); seq_printf(m, "lcd:\t\t%s\n", str_enabled_disabled(status & BIT(0))); seq_printf(m, "crt:\t\t%s\n", str_enabled_disabled(status & BIT(1))); if (video_supported == TPACPI_VIDEO_NEW) seq_printf(m, "dvi:\t\t%s\n", str_enabled_disabled(status & BIT(3))); seq_printf(m, "auto:\t\t%s\n", str_enabled_disabled(autosw & BIT(0))); - seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); - seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); + seq_puts(m, "commands:\tlcd_enable, lcd_disable\n"); + seq_puts(m, "commands:\tcrt_enable, crt_disable\n"); if (video_supported == TPACPI_VIDEO_NEW) - seq_printf(m, "commands:\tdvi_enable, dvi_disable\n"); - seq_printf(m, "commands:\tauto_enable, auto_disable\n"); - seq_printf(m, "commands:\tvideo_switch, expand_toggle\n"); + seq_puts(m, "commands:\tdvi_enable, dvi_disable\n"); + seq_puts(m, "commands:\tauto_enable, auto_disable\n"); + seq_puts(m, "commands:\tvideo_switch, expand_toggle\n"); return 0; } @@ -5204,14 +5204,14 @@ static int kbdlight_read(struct seq_file *m) int level; if (!tp_features.kbdlight) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } else { level = kbdlight_get_level(); if (level < 0) seq_printf(m, "status:\t\terror %d\n", level); else seq_printf(m, "status:\t\t%d\n", level); - seq_printf(m, "commands:\t0, 1, 2\n"); + seq_puts(m, "commands:\t0, 1, 2\n"); } return 0; @@ -5378,16 +5378,16 @@ static int light_read(struct seq_file *m) int status; if (!tp_features.light) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } else if (!tp_features.light_status) { - seq_printf(m, "status:\t\tunknown\n"); - seq_printf(m, "commands:\ton, off\n"); + seq_puts(m, "status:\t\tunknown\n"); + seq_puts(m, "commands:\ton, off\n"); } else { status = light_get_status(); if (status < 0) return status; seq_printf(m, "status:\t\t%s\n", str_on_off(status & BIT(0))); - seq_printf(m, "commands:\ton, off\n"); + seq_puts(m, "commands:\ton, off\n"); } return 0; @@ -5477,10 +5477,10 @@ static int cmos_read(struct seq_file *m) /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, R30, R31, T20-22, X20-21 */ if (!cmos_handle) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { - seq_printf(m, "status:\t\tsupported\n"); - seq_printf(m, "commands:\t ( is 0-21)\n"); + seq_puts(m, "status:\t\tsupported\n"); + seq_puts(m, "commands:\t ( is 0-21)\n"); } return 0; @@ -5846,10 +5846,10 @@ static int __init led_init(struct ibm_init_struct *iibm) static int led_read(struct seq_file *m) { if (!led_supported) { - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); return 0; } - seq_printf(m, "status:\t\tsupported\n"); + seq_puts(m, "status:\t\tsupported\n"); if (led_supported == TPACPI_LED_570) { /* 570 */ @@ -5862,7 +5862,7 @@ static int led_read(struct seq_file *m) } } - seq_printf(m, "commands:\t on, off, blink ( is 0-15)\n"); + seq_puts(m, "commands:\t on, off, blink ( is 0-15)\n"); return 0; } @@ -5946,10 +5946,10 @@ static int __init beep_init(struct ibm_init_struct *iibm) static int beep_read(struct seq_file *m) { if (!beep_handle) - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); else { - seq_printf(m, "status:\t\tsupported\n"); - seq_printf(m, "commands:\t ( is 0-17)\n"); + seq_puts(m, "status:\t\tsupported\n"); + seq_puts(m, "commands:\t ( is 0-17)\n"); } return 0; @@ -6398,14 +6398,14 @@ static int thermal_read(struct seq_file *m) if (unlikely(n < 0)) return n; - seq_printf(m, "temperatures:\t"); + seq_puts(m, "temperatures:\t"); if (n > 0) { for (i = 0; i < (n - 1); i++) seq_printf(m, "%d ", t.temp[i] / 1000); seq_printf(m, "%d\n", t.temp[i] / 1000); } else - seq_printf(m, "not supported\n"); + seq_puts(m, "not supported\n"); return 0; } @@ -6918,10 +6918,10 @@ static int brightness_read(struct seq_file *m) level = brightness_get(NULL); if (level < 0) { - seq_printf(m, "level:\t\tunreadable\n"); + seq_puts(m, "level:\t\tunreadable\n"); } else { seq_printf(m, "level:\t\t%d\n", level); - seq_printf(m, "commands:\tup, down\n"); + seq_puts(m, "commands:\tup, down\n"); seq_printf(m, "commands:\tlevel ( is 0-%d)\n", bright_maxlvl); } @@ -7637,10 +7637,10 @@ static int volume_read(struct seq_file *m) u8 status; if (volume_get_status(&status) < 0) { - seq_printf(m, "level:\t\tunreadable\n"); + seq_puts(m, "level:\t\tunreadable\n"); } else { if (tp_features.mixer_no_level_control) - seq_printf(m, "level:\t\tunsupported\n"); + seq_puts(m, "level:\t\tunsupported\n"); else seq_printf(m, "level:\t\t%d\n", status & TP_EC_AUDIO_LVL_MSK); @@ -7648,9 +7648,9 @@ static int volume_read(struct seq_file *m) seq_printf(m, "mute:\t\t%s\n", str_on_off(status & BIT(TP_EC_AUDIO_MUTESW))); if (volume_control_allowed) { - seq_printf(m, "commands:\tunmute, mute\n"); + seq_puts(m, "commands:\tunmute, mute\n"); if (!tp_features.mixer_no_level_control) { - seq_printf(m, "commands:\tup, down\n"); + seq_puts(m, "commands:\tup, down\n"); seq_printf(m, "commands:\tlevel ( is 0-%d)\n", TP_EC_VOLUME_MAX); } @@ -9156,9 +9156,9 @@ static int fan_read(struct seq_file *m) } else if (fan_status_access_mode == TPACPI_FAN_RD_TPEC) { if (status & TP_EC_FAN_FULLSPEED) /* Disengaged mode takes precedence */ - seq_printf(m, "level:\t\tdisengaged\n"); + seq_puts(m, "level:\t\tdisengaged\n"); else if (status & TP_EC_FAN_AUTO) - seq_printf(m, "level:\t\tauto\n"); + seq_puts(m, "level:\t\tauto\n"); else seq_printf(m, "level:\t\t%d\n", status); } @@ -9166,19 +9166,19 @@ static int fan_read(struct seq_file *m) case TPACPI_FAN_NONE: default: - seq_printf(m, "status:\t\tnot supported\n"); + seq_puts(m, "status:\t\tnot supported\n"); } if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { - seq_printf(m, "commands:\tlevel "); + seq_puts(m, "commands:\tlevel "); switch (fan_control_access_mode) { case TPACPI_FAN_WR_ACPI_SFAN: - seq_printf(m, " ( is 0-7)\n"); + seq_puts(m, " ( is 0-7)\n"); break; default: - seq_printf(m, " ( is 0-7, auto, disengaged, full-speed)\n"); + seq_puts(m, " ( is 0-7, auto, disengaged, full-speed)\n"); break; } } @@ -9188,7 +9188,7 @@ static int fan_read(struct seq_file *m) "commands:\twatchdog ( is 0 (off), 1-120 (seconds))\n"); if (fan_control_commands & TPACPI_FAN_CMD_SPEED) - seq_printf(m, "commands:\tspeed ( is 0-65535)\n"); + seq_puts(m, "commands:\tspeed ( is 0-65535)\n"); return 0; } From 6fdc70794cc15b450e3fd750ca048318764a343e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:18:01 +0100 Subject: [PATCH 005/106] platform/x86: acer-wireless: 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/10810967.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/acer-wireless.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/acer-wireless.c b/drivers/platform/x86/acer-wireless.c index 1b5d935d085a..f44d65d97023 100644 --- a/drivers/platform/x86/acer-wireless.c +++ b/drivers/platform/x86/acer-wireless.c @@ -18,8 +18,9 @@ static const struct acpi_device_id acer_wireless_acpi_ids[] = { }; MODULE_DEVICE_TABLE(acpi, acer_wireless_acpi_ids); -static void acer_wireless_notify(struct acpi_device *adev, u32 event) +static void acer_wireless_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *adev = data; struct input_dev *idev = acpi_driver_data(adev); dev_dbg(&adev->dev, "event=%#x\n", event); @@ -36,6 +37,7 @@ static void acer_wireless_notify(struct acpi_device *adev, u32 event) static int acer_wireless_add(struct acpi_device *adev) { struct input_dev *idev; + int ret; idev = devm_input_allocate_device(&adev->dev); if (!idev) @@ -50,7 +52,18 @@ static int acer_wireless_add(struct acpi_device *adev) set_bit(EV_KEY, idev->evbit); set_bit(KEY_RFKILL, idev->keybit); - return input_register_device(idev); + ret = input_register_device(idev); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, + acer_wireless_notify, adev); +} + +static void acer_wireless_remove(struct acpi_device *adev) +{ + acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, + acer_wireless_notify); } static struct acpi_driver acer_wireless_driver = { @@ -59,7 +72,7 @@ static struct acpi_driver acer_wireless_driver = { .ids = acer_wireless_acpi_ids, .ops = { .add = acer_wireless_add, - .notify = acer_wireless_notify, + .remove = acer_wireless_remove, }, }; module_acpi_driver(acer_wireless_driver); From b30a462720ad15613ede9e365938d401ed464095 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:18:52 +0100 Subject: [PATCH 006/106] platform/x86: acer-wireless: 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 Acer wireless ACPI driver to a platform one. After this change, the subordinate input device will be registered under the platform device used for driver binding instead of its ACPI companion. 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/2032103.PYKUYFuaPT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/acer-wireless.c | 41 +++++++++++++++------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/acer-wireless.c b/drivers/platform/x86/acer-wireless.c index f44d65d97023..f464b13a58af 100644 --- a/drivers/platform/x86/acer-wireless.c +++ b/drivers/platform/x86/acer-wireless.c @@ -10,6 +10,7 @@ #include #include #include +#include #include static const struct acpi_device_id acer_wireless_acpi_ids[] = { @@ -20,12 +21,12 @@ MODULE_DEVICE_TABLE(acpi, acer_wireless_acpi_ids); static void acer_wireless_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *adev = data; - struct input_dev *idev = acpi_driver_data(adev); + struct device *dev = data; + struct input_dev *idev = dev_get_drvdata(dev); - dev_dbg(&adev->dev, "event=%#x\n", event); + dev_dbg(dev, "event=%#x\n", event); if (event != 0x80) { - dev_notice(&adev->dev, "Unknown SMKB event: %#x\n", event); + dev_notice(dev, "Unknown SMKB event: %#x\n", event); return; } input_report_key(idev, KEY_RFKILL, 1); @@ -34,16 +35,16 @@ static void acer_wireless_notify(acpi_handle handle, u32 event, void *data) input_sync(idev); } -static int acer_wireless_add(struct acpi_device *adev) +static int acer_wireless_probe(struct platform_device *pdev) { struct input_dev *idev; int ret; - idev = devm_input_allocate_device(&adev->dev); + idev = devm_input_allocate_device(&pdev->dev); if (!idev) return -ENOMEM; - adev->driver_data = idev; + platform_set_drvdata(pdev, idev); idev->name = "Acer Wireless Radio Control"; idev->phys = "acer-wireless/input0"; idev->id.bustype = BUS_HOST; @@ -56,26 +57,28 @@ static int acer_wireless_add(struct acpi_device *adev) if (ret) return ret; - return acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acer_wireless_notify, adev); + return acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, + acer_wireless_notify, + &pdev->dev); } -static void acer_wireless_remove(struct acpi_device *adev) +static void acer_wireless_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acer_wireless_notify); } -static struct acpi_driver acer_wireless_driver = { - .name = "Acer Wireless Radio Control Driver", - .class = "hotkey", - .ids = acer_wireless_acpi_ids, - .ops = { - .add = acer_wireless_add, - .remove = acer_wireless_remove, +static struct platform_driver acer_wireless_driver = { + .probe = acer_wireless_probe, + .remove = acer_wireless_remove, + .driver = { + .name = "Acer Wireless Radio Control Driver", + .acpi_match_table = acer_wireless_acpi_ids, }, }; -module_acpi_driver(acer_wireless_driver); +module_platform_driver(acer_wireless_driver); MODULE_DESCRIPTION("Acer Wireless Radio Control Driver"); MODULE_AUTHOR("Chris Chiu "); From 5eea33b4d301e279abe49cdbd28f8be24e8932a1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:22:08 +0100 Subject: [PATCH 007/106] platform/x86: eeepc-laptop: 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/3681264.iIbC2pHGDl@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/eeepc-laptop.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 974f55e0b36f..626a99a71fce 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1204,9 +1204,10 @@ static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) pr_info("Unknown key %x pressed\n", event); } -static void eeepc_acpi_notify(struct acpi_device *device, u32 event) +static void eeepc_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct eeepc_laptop *eeepc = acpi_driver_data(device); + struct eeepc_laptop *eeepc = data; + struct acpi_device *device = eeepc->device; int old_brightness, new_brightness; u16 count; @@ -1422,9 +1423,16 @@ static int eeepc_acpi_add(struct acpi_device *device) if (result) goto fail_rfkill; + result = acpi_dev_install_notify_handler(device, ACPI_ALL_NOTIFY, + eeepc_acpi_notify, eeepc); + if (result) + goto fail_acpi_notifier; + eeepc_device_present = true; return 0; +fail_acpi_notifier: + eeepc_rfkill_exit(eeepc); fail_rfkill: eeepc_led_exit(eeepc); fail_led: @@ -1444,6 +1452,7 @@ static void eeepc_acpi_remove(struct acpi_device *device) { struct eeepc_laptop *eeepc = acpi_driver_data(device); + acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY, eeepc_acpi_notify); eeepc_backlight_exit(eeepc); eeepc_rfkill_exit(eeepc); eeepc_input_exit(eeepc); @@ -1464,11 +1473,9 @@ static struct acpi_driver eeepc_acpi_driver = { .name = EEEPC_LAPTOP_NAME, .class = EEEPC_ACPI_CLASS, .ids = eeepc_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = eeepc_acpi_add, .remove = eeepc_acpi_remove, - .notify = eeepc_acpi_notify, }, }; From 079b59fd2d79e4f492cba7013c5f60787d573fc8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:22:54 +0100 Subject: [PATCH 008/106] platform/x86: eeepc-laptop: 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 EEEPC laptop ACPI driver to a platform one. 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/3329436.5fSG56mABF@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/eeepc-laptop.c | 32 +++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 626a99a71fce..02a71095920e 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1361,8 +1361,9 @@ static void eeepc_enable_camera(struct eeepc_laptop *eeepc) static bool eeepc_device_present; -static int eeepc_acpi_add(struct acpi_device *device) +static int eeepc_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct eeepc_laptop *eeepc; int result; @@ -1373,9 +1374,10 @@ static int eeepc_acpi_add(struct acpi_device *device) eeepc->handle = device->handle; strscpy(acpi_device_name(device), EEEPC_ACPI_DEVICE_NAME); strscpy(acpi_device_class(device), EEEPC_ACPI_CLASS); - device->driver_data = eeepc; eeepc->device = device; + platform_set_drvdata(pdev, eeepc); + eeepc->hotplug_disabled = hotplug_disabled; eeepc_dmi_check(eeepc); @@ -1448,11 +1450,12 @@ fail_platform: return result; } -static void eeepc_acpi_remove(struct acpi_device *device) +static void eeepc_acpi_remove(struct platform_device *pdev) { - struct eeepc_laptop *eeepc = acpi_driver_data(device); + struct eeepc_laptop *eeepc = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(device, ACPI_ALL_NOTIFY, eeepc_acpi_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_ALL_NOTIFY, eeepc_acpi_notify); eeepc_backlight_exit(eeepc); eeepc_rfkill_exit(eeepc); eeepc_input_exit(eeepc); @@ -1469,13 +1472,12 @@ static const struct acpi_device_id eeepc_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, eeepc_device_ids); -static struct acpi_driver eeepc_acpi_driver = { - .name = EEEPC_LAPTOP_NAME, - .class = EEEPC_ACPI_CLASS, - .ids = eeepc_device_ids, - .ops = { - .add = eeepc_acpi_add, - .remove = eeepc_acpi_remove, +static struct platform_driver eeepc_acpi_driver = { + .probe = eeepc_acpi_probe, + .remove = eeepc_acpi_remove, + .driver = { + .name = EEEPC_LAPTOP_NAME, + .acpi_match_table = eeepc_device_ids, }, }; @@ -1488,7 +1490,7 @@ static int __init eeepc_laptop_init(void) if (result < 0) return result; - result = acpi_bus_register_driver(&eeepc_acpi_driver); + result = platform_driver_register(&eeepc_acpi_driver); if (result < 0) goto fail_acpi_driver; @@ -1500,7 +1502,7 @@ static int __init eeepc_laptop_init(void) return 0; fail_no_device: - acpi_bus_unregister_driver(&eeepc_acpi_driver); + platform_driver_unregister(&eeepc_acpi_driver); fail_acpi_driver: platform_driver_unregister(&platform_driver); return result; @@ -1508,7 +1510,7 @@ fail_acpi_driver: static void __exit eeepc_laptop_exit(void) { - acpi_bus_unregister_driver(&eeepc_acpi_driver); + platform_driver_unregister(&eeepc_acpi_driver); platform_driver_unregister(&platform_driver); } From 163a68a31f743c5a820f348322b9162bc89c720a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:27:33 +0100 Subject: [PATCH 009/106] platform/x86: intel/rst: 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 Intel Rapid Start Technology (rst) ACPI driver to a platform one. 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/3599223.QJadu78ljV@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/rst.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/rst.c b/drivers/platform/x86/intel/rst.c index f3a60e14d4c1..4bd10927aad9 100644 --- a/drivers/platform/x86/intel/rst.c +++ b/drivers/platform/x86/intel/rst.c @@ -5,6 +5,7 @@ #include #include +#include #include MODULE_DESCRIPTION("Intel Rapid Start Technology Driver"); @@ -99,8 +100,9 @@ static struct device_attribute irst_timeout_attr = { .store = irst_store_wakeup_time }; -static int irst_add(struct acpi_device *acpi) +static int irst_probe(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); int error; error = device_create_file(&acpi->dev, &irst_timeout_attr); @@ -114,8 +116,10 @@ static int irst_add(struct acpi_device *acpi) return error; } -static void irst_remove(struct acpi_device *acpi) +static void irst_remove(struct platform_device *pdev) { + struct acpi_device *acpi = ACPI_COMPANION(&pdev->dev); + device_remove_file(&acpi->dev, &irst_wakeup_attr); device_remove_file(&acpi->dev, &irst_timeout_attr); } @@ -125,16 +129,15 @@ static const struct acpi_device_id irst_ids[] = { {"", 0} }; -static struct acpi_driver irst_driver = { - .name = "intel_rapid_start", - .class = "intel_rapid_start", - .ids = irst_ids, - .ops = { - .add = irst_add, - .remove = irst_remove, +static struct platform_driver irst_driver = { + .probe = irst_probe, + .remove = irst_remove, + .driver = { + .name = "intel_rapid_start", + .acpi_match_table = irst_ids, }, }; -module_acpi_driver(irst_driver); +module_platform_driver(irst_driver); MODULE_DEVICE_TABLE(acpi, irst_ids); From 8a44bd3ffdb269c028236d9165ce06a46c091373 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:28:19 +0100 Subject: [PATCH 010/106] platform/x86: intel/smartconnect: 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 Intel Smart Connect disabling ACPI driver to a platform one. 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/24282289.6Emhk5qWAg@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/smartconnect.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/intel/smartconnect.c b/drivers/platform/x86/intel/smartconnect.c index 31019a1a6d5e..4d866b6366d6 100644 --- a/drivers/platform/x86/intel/smartconnect.c +++ b/drivers/platform/x86/intel/smartconnect.c @@ -5,22 +5,24 @@ #include #include +#include MODULE_DESCRIPTION("Intel Smart Connect disabling driver"); MODULE_LICENSE("GPL"); -static int smartconnect_acpi_init(struct acpi_device *acpi) +static int smartconnect_acpi_probe(struct platform_device *pdev) { + acpi_handle handle = ACPI_HANDLE(&pdev->dev); unsigned long long value; acpi_status status; - status = acpi_evaluate_integer(acpi->handle, "GAOS", NULL, &value); + status = acpi_evaluate_integer(handle, "GAOS", NULL, &value); if (ACPI_FAILURE(status)) return -EINVAL; if (value & 0x1) { - dev_info(&acpi->dev, "Disabling Intel Smart Connect\n"); - status = acpi_execute_simple_method(acpi->handle, "SAOS", 0); + dev_info(&pdev->dev, "Disabling Intel Smart Connect\n"); + status = acpi_execute_simple_method(handle, "SAOS", 0); } return 0; @@ -32,13 +34,12 @@ static const struct acpi_device_id smartconnect_ids[] = { }; MODULE_DEVICE_TABLE(acpi, smartconnect_ids); -static struct acpi_driver smartconnect_driver = { - .name = "intel_smart_connect", - .class = "intel_smart_connect", - .ids = smartconnect_ids, - .ops = { - .add = smartconnect_acpi_init, +static struct platform_driver smartconnect_driver = { + .probe = smartconnect_acpi_probe, + .driver = { + .name = "intel_smart_connect", + .acpi_match_table = smartconnect_ids, }, }; -module_acpi_driver(smartconnect_driver); +module_platform_driver(smartconnect_driver); From 1410a228ab2d36fe2b383415a632ae12048d4f3a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Mar 2026 19:54:08 +0100 Subject: [PATCH 011/106] platform/surface: surfacepro3_button: Drop wakeup source on remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The wakeup source added by device_init_wakeup() in surface_button_add() needs to be dropped during driver removal, so update the driver to do that. Fixes: 19351f340765 ("platform/x86: surfacepro3: Support for wakeup from suspend-to-idle") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4368848.1IzOArtZ34@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/surface/surfacepro3_button.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 9bd39f09c7db..a6c9d4d370be 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -242,6 +242,7 @@ static void surface_button_remove(struct acpi_device *device) { struct surface_button *button = acpi_driver_data(device); + device_init_wakeup(&device->dev, false); input_unregister_device(button->input); kfree(button); } From 639d8c601c7a9aab44803245a22f6e3c365b08be Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Mar 2026 19:54:47 +0100 Subject: [PATCH 012/106] platform/surface: surfacepro3_button: Register ACPI notify handler 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/1881356.TLkxdtWsSY@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/surface/surfacepro3_button.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index a6c9d4d370be..6d394daf5bc4 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -72,8 +72,9 @@ struct surface_button { bool suspended; }; -static void surface_button_notify(struct acpi_device *device, u32 event) +static void surface_button_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; struct surface_button *button = acpi_driver_data(device); struct input_dev *input; int key_code = KEY_RESERVED; @@ -227,6 +228,15 @@ static int surface_button_add(struct acpi_device *device) goto err_free_input; device_init_wakeup(&device->dev, true); + + error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + surface_button_notify, device); + if (error) { + device_init_wakeup(&device->dev, false); + input_unregister_device(input); + goto err_free_button; + } + dev_info(&device->dev, "%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); return 0; @@ -242,6 +252,8 @@ static void surface_button_remove(struct acpi_device *device) { struct surface_button *button = acpi_driver_data(device); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + surface_button_notify); device_init_wakeup(&device->dev, false); input_unregister_device(button->input); kfree(button); @@ -257,7 +269,6 @@ static struct acpi_driver surface_button_driver = { .ops = { .add = surface_button_add, .remove = surface_button_remove, - .notify = surface_button_notify, }, .drv.pm = &surface_button_pm, }; From d913a5a12b4036e4219b02777d0a9c70e37a6620 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 4 Mar 2026 19:55:28 +0100 Subject: [PATCH 013/106] platform/surface: surfacepro3_button: Convert to a platform driver 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 Surface Pro 3 button ACPI driver to a platform one. After this change, the subordinate input device and wakeup source class device will be registered under the platform device used for driver binding instead of its ACPI companion. 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/3207406.CbtlEUcBR6@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/surface/surfacepro3_button.c | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/drivers/platform/surface/surfacepro3_button.c b/drivers/platform/surface/surfacepro3_button.c index 6d394daf5bc4..0293bc517b54 100644 --- a/drivers/platform/surface/surfacepro3_button.c +++ b/drivers/platform/surface/surfacepro3_button.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #define SURFACE_PRO3_BUTTON_HID "MSHW0028" @@ -74,8 +75,8 @@ struct surface_button { static void surface_button_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct surface_button *button = acpi_driver_data(device); + struct device *dev = data; + struct surface_button *button = dev_get_drvdata(dev); struct input_dev *input; int key_code = KEY_RESERVED; bool pressed = false; @@ -110,18 +111,17 @@ static void surface_button_notify(acpi_handle handle, u32 event, void *data) key_code = KEY_VOLUMEDOWN; break; case SURFACE_BUTTON_NOTIFY_TABLET_MODE: - dev_warn_once(&device->dev, "Tablet mode is not supported\n"); + dev_warn_once(dev, "Tablet mode is not supported\n"); break; default: - dev_info_ratelimited(&device->dev, - "Unsupported event [0x%x]\n", event); + dev_info_ratelimited(dev, "Unsupported event [0x%x]\n", event); break; } input = button->input; if (key_code == KEY_RESERVED) return; if (pressed) - pm_wakeup_dev_event(&device->dev, 0, button->suspended); + pm_wakeup_dev_event(dev, 0, button->suspended); if (button->suspended) return; input_report_key(input, key_code, pressed?1:0); @@ -131,8 +131,7 @@ static void surface_button_notify(acpi_handle handle, u32 event, void *data) #ifdef CONFIG_PM_SLEEP static int surface_button_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = dev_get_drvdata(dev); button->suspended = true; return 0; @@ -140,8 +139,7 @@ static int surface_button_suspend(struct device *dev) static int surface_button_resume(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = dev_get_drvdata(dev); button->suspended = false; return 0; @@ -156,9 +154,8 @@ static int surface_button_resume(struct device *dev) * Returns true if the driver should bind to this device, i.e. the device is * either MSWH0028 (Pro 3) or MSHW0040 on a Pro 4 or Book 1. */ -static bool surface_button_check_MSHW0040(struct acpi_device *dev) +static bool surface_button_check_MSHW0040(struct device *dev, acpi_handle handle) { - acpi_handle handle = dev->handle; union acpi_object *result; u64 oem_platform_rev = 0; // valid revisions are nonzero @@ -180,14 +177,15 @@ static bool surface_button_check_MSHW0040(struct acpi_device *dev) ACPI_FREE(result); } - dev_dbg(&dev->dev, "OEM Platform Revision %llu\n", oem_platform_rev); + dev_dbg(dev, "OEM Platform Revision %llu\n", oem_platform_rev); return oem_platform_rev == 0; } -static int surface_button_add(struct acpi_device *device) +static int surface_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct surface_button *button; struct input_dev *input; const char *hid = acpi_device_hid(device); @@ -197,14 +195,14 @@ static int surface_button_add(struct acpi_device *device) strlen(SURFACE_BUTTON_OBJ_NAME))) return -ENODEV; - if (!surface_button_check_MSHW0040(device)) + if (!surface_button_check_MSHW0040(&pdev->dev, device->handle)) return -ENODEV; button = kzalloc_obj(struct surface_button); if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); button->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; @@ -217,7 +215,7 @@ static int surface_button_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_set_capability(input, EV_KEY, KEY_POWER); input_set_capability(input, EV_KEY, KEY_LEFTMETA); input_set_capability(input, EV_KEY, KEY_VOLUMEUP); @@ -227,17 +225,17 @@ static int surface_button_add(struct acpi_device *device) if (error) goto err_free_input; - device_init_wakeup(&device->dev, true); + device_init_wakeup(&pdev->dev, true); error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - surface_button_notify, device); + surface_button_notify, &pdev->dev); if (error) { - device_init_wakeup(&device->dev, false); + device_init_wakeup(&pdev->dev, false); input_unregister_device(input); goto err_free_button; } - dev_info(&device->dev, "%s [%s]\n", acpi_device_name(device), + dev_info(&pdev->dev, "%s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); return 0; @@ -248,13 +246,13 @@ static int surface_button_add(struct acpi_device *device) return error; } -static void surface_button_remove(struct acpi_device *device) +static void surface_button_remove(struct platform_device *pdev) { - struct surface_button *button = acpi_driver_data(device); + struct surface_button *button = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, - surface_button_notify); - device_init_wakeup(&device->dev, false); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, surface_button_notify); + device_init_wakeup(&pdev->dev, false); input_unregister_device(button->input); kfree(button); } @@ -262,15 +260,14 @@ static void surface_button_remove(struct acpi_device *device) static SIMPLE_DEV_PM_OPS(surface_button_pm, surface_button_suspend, surface_button_resume); -static struct acpi_driver surface_button_driver = { - .name = "surface_pro3_button", - .class = "SurfacePro3", - .ids = surface_button_device_ids, - .ops = { - .add = surface_button_add, - .remove = surface_button_remove, +static struct platform_driver surface_button_driver = { + .probe = surface_button_probe, + .remove = surface_button_remove, + .driver = { + .name = "surface_pro3_button", + .acpi_match_table = surface_button_device_ids, + .pm = &surface_button_pm, }, - .drv.pm = &surface_button_pm, }; -module_acpi_driver(surface_button_driver); +module_platform_driver(surface_button_driver); From 9b0ee949f7940786b0292329c2ae1f5dd63e69ba Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Thu, 5 Mar 2026 15:09:13 +0100 Subject: [PATCH 014/106] platform/x86: dell-wmi-sysman: Use sysfs_emit{_at} in show functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace sprintf() with sysfs_emit() and sysfs_emit_at() in sysfs show functions. sysfs_emit() and sysfs_emit_at() are preferred for formatting sysfs output because they provide safer bounds checking. In reset_bios_show(), use sysfs_emit_at() to avoid manual buffer size accounting. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260305140912.258090-2-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 6241f16fd3da..069cf958a90d 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include "dell-wmi-sysman.h" #include "../../firmware_attributes_class.h" @@ -143,17 +144,17 @@ int map_wmi_error(int error_code) */ static ssize_t reset_bios_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - char *start = buf; + ssize_t len = 0; int i; for (i = 0; i < MAX_TYPES; i++) { if (i == reset_option) - buf += sprintf(buf, "[%s] ", reset_types[i]); + len += sysfs_emit_at(buf, len, "[%s] ", reset_types[i]); else - buf += sprintf(buf, "%s ", reset_types[i]); + len += sysfs_emit_at(buf, len, "%s ", reset_types[i]); } - buf += sprintf(buf, "\n"); - return buf-start; + len += sysfs_emit_at(buf, len, "\n"); + return len; } /** @@ -194,7 +195,7 @@ static ssize_t reset_bios_store(struct kobject *kobj, static ssize_t pending_reboot_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", wmi_priv.pending_changes); + return sysfs_emit(buf, "%d\n", wmi_priv.pending_changes); } static struct kobj_attribute reset_bios = __ATTR_RW(reset_bios); From 9a98ebe630cf13c1a6063afa676d1cecc44fb2c9 Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Wed, 11 Mar 2026 23:31:42 +0900 Subject: [PATCH 015/106] input: trackpoint - Enable doubletap by default on capable devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable doubletap functionality by default on TrackPoint devices that support it. The feature is detected using firmware ID pattern matching (PNP: LEN03xxx) with a deny list of incompatible devices. This provides immediate doubletap functionality without requiring userspace configuration. The hardware is enabled during device detection, while event filtering continues to be handled by the thinkpad_acpi driver as before. Signed-off-by: Vishnu Sankar Suggested-by: Mark Pearson Acked-by: Dmitry Torokhov Link: https://patch.msgid.link/20260311143144.482145-2-vishnuocv@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/input/mouse/trackpoint.c | 46 ++++++++++++++++++++++++++++++++ drivers/input/mouse/trackpoint.h | 5 ++++ 2 files changed, 51 insertions(+) diff --git a/drivers/input/mouse/trackpoint.c b/drivers/input/mouse/trackpoint.c index b06c7ad721fe..3bd8fdf56cd3 100644 --- a/drivers/input/mouse/trackpoint.c +++ b/drivers/input/mouse/trackpoint.c @@ -5,6 +5,7 @@ * Trademarks are the property of their respective owners. */ +#include #include #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #include #include "psmouse.h" #include "trackpoint.h" @@ -393,6 +395,44 @@ static int trackpoint_reconnect(struct psmouse *psmouse) return 0; } +/* List of known incapable device PNP IDs */ +static const char * const dt_incompatible_devices[] = { + "LEN0304", + "LEN0306", + "LEN0317", + "LEN031A", + "LEN031B", + "LEN031C", + "LEN031D", +}; + +/* + * Checks if it's a doubletap capable device. + * The PNP ID format is "PNP: LEN030d PNP0f13". + */ +static bool trackpoint_is_dt_capable(const char *pnp_id) +{ + size_t i; + + if (!pnp_id) + return false; + + /* Must start with "PNP: LEN03" */ + if (!strstarts(pnp_id, "PNP: LEN03")) + return false; + + /* Ensure enough length before comparing */ + if (strlen(pnp_id) < 12) + return false; + + /* Check deny-list */ + for (i = 0; i < ARRAY_SIZE(dt_incompatible_devices); i++) { + if (!strncmp(pnp_id + 5, dt_incompatible_devices[i], 7)) + return false; + } + return true; +} + int trackpoint_detect(struct psmouse *psmouse, bool set_properties) { struct ps2dev *ps2dev = &psmouse->ps2dev; @@ -470,6 +510,12 @@ int trackpoint_detect(struct psmouse *psmouse, bool set_properties) psmouse->vendor, firmware_id, (button_info & 0xf0) >> 4, button_info & 0x0f); + if (trackpoint_is_dt_capable(ps2dev->serio->firmware_id)) { + error = trackpoint_write(ps2dev, TP_DOUBLETAP, TP_DOUBLETAP_ENABLE); + if (error) + psmouse_warn(psmouse, "Failed to enable doubletap: %d\n", error); + } + return 0; } diff --git a/drivers/input/mouse/trackpoint.h b/drivers/input/mouse/trackpoint.h index eb5412904fe0..3e03cdb39449 100644 --- a/drivers/input/mouse/trackpoint.h +++ b/drivers/input/mouse/trackpoint.h @@ -69,6 +69,8 @@ /* (how hard it is to drag */ /* with Z-axis pressed) */ +#define TP_DOUBLETAP 0x58 /* TrackPoint doubletap register */ + #define TP_MINDRAG 0x59 /* Minimum amount of force needed */ /* to trigger dragging */ @@ -110,6 +112,9 @@ external device will be forced to 1 */ #define TP_MASK_EXT_TAG 0x04 +/* Doubletap register values */ +#define TP_DOUBLETAP_ENABLE 0xFF /* Enable value */ +#define TP_DOUBLETAP_DISABLE 0xFE /* Disable value */ /* Power on Self Test Results */ #define TP_POR_SUCCESS 0x3B From 6227cc32fa01ffbf5bef8dcc6759743a28a2ad57 Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Wed, 11 Mar 2026 23:31:43 +0900 Subject: [PATCH 016/106] platform/x86: thinkpad_acpi: Add sysfs control for TrackPoint double-tap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a sysfs attribute to enable or disable TrackPoint double-tap hotkey events at the kernel level. The TrackPoint firmware enables double-tap support automatically. This interface allows userspace to control whether double-tap events are forwarded to userspace. The attribute is available at: /sys/devices/platform/thinkpad_acpi/doubletap_enable 0 - Disable double-tap hotkey events 1 - Enable double-tap hotkey events (default) Filtering is implemented by suppressing ACPI hotkey delivery without injecting synthetic input events. Signed-off-by: Vishnu Sankar Suggested-by: Mark Pearson Link: https://patch.msgid.link/20260311143144.482145-3-vishnuocv@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 42 +++++++++++++++++---- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 17590f939060..e6301d78ca1e 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -374,7 +374,7 @@ static struct { u32 hotkey_poll_active:1; u32 has_adaptive_kbd:1; u32 kbd_lang:1; - u32 trackpoint_doubletap:1; + u32 trackpoint_doubletap_enable:1; struct quirk_entry *quirks; } tp_features; @@ -3019,6 +3019,31 @@ static const struct attribute_group adaptive_kbd_attr_group = { .attrs = adaptive_kbd_attributes, }; +/* sysfs doubletap enable --------------------------------------------- */ +static ssize_t doubletap_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return sysfs_emit(buf, "%d\n", tp_features.trackpoint_doubletap_enable); +} + +static ssize_t doubletap_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool enable; + int err; + + err = kstrtobool(buf, &enable); + if (err) + return err; + + tp_features.trackpoint_doubletap_enable = enable; + return count; +} + +static DEVICE_ATTR_RW(doubletap_enable); + /* --------------------------------------------------------------------- */ static struct attribute *hotkey_attributes[] = { @@ -3033,6 +3058,7 @@ static struct attribute *hotkey_attributes[] = { &dev_attr_hotkey_recommended_mask.attr, &dev_attr_hotkey_tablet_mode.attr, &dev_attr_hotkey_radio_sw.attr, + &dev_attr_doubletap_enable.attr, #ifdef CONFIG_THINKPAD_ACPI_HOTKEY_POLL &dev_attr_hotkey_source_mask.attr, &dev_attr_hotkey_poll_freq.attr, @@ -3558,8 +3584,8 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) hotkey_poll_setup_safe(true); - /* Enable doubletap by default */ - tp_features.trackpoint_doubletap = 1; + /* Enable TrackPoint doubletap event reporting by default. */ + tp_features.trackpoint_doubletap_enable = 1; return 0; } @@ -3864,9 +3890,9 @@ static bool hotkey_notify_8xxx(const u32 hkey, bool *send_acpi_ev) { switch (hkey) { case TP_HKEY_EV_TRACK_DOUBLETAP: - if (tp_features.trackpoint_doubletap) - tpacpi_input_send_key(hkey, send_acpi_ev); - + /* Only send event if doubletap is enabled */ + if (!tp_features.trackpoint_doubletap_enable) + *send_acpi_ev = false; return true; default: return false; @@ -11486,7 +11512,9 @@ static bool tpacpi_driver_event(const unsigned int hkey_event) mutex_unlock(&tpacpi_inputdev_send_mutex); return true; case TP_HKEY_EV_DOUBLETAP_TOGGLE: - tp_features.trackpoint_doubletap = !tp_features.trackpoint_doubletap; + /* Toggle kernel-level doubletap event filtering */ + tp_features.trackpoint_doubletap_enable = + !tp_features.trackpoint_doubletap_enable; return true; case TP_HKEY_EV_PROFILE_TOGGLE: case TP_HKEY_EV_PROFILE_TOGGLE2: From fa5062e99b984448b7c8ca9aea47e7fc033b6e2f Mon Sep 17 00:00:00 2001 From: Vishnu Sankar Date: Wed, 11 Mar 2026 23:31:44 +0900 Subject: [PATCH 017/106] Documentation: thinkpad-acpi - Document doubletap_enable attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document the doubletap_enable sysfs attribute for ThinkPad ACPI driver. Signed-off-by: Vishnu Sankar Link: https://patch.msgid.link/20260311143144.482145-4-vishnuocv@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../admin-guide/laptops/thinkpad-acpi.rst | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/admin-guide/laptops/thinkpad-acpi.rst b/Documentation/admin-guide/laptops/thinkpad-acpi.rst index 03951ed6b628..f874db31801d 100644 --- a/Documentation/admin-guide/laptops/thinkpad-acpi.rst +++ b/Documentation/admin-guide/laptops/thinkpad-acpi.rst @@ -1522,6 +1522,27 @@ Currently 2 antenna types are supported as mentioned below: The property is read-only. If the platform doesn't have support the sysfs class is not created. +doubletap_enable +---------------- + +sysfs: doubletap_enable + +Controls whether TrackPoint doubletap events are filtered out. Doubletap is a +feature where quickly tapping the TrackPoint twice triggers a special function key event. + +The available commands are:: + + cat /sys/devices/platform/thinkpad_acpi/doubletap_enable + echo 1 | sudo tee /sys/devices/platform/thinkpad_acpi/doubletap_enable + echo 0 | sudo tee /sys/devices/platform/thinkpad_acpi/doubletap_enable + +Values: + + * 1 - doubletap events are processed (default) + * 0 - doubletap events are filtered out (ignored) + + This setting can also be toggled via the Fn+doubletap hotkey. + Auxmac ------ From 904b333fc51cc045941df9656302449a0fc9978e Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:40 -0700 Subject: [PATCH 018/106] platform/x86/intel/vsec: Refactor base_addr handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The base_addr field in intel_vsec_platform_info was originally added to support devices that emulate PCI VSEC capabilities in MMIO. Previously, the code would check at registration time whether base_addr was set, falling back to the PCI BAR if not. Refactor this by making base_addr an explicit function parameter. This clarifies ownership of the value and removes conditional logic from intel_vsec_add_dev(). It also enables making intel_vsec_platform_info const in a later patch, since the function no longer needs to write to info->base_addr. No functional change intended. Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-2-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 5059d320edf8..46966edca03b 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -271,14 +271,13 @@ EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, struct intel_vsec_platform_info *info, - unsigned long cap_id) + 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 *tmp; struct device *parent; unsigned long quirks = info->quirks; - u64 base_addr; int i; if (info->parent) @@ -310,11 +309,6 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (quirks & VSEC_QUIRK_TABLE_SHIFT) header->offset >>= TABLE_OFFSET_SHIFT; - if (info->base_addr) - base_addr = info->base_addr; - else - base_addr = pdev->resource[header->tbir].start; - /* * The DVSEC/VSEC contains the starting offset and count for a block of * discovery tables. Create a resource array of these tables to the @@ -412,7 +406,8 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) static int intel_vsec_register_device(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info) + struct intel_vsec_platform_info *info, + u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; struct vsec_priv *priv; @@ -428,7 +423,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, * For others using the exported APIs, add the device directly. */ if (!pci_match_id(intel_vsec_pci_ids, pdev)) - return intel_vsec_add_dev(pdev, header, info, cap_id); + return intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); priv = pci_get_drvdata(pdev); if (priv->state[cap_id] == STATE_REGISTERED || @@ -444,7 +439,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, consumer_deps = get_consumer_dependencies(priv, cap_id); if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { - ret = intel_vsec_add_dev(pdev, header, info, cap_id); + ret = intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); if (ret) priv->state[cap_id] = STATE_SKIP; else @@ -464,7 +459,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(pdev, *header, info); + ret = intel_vsec_register_device(pdev, *header, info, info->base_addr); if (!ret) have_devices = true; } @@ -512,7 +507,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -557,7 +553,8 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_register_device(pdev, &header, info); + ret = intel_vsec_register_device(pdev, &header, info, + pci_resource_start(pdev, header.tbir)); if (ret) continue; From 9577c74c96f88d807d1ba005adbf5952e7127e55 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:41 -0700 Subject: [PATCH 019/106] platform/x86/intel/vsec: Make driver_data info const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Treat PCI id->driver_data (intel_vsec_platform_info) as read-only by making vsec_priv->info a const pointer and updating all function signatures to accept const intel_vsec_platform_info *. This improves const-correctness and clarifies that the platform info data from the driver_data table is not meant to be modified at runtime. No functional changes intended. Signed-off-by: David E. Box Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-3-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 20 ++++++++++---------- include/linux/intel_vsec.h | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 46966edca03b..e0096be605d9 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -42,7 +42,7 @@ enum vsec_device_state { }; struct vsec_priv { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct device *suppliers[VSEC_FEATURE_COUNT]; struct oobmsm_plat_info plat_info; enum vsec_device_state state[VSEC_FEATURE_COUNT]; @@ -270,7 +270,7 @@ cleanup_aux: EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info, + const struct intel_vsec_platform_info *info, unsigned long cap_id, u64 base_addr) { struct intel_vsec_device __free(kfree) *intel_vsec_dev = NULL; @@ -406,7 +406,7 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) static int intel_vsec_register_device(struct pci_dev *pdev, struct intel_vsec_header *header, - struct intel_vsec_platform_info *info, + const struct intel_vsec_platform_info *info, u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; @@ -452,7 +452,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, } static bool intel_vsec_walk_header(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; bool have_devices = false; @@ -468,7 +468,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, } static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -519,7 +519,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, } static bool intel_vsec_walk_vsec(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -565,7 +565,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, } int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { if (!pdev || !info || !info->headers) return -EINVAL; @@ -578,7 +578,7 @@ int intel_vsec_register(struct pci_dev *pdev, EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); static bool intel_vsec_get_features(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { bool found = false; @@ -622,7 +622,7 @@ static void intel_vsec_skip_missing_dependencies(struct pci_dev *pdev) static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - struct intel_vsec_platform_info *info; + const struct intel_vsec_platform_info *info; struct vsec_priv *priv; int num_caps, ret; int run_once = 0; @@ -633,7 +633,7 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id return ret; pci_save_state(pdev); - info = (struct intel_vsec_platform_info *)id->driver_data; + info = (const struct intel_vsec_platform_info *)id->driver_data; if (!info) return -EINVAL; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 1a0f357c2427..d551174b0049 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -200,13 +200,13 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device #if IS_ENABLED(CONFIG_INTEL_VSEC) int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info); + const struct intel_vsec_platform_info *info); int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, struct intel_vsec_device *vsec_dev); struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); #else static inline int intel_vsec_register(struct pci_dev *pdev, - struct intel_vsec_platform_info *info) + const struct intel_vsec_platform_info *info) { return -ENODEV; } From c62fd96a04e4a7b847448f97ecfe9f3fe706e7b3 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:42 -0700 Subject: [PATCH 020/106] platform/x86/intel/vsec: Decouple add/link helpers from PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This refactor prepares for adding ACPI-enumerated PMT endpoints. While intel_vsec is bound to PCI today, some helpers are used by code that will also register PMT endpoints from non-PCI (ACPI) paths. Clean up PCI-specific plumbing where it isn’t strictly required and rely on generic struct device where possible. Signed-off-by: David E. Box Reviewed-by: Ilpo Järvinen Reviewed-by: Michael J. Ruhl Link: https://patch.msgid.link/20260313015202.3660072-4-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 13 +++++++++---- drivers/platform/x86/intel/vsec_tpmi.c | 2 +- include/linux/intel_vsec.h | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index e0096be605d9..938648b9ef09 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -158,18 +158,23 @@ static bool vsec_driver_present(int cap_id) */ static const struct pci_device_id intel_vsec_pci_ids[]; -static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, +static int intel_vsec_link_devices(struct device *parent, struct device *dev, int consumer_id) { const struct vsec_feature_dependency *deps; enum vsec_device_state *state; struct device **suppliers; struct vsec_priv *priv; + struct pci_dev *pdev; int supplier_id; if (!consumer_id) return 0; + if (!dev_is_pci(parent)) + return 0; + + pdev = to_pci_dev(parent); if (!pci_match_id(intel_vsec_pci_ids, pdev)) return 0; @@ -204,7 +209,7 @@ static int intel_vsec_link_devices(struct pci_dev *pdev, struct device *dev, return 0; } -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name) { @@ -252,7 +257,7 @@ int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, if (ret) goto cleanup_aux; - ret = intel_vsec_link_devices(pdev, &auxdev->dev, intel_vsec_dev->cap_id); + ret = intel_vsec_link_devices(parent, &auxdev->dev, intel_vsec_dev->cap_id); if (ret) goto cleanup_aux; @@ -343,7 +348,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he * Pass the ownership of intel_vsec_dev and resource within it to * intel_vsec_add_aux() */ - return intel_vsec_add_aux(pdev, parent, no_free_ptr(intel_vsec_dev), + return intel_vsec_add_aux(parent, no_free_ptr(intel_vsec_dev), intel_vsec_name(header->id)); } diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 98846e88d3d0..2298b6361094 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -655,7 +655,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, * feature_vsec_dev and res memory are also freed as part of * device deletion. */ - return intel_vsec_add_aux(vsec_dev->pcidev, &vsec_dev->auxdev.dev, + return intel_vsec_add_aux(&vsec_dev->auxdev.dev, feature_vsec_dev, feature_id_name); } diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index d551174b0049..49a746ec0128 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -184,7 +184,7 @@ struct pmt_feature_group { struct telemetry_region regions[]; }; -int intel_vsec_add_aux(struct pci_dev *pdev, struct device *parent, +int intel_vsec_add_aux(struct device *parent, struct intel_vsec_device *intel_vsec_dev, const char *name); From 353042d54d82f6c46449f0ee38c244b5a13c1fe4 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:43 -0700 Subject: [PATCH 021/106] platform/x86/intel/vsec: Switch exported helpers from pci_dev to device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preparatory refactor for ACPI-enumerated PMT endpoints. Several exported PMT/VSEC interfaces and structs carried struct pci_dev * even though callers only need a generic struct device. Move those to struct device * so the same APIs work for PCI and ACPI parents. Acked-by: Rodrigo Vivi Signed-off-by: David E. Box Link: https://patch.msgid.link/20260313015202.3660072-5-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/gpu/drm/xe/xe_debugfs.c | 2 +- drivers/gpu/drm/xe/xe_hwmon.c | 2 +- drivers/gpu/drm/xe/xe_vsec.c | 7 ++- drivers/gpu/drm/xe/xe_vsec.h | 4 +- drivers/platform/x86/intel/pmc/core.c | 4 +- .../platform/x86/intel/pmc/ssram_telemetry.c | 2 +- drivers/platform/x86/intel/pmt/class.c | 8 ++-- drivers/platform/x86/intel/pmt/class.h | 5 ++- drivers/platform/x86/intel/pmt/discovery.c | 4 +- drivers/platform/x86/intel/pmt/telemetry.c | 13 +++--- drivers/platform/x86/intel/pmt/telemetry.h | 12 ++--- drivers/platform/x86/intel/sdsi.c | 5 ++- drivers/platform/x86/intel/vsec.c | 44 +++++++++++-------- drivers/platform/x86/intel/vsec_tpmi.c | 6 +-- include/linux/intel_vsec.h | 13 +++--- 15 files changed, 71 insertions(+), 60 deletions(-) diff --git a/drivers/gpu/drm/xe/xe_debugfs.c b/drivers/gpu/drm/xe/xe_debugfs.c index 844cfafe1ec7..ad2d8f179eb6 100644 --- a/drivers/gpu/drm/xe/xe_debugfs.c +++ b/drivers/gpu/drm/xe/xe_debugfs.c @@ -45,7 +45,7 @@ static void read_residency_counter(struct xe_device *xe, struct xe_mmio *mmio, u64 residency = 0; int ret; - ret = xe_pmt_telem_read(to_pci_dev(xe->drm.dev), + ret = xe_pmt_telem_read(xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &residency, offset, sizeof(residency)); if (ret != sizeof(residency)) { diff --git a/drivers/gpu/drm/xe/xe_hwmon.c b/drivers/gpu/drm/xe/xe_hwmon.c index 0fd4d4f1014a..92e423a339f1 100644 --- a/drivers/gpu/drm/xe/xe_hwmon.c +++ b/drivers/gpu/drm/xe/xe_hwmon.c @@ -506,7 +506,7 @@ xe_hwmon_energy_get(struct xe_hwmon *hwmon, int channel, long *energy) if (hwmon->xe->info.platform == XE_BATTLEMAGE) { u64 pmt_val; - ret = xe_pmt_telem_read(to_pci_dev(hwmon->xe->drm.dev), + ret = xe_pmt_telem_read(hwmon->xe->drm.dev, xe_mmio_read32(mmio, PUNIT_TELEMETRY_GUID), &pmt_val, BMG_ENERGY_STATUS_PMT_OFFSET, sizeof(pmt_val)); if (ret != sizeof(pmt_val)) { diff --git a/drivers/gpu/drm/xe/xe_vsec.c b/drivers/gpu/drm/xe/xe_vsec.c index 4ebb4dbe1c9b..a9baf0bfe572 100644 --- a/drivers/gpu/drm/xe/xe_vsec.c +++ b/drivers/gpu/drm/xe/xe_vsec.c @@ -140,10 +140,10 @@ static int xe_guid_decode(u32 guid, int *index, u32 *offset) return 0; } -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count) { - struct xe_device *xe = pdev_to_xe_device(pdev); + struct xe_device *xe = kdev_to_xe_device(dev); void __iomem *telem_addr = xe->mmio.regs + BMG_TELEMETRY_OFFSET; u32 mem_region; u32 offset; @@ -198,7 +198,6 @@ void xe_vsec_init(struct xe_device *xe) { struct intel_vsec_platform_info *info; struct device *dev = xe->drm.dev; - struct pci_dev *pdev = to_pci_dev(dev); enum xe_vsec platform; platform = get_platform_info(xe); @@ -221,6 +220,6 @@ void xe_vsec_init(struct xe_device *xe) * Register a VSEC. Cleanup is handled using device managed * resources. */ - intel_vsec_register(pdev, info); + intel_vsec_register(dev, info); } MODULE_IMPORT_NS("INTEL_VSEC"); diff --git a/drivers/gpu/drm/xe/xe_vsec.h b/drivers/gpu/drm/xe/xe_vsec.h index dabfb4e02d70..a25b4e6e681b 100644 --- a/drivers/gpu/drm/xe/xe_vsec.h +++ b/drivers/gpu/drm/xe/xe_vsec.h @@ -6,10 +6,10 @@ #include -struct pci_dev; +struct device; struct xe_device; void xe_vsec_init(struct xe_device *xe); -int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, u32 count); +int xe_pmt_telem_read(struct device *dev, u32 guid, u64 *data, loff_t user_offset, u32 count); #endif diff --git a/drivers/platform/x86/intel/pmc/core.c b/drivers/platform/x86/intel/pmc/core.c index 02b303418d18..d91e1ab842d6 100644 --- a/drivers/platform/x86/intel/pmc/core.c +++ b/drivers/platform/x86/intel/pmc/core.c @@ -1315,7 +1315,7 @@ static struct telem_endpoint *pmc_core_register_endpoint(struct pci_dev *pcidev, unsigned int i; for (i = 0; guids[i]; i++) { - ep = pmt_telem_find_and_register_endpoint(pcidev, guids[i], 0); + ep = pmt_telem_find_and_register_endpoint(&pcidev->dev, guids[i], 0); if (!IS_ERR(ep)) return ep; } @@ -1600,7 +1600,7 @@ static int pmc_core_get_telem_info(struct pmc_dev *pmcdev, struct pmc_dev_info * if (!pmc->map->lpm_req_guid) return -ENXIO; - ep = pmt_telem_find_and_register_endpoint(pcidev, pmc->map->lpm_req_guid, 0); + 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); return -EPROBE_DEFER; diff --git a/drivers/platform/x86/intel/pmc/ssram_telemetry.c b/drivers/platform/x86/intel/pmc/ssram_telemetry.c index 03fad9331fc0..6f6e83e70fc5 100644 --- a/drivers/platform/x86/intel/pmc/ssram_telemetry.c +++ b/drivers/platform/x86/intel/pmc/ssram_telemetry.c @@ -60,7 +60,7 @@ pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem info.base_addr = ssram_base; info.parent = &pcidev->dev; - return intel_vsec_register(pcidev, &info); + return intel_vsec_register(&pcidev->dev, &info); } static inline u64 get_base(void __iomem *addr, u32 offset) diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index be3c8d9e4fff..b4c9964df807 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -60,11 +60,11 @@ pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) return count; } -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count) { if (cb && cb->read_telem) - return cb->read_telem(pdev, guid, buf, off, count); + return cb->read_telem(dev, guid, buf, off, count); addr += off; @@ -99,7 +99,7 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, if (count > entry->size - off) count = entry->size - off; - count = pmt_telem_read_mmio(entry->pcidev, entry->cb, entry->header.guid, buf, + count = pmt_telem_read_mmio(entry->ep->dev, entry->cb, entry->header.guid, buf, entry->base, off, count); return count; @@ -208,7 +208,7 @@ static int intel_pmt_populate_entry(struct intel_pmt_entry *entry, struct intel_vsec_device *ivdev, struct resource *disc_res) { - struct pci_dev *pci_dev = ivdev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(ivdev->dev); struct device *dev = &ivdev->auxdev.dev; struct intel_pmt_header *header = &entry->header; u8 bir; diff --git a/drivers/platform/x86/intel/pmt/class.h b/drivers/platform/x86/intel/pmt/class.h index 3c5ad5f52bca..1ae56a5baad2 100644 --- a/drivers/platform/x86/intel/pmt/class.h +++ b/drivers/platform/x86/intel/pmt/class.h @@ -19,11 +19,12 @@ #define GET_BIR(v) ((v) & GENMASK(2, 0)) #define GET_ADDRESS(v) ((v) & GENMASK(31, 3)) +struct device; struct pci_dev; extern struct class intel_pmt_class; struct telem_endpoint { - struct pci_dev *pcidev; + struct device *dev; struct telem_header header; struct pmt_callbacks *cb; void __iomem *base; @@ -65,7 +66,7 @@ struct intel_pmt_namespace { struct intel_pmt_entry *entry); }; -int pmt_telem_read_mmio(struct pci_dev *pdev, struct pmt_callbacks *cb, u32 guid, void *buf, +int pmt_telem_read_mmio(struct device *dev, struct pmt_callbacks *cb, u32 guid, void *buf, void __iomem *addr, loff_t off, u32 count); bool intel_pmt_is_early_client_hw(struct device *dev); int intel_pmt_dev_create(struct intel_pmt_entry *entry, diff --git a/drivers/platform/x86/intel/pmt/discovery.c b/drivers/platform/x86/intel/pmt/discovery.c index e500aa327d23..c482368bfaae 100644 --- a/drivers/platform/x86/intel/pmt/discovery.c +++ b/drivers/platform/x86/intel/pmt/discovery.c @@ -542,7 +542,7 @@ static int pmt_features_probe(struct auxiliary_device *auxdev, const struct auxi if (!priv) return -ENOMEM; - priv->parent = &ivdev->pcidev->dev; + priv->parent = ivdev->dev; auxiliary_set_drvdata(auxdev, priv); priv->dev = device_create(&intel_pmt_class, &auxdev->dev, MKDEV(0, 0), priv, @@ -609,7 +609,7 @@ void intel_pmt_get_features(struct intel_pmt_entry *entry) mutex_lock(&feature_list_lock); list_for_each_entry(feature, &pmt_feature_list, list) { - if (feature->priv->parent != &entry->ep->pcidev->dev) + if (feature->priv->parent != entry->ep->dev) continue; pmt_get_features(entry, feature); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index a52803bfe124..bdc7c24a3678 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -112,7 +112,7 @@ static int pmt_telem_add_endpoint(struct intel_vsec_device *ivdev, return -ENOMEM; ep = entry->ep; - ep->pcidev = ivdev->pcidev; + ep->dev = ivdev->dev; ep->header.access_type = entry->header.access_type; ep->header.guid = entry->header.guid; ep->header.base_offset = entry->header.base_offset; @@ -204,7 +204,7 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info) goto unlock; } - info->pdev = entry->ep->pcidev; + info->dev = entry->ep->dev; info->header = entry->ep->header; unlock: @@ -218,9 +218,10 @@ static int pmt_copy_region(struct telemetry_region *region, struct intel_pmt_entry *entry) { + struct pci_dev *pdev = to_pci_dev(entry->ep->dev); struct oobmsm_plat_info *plat_info; - plat_info = intel_vsec_get_mapping(entry->ep->pcidev); + plat_info = intel_vsec_get_mapping(pdev); if (IS_ERR(plat_info)) return PTR_ERR(plat_info); @@ -308,7 +309,7 @@ int pmt_telem_read(struct telem_endpoint *ep, u32 id, u64 *data, u32 count) if (offset + NUM_BYTES_QWORD(count) > size) return -EINVAL; - pmt_telem_read_mmio(ep->pcidev, ep->cb, ep->header.guid, data, ep->base, offset, + pmt_telem_read_mmio(ep->dev, ep->cb, ep->header.guid, data, ep->base, offset, NUM_BYTES_QWORD(count)); return ep->present ? 0 : -EPIPE; @@ -335,7 +336,7 @@ int pmt_telem_read32(struct telem_endpoint *ep, u32 id, u32 *data, u32 count) EXPORT_SYMBOL_NS_GPL(pmt_telem_read32, "INTEL_PMT_TELEMETRY"); struct telem_endpoint * -pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos) { int devid = 0; int inst = 0; @@ -348,7 +349,7 @@ pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, u32 guid, u16 pos) if (err) return ERR_PTR(err); - if (ep_info.header.guid == guid && ep_info.pdev == pcidev) { + if (ep_info.header.guid == guid && ep_info.dev == dev) { if (inst == pos) return pmt_telem_register_endpoint(devid); ++inst; diff --git a/drivers/platform/x86/intel/pmt/telemetry.h b/drivers/platform/x86/intel/pmt/telemetry.h index d45af5512b4e..0f88c5e7d90e 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.h +++ b/drivers/platform/x86/intel/pmt/telemetry.h @@ -6,8 +6,8 @@ #define PMT_TELEM_TELEMETRY 0 #define PMT_TELEM_CRASHLOG 1 +struct device; struct telem_endpoint; -struct pci_dev; struct telem_header { u8 access_type; @@ -17,7 +17,7 @@ struct telem_header { }; struct telem_endpoint_info { - struct pci_dev *pdev; + struct device *dev; struct telem_header header; }; @@ -71,8 +71,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); /** * pmt_telem_find_and_register_endpoint() - Get a telemetry endpoint from - * pci_dev device, guid and pos - * @pdev: PCI device inside the Intel vsec + * device, guid and pos + * @dev: device inside the Intel vsec * @guid: GUID of the telemetry space * @pos: Instance of the guid * @@ -80,8 +80,8 @@ int pmt_telem_get_endpoint_info(int devid, struct telem_endpoint_info *info); * * endpoint - On success returns pointer to the telemetry endpoint * * -ENXIO - telemetry endpoint not found */ -struct telem_endpoint *pmt_telem_find_and_register_endpoint(struct pci_dev *pcidev, - u32 guid, u16 pos); +struct telem_endpoint * +pmt_telem_find_and_register_endpoint(struct device *dev, u32 guid, u16 pos); /** * pmt_telem_read() - Read qwords from counter sram using sample id diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index da75f53d0bcc..d7e37d4ace23 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -599,13 +599,14 @@ static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table) return 0; } -static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent, +static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct device *dev, struct disc_table *disc_table, struct resource *disc_res) { u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info); u32 size = FIELD_GET(DT_SIZE, disc_table->access_info); u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset); u32 offset = DT_OFFSET(disc_table->offset); + struct pci_dev *parent = to_pci_dev(dev); struct resource res = {}; /* Starting location of SDSi MMIO region based on access type */ @@ -681,7 +682,7 @@ static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_de return ret; /* Map the SDSi mailbox registers */ - ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res); + ret = sdsi_map_mbox_registers(priv, intel_cap_dev->dev, &disc_table, disc_res); if (ret) return ret; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 938648b9ef09..a547e4b98245 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -274,7 +274,7 @@ cleanup_aux: } EXPORT_SYMBOL_NS_GPL(intel_vsec_add_aux, "INTEL_VSEC"); -static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, +static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *header, const struct intel_vsec_platform_info *info, unsigned long cap_id, u64 base_addr) { @@ -288,18 +288,18 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he if (info->parent) parent = info->parent; else - parent = &pdev->dev; + parent = dev; if (!intel_vsec_supported(header->id, info->caps)) return -EINVAL; if (!header->num_entries) { - dev_dbg(&pdev->dev, "Invalid 0 entry count for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry count for header id %d\n", header->id); return -EINVAL; } if (!header->entry_size) { - dev_dbg(&pdev->dev, "Invalid 0 entry size for header id %d\n", header->id); + dev_dbg(dev, "Invalid 0 entry size for header id %d\n", header->id); return -EINVAL; } @@ -331,7 +331,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he release_mem_region(tmp->start, resource_size(tmp)); } - intel_vsec_dev->pcidev = pdev; + 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; @@ -409,13 +409,14 @@ static int get_cap_id(u32 header_id, unsigned long *cap_id) return 0; } -static int intel_vsec_register_device(struct pci_dev *pdev, +static int intel_vsec_register_device(struct device *dev, struct intel_vsec_header *header, const struct intel_vsec_platform_info *info, u64 base_addr) { const struct vsec_feature_dependency *consumer_deps; struct vsec_priv *priv; + struct pci_dev *pdev; unsigned long cap_id; int ret; @@ -427,8 +428,12 @@ static int intel_vsec_register_device(struct pci_dev *pdev, * Only track dependencies for devices probed by the VSEC driver. * For others using the exported APIs, add the device directly. */ + if (!dev_is_pci(dev)) + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); + + pdev = to_pci_dev(dev); if (!pci_match_id(intel_vsec_pci_ids, pdev)) - return intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); + return intel_vsec_add_dev(dev, header, info, cap_id, base_addr); priv = pci_get_drvdata(pdev); if (priv->state[cap_id] == STATE_REGISTERED || @@ -444,7 +449,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, consumer_deps = get_consumer_dependencies(priv, cap_id); if (!consumer_deps || suppliers_ready(priv, consumer_deps, cap_id)) { - ret = intel_vsec_add_dev(pdev, header, info, cap_id, base_addr); + ret = intel_vsec_add_dev(dev, header, info, cap_id, base_addr); if (ret) priv->state[cap_id] = STATE_SKIP; else @@ -456,7 +461,7 @@ static int intel_vsec_register_device(struct pci_dev *pdev, return -EAGAIN; } -static bool intel_vsec_walk_header(struct pci_dev *pdev, +static bool intel_vsec_walk_header(struct device *dev, const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; @@ -464,7 +469,7 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, int ret; for ( ; *header; header++) { - ret = intel_vsec_register_device(pdev, *header, info, info->base_addr); + ret = intel_vsec_register_device(dev, *header, info, info->base_addr); if (!ret) have_devices = true; } @@ -512,7 +517,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_register_device(pdev, &header, info, + ret = intel_vsec_register_device(&pdev->dev, &header, info, pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -558,7 +563,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_register_device(pdev, &header, info, + ret = intel_vsec_register_device(&pdev->dev, &header, info, pci_resource_start(pdev, header.tbir)); if (ret) continue; @@ -569,13 +574,13 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, return have_devices; } -int intel_vsec_register(struct pci_dev *pdev, +int intel_vsec_register(struct device *dev, const struct intel_vsec_platform_info *info) { - if (!pdev || !info || !info->headers) + if (!dev || !info || !info->headers) return -EINVAL; - if (!intel_vsec_walk_header(pdev, info)) + if (!intel_vsec_walk_header(dev, info)) return -ENODEV; else return 0; @@ -601,7 +606,7 @@ static bool intel_vsec_get_features(struct pci_dev *pdev, found = true; if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, info)) + intel_vsec_walk_header(&pdev->dev, info)) found = true; return found; @@ -673,7 +678,10 @@ int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, { struct vsec_priv *priv; - priv = pci_get_drvdata(vsec_dev->pcidev); + if (!dev_is_pci(vsec_dev->dev)) + return -ENODEV; + + priv = pci_get_drvdata(to_pci_dev(vsec_dev->dev)); if (!priv) return -EINVAL; @@ -821,7 +829,7 @@ static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) xa_for_each(&auxdev_array, index, intel_vsec_dev) { /* check if pdev doesn't match */ - if (pdev != intel_vsec_dev->pcidev) + if (&pdev->dev != intel_vsec_dev->dev) continue; devm_release_action(&pdev->dev, intel_vsec_remove_aux, &intel_vsec_dev->auxdev); diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 2298b6361094..9dddf4e5863e 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -530,7 +530,7 @@ static const struct file_operations mem_write_ops = { .release = single_release, }; -#define tpmi_to_dev(info) (&info->vsec_dev->pcidev->dev) +#define tpmi_to_dev(info) ((info)->vsec_dev->dev) static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) { @@ -642,7 +642,7 @@ static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, tmp->flags = IORESOURCE_MEM; } - feature_vsec_dev->pcidev = vsec_dev->pcidev; + 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; @@ -742,7 +742,7 @@ static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, i static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) { struct intel_vsec_device *vsec_dev = auxdev_to_ivdev(auxdev); - struct pci_dev *pci_dev = vsec_dev->pcidev; + struct pci_dev *pci_dev = to_pci_dev(vsec_dev->dev); struct intel_tpmi_info *tpmi_info; u64 pfs_start = 0; int ret, i; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 49a746ec0128..4eecb2a6bac4 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -29,6 +29,7 @@ #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +struct device; struct pci_dev; struct resource; @@ -82,14 +83,14 @@ enum intel_vsec_quirks { * struct pmt_callbacks - Callback infrastructure for PMT devices * @read_telem: when specified, called by client driver to access PMT * data (instead of direct copy). - * * pdev: PCI device reference for the callback's use + * * dev: device reference for the callback's use * * guid: ID of data to acccss * * data: buffer for the data to be copied * * off: offset into the requested buffer * * count: size of buffer */ struct pmt_callbacks { - int (*read_telem)(struct pci_dev *pdev, u32 guid, u64 *data, loff_t off, u32 count); + int (*read_telem)(struct device *dev, u32 guid, u64 *data, loff_t off, u32 count); }; struct vsec_feature_dependency { @@ -122,7 +123,7 @@ struct intel_vsec_platform_info { /** * struct intel_vsec_device - Auxbus specific device information * @auxdev: auxbus device struct for auxbus access - * @pcidev: pci device associated with the device + * @dev: struct device associated with the device * @resource: any resources shared by the parent * @ida: id reference * @num_resources: number of resources @@ -135,7 +136,7 @@ struct intel_vsec_platform_info { */ struct intel_vsec_device { struct auxiliary_device auxdev; - struct pci_dev *pcidev; + struct device *dev; struct resource *resource; struct ida *ida; int num_resources; @@ -199,13 +200,13 @@ static inline struct intel_vsec_device *auxdev_to_ivdev(struct auxiliary_device } #if IS_ENABLED(CONFIG_INTEL_VSEC) -int intel_vsec_register(struct pci_dev *pdev, +int intel_vsec_register(struct device *dev, const struct intel_vsec_platform_info *info); int intel_vsec_set_mapping(struct oobmsm_plat_info *plat_info, struct intel_vsec_device *vsec_dev); struct oobmsm_plat_info *intel_vsec_get_mapping(struct pci_dev *pdev); #else -static inline int intel_vsec_register(struct pci_dev *pdev, +static inline int intel_vsec_register(struct device *dev, const struct intel_vsec_platform_info *info) { return -ENODEV; From a6ce8bf3c993d8c2e8a6aeb2596429c101fe4462 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:44 -0700 Subject: [PATCH 022/106] platform/x86/intel/vsec: Return real error codes from registration path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stop collapsing registration results into booleans. Make intel_vsec_walk_header() return int and propagate the first non-zero error from intel_vsec_register_device(). intel_vsec_register() now returns that error directly and 0 on success. This preserves success behavior while surfacing meaningful errors instead of hiding them behind a bool/-ENODEV, which makes debugging and probe ordering issues clearer. Reviewed-by: Ilpo Järvinen Signed-off-by: David E. Box Link: https://patch.msgid.link/20260313015202.3660072-6-david.e.box@linux.intel.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index a547e4b98245..34b2c19ecff0 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -461,20 +461,19 @@ static int intel_vsec_register_device(struct device *dev, return -EAGAIN; } -static bool intel_vsec_walk_header(struct device *dev, - const struct intel_vsec_platform_info *info) +static int intel_vsec_walk_header(struct device *dev, + const struct intel_vsec_platform_info *info) { struct intel_vsec_header **header = info->headers; - bool have_devices = false; int ret; for ( ; *header; header++) { ret = intel_vsec_register_device(dev, *header, info, info->base_addr); - if (!ret) - have_devices = true; + if (ret) + return ret; } - return have_devices; + return 0; } static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, @@ -580,10 +579,7 @@ int intel_vsec_register(struct device *dev, if (!dev || !info || !info->headers) return -EINVAL; - if (!intel_vsec_walk_header(dev, info)) - return -ENODEV; - else - return 0; + return intel_vsec_walk_header(dev, info); } EXPORT_SYMBOL_NS_GPL(intel_vsec_register, "INTEL_VSEC"); From 22fa2ebc11a164e1ea529da6c356e3e01aef8ac8 Mon Sep 17 00:00:00 2001 From: "David E. Box" Date: Thu, 12 Mar 2026 18:51:45 -0700 Subject: [PATCH 023/106] platform/x86/intel/vsec: Plumb ACPI PMT discovery tables through vsec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some platforms expose PMT discovery via ACPI instead of PCI BARs. Add a generic discovery source flag and carry ACPI discovery entries alongside the existing PCI resource path so PMT clients can consume either. Changes: - Add enum intel_vsec_disc_source { _PCI, _ACPI }. - Extend intel_vsec_platform_info and intel_vsec_device with source enum and ACPI discovery table pointer/ - When src==ACPI, skip BAR resource setup and copy the ACPI discovery entries into the aux device. No user-visible behavior change yet; this only wires ACPI data through vsec in preparation for ACPI-enumerated PMT clients. Signed-off-by: David E. Box Link: https://patch.msgid.link/20260313015202.3660072-7-david.e.box@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec.c | 23 +++++++++++++++++++++++ include/linux/intel_vsec.h | 20 +++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index 34b2c19ecff0..7d5dbc1c1d05 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #define PMT_XA_START 0 @@ -109,6 +111,7 @@ 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); } @@ -320,6 +323,13 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head * auxiliary device driver. */ for (i = 0, tmp = res; i < header->num_entries; i++, tmp++) { + /* + * Skip resource mapping check for ACPI-based discovery + * since those tables are read from _DSD, not MMIO. + */ + if (info->src == INTEL_VSEC_DISC_ACPI) + break; + tmp->start = base_addr + header->offset + i * (header->entry_size * sizeof(u32)); tmp->end = tmp->start + (header->entry_size * sizeof(u32)) - 1; tmp->flags = IORESOURCE_MEM; @@ -338,6 +348,19 @@ static int intel_vsec_add_dev(struct device *dev, struct intel_vsec_header *head intel_vsec_dev->base_addr = info->base_addr; intel_vsec_dev->priv_data = info->priv_data; intel_vsec_dev->cap_id = cap_id; + intel_vsec_dev->src = info->src; + + if (info->src == INTEL_VSEC_DISC_ACPI) { + size_t bytes; + + if (check_mul_overflow(intel_vsec_dev->num_resources, + sizeof(*info->acpi_disc), &bytes)) + return -EOVERFLOW; + + intel_vsec_dev->acpi_disc = kmemdup(info->acpi_disc, bytes, GFP_KERNEL); + if (!intel_vsec_dev->acpi_disc) + return -ENOMEM; + } if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; diff --git a/include/linux/intel_vsec.h b/include/linux/intel_vsec.h index 4eecb2a6bac4..1fe5665a9d02 100644 --- a/include/linux/intel_vsec.h +++ b/include/linux/intel_vsec.h @@ -33,6 +33,11 @@ struct device; struct pci_dev; struct resource; +enum intel_vsec_disc_source { + INTEL_VSEC_DISC_PCI, /* PCI, default */ + INTEL_VSEC_DISC_ACPI, /* ACPI */ +}; + enum intel_vsec_id { VSEC_ID_TELEMETRY = 2, VSEC_ID_WATCHER = 3, @@ -103,6 +108,10 @@ struct vsec_feature_dependency { * @parent: parent device in the auxbus chain * @headers: list of headers to define the PMT client devices to create * @deps: array of feature dependencies + * @acpi_disc: ACPI discovery tables, each entry is two QWORDs + * in little-endian format as defined by the PMT ACPI spec. + * Valid only when @provider == INTEL_VSEC_DISC_ACPI. + * @src: source of discovery table data * @priv_data: private data, usable by parent devices, currently a callback * @caps: bitmask of PMT capabilities for the given headers * @quirks: bitmask of VSEC device quirks @@ -113,6 +122,8 @@ struct intel_vsec_platform_info { struct device *parent; struct intel_vsec_header **headers; const struct vsec_feature_dependency *deps; + u32 (*acpi_disc)[4]; + enum intel_vsec_disc_source src; void *priv_data; unsigned long caps; unsigned long quirks; @@ -124,7 +135,12 @@ 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: any resources shared by the parent + * @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. + * @src: source of discovery table data * @ida: id reference * @num_resources: number of resources * @id: xarray id @@ -138,6 +154,8 @@ 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; int num_resources; int id; /* xa */ From c12fe0b2c12195e0d1c56e0f670a6bd792b0567e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 12:14:03 +0100 Subject: [PATCH 024/106] platform/x86: lg-laptop: Drop debug-only ACPI notify handler 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, drop the debug-only notify handler method from the driver. No intentional functional impact beyond debug. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3346280.5fSG56mABF@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lg-laptop.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 61ef7a218a80..7122d96abf86 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -271,11 +271,6 @@ static void wmi_input_setup(void) } } -static void acpi_notify(struct acpi_device *device, u32 event) -{ - acpi_handle_debug(device->handle, "notify: %d\n", event); -} - static ssize_t fan_mode_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) @@ -906,7 +901,6 @@ static struct acpi_driver acpi_driver = { .ops = { .add = acpi_add, .remove = acpi_remove, - .notify = acpi_notify, }, }; From 2d9cb20610f75ca48c1cac064aede90196787507 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 12:14:47 +0100 Subject: [PATCH 025/106] platform/x86: lg-laptop: 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 LG Gram ACPI features and hotkeys driver from an ACPI driver to a platform one. 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/1868365.VLH7GnMWUR@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lg-laptop.c | 45 +++++++++----------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/drivers/platform/x86/lg-laptop.c b/drivers/platform/x86/lg-laptop.c index 7122d96abf86..9681412d694b 100644 --- a/drivers/platform/x86/lg-laptop.c +++ b/drivers/platform/x86/lg-laptop.c @@ -759,8 +759,9 @@ static void lg_laptop_remove_address_space_handler(void *data) &lg_laptop_address_space_handler); } -static int acpi_add(struct acpi_device *device) +static int acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct platform_device_info pdev_info = { .fwnode = acpi_fwnode_handle(device), .name = PLATFORM_NAME, @@ -776,11 +777,11 @@ static int acpi_add(struct acpi_device *device) status = acpi_install_address_space_handler(device->handle, LG_ADDRESS_SPACE_ID, &lg_laptop_address_space_handler, - NULL, &device->dev); + NULL, &pdev->dev); if (ACPI_FAILURE(status)) return -ENODEV; - ret = devm_add_action_or_reset(&device->dev, lg_laptop_remove_address_space_handler, + ret = devm_add_action_or_reset(&pdev->dev, lg_laptop_remove_address_space_handler, device); if (ret < 0) return ret; @@ -874,7 +875,7 @@ out_platform_registered: return ret; } -static void acpi_remove(struct acpi_device *device) +static void acpi_remove(struct platform_device *pdev) { sysfs_remove_group(&pf_device->dev.kobj, &dev_attribute_group); @@ -894,33 +895,13 @@ static const struct acpi_device_id device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, device_ids); -static struct acpi_driver acpi_driver = { - .name = "LG Gram Laptop Support", - .class = "lg-laptop", - .ids = device_ids, - .ops = { - .add = acpi_add, - .remove = acpi_remove, - }, +static struct platform_driver acpi_driver = { + .probe = acpi_probe, + .remove = acpi_remove, + .driver = { + .name = "LG Gram Laptop Support", + .acpi_match_table = device_ids, + }, }; -static int __init acpi_init(void) -{ - int result; - - result = acpi_bus_register_driver(&acpi_driver); - if (result < 0) { - pr_debug("Error registering driver\n"); - return -ENODEV; - } - - return 0; -} - -static void __exit acpi_exit(void) -{ - acpi_bus_unregister_driver(&acpi_driver); -} - -module_init(acpi_init); -module_exit(acpi_exit); +module_platform_driver(acpi_driver); From 0f2ca62fdfbcada9568fbc7e27e94e4b3bffbf7a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 12:28:52 +0100 Subject: [PATCH 026/106] platform/x86: sony-laptop: 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/2559802.jE0xQCEvom@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sony-laptop.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d3e7a52c22a7..6f417b5cdfe2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -1176,7 +1176,7 @@ enum event_types { KILLSWITCH, GFX_SWITCH }; -static void sony_nc_notify(struct acpi_device *device, u32 event) +static void sony_nc_notify(acpi_handle ah, u32 event, void *data) { u32 real_ev = event; u8 ev_type = 0; @@ -3244,6 +3244,11 @@ static int sony_nc_add(struct acpi_device *device) } } + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + sony_nc_notify, NULL); + if (result) + goto out_sysfs; + pr_info("SNC setup done.\n"); return 0; @@ -3270,6 +3275,8 @@ static void sony_nc_remove(struct acpi_device *device) { struct sony_nc_value *item; + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, sony_nc_notify); + sony_nc_backlight_cleanup(); sony_nc_acpi_device = NULL; @@ -3304,7 +3311,6 @@ static struct acpi_driver sony_nc_driver = { .ops = { .add = sony_nc_add, .remove = sony_nc_remove, - .notify = sony_nc_notify, }, .drv.pm = &sony_nc_pm, }; From 14004dd31caae2e49791d1ecc8f9ba87478df023 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 12:29:44 +0100 Subject: [PATCH 027/106] platform/x86: sony-laptop: Convert NC 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 Notebook Control (nc) part of the Sony laptop driver from an ACPI driver to a platform one. 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/886676729.0ifERbkFSE@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sony-laptop.c | 65 ++++++++++++++---------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 6f417b5cdfe2..bc9a3ae71be8 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -178,8 +178,7 @@ enum sony_nc_rfkill { static int sony_rfkill_handle; static struct rfkill *sony_rfkill_devices[N_SONY_RFKILL]; static int sony_rfkill_address[N_SONY_RFKILL] = {0x300, 0x500, 0x700, 0x900}; -static int sony_nc_rfkill_setup(struct acpi_device *device, - unsigned int handle); +static int sony_nc_rfkill_setup(struct device *dev, unsigned int handle); static void sony_nc_rfkill_cleanup(void); static void sony_nc_rfkill_update(void); @@ -435,7 +434,7 @@ static void sony_laptop_report_input_event(u8 event) dprintk("unknown input event %.2x\n", event); } -static int sony_laptop_setup_input(struct acpi_device *acpi_device) +static int sony_laptop_setup_input(struct device *parent) { struct input_dev *jog_dev; struct input_dev *key_dev; @@ -468,7 +467,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) key_dev->name = "Sony Vaio Keys"; key_dev->id.bustype = BUS_ISA; key_dev->id.vendor = PCI_VENDOR_ID_SONY; - key_dev->dev.parent = &acpi_device->dev; + key_dev->dev.parent = parent; /* Initialize the Input Drivers: special keys */ input_set_capability(key_dev, EV_MSC, MSC_SCAN); @@ -497,7 +496,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) jog_dev->name = "Sony Vaio Jogdial"; jog_dev->id.bustype = BUS_ISA; jog_dev->id.vendor = PCI_VENDOR_ID_SONY; - jog_dev->dev.parent = &acpi_device->dev; + jog_dev->dev.parent = parent; input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); input_set_capability(jog_dev, EV_REL, REL_WHEEL); @@ -1287,7 +1286,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level, /* * ACPI device */ -static void sony_nc_function_setup(struct acpi_device *device, +static void sony_nc_function_setup(struct device *dev, struct platform_device *pf_device) { unsigned int i, result, bitmask, arg; @@ -1360,7 +1359,7 @@ static void sony_nc_function_setup(struct acpi_device *device, break; case 0x0124: case 0x0135: - result = sony_nc_rfkill_setup(device, handle); + result = sony_nc_rfkill_setup(dev, handle); if (result) pr_err("couldn't set up rfkill support (%d)\n", result); @@ -1600,8 +1599,7 @@ static const struct rfkill_ops sony_rfkill_ops = { .set_block = sony_nc_rfkill_set, }; -static int sony_nc_setup_rfkill(struct acpi_device *device, - enum sony_nc_rfkill nc_type) +static int sony_nc_setup_rfkill(struct device *parent, enum sony_nc_rfkill nc_type) { int err; struct rfkill *rfk; @@ -1631,8 +1629,7 @@ static int sony_nc_setup_rfkill(struct acpi_device *device, return -EINVAL; } - rfk = rfkill_alloc(name, &device->dev, type, - &sony_rfkill_ops, (void *)nc_type); + rfk = rfkill_alloc(name, parent, type, &sony_rfkill_ops, (void *)nc_type); if (!rfk) return -ENOMEM; @@ -1692,8 +1689,7 @@ static void sony_nc_rfkill_update(void) } } -static int sony_nc_rfkill_setup(struct acpi_device *device, - unsigned int handle) +static int sony_nc_rfkill_setup(struct device *parent, unsigned int handle) { u64 offset; int i; @@ -1734,18 +1730,18 @@ static int sony_nc_rfkill_setup(struct acpi_device *device, dprintk("Radio devices, found 0x%.2x\n", buffer[i]); if (buffer[i] == 0 && !sony_rfkill_devices[SONY_WIFI]) - sony_nc_setup_rfkill(device, SONY_WIFI); + sony_nc_setup_rfkill(parent, SONY_WIFI); if (buffer[i] == 0x10 && !sony_rfkill_devices[SONY_BLUETOOTH]) - sony_nc_setup_rfkill(device, SONY_BLUETOOTH); + sony_nc_setup_rfkill(parent, SONY_BLUETOOTH); if (((0xf0 & buffer[i]) == 0x20 || (0xf0 & buffer[i]) == 0x50) && !sony_rfkill_devices[SONY_WWAN]) - sony_nc_setup_rfkill(device, SONY_WWAN); + sony_nc_setup_rfkill(parent, SONY_WWAN); if (buffer[i] == 0x30 && !sony_rfkill_devices[SONY_WIMAX]) - sony_nc_setup_rfkill(device, SONY_WIMAX); + sony_nc_setup_rfkill(parent, SONY_WIMAX); } return 0; } @@ -3149,8 +3145,9 @@ static void sony_nc_backlight_cleanup(void) backlight_device_unregister(sony_bl_props.dev); } -static int sony_nc_add(struct acpi_device *device) +static int sony_nc_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; int result = 0; struct sony_nc_value *item; @@ -3184,7 +3181,7 @@ static int sony_nc_add(struct acpi_device *device) } } - result = sony_laptop_setup_input(device); + result = sony_laptop_setup_input(&pdev->dev); if (result) { pr_err("Unable to create input devices\n"); goto outplatform; @@ -3201,7 +3198,7 @@ static int sony_nc_add(struct acpi_device *device) /* retrieve the available handles */ result = sony_nc_handles_setup(sony_pf_device); if (!result) - sony_nc_function_setup(device, sony_pf_device); + sony_nc_function_setup(&pdev->dev, sony_pf_device); } if (acpi_video_get_backlight_type() == acpi_backlight_vendor) @@ -3271,11 +3268,12 @@ outwalk: return result; } -static void sony_nc_remove(struct acpi_device *device) +static void sony_nc_remove(struct platform_device *pdev) { struct sony_nc_value *item; - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, sony_nc_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, sony_nc_notify); sony_nc_backlight_cleanup(); @@ -3304,15 +3302,14 @@ static const struct acpi_device_id sony_nc_device_ids[] = { {"", 0}, }; -static struct acpi_driver sony_nc_driver = { - .name = SONY_NC_DRIVER_NAME, - .class = SONY_NC_CLASS, - .ids = sony_nc_device_ids, - .ops = { - .add = sony_nc_add, - .remove = sony_nc_remove, - }, - .drv.pm = &sony_nc_pm, +static struct platform_driver sony_nc_driver = { + .probe = sony_nc_probe, + .remove = sony_nc_remove, + .driver = { + .name = SONY_NC_DRIVER_NAME, + .acpi_match_table = sony_nc_device_ids, + .pm = &sony_nc_pm, + }, }; /*********** SPIC (SNY6001) Device ***********/ @@ -4529,7 +4526,7 @@ static int sony_pic_add(struct acpi_device *device) } /* setup input devices and helper fifo */ - result = sony_laptop_setup_input(device); + result = sony_laptop_setup_input(&device->dev); if (result) { pr_err("Unable to create input devices\n"); goto err_free_resources; @@ -4720,7 +4717,7 @@ static int __init sony_laptop_init(void) spic_drv_registered = 1; } - result = acpi_bus_register_driver(&sony_nc_driver); + result = platform_driver_register(&sony_nc_driver); if (result) { pr_err("Unable to register SNC driver\n"); goto out_unregister_pic; @@ -4737,7 +4734,7 @@ out: static void __exit sony_laptop_exit(void) { - acpi_bus_unregister_driver(&sony_nc_driver); + platform_driver_unregister(&sony_nc_driver); if (spic_drv_registered) acpi_bus_unregister_driver(&sony_pic_driver); } From 138db7ee58c0c0911fc4f79ccd7c5846f7b9408b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 12:30:27 +0100 Subject: [PATCH 028/106] platform/x86: sony-laptop: Convert PIC 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 Programmable IO Control (pic) part of the Sony laptop driver from an ACPI driver to a platform one. 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/2059875.yKVeVyVuyW@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/sony-laptop.c | 51 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index bc9a3ae71be8..b18f00e9082f 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4279,9 +4279,9 @@ end: /* * Disable the spic device by calling its _DIS method */ -static int sony_pic_disable(struct acpi_device *device) +static int sony_pic_disable(struct device *dev) { - acpi_status ret = acpi_evaluate_object(device->handle, "_DIS", NULL, + acpi_status ret = acpi_evaluate_object(ACPI_HANDLE(dev), "_DIS", NULL, NULL); if (ACPI_FAILURE(ret) && ret != AE_NOT_FOUND) @@ -4297,7 +4297,7 @@ static int sony_pic_disable(struct acpi_device *device) * * Call _SRS to set current resources */ -static int sony_pic_enable(struct acpi_device *device, +static int sony_pic_enable(struct device *dev, struct sony_pic_ioport *ioport, struct sony_pic_irq *irq) { acpi_status status; @@ -4379,7 +4379,7 @@ static int sony_pic_enable(struct acpi_device *device, /* Attempt to set the resource */ dprintk("Evaluating _SRS\n"); - status = acpi_set_current_resources(device->handle, &buffer); + status = acpi_set_current_resources(ACPI_HANDLE(dev), &buffer); /* check for total failure */ if (ACPI_FAILURE(status)) { @@ -4468,12 +4468,12 @@ found: * ACPI driver * *****************/ -static void sony_pic_remove(struct acpi_device *device) +static void sony_pic_remove(struct platform_device *pdev) { struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - if (sony_pic_disable(device)) { + if (sony_pic_disable(&pdev->dev)) { pr_err("Couldn't disable device\n"); return; } @@ -4507,11 +4507,12 @@ static void sony_pic_remove(struct acpi_device *device) dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); } -static int sony_pic_add(struct acpi_device *device) +static int sony_pic_probe(struct platform_device *pdev) { - int result; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; + int result; spic_dev.acpi_dev = device; strscpy(acpi_device_class(device), "sony/hotkey"); @@ -4526,7 +4527,7 @@ static int sony_pic_add(struct acpi_device *device) } /* setup input devices and helper fifo */ - result = sony_laptop_setup_input(&device->dev); + result = sony_laptop_setup_input(&pdev->dev); if (result) { pr_err("Unable to create input devices\n"); goto err_free_resources; @@ -4596,7 +4597,7 @@ static int sony_pic_add(struct acpi_device *device) } /* set resource status _SRS */ - result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + result = sony_pic_enable(&pdev->dev, spic_dev.cur_ioport, spic_dev.cur_irq); if (result) { pr_err("Couldn't enable device\n"); goto err_free_irq; @@ -4619,7 +4620,7 @@ err_remove_pf: sony_pf_remove(); err_disable_device: - sony_pic_disable(device); + sony_pic_disable(&pdev->dev); err_free_irq: free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); @@ -4655,15 +4656,14 @@ err_free_resources: #ifdef CONFIG_PM_SLEEP static int sony_pic_suspend(struct device *dev) { - if (sony_pic_disable(to_acpi_device(dev))) + if (sony_pic_disable(dev)) return -ENXIO; return 0; } static int sony_pic_resume(struct device *dev) { - sony_pic_enable(to_acpi_device(dev), - spic_dev.cur_ioport, spic_dev.cur_irq); + sony_pic_enable(dev, spic_dev.cur_ioport, spic_dev.cur_irq); return 0; } #endif @@ -4675,15 +4675,14 @@ static const struct acpi_device_id sony_pic_device_ids[] = { {"", 0}, }; -static struct acpi_driver sony_pic_driver = { - .name = SONY_PIC_DRIVER_NAME, - .class = SONY_PIC_CLASS, - .ids = sony_pic_device_ids, - .ops = { - .add = sony_pic_add, - .remove = sony_pic_remove, - }, - .drv.pm = &sony_pic_pm, +static struct platform_driver sony_pic_driver = { + .probe = sony_pic_probe, + .remove = sony_pic_remove, + .driver = { + .name = SONY_PIC_DRIVER_NAME, + .acpi_match_table = sony_pic_device_ids, + .pm = &sony_pic_pm, + }, }; static const struct dmi_system_id sonypi_dmi_table[] __initconst = { @@ -4709,7 +4708,7 @@ static int __init sony_laptop_init(void) int result; if (!no_spic && dmi_check_system(sonypi_dmi_table)) { - result = acpi_bus_register_driver(&sony_pic_driver); + result = platform_driver_register(&sony_pic_driver); if (result) { pr_err("Unable to register SPIC driver\n"); goto out; @@ -4727,7 +4726,7 @@ static int __init sony_laptop_init(void) out_unregister_pic: if (spic_drv_registered) - acpi_bus_unregister_driver(&sony_pic_driver); + platform_driver_unregister(&sony_pic_driver); out: return result; } @@ -4736,7 +4735,7 @@ static void __exit sony_laptop_exit(void) { platform_driver_unregister(&sony_nc_driver); if (spic_drv_registered) - acpi_bus_unregister_driver(&sony_pic_driver); + platform_driver_unregister(&sony_pic_driver); } module_init(sony_laptop_init); From a15d1839c77884ad08b72fe2e18d8df7b32c5c0f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 15:32:36 +0100 Subject: [PATCH 029/106] platform/x86: topstar-laptop: 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/3425557.44csPzL39Z@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/topstar-laptop.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index a7b4b6c8e549..695ec1d25bcd 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -232,9 +232,9 @@ static int topstar_acpi_fncx_switch(struct acpi_device *device, bool state) return 0; } -static void topstar_acpi_notify(struct acpi_device *device, u32 event) +static void topstar_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct topstar_laptop *topstar = acpi_driver_data(device); + struct topstar_laptop *topstar = data; static bool dup_evnt[2]; bool *dup; @@ -313,14 +313,21 @@ static int topstar_acpi_add(struct acpi_device *device) if (err) goto err_platform_exit; + err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + topstar_acpi_notify, topstar); + if (err) + goto err_input_exit; + if (led_workaround) { err = topstar_led_init(topstar); if (err) - goto err_input_exit; + goto err_notify_handler_exit; } return 0; +err_notify_handler_exit: + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, topstar_acpi_notify); err_input_exit: topstar_input_exit(topstar); err_platform_exit: @@ -339,6 +346,7 @@ static void topstar_acpi_remove(struct acpi_device *device) if (led_workaround) topstar_led_exit(topstar); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, topstar_acpi_notify); topstar_input_exit(topstar); topstar_platform_exit(topstar); topstar_acpi_exit(topstar); @@ -360,7 +368,6 @@ static struct acpi_driver topstar_acpi_driver = { .ops = { .add = topstar_acpi_add, .remove = topstar_acpi_remove, - .notify = topstar_acpi_notify, }, }; From 3471415c8186f952b805c441d905d7033c23304a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 15:34:25 +0100 Subject: [PATCH 030/106] platform/x86: topstar-laptop: 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 Topstar Laptop ACPI Extras driver from an ACPI driver to a platform one. 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/10834824.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/topstar-laptop.c | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index 695ec1d25bcd..e09d7f8ce45f 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -285,8 +285,9 @@ static const struct dmi_system_id topstar_dmi_ids[] = { {} }; -static int topstar_acpi_add(struct acpi_device *device) +static int topstar_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct topstar_laptop *topstar; int err; @@ -296,9 +297,10 @@ static int topstar_acpi_add(struct acpi_device *device) if (!topstar) return -ENOMEM; + platform_set_drvdata(pdev, topstar); + strscpy(acpi_device_name(device), "Topstar TPSACPI"); strscpy(acpi_device_class(device), TOPSTAR_LAPTOP_CLASS); - device->driver_data = topstar; topstar->device = device; err = topstar_acpi_init(topstar); @@ -339,14 +341,15 @@ err_free: return err; } -static void topstar_acpi_remove(struct acpi_device *device) +static void topstar_acpi_remove(struct platform_device *pdev) { - struct topstar_laptop *topstar = acpi_driver_data(device); + struct topstar_laptop *topstar = platform_get_drvdata(pdev); if (led_workaround) topstar_led_exit(topstar); - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, topstar_acpi_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, topstar_acpi_notify); topstar_input_exit(topstar); topstar_platform_exit(topstar); topstar_acpi_exit(topstar); @@ -361,13 +364,12 @@ static const struct acpi_device_id topstar_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, topstar_device_ids); -static struct acpi_driver topstar_acpi_driver = { - .name = "Topstar laptop ACPI driver", - .class = TOPSTAR_LAPTOP_CLASS, - .ids = topstar_device_ids, - .ops = { - .add = topstar_acpi_add, - .remove = topstar_acpi_remove, +static struct platform_driver topstar_acpi_driver = { + .probe = topstar_acpi_probe, + .remove = topstar_acpi_remove, + .driver = { + .name = "Topstar laptop ACPI driver", + .acpi_match_table = topstar_device_ids, }, }; @@ -379,7 +381,7 @@ static int __init topstar_laptop_init(void) if (ret < 0) return ret; - ret = acpi_bus_register_driver(&topstar_acpi_driver); + ret = platform_driver_register(&topstar_acpi_driver); if (ret < 0) goto err_driver_unreg; @@ -393,7 +395,7 @@ err_driver_unreg: static void __exit topstar_laptop_exit(void) { - acpi_bus_unregister_driver(&topstar_acpi_driver); + platform_driver_unregister(&topstar_acpi_driver); platform_driver_unregister(&topstar_platform_driver); } From cfc897f6d3f7e9e284d498cd9fc4c68488166f48 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 15:38:37 +0100 Subject: [PATCH 031/106] platform/x86: wireless-hotkey: 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/3953848.kQq0lBPeGt@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wireless-hotkey.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index e5083c0e1515..a0ae757a277e 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -71,9 +71,9 @@ static void wireless_input_destroy(struct acpi_device *device) kfree(button); } -static void wl_notify(struct acpi_device *acpi_dev, u32 event) +static void wl_notify(acpi_handle handle, u32 event, void *data) { - struct wl_button *button = acpi_driver_data(acpi_dev); + struct wl_button *button = data; if (event != 0x80) { pr_info("Received unknown event (0x%x)\n", event); @@ -101,6 +101,13 @@ static int wl_add(struct acpi_device *device) if (err) { pr_err("Failed to setup wireless hotkeys\n"); kfree(button); + return err; + } + err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + wl_notify, button); + if (err) { + pr_err("Failed to install ACPI notify handler\n"); + wireless_input_destroy(device); } return err; @@ -108,6 +115,7 @@ static int wl_add(struct acpi_device *device) static void wl_remove(struct acpi_device *device) { + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, wl_notify); wireless_input_destroy(device); } @@ -117,7 +125,6 @@ static struct acpi_driver wl_driver = { .ops = { .add = wl_add, .remove = wl_remove, - .notify = wl_notify, }, }; From 8507277ef1326d6854a6445354cd43e93e2b95fa Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Mar 2026 15:40:36 +0100 Subject: [PATCH 032/106] platform/x86: wireless-hotkey: 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 airplane mode button for AMD, HP and Xiaomi laptops driver from an ACPI driver to a platform one. 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/9607409.CDJkKcVGEf@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/wireless-hotkey.c | 44 ++++++++++++++------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/wireless-hotkey.c b/drivers/platform/x86/wireless-hotkey.c index a0ae757a277e..f680d8ff8e87 100644 --- a/drivers/platform/x86/wireless-hotkey.c +++ b/drivers/platform/x86/wireless-hotkey.c @@ -35,16 +35,17 @@ static const struct acpi_device_id wl_ids[] = { {"", 0}, }; -static int wireless_input_setup(struct acpi_device *device) +static int wireless_input_setup(struct device *dev) { - struct wl_button *button = acpi_driver_data(device); + struct wl_button *button = dev_get_drvdata(dev); int err; button->input_dev = input_allocate_device(); if (!button->input_dev) return -ENOMEM; - snprintf(button->phys, sizeof(button->phys), "%s/input0", acpi_device_hid(device)); + snprintf(button->phys, sizeof(button->phys), "%s/input0", + acpi_device_hid(ACPI_COMPANION(dev))); button->input_dev->name = "Wireless hotkeys"; button->input_dev->phys = button->phys; @@ -63,9 +64,9 @@ err_free_dev: return err; } -static void wireless_input_destroy(struct acpi_device *device) +static void wireless_input_destroy(struct device *dev) { - struct wl_button *button = acpi_driver_data(device); + struct wl_button *button = dev_get_drvdata(dev); input_unregister_device(button->input_dev); kfree(button); @@ -86,7 +87,7 @@ static void wl_notify(acpi_handle handle, u32 event, void *data) input_sync(button->input_dev); } -static int wl_add(struct acpi_device *device) +static int wl_probe(struct platform_device *pdev) { struct wl_button *button; int err; @@ -95,37 +96,38 @@ static int wl_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); - err = wireless_input_setup(device); + err = wireless_input_setup(&pdev->dev); if (err) { pr_err("Failed to setup wireless hotkeys\n"); kfree(button); return err; } - err = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - wl_notify, button); + err = acpi_dev_install_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, wl_notify, button); if (err) { pr_err("Failed to install ACPI notify handler\n"); - wireless_input_destroy(device); + wireless_input_destroy(&pdev->dev); } return err; } -static void wl_remove(struct acpi_device *device) +static void wl_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, wl_notify); - wireless_input_destroy(device); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, wl_notify); + wireless_input_destroy(&pdev->dev); } -static struct acpi_driver wl_driver = { - .name = "wireless-hotkey", - .ids = wl_ids, - .ops = { - .add = wl_add, - .remove = wl_remove, +static struct platform_driver wl_driver = { + .probe = wl_probe, + .remove = wl_remove, + .driver = { + .name = "wireless-hotkey", + .acpi_match_table = wl_ids, }, }; -module_acpi_driver(wl_driver); +module_platform_driver(wl_driver); From bd13b265d386afb15d9ee5feb4f2cb605a5770db Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2026 20:39:07 +0100 Subject: [PATCH 033/106] platform/x86: fujitsu-tablet: 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 fujitsu-tablet ACPI driver to a platform one. After this change, the subordinate input device will be registered under the platform device used for driver binding instead of its ACPI companion. 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] Acked-by: Jonathan Woithe Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2403432.ElGaqSPkdT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-tablet.c | 30 +++++++++++++-------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 17f08ce7552d..8319df28e9b8 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -18,6 +18,7 @@ #include #include #include +#include #define MODULENAME "fujitsu-tablet" @@ -442,14 +443,12 @@ static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data) } } -static int acpi_fujitsu_add(struct acpi_device *adev) +static int acpi_fujitsu_probe(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); acpi_status status; int error; - if (!adev) - return -EINVAL; - status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS, fujitsu_walk_resources, NULL); if (ACPI_FAILURE(status) || !fujitsu.irq || !fujitsu.io_base) @@ -461,7 +460,7 @@ static int acpi_fujitsu_add(struct acpi_device *adev) snprintf(fujitsu.phys, sizeof(fujitsu.phys), "%s/input0", acpi_device_hid(adev)); - error = input_fujitsu_setup(&adev->dev, + error = input_fujitsu_setup(&pdev->dev, acpi_device_name(adev), fujitsu.phys); if (error) return error; @@ -484,7 +483,7 @@ static int acpi_fujitsu_add(struct acpi_device *adev) return 0; } -static void acpi_fujitsu_remove(struct acpi_device *adev) +static void acpi_fujitsu_remove(struct platform_device *pdev) { free_irq(fujitsu.irq, fujitsu_interrupt); release_region(fujitsu.io_base, fujitsu.io_length); @@ -501,15 +500,14 @@ static int acpi_fujitsu_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume); -static struct acpi_driver acpi_fujitsu_driver = { - .name = MODULENAME, - .class = "hotkey", - .ids = fujitsu_ids, - .ops = { - .add = acpi_fujitsu_add, - .remove = acpi_fujitsu_remove, +static struct platform_driver acpi_fujitsu_driver = { + .probe = acpi_fujitsu_probe, + .remove = acpi_fujitsu_remove, + .driver = { + .name = MODULENAME, + .acpi_match_table = fujitsu_ids, + .pm = &acpi_fujitsu_pm, }, - .drv.pm = &acpi_fujitsu_pm, }; static int __init fujitsu_module_init(void) @@ -518,7 +516,7 @@ static int __init fujitsu_module_init(void) dmi_check_system(dmi_ids); - error = acpi_bus_register_driver(&acpi_fujitsu_driver); + error = platform_driver_register(&acpi_fujitsu_driver); if (error) return error; @@ -527,7 +525,7 @@ static int __init fujitsu_module_init(void) static void __exit fujitsu_module_exit(void) { - acpi_bus_unregister_driver(&acpi_fujitsu_driver); + platform_driver_unregister(&acpi_fujitsu_driver); } module_init(fujitsu_module_init); From c256927c62475f222bd9fc913538dd484f88fed6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2026 20:40:02 +0100 Subject: [PATCH 034/106] platform/x86: fujitsu: Reorder code to avoid forward declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the definitions of acpi_fujitsu_bl_notify() and acpi_fujitsu_laptop_notify() along with some helpers above the definitions of the functions that will refer to them after subsequent changes, to avoid having to add forward declarations of them. No intentional functional impact. Acked-by: Jonathan Woithe Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1950259.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 216 +++++++++++++------------- 1 file changed, 108 insertions(+), 108 deletions(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 931fbcdd21b8..1adce90ae3e6 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -500,6 +500,36 @@ static int fujitsu_backlight_register(struct acpi_device *device) return 0; } +/* Brightness notify */ + +static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) +{ + struct fujitsu_bl *priv = acpi_driver_data(device); + int oldb, newb; + + if (event != ACPI_FUJITSU_NOTIFY_CODE) { + acpi_handle_info(device->handle, "unsupported event [0x%x]\n", + event); + sparse_keymap_report_event(priv->input, -1, 1, true); + return; + } + + oldb = priv->brightness_level; + get_lcd_level(device); + newb = priv->brightness_level; + + acpi_handle_debug(device->handle, + "brightness button event [%i -> %i]\n", oldb, newb); + + if (oldb == newb) + return; + + if (!disable_brightness_adjust) + set_lcd_level(device, newb); + + sparse_keymap_report_event(priv->input, oldb < newb, 1, true); +} + static int acpi_fujitsu_bl_add(struct acpi_device *device) { struct fujitsu_bl *priv; @@ -531,36 +561,6 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) return fujitsu_backlight_register(device); } -/* Brightness notify */ - -static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) -{ - struct fujitsu_bl *priv = acpi_driver_data(device); - int oldb, newb; - - if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "unsupported event [0x%x]\n", - event); - sparse_keymap_report_event(priv->input, -1, 1, true); - return; - } - - oldb = priv->brightness_level; - get_lcd_level(device); - newb = priv->brightness_level; - - acpi_handle_debug(device->handle, - "brightness button event [%i -> %i]\n", oldb, newb); - - if (oldb == newb) - return; - - if (!disable_brightness_adjust) - set_lcd_level(device, newb); - - sparse_keymap_report_event(priv->input, oldb < newb, 1, true); -} - /* ACPI device for hotkey handling */ static const struct key_entry keymap_default[] = { @@ -908,6 +908,84 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) return 0; } +static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) +{ + struct fujitsu_laptop *priv = acpi_driver_data(device); + int ret; + + ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, + sizeof(scancode), &priv->fifo_lock); + if (ret != sizeof(scancode)) { + dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", + scancode); + return; + } + sparse_keymap_report_event(priv->input, scancode, 1, false); + dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n", + scancode); +} + +static void acpi_fujitsu_laptop_release(struct acpi_device *device) +{ + struct fujitsu_laptop *priv = acpi_driver_data(device); + int scancode, ret; + + while (true) { + ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode, + sizeof(scancode), &priv->fifo_lock); + if (ret != sizeof(scancode)) + return; + sparse_keymap_report_event(priv->input, scancode, 0, false); + dev_dbg(&priv->input->dev, + "Pop scancode from ringbuffer [0x%x]\n", scancode); + } +} + +static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) +{ + struct fujitsu_laptop *priv = acpi_driver_data(device); + unsigned long flags; + int scancode, i = 0; + unsigned int irb; + + if (event != ACPI_FUJITSU_NOTIFY_CODE) { + acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", + event); + sparse_keymap_report_event(priv->input, -1, 1, true); + return; + } + + if (priv->flags_supported) + priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, + 0x0); + + while ((irb = call_fext_func(device, + FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && + i++ < MAX_HOTKEY_RINGBUFFER_SIZE) { + scancode = irb & 0x4ff; + if (sparse_keymap_entry_from_scancode(priv->input, scancode)) + acpi_fujitsu_laptop_press(device, scancode); + else if (scancode == 0) + acpi_fujitsu_laptop_release(device); + else + acpi_handle_info(device->handle, + "Unknown GIRB result [%x]\n", irb); + } + + /* + * First seen on the Skylake-based Lifebook E736/E746/E756), the + * touchpad toggle hotkey (Fn+F4) is handled in software. Other models + * have since added additional "soft keys". These are reported in the + * status flags queried using FUNC_FLAGS. + */ + if (priv->flags_supported & (FLAG_SOFTKEYS)) { + flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0); + flags &= (FLAG_SOFTKEYS); + for_each_set_bit(i, &flags, BITS_PER_LONG) + sparse_keymap_report_event(priv->input, BIT(i), 1, true); + } +} + static int acpi_fujitsu_laptop_add(struct acpi_device *device) { struct fujitsu_laptop *priv; @@ -1001,84 +1079,6 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device) kfifo_free(&priv->fifo); } -static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) -{ - struct fujitsu_laptop *priv = acpi_driver_data(device); - int ret; - - ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, - sizeof(scancode), &priv->fifo_lock); - if (ret != sizeof(scancode)) { - dev_info(&priv->input->dev, "Could not push scancode [0x%x]\n", - scancode); - return; - } - sparse_keymap_report_event(priv->input, scancode, 1, false); - dev_dbg(&priv->input->dev, "Push scancode into ringbuffer [0x%x]\n", - scancode); -} - -static void acpi_fujitsu_laptop_release(struct acpi_device *device) -{ - struct fujitsu_laptop *priv = acpi_driver_data(device); - int scancode, ret; - - while (true) { - ret = kfifo_out_locked(&priv->fifo, (unsigned char *)&scancode, - sizeof(scancode), &priv->fifo_lock); - if (ret != sizeof(scancode)) - return; - sparse_keymap_report_event(priv->input, scancode, 0, false); - dev_dbg(&priv->input->dev, - "Pop scancode from ringbuffer [0x%x]\n", scancode); - } -} - -static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) -{ - struct fujitsu_laptop *priv = acpi_driver_data(device); - unsigned long flags; - int scancode, i = 0; - unsigned int irb; - - if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", - event); - sparse_keymap_report_event(priv->input, -1, 1, true); - return; - } - - if (priv->flags_supported) - priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, - 0x0); - - while ((irb = call_fext_func(device, - FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && - i++ < MAX_HOTKEY_RINGBUFFER_SIZE) { - scancode = irb & 0x4ff; - if (sparse_keymap_entry_from_scancode(priv->input, scancode)) - acpi_fujitsu_laptop_press(device, scancode); - else if (scancode == 0) - acpi_fujitsu_laptop_release(device); - else - acpi_handle_info(device->handle, - "Unknown GIRB result [%x]\n", irb); - } - - /* - * First seen on the Skylake-based Lifebook E736/E746/E756), the - * touchpad toggle hotkey (Fn+F4) is handled in software. Other models - * have since added additional "soft keys". These are reported in the - * status flags queried using FUNC_FLAGS. - */ - if (priv->flags_supported & (FLAG_SOFTKEYS)) { - flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0); - flags &= (FLAG_SOFTKEYS); - for_each_set_bit(i, &flags, BITS_PER_LONG) - sparse_keymap_report_event(priv->input, BIT(i), 1, true); - } -} - /* Initialization */ static const struct acpi_device_id fujitsu_bl_device_ids[] = { From 9b9271ac2a25f427dd38a3f486e8b4d7afe7e0ec Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2026 20:42:09 +0100 Subject: [PATCH 035/106] platform/x86: fujitsu: 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: Jonathan Woithe Link: https://patch.msgid.link/3035290.e9J7NaK4W3@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 32 ++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 1adce90ae3e6..88fdc4fce05c 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -502,8 +502,9 @@ static int fujitsu_backlight_register(struct acpi_device *device) /* Brightness notify */ -static void acpi_fujitsu_bl_notify(struct acpi_device *device, u32 event) +static void acpi_fujitsu_bl_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; struct fujitsu_bl *priv = acpi_driver_data(device); int oldb, newb; @@ -558,7 +559,18 @@ static int acpi_fujitsu_bl_add(struct acpi_device *device) if (ret) return ret; - return fujitsu_backlight_register(device); + ret = fujitsu_backlight_register(device); + if (ret) + return ret; + + return acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_bl_notify, device); +} + +static void acpi_fujitsu_bl_remove(struct acpi_device *device) +{ + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_bl_notify); } /* ACPI device for hotkey handling */ @@ -941,8 +953,9 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device) } } -static void acpi_fujitsu_laptop_notify(struct acpi_device *device, u32 event) +static void acpi_fujitsu_laptop_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; struct fujitsu_laptop *priv = acpi_driver_data(device); unsigned long flags; int scancode, i = 0; @@ -1056,12 +1069,19 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) if (ret) goto err_free_fifo; + ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_laptop_notify, device); + if (ret) + goto err_platform_remove; + ret = fujitsu_battery_charge_control_add(device); if (ret < 0) pr_warn("Unable to register battery charge control: %d\n", ret); return 0; +err_platform_remove: + fujitsu_laptop_platform_remove(device); err_free_fifo: kfifo_free(&priv->fifo); @@ -1074,6 +1094,9 @@ static void acpi_fujitsu_laptop_remove(struct acpi_device *device) fujitsu_battery_charge_control_remove(device); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_fujitsu_laptop_notify); + fujitsu_laptop_platform_remove(device); kfifo_free(&priv->fifo); @@ -1092,7 +1115,7 @@ static struct acpi_driver acpi_fujitsu_bl_driver = { .ids = fujitsu_bl_device_ids, .ops = { .add = acpi_fujitsu_bl_add, - .notify = acpi_fujitsu_bl_notify, + .remove = acpi_fujitsu_bl_remove, }, }; @@ -1108,7 +1131,6 @@ static struct acpi_driver acpi_fujitsu_laptop_driver = { .ops = { .add = acpi_fujitsu_laptop_add, .remove = acpi_fujitsu_laptop_remove, - .notify = acpi_fujitsu_laptop_notify, }, }; From d5c9212ccfaa7bd453bf4b198eb19937f5deb58b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2026 20:43:06 +0100 Subject: [PATCH 036/106] platform/x86: fujitsu: Convert backlight 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 backlight part of the Fujitsu laptop driver from an ACPI driver to a platform one. After this change, the backlight and input subordinate devices will be registered under the platform device used for driver binding instead of its ACPI companion. 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: Jonathan Woithe Link: https://patch.msgid.link/3407755.44csPzL39Z@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 117 +++++++++++++------------- 1 file changed, 58 insertions(+), 59 deletions(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 88fdc4fce05c..18afd887659f 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -284,15 +284,16 @@ static void fujitsu_battery_charge_control_remove(struct acpi_device *device) /* Hardware access for LCD brightness control */ -static int set_lcd_level(struct acpi_device *device, int level) +static int set_lcd_level(struct device *dev, int level) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); acpi_status status; char *method; switch (use_alt_lcd_levels) { case -1: - if (acpi_has_method(device->handle, "SBL2")) + if (acpi_has_method(handle, "SBL2")) method = "SBL2"; else method = "SBLL"; @@ -305,16 +306,14 @@ static int set_lcd_level(struct acpi_device *device, int level) break; } - acpi_handle_debug(device->handle, "set lcd level via %s [%d]\n", method, - level); + acpi_handle_debug(handle, "set lcd level via %s [%d]\n", method, level); if (level < 0 || level >= priv->max_brightness) return -EINVAL; - status = acpi_execute_simple_method(device->handle, method, level); + status = acpi_execute_simple_method(handle, method, level); if (ACPI_FAILURE(status)) { - acpi_handle_err(device->handle, "Failed to evaluate %s\n", - method); + acpi_handle_err(handle, "Failed to evaluate %s\n", method); return -ENODEV; } @@ -323,15 +322,16 @@ static int set_lcd_level(struct acpi_device *device, int level) return 0; } -static int get_lcd_level(struct acpi_device *device) +static int get_lcd_level(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long state = 0; acpi_status status = AE_OK; - acpi_handle_debug(device->handle, "get lcd level via GBLL\n"); + acpi_handle_debug(handle, "get lcd level via GBLL\n"); - status = acpi_evaluate_integer(device->handle, "GBLL", NULL, &state); + status = acpi_evaluate_integer(handle, "GBLL", NULL, &state); if (ACPI_FAILURE(status)) return 0; @@ -340,15 +340,16 @@ static int get_lcd_level(struct acpi_device *device) return priv->brightness_level; } -static int get_max_brightness(struct acpi_device *device) +static int get_max_brightness(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long state = 0; acpi_status status = AE_OK; - acpi_handle_debug(device->handle, "get max lcd level via RBLL\n"); + acpi_handle_debug(handle, "get max lcd level via RBLL\n"); - status = acpi_evaluate_integer(device->handle, "RBLL", NULL, &state); + status = acpi_evaluate_integer(handle, "RBLL", NULL, &state); if (ACPI_FAILURE(status)) return -1; @@ -361,15 +362,13 @@ static int get_max_brightness(struct acpi_device *device) static int bl_get_brightness(struct backlight_device *b) { - struct acpi_device *device = bl_get_data(b); + struct device *dev = bl_get_data(b); - return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(device); + return b->props.power == BACKLIGHT_POWER_OFF ? 0 : get_lcd_level(dev); } static int bl_update_status(struct backlight_device *b) { - struct acpi_device *device = bl_get_data(b); - if (fext) { if (b->props.power == BACKLIGHT_POWER_OFF) call_fext_func(fext, FUNC_BACKLIGHT, 0x1, @@ -379,7 +378,7 @@ static int bl_update_status(struct backlight_device *b) BACKLIGHT_PARAM_POWER, BACKLIGHT_ON); } - return set_lcd_level(device, b->props.brightness); + return set_lcd_level(bl_get_data(b), b->props.brightness); } static const struct backlight_ops fujitsu_bl_ops = { @@ -455,12 +454,13 @@ static const struct key_entry keymap_backlight[] = { { KE_END, 0 } }; -static int acpi_fujitsu_bl_input_setup(struct acpi_device *device) +static int acpi_fujitsu_bl_input_setup(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); int ret; - priv->input = devm_input_allocate_device(&device->dev); + priv->input = devm_input_allocate_device(dev); if (!priv->input) return -ENOMEM; @@ -479,9 +479,9 @@ static int acpi_fujitsu_bl_input_setup(struct acpi_device *device) return input_register_device(priv->input); } -static int fujitsu_backlight_register(struct acpi_device *device) +static int fujitsu_backlight_register(struct device *dev) { - struct fujitsu_bl *priv = acpi_driver_data(device); + struct fujitsu_bl *priv = dev_get_drvdata(dev); const struct backlight_properties props = { .brightness = priv->brightness_level, .max_brightness = priv->max_brightness - 1, @@ -489,9 +489,8 @@ static int fujitsu_backlight_register(struct acpi_device *device) }; struct backlight_device *bd; - bd = devm_backlight_device_register(&device->dev, "fujitsu-laptop", - &device->dev, device, - &fujitsu_bl_ops, &props); + bd = devm_backlight_device_register(dev, "fujitsu-laptop", + dev, dev, &fujitsu_bl_ops, &props); if (IS_ERR(bd)) return PTR_ERR(bd); @@ -504,73 +503,74 @@ static int fujitsu_backlight_register(struct acpi_device *device) static void acpi_fujitsu_bl_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct fujitsu_bl *priv = acpi_driver_data(device); + struct device *dev = data; + struct fujitsu_bl *priv = dev_get_drvdata(dev); int oldb, newb; if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "unsupported event [0x%x]\n", - event); + acpi_handle_info(handle, "unsupported event [0x%x]\n", event); sparse_keymap_report_event(priv->input, -1, 1, true); return; } oldb = priv->brightness_level; - get_lcd_level(device); + get_lcd_level(dev); newb = priv->brightness_level; - acpi_handle_debug(device->handle, - "brightness button event [%i -> %i]\n", oldb, newb); + acpi_handle_debug(handle, "brightness button event [%i -> %i]\n", + oldb, newb); if (oldb == newb) return; if (!disable_brightness_adjust) - set_lcd_level(device, newb); + set_lcd_level(dev, newb); sparse_keymap_report_event(priv->input, oldb < newb, 1, true); } -static int acpi_fujitsu_bl_add(struct acpi_device *device) +static int acpi_fujitsu_bl_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct fujitsu_bl *priv; int ret; if (acpi_video_get_backlight_type() != acpi_backlight_vendor) return -ENODEV; - priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; fujitsu_bl = priv; strscpy(acpi_device_name(device), ACPI_FUJITSU_BL_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); - device->driver_data = priv; + + platform_set_drvdata(pdev, priv); pr_info("ACPI: %s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); - if (get_max_brightness(device) <= 0) + if (get_max_brightness(&pdev->dev) <= 0) priv->max_brightness = FUJITSU_LCD_N_LEVELS; - get_lcd_level(device); + get_lcd_level(&pdev->dev); - ret = acpi_fujitsu_bl_input_setup(device); + ret = acpi_fujitsu_bl_input_setup(&pdev->dev); if (ret) return ret; - ret = fujitsu_backlight_register(device); + ret = fujitsu_backlight_register(&pdev->dev); if (ret) return ret; return acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_fujitsu_bl_notify, device); + acpi_fujitsu_bl_notify, &pdev->dev); } -static void acpi_fujitsu_bl_remove(struct acpi_device *device) +static void acpi_fujitsu_bl_remove(struct platform_device *pdev) { - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_fujitsu_bl_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, acpi_fujitsu_bl_notify); } /* ACPI device for hotkey handling */ @@ -1109,14 +1109,13 @@ static const struct acpi_device_id fujitsu_bl_device_ids[] = { {"", 0}, }; -static struct acpi_driver acpi_fujitsu_bl_driver = { - .name = ACPI_FUJITSU_BL_DRIVER_NAME, - .class = ACPI_FUJITSU_CLASS, - .ids = fujitsu_bl_device_ids, - .ops = { - .add = acpi_fujitsu_bl_add, - .remove = acpi_fujitsu_bl_remove, - }, +static struct platform_driver acpi_fujitsu_bl_driver = { + .probe = acpi_fujitsu_bl_probe, + .remove = acpi_fujitsu_bl_remove, + .driver = { + .name = ACPI_FUJITSU_BL_DRIVER_NAME, + .acpi_match_table = fujitsu_bl_device_ids, + }, }; static const struct acpi_device_id fujitsu_laptop_device_ids[] = { @@ -1145,7 +1144,7 @@ static int __init fujitsu_init(void) { int ret; - ret = acpi_bus_register_driver(&acpi_fujitsu_bl_driver); + ret = platform_driver_register(&acpi_fujitsu_bl_driver); if (ret) return ret; @@ -1168,7 +1167,7 @@ static int __init fujitsu_init(void) err_unregister_platform_driver: platform_driver_unregister(&fujitsu_pf_driver); err_unregister_acpi: - acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); + platform_driver_unregister(&acpi_fujitsu_bl_driver); return ret; } @@ -1179,7 +1178,7 @@ static void __exit fujitsu_cleanup(void) platform_driver_unregister(&fujitsu_pf_driver); - acpi_bus_unregister_driver(&acpi_fujitsu_bl_driver); + platform_driver_unregister(&acpi_fujitsu_bl_driver); pr_info("driver unloaded\n"); } From 6da22b031a3cf995d6488500a5edae07cae3df2d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 17 Mar 2026 20:44:38 +0100 Subject: [PATCH 037/106] platform/x86: fujitsu: Convert laptop 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 main part of the Fujitsu laptop driver from an ACPI driver to a platform one. After this change, the subordinate LED and input devices will be registered under the platform device used for driver binding instead of its ACPI companion. 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: Jonathan Woithe Link: https://patch.msgid.link/10818905.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/fujitsu-laptop.c | 202 +++++++++++++------------- 1 file changed, 97 insertions(+), 105 deletions(-) diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index 18afd887659f..2e265be2267e 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -144,11 +144,11 @@ struct fujitsu_laptop { bool charge_control_supported; }; -static struct acpi_device *fext; +static struct device *fext; /* Fujitsu ACPI interface function */ -static int call_fext_func(struct acpi_device *device, +static int call_fext_func(struct device *dev, int func, int op, int feature, int state) { union acpi_object params[4] = { @@ -158,18 +158,17 @@ static int call_fext_func(struct acpi_device *device, { .integer.type = ACPI_TYPE_INTEGER, .integer.value = state } }; struct acpi_object_list arg_list = { 4, params }; + acpi_handle handle = ACPI_HANDLE(dev); unsigned long long value; acpi_status status; - status = acpi_evaluate_integer(device->handle, "FUNC", &arg_list, - &value); + status = acpi_evaluate_integer(handle, "FUNC", &arg_list, &value); if (ACPI_FAILURE(status)) { - acpi_handle_err(device->handle, "Failed to evaluate FUNC\n"); + acpi_handle_err(handle, "Failed to evaluate FUNC\n"); return -ENODEV; } - acpi_handle_debug(device->handle, - "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", + acpi_handle_debug(handle, "FUNC 0x%x (args 0x%x, 0x%x, 0x%x) returned 0x%x\n", func, op, feature, state, (int)value); return value; } @@ -251,9 +250,9 @@ static struct acpi_battery_hook battery_hook = { * These functions are intended to be called from acpi_fujitsu_laptop_add and * acpi_fujitsu_laptop_remove. */ -static int fujitsu_battery_charge_control_add(struct acpi_device *device) +static int fujitsu_battery_charge_control_add(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int s006_cc_return; priv->charge_control_supported = false; @@ -274,9 +273,9 @@ static int fujitsu_battery_charge_control_add(struct acpi_device *device) return 0; } -static void fujitsu_battery_charge_control_remove(struct acpi_device *device) +static void fujitsu_battery_charge_control_remove(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); if (priv->charge_control_supported) battery_hook_unregister(&battery_hook); @@ -665,12 +664,13 @@ static const struct dmi_system_id fujitsu_laptop_dmi_table[] = { {} }; -static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device) +static int acpi_fujitsu_laptop_input_setup(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); int ret; - priv->input = devm_input_allocate_device(&device->dev); + priv->input = devm_input_allocate_device(dev); if (!priv->input) return -ENOMEM; @@ -689,9 +689,9 @@ static int acpi_fujitsu_laptop_input_setup(struct acpi_device *device) return input_register_device(priv->input); } -static int fujitsu_laptop_platform_add(struct acpi_device *device) +static int fujitsu_laptop_platform_add(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int ret; priv->pf_device = platform_device_alloc("fujitsu-laptop", PLATFORM_DEVID_NONE); @@ -719,9 +719,9 @@ err_put_platform_device: return ret; } -static void fujitsu_laptop_platform_remove(struct acpi_device *device) +static void fujitsu_laptop_platform_remove(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); sysfs_remove_group(&priv->pf_device->dev.kobj, &fujitsu_pf_attribute_group); @@ -731,7 +731,7 @@ static void fujitsu_laptop_platform_remove(struct acpi_device *device) static int logolamp_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int poweron = FUNC_LED_ON, always = FUNC_LED_ON; int ret; @@ -741,23 +741,23 @@ static int logolamp_set(struct led_classdev *cdev, if (brightness < LED_FULL) always = FUNC_LED_OFF; - ret = call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); + ret = call_fext_func(parent, FUNC_LEDS, 0x1, LOGOLAMP_POWERON, poweron); if (ret < 0) return ret; - return call_fext_func(device, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); + return call_fext_func(parent, FUNC_LEDS, 0x1, LOGOLAMP_ALWAYS, always); } static enum led_brightness logolamp_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int ret; - ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); + ret = call_fext_func(parent, FUNC_LEDS, 0x2, LOGOLAMP_ALWAYS, 0x0); if (ret == FUNC_LED_ON) return LED_FULL; - ret = call_fext_func(device, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); + ret = call_fext_func(parent, FUNC_LEDS, 0x2, LOGOLAMP_POWERON, 0x0); if (ret == FUNC_LED_ON) return LED_HALF; @@ -767,22 +767,21 @@ static enum led_brightness logolamp_get(struct led_classdev *cdev) static int kblamps_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, + return call_fext_func(parent, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_ON); else - return call_fext_func(device, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, + return call_fext_func(parent, FUNC_LEDS, 0x1, KEYBOARD_LAMPS, FUNC_LED_OFF); } static enum led_brightness kblamps_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, + if (call_fext_func(cdev->dev->parent, FUNC_LEDS, 0x2, KEYBOARD_LAMPS, 0x0) == FUNC_LED_ON) brightness = LED_FULL; @@ -792,22 +791,22 @@ static enum led_brightness kblamps_get(struct led_classdev *cdev) static int radio_led_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON, + return call_fext_func(parent, FUNC_FLAGS, 0x5, RADIO_LED_ON, RADIO_LED_ON); else - return call_fext_func(device, FUNC_FLAGS, 0x5, RADIO_LED_ON, + return call_fext_func(parent, FUNC_FLAGS, 0x5, RADIO_LED_ON, 0x0); } static enum led_brightness radio_led_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) + if (call_fext_func(parent, FUNC_FLAGS, 0x4, 0x0, 0x0) & RADIO_LED_ON) brightness = LED_FULL; return brightness; @@ -816,60 +815,58 @@ static enum led_brightness radio_led_get(struct led_classdev *cdev) static int eco_led_set(struct led_classdev *cdev, enum led_brightness brightness) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; int curr; - curr = call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0); + curr = call_fext_func(parent, FUNC_LEDS, 0x2, ECO_LED, 0x0); if (brightness >= LED_FULL) - return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(parent, FUNC_LEDS, 0x1, ECO_LED, curr | ECO_LED_ON); else - return call_fext_func(device, FUNC_LEDS, 0x1, ECO_LED, + return call_fext_func(parent, FUNC_LEDS, 0x1, ECO_LED, curr & ~ECO_LED_ON); } static enum led_brightness eco_led_get(struct led_classdev *cdev) { - struct acpi_device *device = to_acpi_device(cdev->dev->parent); + struct device *parent = cdev->dev->parent; enum led_brightness brightness = LED_OFF; - if (call_fext_func(device, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) + if (call_fext_func(parent, FUNC_LEDS, 0x2, ECO_LED, 0x0) & ECO_LED_ON) brightness = LED_FULL; return brightness; } -static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) +static int acpi_fujitsu_laptop_leds_register(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); struct led_classdev *led; int ret; - if (call_fext_func(device, - FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if (call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & LOGOLAMP_POWERON) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::logolamp"; led->brightness_set_blocking = logolamp_set; led->brightness_get = logolamp_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } - if ((call_fext_func(device, - FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && - (call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if ((call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & KEYBOARD_LAMPS) && + (call_fext_func(dev, FUNC_BUTTONS, 0x0, 0x0, 0x0) == 0x0)) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::kblamps"; led->brightness_set_blocking = kblamps_set; led->brightness_get = kblamps_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -884,7 +881,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) * whether given model has a radio toggle button. */ if (priv->flags_supported & BIT(17)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; @@ -892,7 +889,7 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) led->brightness_set_blocking = radio_led_set; led->brightness_get = radio_led_get; led->default_trigger = "rfkill-any"; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -902,17 +899,16 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) * bit 14 seems to indicate presence of said led as well. * Confirm by testing the status. */ - if ((call_fext_func(device, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && - (call_fext_func(device, - FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { - led = devm_kzalloc(&device->dev, sizeof(*led), GFP_KERNEL); + if ((call_fext_func(dev, FUNC_LEDS, 0x0, 0x0, 0x0) & BIT(14)) && + (call_fext_func(dev, FUNC_LEDS, 0x2, ECO_LED, 0x0) != UNSUPPORTED_CMD)) { + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); if (!led) return -ENOMEM; led->name = "fujitsu::eco_led"; led->brightness_set_blocking = eco_led_set; led->brightness_get = eco_led_get; - ret = devm_led_classdev_register(&device->dev, led); + ret = devm_led_classdev_register(dev, led); if (ret) return ret; } @@ -920,9 +916,9 @@ static int acpi_fujitsu_laptop_leds_register(struct acpi_device *device) return 0; } -static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) +static void acpi_fujitsu_laptop_press(struct device *dev, int scancode) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int ret; ret = kfifo_in_locked(&priv->fifo, (unsigned char *)&scancode, @@ -937,9 +933,9 @@ static void acpi_fujitsu_laptop_press(struct acpi_device *device, int scancode) scancode); } -static void acpi_fujitsu_laptop_release(struct acpi_device *device) +static void acpi_fujitsu_laptop_release(struct device *dev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = dev_get_drvdata(dev); int scancode, ret; while (true) { @@ -955,34 +951,30 @@ static void acpi_fujitsu_laptop_release(struct acpi_device *device) static void acpi_fujitsu_laptop_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct device *dev = data; + struct fujitsu_laptop *priv = dev_get_drvdata(dev); unsigned long flags; int scancode, i = 0; unsigned int irb; if (event != ACPI_FUJITSU_NOTIFY_CODE) { - acpi_handle_info(device->handle, "Unsupported event [0x%x]\n", - event); + acpi_handle_info(handle, "Unsupported event [0x%x]\n", event); sparse_keymap_report_event(priv->input, -1, 1, true); return; } if (priv->flags_supported) - priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, - 0x0); + priv->flags_state = call_fext_func(dev, FUNC_FLAGS, 0x4, 0x0, 0x0); - while ((irb = call_fext_func(device, - FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && + while ((irb = call_fext_func(dev, FUNC_BUTTONS, 0x1, 0x0, 0x0)) != 0 && i++ < MAX_HOTKEY_RINGBUFFER_SIZE) { scancode = irb & 0x4ff; if (sparse_keymap_entry_from_scancode(priv->input, scancode)) - acpi_fujitsu_laptop_press(device, scancode); + acpi_fujitsu_laptop_press(dev, scancode); else if (scancode == 0) - acpi_fujitsu_laptop_release(device); + acpi_fujitsu_laptop_release(dev); else - acpi_handle_info(device->handle, - "Unknown GIRB result [%x]\n", irb); + acpi_handle_info(handle, "Unknown GIRB result [%x]\n", irb); } /* @@ -992,28 +984,30 @@ static void acpi_fujitsu_laptop_notify(acpi_handle handle, u32 event, void *data * status flags queried using FUNC_FLAGS. */ if (priv->flags_supported & (FLAG_SOFTKEYS)) { - flags = call_fext_func(device, FUNC_FLAGS, 0x1, 0x0, 0x0); + flags = call_fext_func(dev, FUNC_FLAGS, 0x1, 0x0, 0x0); flags &= (FLAG_SOFTKEYS); for_each_set_bit(i, &flags, BITS_PER_LONG) sparse_keymap_report_event(priv->input, BIT(i), 1, true); } } -static int acpi_fujitsu_laptop_add(struct acpi_device *device) +static int acpi_fujitsu_laptop_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct fujitsu_laptop *priv; int ret, i = 0; - priv = devm_kzalloc(&device->dev, sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; WARN_ONCE(fext, "More than one FUJ02E3 ACPI device was found. Driver may not work as intended."); - fext = device; + fext = &pdev->dev; strscpy(acpi_device_name(device), ACPI_FUJITSU_LAPTOP_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_FUJITSU_CLASS); - device->driver_data = priv; + + platform_set_drvdata(pdev, priv); /* kfifo */ spin_lock_init(&priv->fifo_lock); @@ -1025,14 +1019,13 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) pr_info("ACPI: %s [%s]\n", acpi_device_name(device), acpi_device_bid(device)); - while (call_fext_func(device, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && + while (call_fext_func(fext, FUNC_BUTTONS, 0x1, 0x0, 0x0) != 0 && i++ < MAX_HOTKEY_RINGBUFFER_SIZE) ; /* No action, result is discarded */ acpi_handle_debug(device->handle, "Discarded %i ringbuffer entries\n", i); - priv->flags_supported = call_fext_func(device, FUNC_FLAGS, 0x0, 0x0, - 0x0); + priv->flags_supported = call_fext_func(fext, FUNC_FLAGS, 0x0, 0x0, 0x0); /* Make sure our bitmask of supported functions is cleared if the RFKILL function block is not implemented, like on the S7020. */ @@ -1040,12 +1033,12 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) priv->flags_supported = 0; if (priv->flags_supported) - priv->flags_state = call_fext_func(device, FUNC_FLAGS, 0x4, 0x0, + priv->flags_state = call_fext_func(fext, FUNC_FLAGS, 0x4, 0x0, 0x0); /* Suspect this is a keymap of the application panel, print it */ acpi_handle_info(device->handle, "BTNI: [0x%x]\n", - call_fext_func(device, FUNC_BUTTONS, 0x0, 0x0, 0x0)); + call_fext_func(fext, FUNC_BUTTONS, 0x0, 0x0, 0x0)); /* Sync backlight power status */ if (fujitsu_bl && fujitsu_bl->bl_device && @@ -1057,47 +1050,47 @@ static int acpi_fujitsu_laptop_add(struct acpi_device *device) fujitsu_bl->bl_device->props.power = BACKLIGHT_POWER_ON; } - ret = acpi_fujitsu_laptop_input_setup(device); + ret = acpi_fujitsu_laptop_input_setup(fext); if (ret) goto err_free_fifo; - ret = acpi_fujitsu_laptop_leds_register(device); + ret = acpi_fujitsu_laptop_leds_register(fext); if (ret) goto err_free_fifo; - ret = fujitsu_laptop_platform_add(device); + ret = fujitsu_laptop_platform_add(fext); if (ret) goto err_free_fifo; ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_fujitsu_laptop_notify, device); + acpi_fujitsu_laptop_notify, fext); if (ret) goto err_platform_remove; - ret = fujitsu_battery_charge_control_add(device); + ret = fujitsu_battery_charge_control_add(fext); if (ret < 0) pr_warn("Unable to register battery charge control: %d\n", ret); return 0; err_platform_remove: - fujitsu_laptop_platform_remove(device); + fujitsu_laptop_platform_remove(fext); err_free_fifo: kfifo_free(&priv->fifo); return ret; } -static void acpi_fujitsu_laptop_remove(struct acpi_device *device) +static void acpi_fujitsu_laptop_remove(struct platform_device *pdev) { - struct fujitsu_laptop *priv = acpi_driver_data(device); + struct fujitsu_laptop *priv = platform_get_drvdata(pdev); - fujitsu_battery_charge_control_remove(device); + fujitsu_battery_charge_control_remove(&pdev->dev); - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), ACPI_DEVICE_NOTIFY, acpi_fujitsu_laptop_notify); - fujitsu_laptop_platform_remove(device); + fujitsu_laptop_platform_remove(&pdev->dev); kfifo_free(&priv->fifo); } @@ -1123,14 +1116,13 @@ static const struct acpi_device_id fujitsu_laptop_device_ids[] = { {"", 0}, }; -static struct acpi_driver acpi_fujitsu_laptop_driver = { - .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME, - .class = ACPI_FUJITSU_CLASS, - .ids = fujitsu_laptop_device_ids, - .ops = { - .add = acpi_fujitsu_laptop_add, - .remove = acpi_fujitsu_laptop_remove, - }, +static struct platform_driver acpi_fujitsu_laptop_driver = { + .probe = acpi_fujitsu_laptop_probe, + .remove = acpi_fujitsu_laptop_remove, + .driver = { + .name = ACPI_FUJITSU_LAPTOP_DRIVER_NAME, + .acpi_match_table = fujitsu_laptop_device_ids, + }, }; static const struct acpi_device_id fujitsu_ids[] __used = { @@ -1156,7 +1148,7 @@ static int __init fujitsu_init(void) /* Register laptop driver */ - ret = acpi_bus_register_driver(&acpi_fujitsu_laptop_driver); + ret = platform_driver_register(&acpi_fujitsu_laptop_driver); if (ret) goto err_unregister_platform_driver; @@ -1174,7 +1166,7 @@ err_unregister_acpi: static void __exit fujitsu_cleanup(void) { - acpi_bus_unregister_driver(&acpi_fujitsu_laptop_driver); + platform_driver_unregister(&acpi_fujitsu_laptop_driver); platform_driver_unregister(&fujitsu_pf_driver); From e2b0cd5c265f01977ed4e7f04bd989cdb0b9eaed Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2026 11:30:32 +0100 Subject: [PATCH 038/106] platform/x86: panasonic-laptop: Make pcc_register_optd_notifier() void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert pcc_register_optd_notifier() whose return value is never used to a void function. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5093613.31r3eYUQgx@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index d923ddaa4849..a481f2602ce3 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -891,7 +891,7 @@ static void pcc_optd_notify(acpi_handle handle, u32 event, void *data) set_optd_power_state(0); } -static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) +static void pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) { acpi_status status; acpi_handle handle; @@ -904,10 +904,7 @@ static int pcc_register_optd_notifier(struct pcc_acpi *pcc, char *node) pcc_optd_notify, pcc); if (ACPI_FAILURE(status)) pr_err("Failed to register notify on %s\n", node); - } else - return -ENODEV; - - return 0; + } } static void pcc_unregister_optd_notifier(struct pcc_acpi *pcc, char *node) From 8baeff2c1d33dad8572216c6ad3a7425852507d4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2026 11:31:54 +0100 Subject: [PATCH 039/106] platform/x86: panasonic-laptop: Fix OPTD notifier registration and cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An ACPI notify handler is leaked if device_create_file() returns an error in acpi_pcc_hotkey_add(). Also, it is pointless to call pcc_unregister_optd_notifier() in acpi_pcc_hotkey_remove() if pcc->platform is NULL and it is better to arrange the cleanup code in that function in the same order as the rollback code in acpi_pcc_hotkey_add(). Address the above by placing the pcc_register_optd_notifier() call in acpi_pcc_hotkey_add() after the device_create_file() return value check and placing the pcc_unregister_optd_notifier() call in acpi_pcc_hotkey_remove() right before the device_remove_file() call. Fixes: d5a81d8e864b ("platform/x86: panasonic-laptop: Add support for optical driver power in Y and W series") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2411055.ElGaqSPkdT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index a481f2602ce3..56b4e61d7e5c 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -1090,9 +1090,10 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) } result = device_create_file(&pcc->platform->dev, &dev_attr_cdpower); - pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); if (result) goto out_platform; + + pcc_register_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); } else { pcc->platform = NULL; } @@ -1126,10 +1127,10 @@ static void acpi_pcc_hotkey_remove(struct acpi_device *device) i8042_remove_filter(panasonic_i8042_filter); if (pcc->platform) { + pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); device_remove_file(&pcc->platform->dev, &dev_attr_cdpower); platform_device_unregister(pcc->platform); } - pcc_unregister_optd_notifier(pcc, "\\_SB.PCI0.EHCI.ERHB.OPTD"); sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); From a347e11252c60bfce1bf15f2f86484cdb9bf6468 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2026 11:33:03 +0100 Subject: [PATCH 040/106] platform/x86: panasonic-laptop: Remove redundant checks from 3 functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The device pointer cannot be NULL in acpi_pcc_hotkey_add() and acpi_pcc_hotkey_remove() because these functions are ACPI driver callbacks and NULL is never passed to any of them as an argument. Likewise, acpi_pcc_hotkey_resume() is a resume callback of a device driver and NULL is never passed to it as an argument, so the dev pointer in it cannot be NULL. Moreover, since acpi_pcc_hotkey_remove() and acpi_pcc_hotkey_resume() can only run after acpi_pcc_hotkey_add() has completed successfully, the acpi_driver_data() of the device object used by them cannot be NULL when they run. Drop all of the redundant NULL checks of the pointers mentioned above. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1957824.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 56b4e61d7e5c..188ec597a14e 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -965,14 +965,7 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) #ifdef CONFIG_PM_SLEEP static int acpi_pcc_hotkey_resume(struct device *dev) { - struct pcc_acpi *pcc; - - if (!dev) - return -EINVAL; - - pcc = acpi_driver_data(to_acpi_device(dev)); - if (!pcc) - return -EINVAL; + struct pcc_acpi *pcc = acpi_driver_data(to_acpi_device(dev)); if (pcc->num_sifr > SINF_MUTE) acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); @@ -994,9 +987,6 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) struct pcc_acpi *pcc; int num_sifr, result; - if (!device) - return -EINVAL; - num_sifr = acpi_pcc_get_sqty(device); /* @@ -1121,9 +1111,6 @@ static void acpi_pcc_hotkey_remove(struct acpi_device *device) { struct pcc_acpi *pcc = acpi_driver_data(device); - if (!device || !pcc) - return; - i8042_remove_filter(panasonic_i8042_filter); if (pcc->platform) { From 0381b6a65716dae778fb4b5ee4ee42aca62402e9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2026 11:34:06 +0100 Subject: [PATCH 041/106] platform/x86: panasonic-laptop: 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/13979037.uLZWGnKmhe@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 188ec597a14e..e563298a6672 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -185,7 +185,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, static int acpi_pcc_hotkey_add(struct acpi_device *device); static void acpi_pcc_hotkey_remove(struct acpi_device *device); -static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event); +static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id pcc_device_ids[] = { { "MAT0012", 0}, @@ -208,7 +208,6 @@ static struct acpi_driver acpi_pcc_driver = { .ops = { .add = acpi_pcc_hotkey_add, .remove = acpi_pcc_hotkey_remove, - .notify = acpi_pcc_hotkey_notify, }, .drv.pm = &acpi_pcc_hotkey_pm, }; @@ -869,9 +868,9 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) pr_err("Unknown hotkey event: 0x%04llx\n", result); } -static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event) +static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data) { - struct pcc_acpi *pcc = acpi_driver_data(device); + struct pcc_acpi *pcc = data; switch (event) { case HKEY_NOTIFY: @@ -1070,13 +1069,18 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) if (result) goto out_backlight; + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify, pcc); + if (result) + goto out_sysfs; + /* optical drive initialization */ if (ACPI_SUCCESS(check_optd_present())) { pcc->platform = platform_device_register_simple("panasonic", PLATFORM_DEVID_NONE, NULL, 0); if (IS_ERR(pcc->platform)) { result = PTR_ERR(pcc->platform); - goto out_sysfs; + goto out_notify_handler; } result = device_create_file(&pcc->platform->dev, &dev_attr_cdpower); @@ -1093,6 +1097,9 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) out_platform: platform_device_unregister(pcc->platform); +out_notify_handler: + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify); out_sysfs: sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); out_backlight: @@ -1119,6 +1126,9 @@ static void acpi_pcc_hotkey_remove(struct acpi_device *device) platform_device_unregister(pcc->platform); } + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_pcc_hotkey_notify); + sysfs_remove_group(&device->dev.kobj, &pcc_attr_group); backlight_device_unregister(pcc->backlight); From de6837243af00dc22b5117f45a35ec20adbf62c5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 20 Mar 2026 11:35:54 +0100 Subject: [PATCH 042/106] platform/x86: panasonic-laptop: 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 Panasonic laptop ACPI driver to a platform one. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. To maintain backwards compatibility with possibly existing user space, the sysfs attributes created by the driver under the ACPI device object used by it are not relocated. Accordingly, the driver will continue to use the driver_data pointer in struct acpi_device which needs to be cleared on driver removal. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8664183.T7Z3S40VBb@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/panasonic-laptop.c | 34 ++++++++++++++----------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index e563298a6672..1337f7c49805 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -183,8 +183,8 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0, }; /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */ -static int acpi_pcc_hotkey_add(struct acpi_device *device); -static void acpi_pcc_hotkey_remove(struct acpi_device *device); +static int acpi_pcc_hotkey_probe(struct platform_device *pdev); +static void acpi_pcc_hotkey_remove(struct platform_device *pdev); static void acpi_pcc_hotkey_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id pcc_device_ids[] = { @@ -201,15 +201,14 @@ static int acpi_pcc_hotkey_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume); -static struct acpi_driver acpi_pcc_driver = { - .name = ACPI_PCC_DRIVER_NAME, - .class = ACPI_PCC_CLASS, - .ids = pcc_device_ids, - .ops = { - .add = acpi_pcc_hotkey_add, - .remove = acpi_pcc_hotkey_remove, - }, - .drv.pm = &acpi_pcc_hotkey_pm, +static struct platform_driver acpi_pcc_driver = { + .probe = acpi_pcc_hotkey_probe, + .remove = acpi_pcc_hotkey_remove, + .driver = { + .name = ACPI_PCC_DRIVER_NAME, + .acpi_match_table = pcc_device_ids, + .pm = &acpi_pcc_hotkey_pm, + }, }; static const struct key_entry panasonic_keymap[] = { @@ -964,7 +963,7 @@ static int acpi_pcc_init_input(struct pcc_acpi *pcc) #ifdef CONFIG_PM_SLEEP static int acpi_pcc_hotkey_resume(struct device *dev) { - struct pcc_acpi *pcc = acpi_driver_data(to_acpi_device(dev)); + struct pcc_acpi *pcc = acpi_driver_data(ACPI_COMPANION(dev)); if (pcc->num_sifr > SINF_MUTE) acpi_pcc_write_sset(pcc, SINF_MUTE, pcc->mute); @@ -980,8 +979,9 @@ static int acpi_pcc_hotkey_resume(struct device *dev) } #endif -static int acpi_pcc_hotkey_add(struct acpi_device *device) +static int acpi_pcc_hotkey_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct backlight_properties props; struct pcc_acpi *pcc; int num_sifr, result; @@ -1107,6 +1107,7 @@ out_backlight: out_input: input_unregister_device(pcc->input_dev); out_sinf: + device->driver_data = NULL; kfree(pcc->sinf); out_hotkey: kfree(pcc); @@ -1114,8 +1115,9 @@ out_hotkey: return result; } -static void acpi_pcc_hotkey_remove(struct acpi_device *device) +static void acpi_pcc_hotkey_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct pcc_acpi *pcc = acpi_driver_data(device); i8042_remove_filter(panasonic_i8042_filter); @@ -1135,8 +1137,10 @@ static void acpi_pcc_hotkey_remove(struct acpi_device *device) input_unregister_device(pcc->input_dev); + device->driver_data = NULL; + kfree(pcc->sinf); kfree(pcc); } -module_acpi_driver(acpi_pcc_driver); +module_platform_driver(acpi_pcc_driver); From 5539165cae8e133275f0eed4cfa131cda974a1a3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2026 14:50:08 +0100 Subject: [PATCH 043/106] platform/x86: system76: Drop redundant devm_led_classdev_unregister() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop two redundant devm_led_classdev_unregister() calls from system76_remove(). No intentional functional impact. Suggested-by: Ilpo Järvinen Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5057164.GXAFRqVoOG@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/system76_acpi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index 3da753b3d00d..d8b64e2c2713 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -800,9 +800,6 @@ static void system76_remove(struct acpi_device *acpi_dev) kfree(data->ntmp); } - devm_led_classdev_unregister(&acpi_dev->dev, &data->ap_led); - devm_led_classdev_unregister(&acpi_dev->dev, &data->kb_led); - system76_get(data, "FINI"); } From 89729c9c6c158259b8de14c57b5aac6a25dfac3d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2026 14:53:37 +0100 Subject: [PATCH 044/106] platform/x86: system76: 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/13970743.uLZWGnKmhe@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/system76_acpi.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index d8b64e2c2713..fc11427c36a5 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -644,11 +644,10 @@ static void input_key(struct system76_data *data, unsigned int code) } // Handle ACPI notification -static void system76_notify(struct acpi_device *acpi_dev, u32 event) +static void system76_notify(acpi_handle handle, u32 event, void *context) { - struct system76_data *data; + struct system76_data *data = context; - data = acpi_driver_data(acpi_dev); switch (event) { case 0x80: kb_led_hotkey_hardware(data); @@ -757,7 +756,12 @@ static int system76_add(struct acpi_device *acpi_dev) err = input_register_device(data->input); if (err) - goto error; + return err; + + err = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + system76_notify, data); + if (err) + return err; if (data->has_open_ec) { err = system76_get_object(data, "NFAN", &data->nfan); @@ -784,6 +788,7 @@ error: kfree(data->ntmp); kfree(data->nfan); } + acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, system76_notify); return err; } @@ -800,6 +805,8 @@ static void system76_remove(struct acpi_device *acpi_dev) kfree(data->ntmp); } + acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, system76_notify); + system76_get(data, "FINI"); } @@ -810,7 +817,6 @@ static struct acpi_driver system76_driver = { .ops = { .add = system76_add, .remove = system76_remove, - .notify = system76_notify, }, }; module_acpi_driver(system76_driver); From 80b8f68b94abcf6f2f8c71a685b2a70fd7c502b0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2026 14:54:45 +0100 Subject: [PATCH 045/106] platform/x86: system76: 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 System76 ACPI driver to a platform one. After this change, the subordinate hwmon, input and LED class devices will be registered under the platform device used for driver binding instead of its ACPI companion. 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/3401648.aeNJFYEL58@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/system76_acpi.c | 48 +++++++++++++++------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index fc11427c36a5..693cbb461382 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -670,16 +671,19 @@ static void system76_notify(acpi_handle handle, u32 event, void *context) } } -// Add a System76 ACPI device -static int system76_add(struct acpi_device *acpi_dev) +// Probe a System76 platform device +static int system76_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct system76_data *data; int err; - data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - acpi_dev->driver_data = data; + + platform_set_drvdata(pdev, data); + data->acpi_dev = acpi_dev; // Some models do not run open EC firmware. Check for an ACPI method @@ -695,7 +699,7 @@ static int system76_add(struct acpi_device *acpi_dev) data->ap_led.brightness_set_blocking = ap_led_set; data->ap_led.max_brightness = 1; data->ap_led.default_trigger = "rfkill-none"; - err = devm_led_classdev_register(&acpi_dev->dev, &data->ap_led); + err = devm_led_classdev_register(&pdev->dev, &data->ap_led); if (err) return err; @@ -739,19 +743,19 @@ static int system76_add(struct acpi_device *acpi_dev) } if (data->kbled_type != KBLED_NONE) { - err = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led); + err = devm_led_classdev_register(&pdev->dev, &data->kb_led); if (err) return err; } - data->input = devm_input_allocate_device(&acpi_dev->dev); + data->input = devm_input_allocate_device(&pdev->dev); if (!data->input) return -ENOMEM; data->input->name = "System76 ACPI Hotkeys"; data->input->phys = "system76_acpi/input0"; data->input->id.bustype = BUS_HOST; - data->input->dev.parent = &acpi_dev->dev; + data->input->dev.parent = &pdev->dev; input_set_capability(data->input, EV_KEY, KEY_SCREENLOCK); err = input_register_device(data->input); @@ -772,7 +776,7 @@ static int system76_add(struct acpi_device *acpi_dev) if (err) goto error; - data->therm = devm_hwmon_device_register_with_info(&acpi_dev->dev, + data->therm = devm_hwmon_device_register_with_info(&pdev->dev, "system76_acpi", data, &thermal_chip_info, NULL); err = PTR_ERR_OR_ZERO(data->therm); if (err) @@ -792,12 +796,10 @@ error: return err; } -// Remove a System76 ACPI device -static void system76_remove(struct acpi_device *acpi_dev) +// Remove a System76 platform device +static void system76_remove(struct platform_device *pdev) { - struct system76_data *data; - - data = acpi_driver_data(acpi_dev); + struct system76_data *data = platform_get_drvdata(pdev); if (data->has_open_ec) { system76_battery_exit(); @@ -805,21 +807,21 @@ static void system76_remove(struct acpi_device *acpi_dev) kfree(data->ntmp); } - acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, system76_notify); + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, system76_notify); system76_get(data, "FINI"); } -static struct acpi_driver system76_driver = { - .name = "System76 ACPI Driver", - .class = "hotkey", - .ids = device_ids, - .ops = { - .add = system76_add, - .remove = system76_remove, +static struct platform_driver system76_driver = { + .probe = system76_probe, + .remove = system76_remove, + .driver = { + .name = "System76 ACPI Driver", + .acpi_match_table = device_ids, }, }; -module_acpi_driver(system76_driver); +module_platform_driver(system76_driver); MODULE_DESCRIPTION("System76 ACPI Driver"); MODULE_AUTHOR("Jeremy Soller "); From da71de1b46bf42075b78e773bd3d3779548ba7a3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2026 15:35:05 +0100 Subject: [PATCH 046/106] platform/x86: dell/dell-rbtn: 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/9591832.CDJkKcVGEf@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-rbtn.c | 50 ++++++++++++++++++--------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index a415c432d4c3..b9038d9ddcad 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -207,7 +207,7 @@ static void rbtn_input_event(struct rbtn_data *rbtn_data) static int rbtn_add(struct acpi_device *device); static void rbtn_remove(struct acpi_device *device); -static void rbtn_notify(struct acpi_device *device, u32 event); +static void rbtn_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id rbtn_ids[] = { { "DELRBTN", 0 }, @@ -293,7 +293,6 @@ static struct acpi_driver rbtn_driver = { .ops = { .add = rbtn_add, .remove = rbtn_remove, - .notify = rbtn_notify, }, }; @@ -382,6 +381,22 @@ EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister); * acpi driver functions */ +static void rbtn_cleanup(struct acpi_device *device) +{ + struct rbtn_data *rbtn_data = device->driver_data; + + switch (rbtn_data->type) { + case RBTN_TOGGLE: + rbtn_input_exit(rbtn_data); + break; + case RBTN_SLIDER: + rbtn_rfkill_exit(device); + break; + default: + break; + } +} + static int rbtn_add(struct acpi_device *device) { struct rbtn_data *rbtn_data; @@ -422,31 +437,32 @@ static int rbtn_add(struct acpi_device *device) break; } if (ret) - rbtn_acquire(device, false); + goto err; + ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + rbtn_notify, device); + if (ret) + goto err_cleanup; + + return 0; + +err_cleanup: + rbtn_cleanup(device); +err: + rbtn_acquire(device, false); return ret; } static void rbtn_remove(struct acpi_device *device) { - struct rbtn_data *rbtn_data = device->driver_data; - - switch (rbtn_data->type) { - case RBTN_TOGGLE: - rbtn_input_exit(rbtn_data); - break; - case RBTN_SLIDER: - rbtn_rfkill_exit(device); - break; - default: - break; - } - + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, rbtn_notify); + rbtn_cleanup(device); rbtn_acquire(device, false); } -static void rbtn_notify(struct acpi_device *device, u32 event) +static void rbtn_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; struct rbtn_data *rbtn_data = device->driver_data; /* From 19ebacfb442b83aa5fe75f9c6851b4edbfff3760 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 18 Mar 2026 15:36:06 +0100 Subject: [PATCH 047/106] platform/x86: dell/dell-rbtn: 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 Dell Airplane Mode Switch (rbtn) ACPI driver to a platform one. After this change, the subordinate rfkill device will be registered under the platform device used for driver binding instead of its ACPI companion. 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/6141354.MhkbZ0Pkbq@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-rbtn.c | 108 +++++++++++++------------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/dell/dell-rbtn.c b/drivers/platform/x86/dell/dell-rbtn.c index b9038d9ddcad..34af9f4ff741 100644 --- a/drivers/platform/x86/dell/dell-rbtn.c +++ b/drivers/platform/x86/dell/dell-rbtn.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "dell-rbtn.h" @@ -109,9 +110,9 @@ static const struct rfkill_ops rbtn_ops = { .set_block = rbtn_rfkill_set_block, }; -static int rbtn_rfkill_init(struct acpi_device *device) +static int rbtn_rfkill_init(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); int ret; if (rbtn_data->rfkill) @@ -122,8 +123,8 @@ static int rbtn_rfkill_init(struct acpi_device *device) * but rfkill interface does not support "ANY" type * so "WLAN" type is used */ - rbtn_data->rfkill = rfkill_alloc("dell-rbtn", &device->dev, - RFKILL_TYPE_WLAN, &rbtn_ops, device); + rbtn_data->rfkill = rfkill_alloc("dell-rbtn", dev, RFKILL_TYPE_WLAN, + &rbtn_ops, ACPI_COMPANION(dev)); if (!rbtn_data->rfkill) return -ENOMEM; @@ -137,9 +138,9 @@ static int rbtn_rfkill_init(struct acpi_device *device) return 0; } -static void rbtn_rfkill_exit(struct acpi_device *device) +static void rbtn_rfkill_exit(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); if (!rbtn_data->rfkill) return; @@ -149,12 +150,12 @@ static void rbtn_rfkill_exit(struct acpi_device *device) rbtn_data->rfkill = NULL; } -static void rbtn_rfkill_event(struct acpi_device *device) +static void rbtn_rfkill_event(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); if (rbtn_data->rfkill) - rbtn_rfkill_query(rbtn_data->rfkill, device); + rbtn_rfkill_query(rbtn_data->rfkill, ACPI_COMPANION(dev)); } @@ -205,8 +206,8 @@ static void rbtn_input_event(struct rbtn_data *rbtn_data) * acpi driver */ -static int rbtn_add(struct acpi_device *device); -static void rbtn_remove(struct acpi_device *device); +static int rbtn_probe(struct platform_device *pdev); +static void rbtn_remove(struct platform_device *pdev); static void rbtn_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id rbtn_ids[] = { @@ -251,8 +252,7 @@ static void ACPI_SYSTEM_XFACE rbtn_clear_suspended_flag(void *context) static int rbtn_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = acpi_driver_data(device); + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); rbtn_data->suspended = true; @@ -261,8 +261,7 @@ static int rbtn_suspend(struct device *dev) static int rbtn_resume(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = acpi_driver_data(device); + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); acpi_status status; /* @@ -286,13 +285,13 @@ static int rbtn_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(rbtn_pm_ops, rbtn_suspend, rbtn_resume); -static struct acpi_driver rbtn_driver = { - .name = "dell-rbtn", - .ids = rbtn_ids, - .drv.pm = &rbtn_pm_ops, - .ops = { - .add = rbtn_add, - .remove = rbtn_remove, +static struct platform_driver rbtn_driver = { + .probe = rbtn_probe, + .remove = rbtn_remove, + .driver = { + .name = "dell-rbtn", + .acpi_match_table = rbtn_ids, + .pm = &rbtn_pm_ops, }, }; @@ -307,8 +306,7 @@ static ATOMIC_NOTIFIER_HEAD(rbtn_chain_head); static int rbtn_inc_count(struct device *dev, void *data) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); int *count = data; if (rbtn_data->type == RBTN_SLIDER) @@ -319,17 +317,16 @@ static int rbtn_inc_count(struct device *dev, void *data) static int rbtn_switch_dev(struct device *dev, void *data) { - struct acpi_device *device = to_acpi_device(dev); - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); bool enable = data; if (rbtn_data->type != RBTN_SLIDER) return 0; if (enable) - rbtn_rfkill_init(device); + rbtn_rfkill_init(dev); else - rbtn_rfkill_exit(device); + rbtn_rfkill_exit(dev); return 0; } @@ -341,7 +338,7 @@ int dell_rbtn_notifier_register(struct notifier_block *nb) int ret; count = 0; - ret = driver_for_each_device(&rbtn_driver.drv, NULL, &count, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, &count, rbtn_inc_count); if (ret || count == 0) return -ENODEV; @@ -353,7 +350,7 @@ int dell_rbtn_notifier_register(struct notifier_block *nb) return ret; if (auto_remove_rfkill && first) - ret = driver_for_each_device(&rbtn_driver.drv, NULL, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, (void *)false, rbtn_switch_dev); return ret; @@ -369,7 +366,7 @@ int dell_rbtn_notifier_unregister(struct notifier_block *nb) return ret; if (auto_remove_rfkill && !rbtn_chain_head.head) - ret = driver_for_each_device(&rbtn_driver.drv, NULL, + ret = driver_for_each_device(&rbtn_driver.driver, NULL, (void *)true, rbtn_switch_dev); return ret; @@ -381,46 +378,48 @@ EXPORT_SYMBOL_GPL(dell_rbtn_notifier_unregister); * acpi driver functions */ -static void rbtn_cleanup(struct acpi_device *device) +static void rbtn_cleanup(struct device *dev) { - struct rbtn_data *rbtn_data = device->driver_data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); switch (rbtn_data->type) { case RBTN_TOGGLE: rbtn_input_exit(rbtn_data); break; case RBTN_SLIDER: - rbtn_rfkill_exit(device); + rbtn_rfkill_exit(dev); break; default: break; } } -static int rbtn_add(struct acpi_device *device) +static int rbtn_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct rbtn_data *rbtn_data; enum rbtn_type type; int ret = 0; type = rbtn_check(device); if (type == RBTN_UNKNOWN) { - dev_info(&device->dev, "Unknown device type\n"); + dev_info(&pdev->dev, "Unknown device type\n"); return -EINVAL; } - rbtn_data = devm_kzalloc(&device->dev, sizeof(*rbtn_data), GFP_KERNEL); + rbtn_data = devm_kzalloc(&pdev->dev, sizeof(*rbtn_data), GFP_KERNEL); if (!rbtn_data) return -ENOMEM; ret = rbtn_acquire(device, true); if (ret < 0) { - dev_err(&device->dev, "Cannot enable device\n"); + dev_err(&pdev->dev, "Cannot enable device\n"); return ret; } + platform_set_drvdata(pdev, rbtn_data); + rbtn_data->type = type; - device->driver_data = rbtn_data; switch (rbtn_data->type) { case RBTN_TOGGLE: @@ -430,7 +429,7 @@ static int rbtn_add(struct acpi_device *device) if (auto_remove_rfkill && rbtn_chain_head.head) ret = 0; else - ret = rbtn_rfkill_init(device); + ret = rbtn_rfkill_init(&pdev->dev); break; default: ret = -EINVAL; @@ -440,42 +439,44 @@ static int rbtn_add(struct acpi_device *device) goto err; ret = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - rbtn_notify, device); + rbtn_notify, &pdev->dev); if (ret) goto err_cleanup; return 0; err_cleanup: - rbtn_cleanup(device); + rbtn_cleanup(&pdev->dev); err: rbtn_acquire(device, false); return ret; } -static void rbtn_remove(struct acpi_device *device) +static void rbtn_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, rbtn_notify); - rbtn_cleanup(device); + rbtn_cleanup(&pdev->dev); rbtn_acquire(device, false); } static void rbtn_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct rbtn_data *rbtn_data = device->driver_data; + struct device *dev = data; + struct rbtn_data *rbtn_data = dev_get_drvdata(dev); /* * Some BIOSes send a notification at resume. * Ignore it to prevent unwanted input events. */ if (rbtn_data->suspended) { - dev_dbg(&device->dev, "ACPI notification ignored\n"); + dev_dbg(dev, "ACPI notification ignored\n"); return; } if (event != 0x80) { - dev_info(&device->dev, "Received unknown event (0x%x)\n", + dev_info(dev, "Received unknown event (0x%x)\n", event); return; } @@ -485,20 +486,15 @@ static void rbtn_notify(acpi_handle handle, u32 event, void *data) rbtn_input_event(rbtn_data); break; case RBTN_SLIDER: - rbtn_rfkill_event(device); - atomic_notifier_call_chain(&rbtn_chain_head, event, device); + rbtn_rfkill_event(dev); + atomic_notifier_call_chain(&rbtn_chain_head, event, NULL); break; default: break; } } - -/* - * module functions - */ - -module_acpi_driver(rbtn_driver); +module_platform_driver(rbtn_driver); module_param(auto_remove_rfkill, bool, 0444); From c62bf0b0c7608b0b47548e0f52d65183ad6ce2eb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Mar 2026 23:11:43 +0100 Subject: [PATCH 048/106] platform/surface: hotplug: Correct inclusion for GPIO APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The modern GPIO APIs are available for users via linux/gpio/consumer.h. The linux/gpio.h is legacy header that is subject to remove. Hence replace the latter by the former in the driver. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260320221143.3237791-1-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/surface/surface_hotplug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/surface/surface_hotplug.c b/drivers/platform/surface/surface_hotplug.c index c0d83ed5a208..33a8a9d41900 100644 --- a/drivers/platform/surface/surface_hotplug.c +++ b/drivers/platform/surface/surface_hotplug.c @@ -14,7 +14,7 @@ */ #include -#include +#include #include #include #include From 8786af7704e6289c69d2723def3ca406a15c0dff Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 20 Mar 2026 23:20:33 +0100 Subject: [PATCH 049/106] platform/mellanox: nvsw-sn2201: Drop unused include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver includes the legacy header but does not use any symbols from it. Drop the inclusion. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260320222033.3238317-1-andriy.shevchenko@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/mellanox/nvsw-sn2201.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/platform/mellanox/nvsw-sn2201.c b/drivers/platform/mellanox/nvsw-sn2201.c index 51504113c17e..92b58ba8f97b 100644 --- a/drivers/platform/mellanox/nvsw-sn2201.c +++ b/drivers/platform/mellanox/nvsw-sn2201.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include From 1c9d30d37aaffe3454d70b89a77f8aaecda257bf Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 18 Mar 2026 19:56:17 -0700 Subject: [PATCH 050/106] platform/x86: barco-p50-gpio: normalize return value of gpio_get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The GPIO get callback is expected to return 0 or 1 (or a negative error code). Ensure that the value returned by p50_gpio_get() is normalized to the [0, 1] range. Fixes: 86ef402d805d606a ("gpiolib: sanitize the return value of gpio_chip::get()") Reviewed-by: Linus Walleij Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260318-barco-p50-gpio-set-v2-1-c0a4a6416163@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/barco-p50-gpio.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 6f13e81f98fb..360ffd8505d6 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -275,8 +275,11 @@ static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) mutex_lock(&p50->lock); ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); - if (ret == 0) + if (ret == 0) { ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + if (ret >= 0) + ret = !!ret; + } mutex_unlock(&p50->lock); From a5877e921389178f994a5ec15a145d7e7ba3ec65 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 18 Mar 2026 19:56:18 -0700 Subject: [PATCH 051/106] platform/x86: barco-p50-gpio: convert to guard() notation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using guard notation simplifies control flow and makes the code clearer. Suggested-by: Ilpo Järvinen Signed-off-by: Dmitry Torokhov Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260318-barco-p50-gpio-set-v2-2-c0a4a6416163@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/barco-p50-gpio.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c index 360ffd8505d6..2a6d8607c402 100644 --- a/drivers/platform/x86/barco-p50-gpio.c +++ b/drivers/platform/x86/barco-p50-gpio.c @@ -272,33 +272,27 @@ static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset) struct p50_gpio *p50 = gpiochip_get_data(gc); int ret; - mutex_lock(&p50->lock); + guard(mutex)(&p50->lock); ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0); - if (ret == 0) { - ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); - if (ret >= 0) - ret = !!ret; - } + if (ret < 0) + return ret; - mutex_unlock(&p50->lock); + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA); + if (ret < 0) + return ret; - return ret; + return !!ret; } static int p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value) { struct p50_gpio *p50 = gpiochip_get_data(gc); - int ret; - mutex_lock(&p50->lock); + guard(mutex)(&p50->lock); - ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, - gpio_params[offset], value); - - mutex_unlock(&p50->lock); - - return ret; + return p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, + gpio_params[offset], value); } static int p50_gpio_probe(struct platform_device *pdev) From dc1ec4fa86b2b8bba2b6122f2b4420217b5bae9e Mon Sep 17 00:00:00 2001 From: Mingyou Chen Date: Mon, 23 Mar 2026 21:22:18 +0800 Subject: [PATCH 052/106] platform/x86: bitland-mifs-wmi: Add new Bitland MIFS WMI driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new driver for Bitland laptops that utilize the MIFS (MiInterface) WMI interface. The driver implements several features through the WMI interface: - Platform Profile: Supports "Quiet", "Balanced", "Performance", and "Full Speed" modes. The "Full Speed" mode is intelligently restricted based on the AC adapter type (requires DC power, not supported on USB-C charging) as required by the hardware. - Hwmon: Provides monitoring for CPU, GPU, and System fan speeds, as well as CPU temperature sensors. - Keyboard Backlight: Integrated with the LED class device for brightness control and provides sysfs attributes for keyboard modes (cyclic, fixed, etc.). - GPU Mode: Allows switching between Hybrid, Discrete, and UMA graphics modes via sysfs. - Hotkeys: Handles WMI events for system hotkeys (Calculator, Browser, App launch) using sparse keymaps and reports status changes for Airplane mode, Touchpad, and CapsLock. - Fan Boost: Provides a sysfs interface to force fans to maximum speed. The driver registers two WMI GUIDs: - B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B: Control methods - 46C93E13-EE9B-4262-8488-563BCA757FEF: Event notifications Reviewed-by: Armin Wolf Signed-off-by: Mingyou Chen Link: https://patch.msgid.link/20260323132218.444383-1-qby140326@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/devices/bitland-mifs-wmi.rst | 207 +++++ drivers/platform/x86/Kconfig | 18 + drivers/platform/x86/Makefile | 1 + drivers/platform/x86/bitland-mifs-wmi.c | 845 ++++++++++++++++++ 4 files changed, 1071 insertions(+) create mode 100644 Documentation/wmi/devices/bitland-mifs-wmi.rst create mode 100644 drivers/platform/x86/bitland-mifs-wmi.c diff --git a/Documentation/wmi/devices/bitland-mifs-wmi.rst b/Documentation/wmi/devices/bitland-mifs-wmi.rst new file mode 100644 index 000000000000..9e86ecc2993c --- /dev/null +++ b/Documentation/wmi/devices/bitland-mifs-wmi.rst @@ -0,0 +1,207 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +======================================== +Bitland MIFS driver (bitland-mifs-wmi) +======================================== + +Introduction +============ + + +EC WMI interface description +============================ + +The EC WMI interface description can be decoded from the embedded binary MOF (bmof) +data using the `bmfdec `_ utility: + +:: + + class WMIEvent : __ExtrinsicEvent { + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT20"), guid("{46c93e13-ee9b-4262-8488-563bca757fef}")] + class HID_EVENT20 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT21"), guid("{fa78e245-2c0f-4ca1-91cf-15f34e474850}")] + class HID_EVENT21 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT22"), guid("{1dceaf0a-4d63-44bb-bd0c-0d6281bfddc5}")] + class HID_EVENT22 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, Provider("WmiProv"), Locale("MS\\0x40A"), Description("Root WMI HID_EVENT23"), guid("{3f9e3c26-b077-4f86-91f5-37ff64d8c7ed}")] + class HID_EVENT23 : WmiEvent { + [key, read] string InstanceName; + [read] boolean Active; + [WmiDataId(1), read, write, Description("Package Data")] uint8 EventDetail[8]; + }; + + [WMI, Dynamic, provider("WmiProv"), Locale("MS\\0x409"), Description("Class used to operate firmware interface"), guid("{b60bfb48-3e5b-49e4-a0e9-8cffe1b3434b}")] + class MICommonInterface { + [key, read] string InstanceName; + [read] boolean Active; + + [WmiMethodId(1), Implemented, read, write, Description("Method used to support system functions.")] void MiInterface([in, Description("WMI Interface")] uint8 InData[32], [out] uint8 OutData[30], [out] uint16 Reserved); + }; + +Reverse-Engineering the EC WMI interface +======================================== + +The OEM software can be download from `this link `_ + +Nothing is obfuscated, In this case, `ILSpy `_ could be helpful. + +WMI Methods (MICommonInterface) +======================================== + +The ``MICommonInterface`` class (GUID: ``{b60bfb48-3e5b-49e4-a0e9-8cffe1b3434b}``) +is the primary control interface. It uses a 32-byte buffer for both input +(``InData``) and output (``OutData``). + +Method Structure +---------------- + +The data packet follows a standardized format: + ++----------+------------------------------------------------------------------+ +| Byte | Description | ++==========+==================================================================+ +| 1 | Method Type: Get (0xFA / 250) or Set (0xFB / 251) | ++----------+------------------------------------------------------------------+ +| 3 | Command ID (Method Name) | ++----------+------------------------------------------------------------------+ +| 4 - 31 | Arguments (for Set) or Return Data (for Get) | ++----------+------------------------------------------------------------------+ + + +Command IDs +----------- + +The following Command IDs are used in the third byte of the buffer: + ++----------+-----------------------+------------------------------------------+ +| ID | Name | Values / Description | ++==========+=======================+==========================================+ +| 8 | SystemPerMode | 0: Balance, 1: Performance, 2: Quiet, | +| | | 3: Full-speed | ++----------+-----------------------+------------------------------------------+ +| 9 | GPUMode | 0: Hybrid, 1: Discrete, 2: UMA | ++----------+-----------------------+------------------------------------------+ +| 10 | KeyboardType | 0: White, 1: Single RGB, 2: Zone RGB | ++----------+-----------------------+------------------------------------------+ +| 11 | FnLock | 0: Off, 1: On | ++----------+-----------------------+------------------------------------------+ +| 12 | TPLock | 0: Unlock, 1: Lock (Touchpad) | ++----------+-----------------------+------------------------------------------+ +| 13 | CPUGPUSYSFanSpeed | Returns 12 bytes of fan data: | +| | | Bytes 4-5: CPU Fan RPM (Little Endian) | +| | | Bytes 6-7: GPU Fan RPM (Little Endian) | +| | | Bytes 10-11: SYS Fan RPM (Little Endian) | ++----------+-----------------------+------------------------------------------+ +| 16 | RGBKeyboardMode | 0: Off, 1: Auto Cyclic, 2: Fixed, | +| | | 3: Custom | ++----------+-----------------------+------------------------------------------+ +| 17 | RGBKeyboardColor | Bytes 4, 5, 6: Red, Green, Blue values | ++----------+-----------------------+------------------------------------------+ +| 18 | RGBKeyboardBrightness | 0-10: Brightness Levels, 128: Auto | ++----------+-----------------------+------------------------------------------+ +| 19 | SystemAcType | 1: Type-C, 2: Circular Hole (DC) | ++----------+-----------------------+------------------------------------------+ +| 20 | MaxFanSpeedSwitch | Byte 4: Fan Type (0: CPU/GPU, 1: SYS) | +| | | Byte 5: State (0: Off, 1: On) | ++----------+-----------------------+------------------------------------------+ +| 21 | MaxFanSpeed | Sets manual fan speed duty cycle | ++----------+-----------------------+------------------------------------------+ +| 22 | CPUThermometer | Returns CPU Temperature | ++----------+-----------------------+------------------------------------------+ + +WMI Events (HID_EVENT20) +======================== + +The driver listens for events from the ``HID_EVENT20`` class +(GUID: ``{46c93e13-ee9b-4262-8488-563bca757fef}``). These events are triggered +by hotkeys or system state changes (e.g., plugging in AC power). + +Event Structure +--------------- + +The event data is provided in an 8-byte array (``EventDetail``): + ++----------+------------------------------------------------------------------+ +| Byte | Description | ++==========+==================================================================+ +| 0 | Event Type (Always 0x01 for HotKey/Notification) | ++----------+------------------------------------------------------------------+ +| 1 | Event ID (Corresponds to the Command IDs above) | ++----------+------------------------------------------------------------------+ +| 2 | Value (The new state or value of the feature) | ++----------+------------------------------------------------------------------+ + +Common Event IDs: +----------------- + +Note: reserved event ids are not listed there + ++----------+------------------------------------------------------------------+ +| Event Id | Description | ++==========+==================================================================+ +| 4 | AirPlane mode change | ++----------+------------------------------------------------------------------+ +| 5 | Keyboard brightness change | ++----------+------------------------------------------------------------------+ +| 6 | Touchpad state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 7 | FnLock state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 8 | Keyboard mode change | ++----------+------------------------------------------------------------------+ +| 9 | CapsLock state change | ++----------+------------------------------------------------------------------+ +| 13 | NumLock state change | ++----------+------------------------------------------------------------------+ +| 14 | ScrollLock state change | ++----------+------------------------------------------------------------------+ +| 15 | Performance plan change | ++----------+------------------------------------------------------------------+ +| 25 | Display refresh rate change | ++----------+------------------------------------------------------------------+ +| 33 | Super key lock state (enabled/disabled) change | ++----------+------------------------------------------------------------------+ +| 35 | Open control center key | ++----------+------------------------------------------------------------------+ + +Implementation Details +====================== + +Performance Modes +----------------- +Changing the performance mode via Command ID 0x08 (SystemPerMode) affects the +power limits (PL1/PL2) and fan curves managed by the Embedded Controller (EC). +Note that the "Full-speed" and "Performance" mode (1, 3) is typically only +available when the system is connected to a DC power source (not USB-C/PD). + +In the driver implementation, switch to performance/full-speed mode without +DC power connected will throw the EOPNOTSUPP error. + +Graphics Switching +------------------ +The ``GPUMode`` (0x09) allows switching between Hybrid (Muxless) and Discrete +(Muxed) graphics. Changing this value usually requires a system reboot to +take effect in the BIOS/Firmware. + +Fan Control +----------- +The system supports both automatic EC control and manual overrides. Command ID +0x14 (``MaxFanSpeedSwitch``) is used to toggle manual control, while ID 0x15 +sets the actual PWM duty cycle. diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 4cb7d97a9fcc..2ffa4ecf65b0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -113,6 +113,24 @@ config GIGABYTE_WMI To compile this driver as a module, choose M here: the module will be called gigabyte-wmi. +config BITLAND_MIFS_WMI + tristate "Bitland MIFS (MiInterface) WMI driver" + depends on ACPI_WMI + depends on HWMON + depends on INPUT + depends on POWER_SUPPLY + select ACPI_PLATFORM_PROFILE + select INPUT_SPARSEKMAP + help + This is a driver for Bitland MiInterface based laptops. + + It provides the access to the temperature, fan speed, gpu + control, keyboard backlight brightness and platform profile + via hwmon and sysfs. + + To compile this driver as a module, choose M here: the module will + be called bitland-mifs-wmi. + config ACERHDF tristate "Acer Aspire One temperature and fan driver" depends on ACPI_EC && THERMAL diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d25762f7114f..872ac3842391 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_NVIDIA_WMI_EC_BACKLIGHT) += nvidia-wmi-ec-backlight.o obj-$(CONFIG_XIAOMI_WMI) += xiaomi-wmi.o obj-$(CONFIG_REDMI_WMI) += redmi-wmi.o obj-$(CONFIG_GIGABYTE_WMI) += gigabyte-wmi.o +obj-$(CONFIG_BITLAND_MIFS_WMI) += bitland-mifs-wmi.o # Acer obj-$(CONFIG_ACERHDF) += acerhdf.o diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c new file mode 100644 index 000000000000..54380708b7b0 --- /dev/null +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -0,0 +1,845 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Linux driver for Bitland notebooks. + * + * Copyright (C) 2026 2 Mingyou Chen + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "bitland-mifs-wmi" +#define BITLAND_MIFS_GUID "B60BFB48-3E5B-49E4-A0E9-8CFFE1B3434B" +#define BITLAND_EVENT_GUID "46C93E13-EE9B-4262-8488-563BCA757FEF" + +enum bitland_mifs_operation { + WMI_METHOD_GET = 250, + WMI_METHOD_SET = 251, +}; + +enum bitland_mifs_function { + WMI_FN_SYSTEM_PER_MODE = 8, + WMI_FN_GPU_MODE = 9, + WMI_FN_KBD_TYPE = 10, + WMI_FN_FN_LOCK = 11, + WMI_FN_TP_LOCK = 12, + WMI_FN_FAN_SPEEDS = 13, + WMI_FN_RGB_KB_MODE = 16, + WMI_FN_RGB_KB_COLOR = 17, + WMI_FN_RGB_KB_BRIGHTNESS = 18, + WMI_FN_SYSTEM_AC_TYPE = 19, + WMI_FN_MAX_FAN_SWITCH = 20, + WMI_FN_MAX_FAN_SPEED = 21, + WMI_FN_CPU_THERMOMETER = 22, + WMI_FN_CPU_POWER = 23, +}; + +enum bitland_system_ac_mode { + WMI_SYSTEM_AC_TYPEC = 1, + /* Unknown type, this is unused in the original driver */ + WMI_SYSTEM_AC_CIRCULARHOLE = 2, +}; + +enum bitland_mifs_power_profile { + WMI_PP_BALANCED = 0, + WMI_PP_PERFORMANCE = 1, + WMI_PP_QUIET = 2, + WMI_PP_FULL_SPEED = 3, +}; + +enum bitland_mifs_event_id { + WMI_EVENT_RESERVED_1 = 1, + WMI_EVENT_RESERVED_2 = 2, + WMI_EVENT_RESERVED_3 = 3, + WMI_EVENT_AIRPLANE_MODE = 4, + WMI_EVENT_KBD_BRIGHTNESS = 5, + WMI_EVENT_TOUCHPAD_STATE = 6, + WMI_EVENT_FNLOCK_STATE = 7, + WMI_EVENT_KBD_MODE = 8, + WMI_EVENT_CAPSLOCK_STATE = 9, + WMI_EVENT_CALCULATOR_START = 11, + WMI_EVENT_BROWSER_START = 12, + WMI_EVENT_NUMLOCK_STATE = 13, + WMI_EVENT_SCROLLLOCK_STATE = 14, + WMI_EVENT_PERFORMANCE_PLAN = 15, + WMI_EVENT_FN_J = 16, + WMI_EVENT_FN_F = 17, + WMI_EVENT_FN_0 = 18, + WMI_EVENT_FN_1 = 19, + WMI_EVENT_FN_2 = 20, + WMI_EVENT_FN_3 = 21, + WMI_EVENT_FN_4 = 22, + WMI_EVENT_FN_5 = 24, + WMI_EVENT_REFRESH_RATE = 25, + WMI_EVENT_CPU_FAN_SPEED = 26, + WMI_EVENT_GPU_FAN_SPEED = 32, + WMI_EVENT_WIN_KEY_LOCK = 33, + WMI_EVENT_RESERVED_23 = 34, + WMI_EVENT_OPEN_APP = 35, +}; + +enum bitland_mifs_event_type { + WMI_EVENT_TYPE_HOTKEY = 1, +}; + +enum bitland_wmi_device_type { + BITLAND_WMI_CONTROL = 0, + BITLAND_WMI_EVENT = 1, +}; + +struct bitland_mifs_input { + u8 reserved1; + u8 operation; + u8 reserved2; + u8 function; + u8 payload[28]; +} __packed; + +struct bitland_mifs_output { + u8 reserved1; + u8 operation; + u8 reserved2; + u8 function; + u8 data[28]; +} __packed; + +struct bitland_mifs_event { + u8 event_type; + u8 event_id; + u8 value_low; /* For most events, this is the value */ + u8 value_high; /* For fan speed events, combined with value_low */ + u8 reserved[4]; +} __packed; + +static BLOCKING_NOTIFIER_HEAD(bitland_notifier_list); + +enum bitland_notifier_actions { + BITLAND_NOTIFY_KBD_BRIGHTNESS, + BITLAND_NOTIFY_PLATFORM_PROFILE, + BITLAND_NOTIFY_HWMON, +}; + +struct bitland_fan_notify_data { + int channel; /* 0 = CPU, 1 = GPU */ + u16 speed; +}; + +struct bitland_mifs_wmi_data { + struct wmi_device *wdev; + struct mutex lock; /* Protects WMI calls */ + struct led_classdev kbd_led; + struct notifier_block notifier; + struct input_dev *input_dev; + struct device *hwmon_dev; + struct device *pp_dev; + enum platform_profile_option saved_profile; +}; + +static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, + const struct bitland_mifs_input *input, + struct bitland_mifs_output *output) +{ + struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input }; + struct wmi_buffer out_buf = { 0 }; + int ret; + + guard(mutex)(&data->lock); + + ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, output ? &out_buf : NULL); + if (ret) + return ret; + + if (output) { + void *out_data __free(kfree) = out_buf.data; + + if (out_buf.length < sizeof(*output)) + return -EIO; + + memcpy(output, out_data, sizeof(*output)); + } + + return 0; +} + +static int laptop_profile_get(struct device *dev, + enum platform_profile_option *profile) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_SYSTEM_PER_MODE, + }; + struct bitland_mifs_output result; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &result); + if (ret) + return ret; + + switch (result.data[0]) { + case WMI_PP_BALANCED: + *profile = PLATFORM_PROFILE_BALANCED; + break; + case WMI_PP_PERFORMANCE: + *profile = PLATFORM_PROFILE_BALANCED_PERFORMANCE; + break; + case WMI_PP_QUIET: + *profile = PLATFORM_PROFILE_LOW_POWER; + break; + case WMI_PP_FULL_SPEED: + *profile = PLATFORM_PROFILE_PERFORMANCE; + break; + default: + return -EINVAL; + } + return 0; +} + +static int bitland_check_performance_capability(struct bitland_mifs_wmi_data *data) +{ + struct bitland_mifs_input input = { + .operation = WMI_METHOD_GET, + .function = WMI_FN_SYSTEM_AC_TYPE, + }; + struct bitland_mifs_output output; + int ret; + + /* Full-speed/performance mode requires DC power (not USB-C) */ + if (!power_supply_is_system_supplied()) + return -EOPNOTSUPP; + + ret = bitland_mifs_wmi_call(data, &input, &output); + if (ret) + return ret; + + if (output.data[0] != WMI_SYSTEM_AC_CIRCULARHOLE) + return -EOPNOTSUPP; + + return 0; +} + +static int laptop_profile_set(struct device *dev, + enum platform_profile_option profile) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_SYSTEM_PER_MODE, + }; + int ret; + u8 val; + + switch (profile) { + case PLATFORM_PROFILE_LOW_POWER: + val = WMI_PP_QUIET; + break; + case PLATFORM_PROFILE_BALANCED: + val = WMI_PP_BALANCED; + break; + case PLATFORM_PROFILE_BALANCED_PERFORMANCE: + ret = bitland_check_performance_capability(data); + if (ret) + return ret; + val = WMI_PP_PERFORMANCE; + break; + case PLATFORM_PROFILE_PERFORMANCE: + ret = bitland_check_performance_capability(data); + if (ret) + return ret; + val = WMI_PP_FULL_SPEED; + break; + default: + return -EOPNOTSUPP; + } + + input.payload[0] = val; + + return bitland_mifs_wmi_call(data, &input, NULL); +} + +static int platform_profile_probe(void *drvdata, unsigned long *choices) +{ + set_bit(PLATFORM_PROFILE_LOW_POWER, choices); + set_bit(PLATFORM_PROFILE_BALANCED, choices); + set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices); + set_bit(PLATFORM_PROFILE_PERFORMANCE, choices); + + return 0; +} + +static int bitland_mifs_wmi_suspend(struct device *dev) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + enum platform_profile_option profile; + int ret; + + ret = laptop_profile_get(data->pp_dev, &profile); + if (ret == 0) + data->saved_profile = profile; + + return ret; +} + +static int bitland_mifs_wmi_resume(struct device *dev) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + + dev_dbg(dev, "Resuming, restoring profile %d\n", data->saved_profile); + return laptop_profile_set(dev, data->saved_profile); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(bitland_mifs_wmi_pm_ops, + bitland_mifs_wmi_suspend, + bitland_mifs_wmi_resume); + +static const struct platform_profile_ops laptop_profile_ops = { + .probe = platform_profile_probe, + .profile_get = laptop_profile_get, + .profile_set = laptop_profile_set, +}; + +static const char *const fan_labels[] = { + "CPU", /* 0 */ + "GPU", /* 1 */ + "SYS", /* 2 */ +}; + +static int laptop_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + }; + struct bitland_mifs_output res; + int ret; + + switch (type) { + case hwmon_temp: + input.function = WMI_FN_CPU_THERMOMETER; + ret = bitland_mifs_wmi_call(data, &input, &res); + if (!ret) + *val = res.data[0] * MILLIDEGREE_PER_DEGREE; + return ret; + case hwmon_fan: + input.function = WMI_FN_FAN_SPEEDS; + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + switch (channel) { + case 0: /* CPU */ + *val = get_unaligned_le16(&res.data[0]); + return 0; + case 1: /* GPU */ + *val = get_unaligned_le16(&res.data[2]); + return 0; + case 2: /* SYS */ + *val = get_unaligned_le16(&res.data[6]); + return 0; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int laptop_hwmon_read_string(struct device *dev, + enum hwmon_sensor_types type, u32 attr, + int channel, const char **str) +{ + if (type == hwmon_fan && attr == hwmon_fan_label) { + if (channel >= 0 && channel < ARRAY_SIZE(fan_labels)) { + *str = fan_labels[channel]; + return 0; + } + } + return -EINVAL; +} + +static const struct hwmon_channel_info *laptop_hwmon_info[] = { + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL, + HWMON_F_INPUT | HWMON_F_LABEL), + NULL +}; + +static const struct hwmon_ops laptop_hwmon_ops = { + .visible = 0444, + .read = laptop_hwmon_read, + .read_string = laptop_hwmon_read_string, +}; + +static const struct hwmon_chip_info laptop_chip_info = { + .ops = &laptop_hwmon_ops, + .info = laptop_hwmon_info, +}; + +static int laptop_kbd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct bitland_mifs_wmi_data *data = + container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_BRIGHTNESS, + }; + + input.payload[0] = (u8)value; + + return bitland_mifs_wmi_call(data, &input, NULL); +} + +static enum led_brightness laptop_kbd_led_get(struct led_classdev *led_cdev) +{ + struct bitland_mifs_wmi_data *data = + container_of(led_cdev, struct bitland_mifs_wmi_data, kbd_led); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_BRIGHTNESS, + }; + struct bitland_mifs_output res; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + return res.data[0]; +} + +static const char *const gpu_mode_strings[] = { + "hybrid", + "discrete", + "uma", +}; + +/* GPU Mode: 0:Hybrid, 1:Discrete, 2:UMA */ +static ssize_t gpu_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_GPU_MODE, + }; + struct bitland_mifs_output res; + u8 mode_val; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + mode_val = res.data[0]; + if (mode_val >= ARRAY_SIZE(gpu_mode_strings)) + return -EPROTO; + + return sysfs_emit(buf, "%s\n", gpu_mode_strings[mode_val]); +} + +static ssize_t gpu_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_GPU_MODE, + }; + int val; + int ret; + + val = sysfs_match_string(gpu_mode_strings, buf); + if (val < 0) + return -EINVAL; + + input.payload[0] = (u8)val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +static const char *const kb_mode_strings[] = { + "off", /* 0 */ + "cyclic", /* 1 */ + "fixed", /* 2 */ + "custom", /* 3 */ +}; + +static ssize_t kb_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_GET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_MODE, + }; + struct bitland_mifs_output res; + u8 mode_val; + int ret; + + ret = bitland_mifs_wmi_call(data, &input, &res); + if (ret) + return ret; + + mode_val = res.data[0]; + if (mode_val >= ARRAY_SIZE(kb_mode_strings)) + return -EPROTO; + + return sysfs_emit(buf, "%s\n", kb_mode_strings[mode_val]); +} + +static ssize_t kb_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_RGB_KB_MODE, + }; + // the wmi value (0, 1, 2 or 3) + int val; + int ret; + + val = sysfs_match_string(kb_mode_strings, buf); + if (val < 0) + return -EINVAL; + + input.payload[0] = (u8)val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +/* Fan Boost: 0:Normal, 1:Max Speed */ +static ssize_t fan_boost_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(dev); + struct bitland_mifs_input input = { + .reserved1 = 0, + .operation = WMI_METHOD_SET, + .reserved2 = 0, + .function = WMI_FN_MAX_FAN_SWITCH, + }; + bool val; + int ret; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + input.payload[0] = 0; /* CPU/GPU Fan */ + input.payload[1] = val; + + ret = bitland_mifs_wmi_call(data, &input, NULL); + if (ret) + return ret; + + return count; +} + +static const DEVICE_ATTR_RW(gpu_mode); +static const DEVICE_ATTR_RW(kb_mode); +static const DEVICE_ATTR_WO(fan_boost); + +static const struct attribute *const laptop_attrs[] = { + &dev_attr_gpu_mode.attr, + &dev_attr_kb_mode.attr, + &dev_attr_fan_boost.attr, + NULL, +}; +ATTRIBUTE_GROUPS(laptop); + +static const struct key_entry bitland_mifs_wmi_keymap[] = { + { KE_KEY, WMI_EVENT_OPEN_APP, { KEY_PROG1 } }, + { KE_KEY, WMI_EVENT_CALCULATOR_START, { KEY_CALC } }, + { KE_KEY, WMI_EVENT_BROWSER_START, { KEY_WWW } }, + { KE_IGNORE, WMI_EVENT_FN_J, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_F, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_0, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_1, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_2, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_3, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_4, { KEY_RESERVED } }, + { KE_IGNORE, WMI_EVENT_FN_5, { KEY_RESERVED } }, + { KE_END, 0 } +}; + +static void bitland_notifier_unregister(void *data) +{ + struct notifier_block *nb = data; + + blocking_notifier_chain_unregister(&bitland_notifier_list, nb); +} + +static int bitland_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct bitland_mifs_wmi_data *data_ctx = + container_of(nb, struct bitland_mifs_wmi_data, notifier); + struct bitland_fan_notify_data *fan_info; + u8 *brightness; + + switch (action) { + case BITLAND_NOTIFY_KBD_BRIGHTNESS: + brightness = data; + led_classdev_notify_brightness_hw_changed(&data_ctx->kbd_led, + *brightness); + break; + case BITLAND_NOTIFY_PLATFORM_PROFILE: + platform_profile_notify(data_ctx->pp_dev); + break; + case BITLAND_NOTIFY_HWMON: + fan_info = data; + + hwmon_notify_event(data_ctx->hwmon_dev, hwmon_fan, + hwmon_fan_input, fan_info->channel); + break; + } + + return NOTIFY_OK; +} + +static int bitland_mifs_wmi_probe(struct wmi_device *wdev, const void *context) +{ + struct bitland_mifs_wmi_data *drv_data; + enum bitland_wmi_device_type dev_type = + (enum bitland_wmi_device_type)(unsigned long)context; + struct led_init_data init_data = { + .devicename = DRV_NAME, + .default_label = ":" LED_FUNCTION_KBD_BACKLIGHT, + .devname_mandatory = true, + }; + int ret; + + drv_data = devm_kzalloc(&wdev->dev, sizeof(*drv_data), GFP_KERNEL); + if (!drv_data) + return -ENOMEM; + + drv_data->wdev = wdev; + + ret = devm_mutex_init(&wdev->dev, &drv_data->lock); + if (ret) + return ret; + + dev_set_drvdata(&wdev->dev, drv_data); + + if (dev_type == BITLAND_WMI_EVENT) { + /* Register input device for hotkeys */ + drv_data->input_dev = devm_input_allocate_device(&wdev->dev); + if (!drv_data->input_dev) + return -ENOMEM; + + drv_data->input_dev->name = "Bitland MIFS WMI hotkeys"; + drv_data->input_dev->phys = "wmi/input0"; + drv_data->input_dev->id.bustype = BUS_HOST; + drv_data->input_dev->dev.parent = &wdev->dev; + + ret = sparse_keymap_setup(drv_data->input_dev, + bitland_mifs_wmi_keymap, NULL); + if (ret) + return ret; + + return input_register_device(drv_data->input_dev); + } + + /* Register platform profile */ + drv_data->pp_dev = devm_platform_profile_register(&wdev->dev, DRV_NAME, drv_data, + &laptop_profile_ops); + if (IS_ERR(drv_data->pp_dev)) + return PTR_ERR(drv_data->pp_dev); + + /* Register hwmon */ + drv_data->hwmon_dev = devm_hwmon_device_register_with_info(&wdev->dev, + "bitland_mifs", + drv_data, + &laptop_chip_info, + NULL); + if (IS_ERR(drv_data->hwmon_dev)) + return PTR_ERR(drv_data->hwmon_dev); + + /* Register keyboard LED */ + drv_data->kbd_led.max_brightness = 3; + drv_data->kbd_led.brightness_set_blocking = laptop_kbd_led_set; + drv_data->kbd_led.brightness_get = laptop_kbd_led_get; + drv_data->kbd_led.brightness = laptop_kbd_led_get(&drv_data->kbd_led); + drv_data->kbd_led.flags = LED_CORE_SUSPENDRESUME | + LED_BRIGHT_HW_CHANGED | + LED_REJECT_NAME_CONFLICT; + ret = devm_led_classdev_register_ext(&wdev->dev, &drv_data->kbd_led, &init_data); + if (ret) + return ret; + + drv_data->notifier.notifier_call = bitland_notifier_callback; + ret = blocking_notifier_chain_register(&bitland_notifier_list, &drv_data->notifier); + if (ret) + return ret; + + return devm_add_action_or_reset(&wdev->dev, + bitland_notifier_unregister, + &drv_data->notifier); +} + +static void bitland_mifs_wmi_notify(struct wmi_device *wdev, + const struct wmi_buffer *buffer) +{ + struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev); + const struct bitland_mifs_event *event; + struct bitland_fan_notify_data fan_data; + u8 brightness; + + if (buffer->length < sizeof(*event)) + return; + + event = buffer->data; + + /* Validate event type */ + if (event->event_type != WMI_EVENT_TYPE_HOTKEY) + return; + + dev_dbg(&wdev->dev, + "WMI event: id=0x%02x value_low=0x%02x value_high=0x%02x\n", + event->event_id, event->value_low, event->value_high); + + switch (event->event_id) { + case WMI_EVENT_KBD_BRIGHTNESS: + brightness = event->value_low; + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_KBD_BRIGHTNESS, + &brightness); + break; + + case WMI_EVENT_PERFORMANCE_PLAN: + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_PLATFORM_PROFILE, + NULL); + break; + + case WMI_EVENT_OPEN_APP: + case WMI_EVENT_CALCULATOR_START: + case WMI_EVENT_BROWSER_START: { + guard(mutex)(&data->lock); + if (!sparse_keymap_report_event(data->input_dev, + event->event_id, 1, true)) + dev_warn(&wdev->dev, "Unknown key pressed: 0x%02x\n", + event->event_id); + break; + } + + /* + * The device has 3 fans (CPU, GPU, SYS), + * but there are only the CPU and GPU fan has events + */ + case WMI_EVENT_CPU_FAN_SPEED: + case WMI_EVENT_GPU_FAN_SPEED: + if (event->event_id == WMI_EVENT_CPU_FAN_SPEED) + fan_data.channel = 0; + else + fan_data.channel = 1; + + /* Fan speed is 16-bit value (value_low is LSB, value_high is MSB) */ + fan_data.speed = (event->value_high << 8) | event->value_low; + blocking_notifier_call_chain(&bitland_notifier_list, + BITLAND_NOTIFY_HWMON, + &fan_data); + break; + + case WMI_EVENT_AIRPLANE_MODE: + case WMI_EVENT_TOUCHPAD_STATE: + case WMI_EVENT_FNLOCK_STATE: + case WMI_EVENT_KBD_MODE: + case WMI_EVENT_CAPSLOCK_STATE: + case WMI_EVENT_NUMLOCK_STATE: + case WMI_EVENT_SCROLLLOCK_STATE: + case WMI_EVENT_REFRESH_RATE: + case WMI_EVENT_WIN_KEY_LOCK: + /* These events are informational or handled by firmware */ + dev_dbg(&wdev->dev, "State change event: id=%d value=%d\n", + event->event_id, event->value_low); + break; + + default: + dev_dbg(&wdev->dev, "Unknown event: id=0x%02x value=0x%02x\n", + event->event_id, event->value_low); + break; + } +} + +static const struct wmi_device_id bitland_mifs_wmi_id_table[] = { + { BITLAND_MIFS_GUID, (void *)BITLAND_WMI_CONTROL }, + { BITLAND_EVENT_GUID, (void *)BITLAND_WMI_EVENT }, + {} +}; +MODULE_DEVICE_TABLE(wmi, bitland_mifs_wmi_id_table); + +static struct wmi_driver bitland_mifs_wmi_driver = { + .no_singleton = true, + .driver = { + .name = DRV_NAME, + .dev_groups = laptop_groups, + .pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops), + }, + .id_table = bitland_mifs_wmi_id_table, + .probe = bitland_mifs_wmi_probe, + .notify_new = bitland_mifs_wmi_notify, +}; + +module_wmi_driver(bitland_mifs_wmi_driver); + +MODULE_AUTHOR("Mingyou Chen "); +MODULE_DESCRIPTION("Bitland MIFS (MiInterface) WMI driver"); +MODULE_LICENSE("GPL"); From 186bf9031666602d61b40832181b6b6fdc3ba4dc Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Wed, 4 Mar 2026 14:26:08 +0100 Subject: [PATCH 053/106] platform/x86: asus-wmi: do not enforce a battery charge threshold MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Users are complaining for the battery limit being reset at 100% during the boot process while the general consensus appears to not apply unsolicited hardware changes, therefore stop resetting the battery charge limit at boot and return -ENODATA on charge_end_threshold to signal for an unknown limit. Suggested-by: Antheas Kapenekakis Suggested-by: Derek J. Clark Signed-off-by: Denis Benato Reviewed-by: Derek J. Clark Tested-by: Antheas Kapenekakis Link: https://patch.msgid.link/20260304132608.33815-1-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 6ba49bd375df..dc330a8ee2f2 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1557,7 +1557,10 @@ static ssize_t charge_control_end_threshold_show(struct device *device, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%d\n", charge_end_threshold); + if ((charge_end_threshold >= 0) && (charge_end_threshold <= 100)) + return sysfs_emit(buf, "%d\n", charge_end_threshold); + + return -ENODATA; } static DEVICE_ATTR_RW(charge_control_end_threshold); @@ -1580,11 +1583,11 @@ static int asus_wmi_battery_add(struct power_supply *battery, struct acpi_batter return -ENODEV; /* The charge threshold is only reset when the system is power cycled, - * and we can't get the current threshold so let set it to 100% when - * a battery is added. + * and we can't read the current threshold, however the majority of + * platforms retains it, therefore signal the threshold as unknown + * until user explicitly sets it to a new value. */ - asus_wmi_set_devstate(ASUS_WMI_DEVID_RSOC, 100, NULL); - charge_end_threshold = 100; + charge_end_threshold = -1; return 0; } From 130d29c5627cd50e786e926ad7ef66322c5a0c09 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Mon, 2 Mar 2026 18:44:30 +0100 Subject: [PATCH 054/106] platform/x86: asus-wmi: adjust screenpad power/brightness handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix illogical screen off control by hardcoding 0 and 1 depending on the requested brightness and also do not rely on the last screenpad power state to issue screen brightness commands. Fixes: 2c97d3e55b70 ("platform/x86: asus-wmi: add support for ASUS screenpad") Signed-off-by: Denis Benato Signed-off-by: Luke Jones Link: https://patch.msgid.link/20260302174431.349816-2-denis.benato@linux.dev Link: https://patch.msgid.link/20260326231154.856729-2-ethantidmore06@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 34 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index dc330a8ee2f2..f4a5da5b1bb8 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -4422,32 +4422,24 @@ static int read_screenpad_brightness(struct backlight_device *bd) static int update_screenpad_bl_status(struct backlight_device *bd) { - struct asus_wmi *asus = bl_get_data(bd); - int power, err = 0; - u32 ctrl_param; + u32 ctrl_param = bd->props.brightness; + int err = 0; - power = read_screenpad_backlight_power(asus); - if (power < 0) - return power; + if (bd->props.power) { + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 1, NULL); + if (err < 0) + return err; - if (bd->props.power != power) { - if (power != BACKLIGHT_POWER_ON) { - /* Only brightness > 0 can power it back on */ - ctrl_param = asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, - ctrl_param, NULL); - } else { - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); - } - } else if (power == BACKLIGHT_POWER_ON) { - /* Only set brightness if powered on or we get invalid/unsync state */ - ctrl_param = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_LIGHT, ctrl_param, NULL); + if (err < 0) + return err; } - /* Ensure brightness is stored to turn back on with */ - if (err == 0) - asus->driver->screenpad_brightness = bd->props.brightness + ASUS_SCREENPAD_BRIGHT_MIN; + if (!bd->props.power) { + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_SCREENPAD_POWER, 0, NULL); + if (err < 0) + return err; + } return err; } From 8d95d1f4aa5c76202b0833a70998769384612488 Mon Sep 17 00:00:00 2001 From: Denis Benato Date: Mon, 2 Mar 2026 18:44:31 +0100 Subject: [PATCH 055/106] platform/x86: asus-wmi: fix screenpad brightness range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix screenpad brightness range being too limited without reason: testing this patch on a Zenbook Duo showed the hardware minimum not being too low, therefore allow the user to configure the entire range, and expose to userspace the hardware brightness range and value. Fixes: 2c97d3e55b70 ("platform/x86: asus-wmi: add support for ASUS screenpad") Signed-off-by: Denis Benato Signed-off-by: Luke Jones Link: https://patch.msgid.link/20260302174431.349816-3-denis.benato@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f4a5da5b1bb8..80144c412b90 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -125,7 +125,6 @@ module_param(fnlock_default, bool, 0444); #define NVIDIA_TEMP_MIN 75 #define NVIDIA_TEMP_MAX 87 -#define ASUS_SCREENPAD_BRIGHT_MIN 20 #define ASUS_SCREENPAD_BRIGHT_MAX 255 #define ASUS_SCREENPAD_BRIGHT_DEFAULT 60 @@ -4411,13 +4410,13 @@ static int read_screenpad_brightness(struct backlight_device *bd) return err; /* The device brightness can only be read if powered, so return stored */ if (err == BACKLIGHT_POWER_OFF) - return asus->driver->screenpad_brightness - ASUS_SCREENPAD_BRIGHT_MIN; + return bd->props.brightness; err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &retval); if (err < 0) return err; - return (retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK) - ASUS_SCREENPAD_BRIGHT_MIN; + return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } static int update_screenpad_bl_status(struct backlight_device *bd) @@ -4457,22 +4456,19 @@ static int asus_screenpad_init(struct asus_wmi *asus) int err, power; int brightness = 0; - power = read_screenpad_backlight_power(asus); + power = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_SCREENPAD_POWER); if (power < 0) return power; - if (power != BACKLIGHT_POWER_OFF) { + if (power) { err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_SCREENPAD_LIGHT, &brightness); if (err < 0) return err; } - /* default to an acceptable min brightness on boot if too low */ - if (brightness < ASUS_SCREENPAD_BRIGHT_MIN) - brightness = ASUS_SCREENPAD_BRIGHT_DEFAULT; memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; /* ensure this bd is last to be picked */ - props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX - ASUS_SCREENPAD_BRIGHT_MIN; + props.max_brightness = ASUS_SCREENPAD_BRIGHT_MAX; bd = backlight_device_register("asus_screenpad", &asus->platform_device->dev, asus, &asus_screenpad_bl_ops, &props); @@ -4483,7 +4479,7 @@ static int asus_screenpad_init(struct asus_wmi *asus) asus->screenpad_backlight_device = bd; asus->driver->screenpad_brightness = brightness; - bd->props.brightness = brightness - ASUS_SCREENPAD_BRIGHT_MIN; + bd->props.brightness = brightness; bd->props.power = power; backlight_update_status(bd); From f3660b13495ae47d0ab2755a7b32999fb718ee5c Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Tue, 24 Mar 2026 21:32:08 +0100 Subject: [PATCH 056/106] platform/x86: uniwill-laptop: Rework hwmon feature defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split hwmon feature define in smaller parts to accommodate for diverse hardware. You can now specify the presence of a cpu and/or a gpu temp sensor separately and if one or 2 fans exists. Signed-off-by: Armin Wolf Reviewed-by: Werner Sembach Tested-by: Werner Sembach Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260324203413.454361-2-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 68 ++++++++++++++++++--- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 6341dca20b76..048b265bff37 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -319,8 +319,11 @@ #define UNIWILL_FEATURE_TOUCHPAD_TOGGLE BIT(2) #define UNIWILL_FEATURE_LIGHTBAR BIT(3) #define UNIWILL_FEATURE_BATTERY BIT(4) -#define UNIWILL_FEATURE_HWMON BIT(5) -#define UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL BIT(6) +#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) struct uniwill_data { struct device *dev; @@ -427,7 +430,7 @@ static const struct key_entry uniwill_keymap[] = { { KE_END } }; -static inline bool uniwill_device_supports(struct uniwill_data *data, +static inline bool uniwill_device_supports(const struct uniwill_data *data, unsigned int features) { return (data->features & features) == features; @@ -937,6 +940,48 @@ static const struct attribute_group *uniwill_groups[] = { NULL }; +static umode_t uniwill_is_visible(const void *drvdata, enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct uniwill_data *data = drvdata; + unsigned int feature; + + switch (type) { + case hwmon_temp: + switch (channel) { + case 0: + feature = UNIWILL_FEATURE_CPU_TEMP; + break; + case 1: + feature = UNIWILL_FEATURE_GPU_TEMP; + break; + default: + return 0; + } + break; + case hwmon_fan: + case hwmon_pwm: + switch (channel) { + case 0: + feature = UNIWILL_FEATURE_PRIMARY_FAN; + break; + case 1: + feature = UNIWILL_FEATURE_SECONDARY_FAN; + break; + default: + return 0; + } + break; + default: + return 0; + } + + if (uniwill_device_supports(data, feature)) + return 0444; + + return 0; +} + static int uniwill_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -1020,7 +1065,7 @@ static int uniwill_read_string(struct device *dev, enum hwmon_sensor_types type, } static const struct hwmon_ops uniwill_ops = { - .visible = 0444, + .is_visible = uniwill_is_visible, .read = uniwill_read, .read_string = uniwill_read_string, }; @@ -1048,7 +1093,10 @@ static int uniwill_hwmon_init(struct uniwill_data *data) { struct device *hdev; - if (!uniwill_device_supports(data, UNIWILL_FEATURE_HWMON)) + if (!uniwill_device_supports(data, UNIWILL_FEATURE_CPU_TEMP) && + !uniwill_device_supports(data, UNIWILL_FEATURE_GPU_TEMP) && + !uniwill_device_supports(data, UNIWILL_FEATURE_PRIMARY_FAN) && + !uniwill_device_supports(data, UNIWILL_FEATURE_SECONDARY_FAN)) return 0; hdev = devm_hwmon_device_register_with_info(data->dev, "uniwill", data, @@ -1687,7 +1735,10 @@ static struct uniwill_device_descriptor lapac71h_descriptor __initdata = { UNIWILL_FEATURE_SUPER_KEY | UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_BATTERY | - UNIWILL_FEATURE_HWMON, + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, }; static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { @@ -1696,7 +1747,10 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { UNIWILL_FEATURE_TOUCHPAD_TOGGLE | UNIWILL_FEATURE_LIGHTBAR | UNIWILL_FEATURE_BATTERY | - UNIWILL_FEATURE_HWMON, + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, }; static int phxarx1_phxaqf1_probe(struct uniwill_data *data) From 03ae0a0d0973b9e584a05136aab08fee2ef8e455 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Tue, 24 Mar 2026 21:32:09 +0100 Subject: [PATCH 057/106] platform/x86: uniwill-laptop: Implement USB-C power priority setting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On some devices Uniwill offers the option to set the USB-C port to prioritise charging or performance. This patch exposes this setting to the userspace via sysfs for all TUXEDO devices supporting it. Reviewed-by: Armin Wolf Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260324203413.454361-3-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 145 +++++++++++++++++++- 1 file changed, 138 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 048b265bff37..48bdf43b4cb5 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -266,8 +266,8 @@ #define BATTERY_CHARGE_FULL_OVER_24H BIT(3) #define BATTERY_ERM_STATUS_REACHED BIT(4) -#define EC_ADDR_CHARGE_PRIO 0x07CC -#define CHARGING_PERFORMANCE BIT(7) +#define EC_ADDR_USB_C_POWER_PRIORITY 0x07CC +#define USB_C_POWER_PRIORITY BIT(7) /* Same bits as EC_ADDR_LIGHTBAR_AC_CTRL except LIGHTBAR_S3_OFF */ #define EC_ADDR_LIGHTBAR_BAT_CTRL 0x07E2 @@ -324,6 +324,12 @@ #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) + +enum usb_c_power_priority_options { + USB_C_POWER_PRIORITY_CHARGING = 0, + USB_C_POWER_PRIORITY_PERFORMANCE, +}; struct uniwill_data { struct device *dev; @@ -343,6 +349,8 @@ struct uniwill_data { struct mutex input_lock; /* Protects input sequence during notify */ struct input_dev *input_device; struct notifier_block nb; + struct mutex usb_c_power_priority_lock; /* Protects dependent bit write and state safe */ + enum usb_c_power_priority_options last_usb_c_power_priority_option; }; struct uniwill_battery_entry { @@ -527,6 +535,7 @@ static bool uniwill_writeable_reg(struct device *dev, unsigned int reg) case EC_ADDR_CTGP_DB_CTGP_OFFSET: case EC_ADDR_CTGP_DB_TPP_OFFSET: case EC_ADDR_CTGP_DB_DB_OFFSET: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -565,6 +574,7 @@ static bool uniwill_readable_reg(struct device *dev, unsigned int reg) case EC_ADDR_CTGP_DB_CTGP_OFFSET: case EC_ADDR_CTGP_DB_TPP_OFFSET: case EC_ADDR_CTGP_DB_DB_OFFSET: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -587,6 +597,7 @@ static bool uniwill_volatile_reg(struct device *dev, unsigned int reg) case EC_ADDR_TRIGGER: case EC_ADDR_SWITCH_STATUS: case EC_ADDR_CHARGE_CTRL: + case EC_ADDR_USB_C_POWER_PRIORITY: return true; default: return false; @@ -883,6 +894,104 @@ static int uniwill_nvidia_ctgp_init(struct uniwill_data *data) return 0; } +static const char * const usb_c_power_priority_text[] = { + [USB_C_POWER_PRIORITY_CHARGING] = "charging", + [USB_C_POWER_PRIORITY_PERFORMANCE] = "performance", +}; + +static const u8 usb_c_power_priority_value[] = { + [USB_C_POWER_PRIORITY_CHARGING] = 0, + [USB_C_POWER_PRIORITY_PERFORMANCE] = USB_C_POWER_PRIORITY, +}; + +static ssize_t usb_c_power_priority_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + enum usb_c_power_priority_options option; + unsigned int value; + int ret; + + option = sysfs_match_string(usb_c_power_priority_text, buf); + if (option < 0) + return option; + + value = usb_c_power_priority_value[option]; + + guard(mutex)(&data->usb_c_power_priority_lock); + + ret = regmap_update_bits(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, + USB_C_POWER_PRIORITY, value); + if (ret < 0) + return ret; + + data->last_usb_c_power_priority_option = option; + + return count; +} + +static ssize_t usb_c_power_priority_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct uniwill_data *data = dev_get_drvdata(dev); + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, &value); + if (ret < 0) + return ret; + + value &= USB_C_POWER_PRIORITY; + + if (usb_c_power_priority_value[USB_C_POWER_PRIORITY_PERFORMANCE] == value) + return sysfs_emit(buf, "%s\n", + usb_c_power_priority_text[USB_C_POWER_PRIORITY_PERFORMANCE]); + + return sysfs_emit(buf, "%s\n", usb_c_power_priority_text[USB_C_POWER_PRIORITY_CHARGING]); +} + +static DEVICE_ATTR_RW(usb_c_power_priority); + +static int usb_c_power_priority_restore(struct uniwill_data *data) +{ + unsigned int value; + + value = usb_c_power_priority_value[data->last_usb_c_power_priority_option]; + + guard(mutex)(&data->usb_c_power_priority_lock); + + return regmap_update_bits(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, + USB_C_POWER_PRIORITY, value); +} + +static int usb_c_power_priority_init(struct uniwill_data *data) +{ + unsigned int value; + int ret; + + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return 0; + + ret = devm_mutex_init(data->dev, &data->usb_c_power_priority_lock); + if (ret < 0) + return ret; + + ret = regmap_read(data->regmap, EC_ADDR_USB_C_POWER_PRIORITY, &value); + if (ret < 0) + return ret; + + value &= USB_C_POWER_PRIORITY; + + data->last_usb_c_power_priority_option = + usb_c_power_priority_value[USB_C_POWER_PRIORITY_PERFORMANCE] == value ? + USB_C_POWER_PRIORITY_PERFORMANCE : + USB_C_POWER_PRIORITY_CHARGING; + + return 0; +} + static struct attribute *uniwill_attrs[] = { /* Keyboard-related */ &dev_attr_fn_lock.attr, @@ -893,6 +1002,7 @@ static struct attribute *uniwill_attrs[] = { &dev_attr_breathing_in_suspend.attr, /* Power-management-related */ &dev_attr_ctgp_offset.attr, + &dev_attr_usb_c_power_priority.attr, NULL }; @@ -927,6 +1037,11 @@ static umode_t uniwill_attr_is_visible(struct kobject *kobj, struct attribute *a return attr->mode; } + if (attr == &dev_attr_usb_c_power_priority.attr) { + if (uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return attr->mode; + } + return 0; } @@ -1417,11 +1532,10 @@ static int uniwill_notifier_call(struct notifier_block *nb, unsigned long action return NOTIFY_OK; case UNIWILL_OSD_DC_ADAPTER_CHANGED: - /* noop for the time being, will change once charging priority - * gets implemented. - */ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return NOTIFY_DONE; - return NOTIFY_OK; + return notifier_from_errno(usb_c_power_priority_restore(data)); case UNIWILL_OSD_FN_LOCK: if (!uniwill_device_supports(data, UNIWILL_FEATURE_FN_LOCK)) return NOTIFY_DONE; @@ -1515,6 +1629,7 @@ static int uniwill_probe(struct platform_device *pdev) return PTR_ERR(regmap); data->regmap = regmap; + ret = devm_mutex_init(&pdev->dev, &data->super_key_lock); if (ret < 0) return ret; @@ -1552,6 +1667,10 @@ static int uniwill_probe(struct platform_device *pdev) if (ret < 0) return ret; + ret = usb_c_power_priority_init(data); + if (ret < 0) + return ret; + return uniwill_input_init(data); } @@ -1681,6 +1800,14 @@ static int uniwill_resume_nvidia_ctgp(struct uniwill_data *data) CTGP_DB_DB_ENABLE | CTGP_DB_CTGP_ENABLE); } +static int uniwill_resume_usb_c_power_priority(struct uniwill_data *data) +{ + if (!uniwill_device_supports(data, UNIWILL_FEATURE_USB_C_POWER_PRIORITY)) + return 0; + + return usb_c_power_priority_restore(data); +} + static int uniwill_resume(struct device *dev) { struct uniwill_data *data = dev_get_drvdata(dev); @@ -1704,7 +1831,11 @@ static int uniwill_resume(struct device *dev) if (ret < 0) return ret; - return uniwill_resume_nvidia_ctgp(data); + ret = uniwill_resume_nvidia_ctgp(data); + if (ret < 0) + return ret; + + return uniwill_resume_usb_c_power_priority(data); } static DEFINE_SIMPLE_DEV_PM_OPS(uniwill_pm_ops, uniwill_suspend, uniwill_resume); From ab97e28be21181a7f08300de1088f48e9f8274a9 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Tue, 24 Mar 2026 21:32:10 +0100 Subject: [PATCH 058/106] platform/x86: uniwill-laptop: Fix XMG Fusion 15 (L19) entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add alternative XMG Fusion system vendor name and clarify edition in ident. Reviewed-by: Armin Wolf Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260324203413.454361-4-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 48bdf43b4cb5..0ead71d12d58 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -1911,7 +1911,7 @@ static struct uniwill_device_descriptor empty_descriptor __initdata = {}; static const struct dmi_system_id uniwill_dmi_table[] __initconst = { { - .ident = "XMG FUSION 15", + .ident = "XMG FUSION 15 (L19)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), @@ -1919,13 +1919,29 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { .driver_data = &empty_descriptor, }, { - .ident = "XMG FUSION 15", + .ident = "XMG FUSION 15 (L19)", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, .driver_data = &empty_descriptor, }, + { + .ident = "XMG FUSION 15 (L19)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), + }, + .driver_data = &empty_descriptor, + }, + { + .ident = "XMG FUSION 15 (L19)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), + }, + .driver_data = &empty_descriptor, + }, { .ident = "Intel NUC x15", .matches = { From a5ee4c959b0efc347cdf6565ebcbc81131ff3677 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Tue, 24 Mar 2026 21:32:11 +0100 Subject: [PATCH 059/106] platform/x86: uniwill-laptop: Apply features across all TUXEDO devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Uses the more fine granular and/or new feature defines to enable more features across the TUXEDO device lineup. Also adds features to TUXEDO devices that where already present in the driver, but not tested until now. Reviewed-by: Armin Wolf Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260324203413.454361-5-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 210 ++++++++++++++------ 1 file changed, 154 insertions(+), 56 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index 0ead71d12d58..faade4cf08be 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -110,6 +110,8 @@ #define EC_ADDR_BAT_CYCLE_COUNT_2 0x04A7 #define EC_ADDR_PROJECT_ID 0x0740 +#define PROJECT_ID_PH4TRX1 0x12 +#define PROJECT_ID_PH6TRX1 0x15 #define EC_ADDR_AP_OEM 0x0741 #define ENABLE_MANUAL_CTRL BIT(0) @@ -1861,6 +1863,15 @@ static struct platform_driver uniwill_driver = { .shutdown = uniwill_shutdown, }; +static struct uniwill_device_descriptor lapqc71a_lapqc71b_descriptor __initdata = { + .features = UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_BATTERY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, +}; + static struct uniwill_device_descriptor lapac71h_descriptor __initdata = { .features = UNIWILL_FEATURE_FN_LOCK | UNIWILL_FEATURE_SUPER_KEY | @@ -1884,6 +1895,85 @@ static struct uniwill_device_descriptor lapkc71f_descriptor __initdata = { UNIWILL_FEATURE_SECONDARY_FAN, }; +/* + * The featuresets below reflect somewhat chronological changes: + * 1 -> 2: UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL is added to the EC firmware. + * 2 -> 3: UNIWILL_FEATURE_USB_C_POWER_PRIORITY is removed from the EC firmware. + * Some devices might divert from this timeline. + */ + +static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_1_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_2_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, +}; + +static struct uniwill_device_descriptor tux_featureset_3_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN, +}; + +static struct uniwill_device_descriptor tux_featureset_3_nvidia_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL, +}; + +static int phxtxx1_probe(struct uniwill_data *data) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, EC_ADDR_PROJECT_ID, &value); + if (ret < 0) + return ret; + + if (value == PROJECT_ID_PH4TRX1 || value == PROJECT_ID_PH6TRX1) + data->features |= UNIWILL_FEATURE_SECONDARY_FAN; + + return 0; +}; + +static struct uniwill_device_descriptor phxtxx1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, + .probe = phxtxx1_probe, +}; + static int phxarx1_phxaqf1_probe(struct uniwill_data *data) { unsigned int value; @@ -1894,21 +1984,29 @@ static int phxarx1_phxaqf1_probe(struct uniwill_data *data) return ret; if (value & HAS_GPU) - data->features |= UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL; + data->features |= UNIWILL_FEATURE_GPU_TEMP | + UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL; return 0; }; static struct uniwill_device_descriptor phxarx1_phxaqf1_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN | + UNIWILL_FEATURE_SECONDARY_FAN | + UNIWILL_FEATURE_USB_C_POWER_PRIORITY, .probe = phxarx1_phxaqf1_probe, }; -static struct uniwill_device_descriptor tux_featureset_1_descriptor __initdata = { - .features = UNIWILL_FEATURE_NVIDIA_CTGP_CONTROL, +static struct uniwill_device_descriptor pf5pu1g_descriptor __initdata = { + .features = UNIWILL_FEATURE_FN_LOCK | + UNIWILL_FEATURE_SUPER_KEY | + UNIWILL_FEATURE_CPU_TEMP | + UNIWILL_FEATURE_PRIMARY_FAN, }; -static struct uniwill_device_descriptor empty_descriptor __initdata = {}; - static const struct dmi_system_id uniwill_dmi_table[] __initconst = { { .ident = "XMG FUSION 15 (L19)", @@ -1916,7 +2014,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { .ident = "XMG FUSION 15 (L19)", @@ -1924,7 +2022,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "SchenkerTechnologiesGmbH"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { .ident = "XMG FUSION 15 (L19)", @@ -1932,7 +2030,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71A"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { .ident = "XMG FUSION 15 (L19)", @@ -1940,7 +2038,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "LAPQC71B"), }, - .driver_data = &empty_descriptor, + .driver_data = &lapqc71a_lapqc71b_descriptor, }, { .ident = "Intel NUC x15", @@ -1964,7 +2062,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTxX1"), }, - .driver_data = &empty_descriptor, + .driver_data = &phxtxx1_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14 Gen6 Intel", @@ -1972,7 +2070,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PHxTQx1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/16 Gen7 Intel", @@ -1988,7 +2086,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6AG01_PH6AQ71_PH6AQI1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/16 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", @@ -1996,7 +2094,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PRX1_PH6PRX1"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14 Gen8 Intel/Commodore Omnia-Book Pro Gen 8", @@ -2004,7 +2102,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH4PG31"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 16 Gen8 Intel", @@ -2012,7 +2110,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PH6PG01_PH6PG71"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 AMD", @@ -2020,7 +2118,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxHRXx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen9 Intel/Commodore Omnia-Book 15 Gen9", @@ -2028,7 +2126,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GXxMRXx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", @@ -2036,7 +2134,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxHP4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 14/15 Gen10 AMD", @@ -2044,7 +2142,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxKK4NAx_XxSP4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Pro 15 Gen10 Intel", @@ -2052,7 +2150,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "XxAR4NAx"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_descriptor, }, { .ident = "TUXEDO InfinityBook Max 15 Gen10 AMD", @@ -2060,7 +2158,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5KK45xS_X5SP45xS"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", @@ -2068,7 +2166,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6HP45xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 AMD", @@ -2076,7 +2174,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6KK45xU_X6SP45xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 15 Gen10 Intel", @@ -2084,7 +2182,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X5AR45xS"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO InfinityBook Max 16 Gen10 Intel", @@ -2092,7 +2190,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR55xU"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 AMD", @@ -2100,7 +2198,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 AMD", @@ -2108,7 +2206,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501A2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 AMD", @@ -2116,7 +2214,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 AMD", @@ -2124,7 +2222,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701A2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 Intel", @@ -2132,7 +2230,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15 Gen1 Intel", @@ -2140,7 +2238,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1501I2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 Intel", @@ -2148,7 +2246,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I1650TI"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 17 Gen1 Intel", @@ -2156,7 +2254,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "POLARIS1701I2060"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Trinity 15 Intel Gen1", @@ -2164,7 +2262,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1501I"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Trinity 17 Intel Gen1", @@ -2172,7 +2270,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "TRINITY1701I"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen2 AMD", @@ -2180,7 +2278,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxMGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen2 Intel", @@ -2188,7 +2286,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxNGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 AMD", @@ -2196,7 +2294,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxZGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen3 Intel", @@ -2204,7 +2302,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxTGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris/Polaris 15/17 Gen4 AMD", @@ -2212,7 +2310,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxRGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 15 Gen4 Intel", @@ -2220,7 +2318,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxAGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Polaris 15/17 Gen5 AMD", @@ -2228,7 +2326,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxXGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_2_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen5 AMD", @@ -2236,7 +2334,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6XGxX"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16/17 Gen5 Intel/Commodore ORION Gen 5", @@ -2244,7 +2342,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxPXxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris Slim 15 Gen6 AMD", @@ -2252,7 +2350,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GMxHGxx"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris Slim 15 Gen6 Intel/Commodore ORION Slim 15 Gen6", @@ -2260,7 +2358,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM5IXxA"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", @@ -2268,7 +2366,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB1"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen6 Intel/Commodore ORION 16 Gen6", @@ -2276,7 +2374,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM6IXxB_MB2"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 17 Gen6 Intel/Commodore ORION 17 Gen6", @@ -2284,7 +2382,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "GM7IXxN"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 AMD", @@ -2292,7 +2390,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6FR5xxY"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 Intel", @@ -2300,7 +2398,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Stellaris 16 Gen7 Intel", @@ -2308,7 +2406,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "X6AR5xxY_mLED"), }, - .driver_data = &tux_featureset_1_descriptor, + .driver_data = &tux_featureset_3_nvidia_descriptor, }, { .ident = "TUXEDO Book BA15 Gen10 AMD", @@ -2316,7 +2414,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5PU1G"), }, - .driver_data = &empty_descriptor, + .driver_data = &pf5pu1g_descriptor, }, { .ident = "TUXEDO Pulse 14 Gen1 AMD", @@ -2324,7 +2422,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1401"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO Pulse 15 Gen1 AMD", @@ -2332,7 +2430,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PULSE1501"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { .ident = "TUXEDO Pulse 15 Gen2 AMD", @@ -2340,7 +2438,7 @@ static const struct dmi_system_id uniwill_dmi_table[] __initconst = { DMI_MATCH(DMI_SYS_VENDOR, "TUXEDO"), DMI_EXACT_MATCH(DMI_BOARD_NAME, "PF5LUXG"), }, - .driver_data = &empty_descriptor, + .driver_data = &tux_featureset_1_descriptor, }, { } }; From 9ec6bf62cf98e30c7126a0f51ee7cdf2e8d458b6 Mon Sep 17 00:00:00 2001 From: Werner Sembach Date: Tue, 24 Mar 2026 21:32:12 +0100 Subject: [PATCH 060/106] Documentation: laptops: Update documentation for uniwill laptops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds short description for two new sysfs entries, ctgp_offset and usb_c_power_priority, to the documentation of uniwill laptops. Reviewed-by: Armin Wolf Reviewed-by: Shuah Khan Signed-off-by: Werner Sembach Link: https://patch.msgid.link/20260324203413.454361-6-wse@tuxedocomputers.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../ABI/testing/sysfs-driver-uniwill-laptop | 27 +++++++++++++++++++ .../admin-guide/laptops/uniwill-laptop.rst | 12 +++++++++ 2 files changed, 39 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop index 2df70792968f..2397c65c969a 100644 --- a/Documentation/ABI/testing/sysfs-driver-uniwill-laptop +++ b/Documentation/ABI/testing/sysfs-driver-uniwill-laptop @@ -51,3 +51,30 @@ Description: Reading this file returns the current status of the breathing animation functionality. + +What: /sys/bus/platform/devices/INOU0000:XX/ctgp_offset +Date: January 2026 +KernelVersion: 7.0 +Contact: Werner Sembach +Description: + Allows userspace applications to set the configurable TGP offset on top of the base + TGP. Base TGP and max TGP and therefore the max cTGP offset are device specific. + Note that setting the maximum cTGP leaves no window open for Dynamic Boost as + Dynamic Boost also can not go over max TGP. Setting the cTGP to maximum is + effectively disabling Dynamic Boost and telling the device to always prioritize the + GPU over the CPU. + + Reading this file returns the current configurable TGP offset. + +What: /sys/bus/platform/devices/INOU0000:XX/usb_c_power_priority +Date: February 2026 +KernelVersion: 7.1 +Contact: Werner Sembach +Description: + Allows userspace applications to choose the USB-C power distribution profile between + one that offers a bigger share of the power to the battery and one that offers more + of it to the CPU. Writing "charging"/"performance" into this file selects the + respective profile. + + Reading this file returns the profile names with the currently active one in + brackets. diff --git a/Documentation/admin-guide/laptops/uniwill-laptop.rst b/Documentation/admin-guide/laptops/uniwill-laptop.rst index aff5f57a6bd4..561334865feb 100644 --- a/Documentation/admin-guide/laptops/uniwill-laptop.rst +++ b/Documentation/admin-guide/laptops/uniwill-laptop.rst @@ -50,6 +50,10 @@ between 1 and 100 percent are supported. Additionally the driver signals the presence of battery charging issues through the standard ``health`` power supply sysfs attribute. +It also lets you set whether a USB-C power source should prioritise charging the battery or +delivering immediate power to the cpu. See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for +details. + Lightbar -------- @@ -58,3 +62,11 @@ LED class device. The default name of this LED class device is ``uniwill:multico See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details on how to control the various animation modes of the lightbar. + +Configurable TGP +---------------- + +The ``uniwill-laptop`` driver allows to set the configurable TGP for devices with NVIDIA GPUs that +allow it. + +See Documentation/ABI/testing/sysfs-driver-uniwill-laptop for details. From 8e0a2fc68ec369f2b6755994da1d318d0898a9d9 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 26 Mar 2026 11:24:46 -0700 Subject: [PATCH 061/106] platform/x86/intel/tpmi: Use 32 bit aligned address for debugfs mem write MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The memory write feature supports 32-bit writes to any TPMI offset. However, future hardware generations may not allow writes to non-32-bit aligned addresses due to hardware optimizations. Since all TPMI addresses are 64-bit aligned and correspond to 64-bit registers, enforce 32-bit alignment for write operations. Signed-off-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260326182446.3478672-1-srinivas.pandruvada@linux.intel.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/vsec_tpmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/platform/x86/intel/vsec_tpmi.c b/drivers/platform/x86/intel/vsec_tpmi.c index 9dddf4e5863e..7fc6ff8d1040 100644 --- a/drivers/platform/x86/intel/vsec_tpmi.c +++ b/drivers/platform/x86/intel/vsec_tpmi.c @@ -46,6 +46,7 @@ * provided by the Intel VSEC driver. */ +#include #include #include #include @@ -479,6 +480,9 @@ static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t l addr = array[2]; value = array[3]; + if (!IS_ALIGNED(addr, sizeof(u32))) + return -EINVAL; + if (punit >= pfs->pfs_header.num_entries) { ret = -EINVAL; goto exit_write; From 99aef5d711c6e463ceff34d0657eca4b4558996a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 20:58:05 +0100 Subject: [PATCH 062/106] platform/x86: toshiba_acpi: Reorder code to avoid forward declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the definition of toshiba_acpi_notify() before the definitions of the functions that will refer to it after subsequent updates to avoid having to add a forward declarations of it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/4734258.LvFx2qVVIh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_acpi.c | 120 ++++++++++++++-------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 18fb558115aa..fbd7b6b6b826 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -3193,6 +3193,66 @@ static void print_supported_features(struct toshiba_acpi_dev *dev) pr_cont("\n"); } +static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + + switch (event) { + case 0x80: /* Hotkeys and some system events */ + /* + * Machines with this WMI GUID aren't supported due to bugs in + * their AML. + * + * Return silently to avoid triggering a netlink event. + */ + if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + return; + toshiba_acpi_process_hotkeys(dev); + break; + case 0x81: /* Dock events */ + case 0x82: + case 0x83: + pr_info("Dock event received %x\n", event); + break; + case 0x88: /* Thermal events */ + pr_info("Thermal event received\n"); + break; + case 0x8f: /* LID closed */ + case 0x90: /* LID is closed and Dock has been ejected */ + break; + case 0x8c: /* SATA power events */ + case 0x8b: + pr_info("SATA power event received %x\n", event); + break; + case 0x92: /* Keyboard backlight mode changed */ + dev->kbd_event_generated = true; + /* Update sysfs entries */ + if (sysfs_update_group(&acpi_dev->dev.kobj, + &toshiba_attr_group)) + pr_err("Unable to update sysfs entries\n"); + /* Notify LED subsystem about keyboard backlight change */ + if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO) + led_classdev_notify_brightness_hw_changed(&dev->kbd_led, + (dev->kbd_mode == SCI_KBD_MODE_ON) ? + LED_FULL : LED_OFF); + break; + case 0x8e: /* Power button pressed */ + break; + case 0x85: /* Unknown */ + case 0x8d: /* Unknown */ + case 0x94: /* Unknown */ + case 0x95: /* Unknown */ + default: + pr_info("Unknown event received %x\n", event); + break; + } + + acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, + dev_name(&acpi_dev->dev), + event, (event == 0x80) ? + dev->last_key_event : 0); +} + static void toshiba_acpi_remove(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -3495,66 +3555,6 @@ error: return ret; } -static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) -{ - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); - - switch (event) { - case 0x80: /* Hotkeys and some system events */ - /* - * Machines with this WMI GUID aren't supported due to bugs in - * their AML. - * - * Return silently to avoid triggering a netlink event. - */ - if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) - return; - toshiba_acpi_process_hotkeys(dev); - break; - case 0x81: /* Dock events */ - case 0x82: - case 0x83: - pr_info("Dock event received %x\n", event); - break; - case 0x88: /* Thermal events */ - pr_info("Thermal event received\n"); - break; - case 0x8f: /* LID closed */ - case 0x90: /* LID is closed and Dock has been ejected */ - break; - case 0x8c: /* SATA power events */ - case 0x8b: - pr_info("SATA power event received %x\n", event); - break; - case 0x92: /* Keyboard backlight mode changed */ - dev->kbd_event_generated = true; - /* Update sysfs entries */ - if (sysfs_update_group(&acpi_dev->dev.kobj, - &toshiba_attr_group)) - pr_err("Unable to update sysfs entries\n"); - /* Notify LED subsystem about keyboard backlight change */ - if (dev->kbd_type == 2 && dev->kbd_mode != SCI_KBD_MODE_AUTO) - led_classdev_notify_brightness_hw_changed(&dev->kbd_led, - (dev->kbd_mode == SCI_KBD_MODE_ON) ? - LED_FULL : LED_OFF); - break; - case 0x8e: /* Power button pressed */ - break; - case 0x85: /* Unknown */ - case 0x8d: /* Unknown */ - case 0x94: /* Unknown */ - case 0x95: /* Unknown */ - default: - pr_info("Unknown event received %x\n", event); - break; - } - - acpi_bus_generate_netlink_event(acpi_dev->pnp.device_class, - dev_name(&acpi_dev->dev), - event, (event == 0x80) ? - dev->last_key_event : 0); -} - #ifdef CONFIG_PM_SLEEP static int toshiba_acpi_suspend(struct device *device) { From b0bcf48c74cc7f72d1b6effc7cecdae871b1785e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 20:58:48 +0100 Subject: [PATCH 063/106] platform/x86: toshiba_acpi: 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/5070377.GXAFRqVoOG@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_acpi.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index fbd7b6b6b826..b839f62ec9cc 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -223,6 +223,7 @@ struct toshiba_acpi_dev { unsigned int cooling_method_supported:1; unsigned int battery_charge_mode_supported:1; unsigned int sysfs_created:1; + unsigned int notify_handler_installed:1; unsigned int special_functions; bool kbd_event_generated; @@ -3193,9 +3194,10 @@ static void print_supported_features(struct toshiba_acpi_dev *dev) pr_cont("\n"); } -static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) +static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = data; + struct acpi_device *acpi_dev = dev->acpi_dev; switch (event) { case 0x80: /* Hotkeys and some system events */ @@ -3261,6 +3263,10 @@ static void toshiba_acpi_remove(struct acpi_device *acpi_dev) remove_toshiba_proc_entries(dev); + if (dev->notify_handler_installed) + acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify); + #if IS_ENABLED(CONFIG_HWMON) if (dev->hwmon_device) hwmon_device_unregister(dev->hwmon_device); @@ -3537,6 +3543,13 @@ iio_error: } dev->sysfs_created = !ret; + ret = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + toshiba_acpi_notify, dev); + if (ret) + goto error; + + dev->notify_handler_installed = 1; + create_toshiba_proc_entries(dev); toshiba_acpi = dev; @@ -3602,7 +3615,6 @@ static struct acpi_driver toshiba_acpi_driver = { .ops = { .add = toshiba_acpi_add, .remove = toshiba_acpi_remove, - .notify = toshiba_acpi_notify, }, .drv.pm = &toshiba_acpi_pm, }; From 246d6cefe525f3fc760017e717bd77a3e751a260 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:00:03 +0100 Subject: [PATCH 064/106] platform/x86: toshiba_acpi: 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 Toshiba Laptop ACPI Extras driver from an ACPI driver to a platform one. After this change, all of the subordinate hwmon, IIO, and LED class devices will be registered under the platform device used for driver binding instead of its ACPI companion. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. However, the sysfs attributes in toshiba_attr_group will still be there in the sysfs directory of the ACPI companion of the platform device used for driver binding to maintain backwards compatibility with possibly existing user space utilities depending on the presence of those attributes. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2269772.irdbgypaU6@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_acpi.c | 50 ++++++++++++++++------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index b839f62ec9cc..35d899c01740 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -3255,16 +3256,17 @@ static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data) dev->last_key_event : 0); } -static void toshiba_acpi_remove(struct acpi_device *acpi_dev) +static void toshiba_acpi_remove(struct platform_device *pdev) { - struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + struct toshiba_acpi_dev *dev = platform_get_drvdata(pdev); misc_deregister(&dev->miscdev); remove_toshiba_proc_entries(dev); if (dev->notify_handler_installed) - acpi_dev_remove_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(ACPI_COMPANION(&pdev->dev), + ACPI_DEVICE_NOTIFY, toshiba_acpi_notify); #if IS_ENABLED(CONFIG_HWMON) @@ -3306,6 +3308,8 @@ static void toshiba_acpi_remove(struct acpi_device *acpi_dev) if (toshiba_acpi) toshiba_acpi = NULL; + dev_set_drvdata(&dev->acpi_dev->dev, NULL); + kfree(dev); } @@ -3368,8 +3372,9 @@ static const struct dmi_system_id toshiba_dmi_quirks[] __initconst = { { } }; -static int toshiba_acpi_add(struct acpi_device *acpi_dev) +static int toshiba_acpi_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct toshiba_acpi_dev *dev; const char *hci_method; u32 dummy; @@ -3403,7 +3408,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) return ret; } - acpi_dev->driver_data = dev; + platform_set_drvdata(pdev, dev); dev_set_drvdata(&acpi_dev->dev, dev); /* Query the BIOS for supported features */ @@ -3434,7 +3439,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->led_dev.max_brightness = 1; dev->led_dev.brightness_set = toshiba_illumination_set; dev->led_dev.brightness_get = toshiba_illumination_get; - led_classdev_register(&acpi_dev->dev, &dev->led_dev); + led_classdev_register(&pdev->dev, &dev->led_dev); } toshiba_eco_mode_available(dev); @@ -3443,7 +3448,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->eco_led.max_brightness = 1; dev->eco_led.brightness_set = toshiba_eco_mode_set_status; dev->eco_led.brightness_get = toshiba_eco_mode_get_status; - led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led); + led_classdev_register(&pdev->dev, &dev->eco_led); } toshiba_kbd_illum_available(dev); @@ -3459,7 +3464,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->kbd_led.max_brightness = 1; dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; - led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led); + led_classdev_register(&pdev->dev, &dev->kbd_led); } ret = toshiba_touchpad_get(dev, &dummy); @@ -3467,7 +3472,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) toshiba_accelerometer_available(dev); if (dev->accelerometer_supported) { - dev->indio_dev = iio_device_alloc(&acpi_dev->dev, sizeof(*dev)); + dev->indio_dev = iio_device_alloc(&pdev->dev, sizeof(*dev)); if (!dev->indio_dev) { pr_err("Unable to allocate iio device\n"); goto iio_error; @@ -3516,7 +3521,7 @@ iio_error: #if IS_ENABLED(CONFIG_HWMON) if (dev->fan_rpm_supported) { dev->hwmon_device = hwmon_device_register_with_info( - &dev->acpi_dev->dev, "toshiba_acpi_sensors", NULL, + &pdev->dev, "toshiba_acpi_sensors", NULL, &toshiba_acpi_hwmon_chip_info, NULL); if (IS_ERR(dev->hwmon_device)) { dev->hwmon_device = NULL; @@ -3564,14 +3569,14 @@ iio_error: return 0; error: - toshiba_acpi_remove(acpi_dev); + toshiba_acpi_remove(pdev); return ret; } #ifdef CONFIG_PM_SLEEP static int toshiba_acpi_suspend(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); + struct toshiba_acpi_dev *dev = dev_get_drvdata(device); if (dev->hotkey_dev) { u32 result; @@ -3586,7 +3591,7 @@ static int toshiba_acpi_suspend(struct device *device) static int toshiba_acpi_resume(struct device *device) { - struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device)); + struct toshiba_acpi_dev *dev = dev_get_drvdata(device); if (dev->hotkey_dev) { if (toshiba_acpi_enable_hotkeys(dev)) @@ -3608,15 +3613,14 @@ static int toshiba_acpi_resume(struct device *device) static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm, toshiba_acpi_suspend, toshiba_acpi_resume); -static struct acpi_driver toshiba_acpi_driver = { - .name = "Toshiba ACPI driver", - .ids = toshiba_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = toshiba_acpi_add, - .remove = toshiba_acpi_remove, +static struct platform_driver toshiba_acpi_driver = { + .probe = toshiba_acpi_probe, + .remove = toshiba_acpi_remove, + .driver = { + .name = "Toshiba ACPI driver", + .acpi_match_table = toshiba_device_ids, + .pm = &toshiba_acpi_pm, }, - .drv.pm = &toshiba_acpi_pm, }; static void __init toshiba_dmi_init(void) @@ -3646,7 +3650,7 @@ static int __init toshiba_acpi_init(void) return -ENODEV; } - ret = acpi_bus_register_driver(&toshiba_acpi_driver); + ret = platform_driver_register(&toshiba_acpi_driver); if (ret) { pr_err("Failed to register ACPI driver: %d\n", ret); remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); @@ -3657,7 +3661,7 @@ static int __init toshiba_acpi_init(void) static void __exit toshiba_acpi_exit(void) { - acpi_bus_unregister_driver(&toshiba_acpi_driver); + platform_driver_unregister(&toshiba_acpi_driver); if (toshiba_proc_dir) remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); } From 4315abf338301090eec70c68117afa854fddb264 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:00:50 +0100 Subject: [PATCH 065/106] platform/x86: toshiba_bluetooth: 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/3048289.e9J7NaK4W3@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_bluetooth.c | 32 ++++++++++++++++++------ 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index e587beef05b9..3824c2beaf11 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -37,7 +37,7 @@ struct toshiba_bluetooth_dev { static int toshiba_bt_rfkill_add(struct acpi_device *device); static void toshiba_bt_rfkill_remove(struct acpi_device *device); -static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event); +static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id bt_device_ids[] = { { "TOS6205", 0}, @@ -57,7 +57,6 @@ static struct acpi_driver toshiba_bt_rfkill_driver = { .ops = { .add = toshiba_bt_rfkill_add, .remove = toshiba_bt_rfkill_remove, - .notify = toshiba_bt_rfkill_notify, }, .drv.pm = &toshiba_bt_pm, }; @@ -203,9 +202,9 @@ static const struct rfkill_ops rfk_ops = { }; /* ACPI driver functions */ -static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event) +static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data) { - struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); + struct toshiba_bluetooth_dev *bt_dev = data; if (toshiba_bluetooth_sync_status(bt_dev)) return; @@ -262,8 +261,8 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) bt_dev); if (!bt_dev->rfk) { pr_err("Unable to allocate rfkill device\n"); - kfree(bt_dev); - return -ENOMEM; + result = -ENOMEM; + goto err_free_bt_dev; } rfkill_set_hw_state(bt_dev->rfk, !bt_dev->killswitch); @@ -271,10 +270,24 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) result = rfkill_register(bt_dev->rfk); if (result) { pr_err("Unable to register rfkill device\n"); - rfkill_destroy(bt_dev->rfk); - kfree(bt_dev); + goto err_rfkill_destroy; } + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_bt_rfkill_notify, bt_dev); + if (result) { + pr_err("Unable to register ACPI notify handler\n"); + goto err_rfkill_unregister; + } + + return 0; + +err_rfkill_unregister: + rfkill_unregister(bt_dev->rfk); +err_rfkill_destroy: + rfkill_destroy(bt_dev->rfk); +err_free_bt_dev: + kfree(bt_dev); return result; } @@ -283,6 +296,9 @@ static void toshiba_bt_rfkill_remove(struct acpi_device *device) struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); /* clean up */ + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_bt_rfkill_notify); + if (bt_dev->rfk) { rfkill_unregister(bt_dev->rfk); rfkill_destroy(bt_dev->rfk); From 553b2ac59fbb434330287fde0cabc64bc6f11ceb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:01:28 +0100 Subject: [PATCH 066/106] platform/x86: toshiba_bluetooth: 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 Toshiba Bluetooth Enable driver from an ACPI driver to a platform one. 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/3420881.44csPzL39Z@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_bluetooth.c | 42 ++++++++++++------------ 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index 3824c2beaf11..e50d4fc1e603 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -17,6 +17,7 @@ #include #include #include +#include #define BT_KILLSWITCH_MASK 0x01 #define BT_PLUGGED_MASK 0x40 @@ -35,8 +36,8 @@ struct toshiba_bluetooth_dev { bool powered; }; -static int toshiba_bt_rfkill_add(struct acpi_device *device); -static void toshiba_bt_rfkill_remove(struct acpi_device *device); +static int toshiba_bt_rfkill_probe(struct platform_device *pdev); +static void toshiba_bt_rfkill_remove(struct platform_device *pdev); static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data); static const struct acpi_device_id bt_device_ids[] = { @@ -50,15 +51,14 @@ static int toshiba_bt_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume); -static struct acpi_driver toshiba_bt_rfkill_driver = { - .name = "Toshiba BT", - .class = "Toshiba", - .ids = bt_device_ids, - .ops = { - .add = toshiba_bt_rfkill_add, - .remove = toshiba_bt_rfkill_remove, - }, - .drv.pm = &toshiba_bt_pm, +static struct platform_driver toshiba_bt_rfkill_driver = { + .probe = toshiba_bt_rfkill_probe, + .remove = toshiba_bt_rfkill_remove, + .driver = { + .name = "Toshiba BT", + .acpi_match_table = bt_device_ids, + .pm = &toshiba_bt_pm, + }, }; static int toshiba_bluetooth_present(acpi_handle handle) @@ -215,11 +215,9 @@ static void toshiba_bt_rfkill_notify(acpi_handle handle, u32 event, void *data) #ifdef CONFIG_PM_SLEEP static int toshiba_bt_resume(struct device *dev) { - struct toshiba_bluetooth_dev *bt_dev; + struct toshiba_bluetooth_dev *bt_dev = dev_get_drvdata(dev); int ret; - bt_dev = acpi_driver_data(to_acpi_device(dev)); - ret = toshiba_bluetooth_sync_status(bt_dev); if (ret) return ret; @@ -230,8 +228,9 @@ static int toshiba_bt_resume(struct device *dev) } #endif -static int toshiba_bt_rfkill_add(struct acpi_device *device) +static int toshiba_bt_rfkill_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct toshiba_bluetooth_dev *bt_dev; int result; @@ -245,8 +244,8 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) if (!bt_dev) return -ENOMEM; bt_dev->acpi_dev = device; - device->driver_data = bt_dev; - dev_set_drvdata(&device->dev, bt_dev); + + platform_set_drvdata(pdev, bt_dev); result = toshiba_bluetooth_sync_status(bt_dev); if (result) { @@ -255,7 +254,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device) } bt_dev->rfk = rfkill_alloc("Toshiba Bluetooth", - &device->dev, + &pdev->dev, RFKILL_TYPE_BLUETOOTH, &rfk_ops, bt_dev); @@ -291,9 +290,10 @@ err_free_bt_dev: return result; } -static void toshiba_bt_rfkill_remove(struct acpi_device *device) +static void toshiba_bt_rfkill_remove(struct platform_device *pdev) { - struct toshiba_bluetooth_dev *bt_dev = acpi_driver_data(device); + struct toshiba_bluetooth_dev *bt_dev = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); /* clean up */ acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, @@ -309,4 +309,4 @@ static void toshiba_bt_rfkill_remove(struct acpi_device *device) toshiba_bluetooth_disable(device->handle); } -module_acpi_driver(toshiba_bt_rfkill_driver); +module_platform_driver(toshiba_bt_rfkill_driver); From 32156fd2fbb8fc2d049ad459336cd71b2556f75a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:02:14 +0100 Subject: [PATCH 067/106] platform/x86: toshiba_haps: 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/10834562.nUPlyArG6x@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_haps.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index e9324bf16aea..6eac306cf92a 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -129,8 +129,10 @@ static const struct attribute_group haps_attr_group = { /* * ACPI stuff */ -static void toshiba_haps_notify(struct acpi_device *device, u32 event) +static void toshiba_haps_notify(acpi_handle handle, u32 event, void *data) { + struct acpi_device *device = data; + pr_debug("Received event: 0x%x\n", event); acpi_bus_generate_netlink_event(device->pnp.device_class, @@ -140,6 +142,9 @@ static void toshiba_haps_notify(struct acpi_device *device, u32 event) static void toshiba_haps_remove(struct acpi_device *device) { + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + toshiba_haps_notify); + sysfs_remove_group(&device->dev.kobj, &haps_attr_group); if (toshiba_haps) @@ -201,9 +206,18 @@ static int toshiba_haps_add(struct acpi_device *acpi_dev) if (ret) return ret; + ret = acpi_dev_install_notify_handler(acpi_dev, ACPI_DEVICE_NOTIFY, + toshiba_haps_notify, acpi_dev); + if (ret) + goto err; + toshiba_haps = haps; return 0; + +err: + sysfs_remove_group(&acpi_dev->dev.kobj, &haps_attr_group); + return ret; } #ifdef CONFIG_PM_SLEEP @@ -256,7 +270,6 @@ static struct acpi_driver toshiba_haps_driver = { .ops = { .add = toshiba_haps_add, .remove = toshiba_haps_remove, - .notify = toshiba_haps_notify, }, .drv.pm = &toshiba_haps_pm, }; From 3a96c7915d93231e128df74901fe8fcb960b0ecb Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:03:01 +0100 Subject: [PATCH 068/106] platform/x86: toshiba_haps: 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 Toshiba HDD Active Protection Sensor driver from an ACPI driver to a platform one. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Note that the sysfs attributes in haps_attr_group will still be there in the sysfs directory of the ACPI companion of the platform device used for driver binding in case there are tools in user space expecting them to be present there. Link: https://lore.kernel.org/all/2396510.ElGaqSPkdT@rafael.j.wysocki/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2045343.PYKUYFuaPT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/toshiba_haps.c | 40 +++++++++++++++-------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/drivers/platform/x86/toshiba_haps.c b/drivers/platform/x86/toshiba_haps.c index 6eac306cf92a..1486252b5983 100644 --- a/drivers/platform/x86/toshiba_haps.c +++ b/drivers/platform/x86/toshiba_haps.c @@ -12,6 +12,7 @@ #include #include #include +#include MODULE_AUTHOR("Azael Avalos "); MODULE_DESCRIPTION("Toshiba HDD Active Protection Sensor"); @@ -140,8 +141,10 @@ static void toshiba_haps_notify(acpi_handle handle, u32 event, void *data) event, 0); } -static void toshiba_haps_remove(struct acpi_device *device) +static void toshiba_haps_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, toshiba_haps_notify); @@ -149,6 +152,8 @@ static void toshiba_haps_remove(struct acpi_device *device) if (toshiba_haps) toshiba_haps = NULL; + + dev_set_drvdata(&device->dev, NULL); } /* Helper function */ @@ -175,8 +180,9 @@ static int toshiba_haps_available(acpi_handle handle) return 1; } -static int toshiba_haps_add(struct acpi_device *acpi_dev) +static int toshiba_haps_probe(struct platform_device *pdev) { + struct acpi_device *acpi_dev = ACPI_COMPANION(&pdev->dev); struct toshiba_haps_dev *haps; int ret; @@ -188,14 +194,15 @@ static int toshiba_haps_add(struct acpi_device *acpi_dev) pr_info("Toshiba HDD Active Protection Sensor device\n"); - haps = devm_kzalloc(&acpi_dev->dev, sizeof(*haps), GFP_KERNEL); + haps = devm_kzalloc(&pdev->dev, sizeof(*haps), GFP_KERNEL); if (!haps) return -ENOMEM; haps->acpi_dev = acpi_dev; haps->protection_level = 2; - acpi_dev->driver_data = haps; + dev_set_drvdata(&acpi_dev->dev, haps); + platform_set_drvdata(pdev, haps); /* Set the protection level, currently at level 2 (Medium) */ ret = toshiba_haps_protection_level(acpi_dev->handle, 2); @@ -223,11 +230,9 @@ err: #ifdef CONFIG_PM_SLEEP static int toshiba_haps_suspend(struct device *device) { - struct toshiba_haps_dev *haps; + struct toshiba_haps_dev *haps = dev_get_drvdata(device); int ret; - haps = acpi_driver_data(to_acpi_device(device)); - /* Deactivate the protection on suspend */ ret = toshiba_haps_protection_level(haps->acpi_dev->handle, 0); @@ -236,11 +241,9 @@ static int toshiba_haps_suspend(struct device *device) static int toshiba_haps_resume(struct device *device) { - struct toshiba_haps_dev *haps; + struct toshiba_haps_dev *haps = dev_get_drvdata(device); int ret; - haps = acpi_driver_data(to_acpi_device(device)); - /* Set the stored protection level */ ret = toshiba_haps_protection_level(haps->acpi_dev->handle, haps->protection_level); @@ -263,15 +266,14 @@ static const struct acpi_device_id haps_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, haps_device_ids); -static struct acpi_driver toshiba_haps_driver = { - .name = "Toshiba HAPS", - .ids = haps_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = toshiba_haps_add, - .remove = toshiba_haps_remove, +static struct platform_driver toshiba_haps_driver = { + .probe = toshiba_haps_probe, + .remove = toshiba_haps_remove, + .driver = { + .name = "Toshiba HAPS", + .acpi_match_table = haps_device_ids, + .pm = &toshiba_haps_pm, }, - .drv.pm = &toshiba_haps_pm, }; -module_acpi_driver(toshiba_haps_driver); +module_platform_driver(toshiba_haps_driver); From ae67f582398611b9f67c06961e292e3a2612346d Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 12 Mar 2026 12:41:08 -0700 Subject: [PATCH 069/106] tools/power/x86/intel-speed-select: Avoid current base freq as maximum SST-PP level change results in online/offline of CPUs with -o option. The Linux intel-pstate driver internally stores the current HWP_REQ MSR value during offline and restores them during online. It is possible that during SST-PP level change, the new HWP_CAP limits can be updated. So, when a CPU is online, the HWP_REQ MSR should be updated to new values based on HWP_CAP values. This is particularly problematic when either turbo is disabled or the current HWP_REQ value (stored before online) is less than the base frequency from the updated HWP_CAP MSR guaranteed value. If the HWP_REQ MSR is not updated, then the performance will be limited to the value before perf level change. Hence the tool updates cpufreq scaling_max_freq to the newer base_frequency value in this case. This step is not required when HWP interrupts are enabled, as the perf level change should result in a new interrupt with HWP_GUARANTEED_PERF_CHANGE_STATUS and the intel_pstate driver will update to new limits. But the tool needs to handle the case when HWP interrupts are not enabled but there is no way for the tool to know that HWP interrupts are enabled or not. So, it has to still update the scaling_max_freq. With the QOS changes in the kernel, user space writes to scaling_max_freq are treated as hard limits. So, when base frequency is increased with SST-BF enabled, the cpufreq subsystem will still not allow setting to the SST-BF high priority core frequency. So, the HWP_REQ MSR will still be capped to the user-set scaling_max_freq after SST-PP level change. To address this, instead of setting scaling_max_freq to the current HWP_CAP highest frequency, set it to the maximum integer value to set the QOS limit as unconstrained. In this case, the actual HWP_REQ maximum frequency will still be capped to HWP_CAP highest performance by the intel-pstate driver. So, it will not result in invalid HWP_REQ values. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index dd9056ddb016..652ef1f567ad 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1744,6 +1744,9 @@ static int no_turbo(void) return parse_int_file(0, "/sys/devices/system/cpu/intel_pstate/no_turbo"); } +#define U32_MAX ((unsigned int)~0U) +#define S32_MAX ((int)(U32_MAX >> 1)) + static void adjust_scaling_max_from_base_freq(int cpu) { int base_freq, scaling_max_freq; @@ -1751,7 +1754,7 @@ static void adjust_scaling_max_from_base_freq(int cpu) scaling_max_freq = parse_int_file(0, "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", cpu); base_freq = get_cpufreq_base_freq(cpu); if (scaling_max_freq < base_freq || no_turbo()) - set_cpufreq_scaling_min_max(cpu, 1, base_freq); + set_cpufreq_scaling_min_max(cpu, 1, S32_MAX); } static void adjust_scaling_min_from_base_freq(int cpu) From df4a83543117c7fc27077fd7f4ffe870556b257b Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Mon, 26 Jan 2026 08:27:01 +0800 Subject: [PATCH 070/106] tools/power/x86/intel-speed-select: Fix cpu extended family ID decoding When decode and use CPU extended family ID in intel-speed-select, there are several potential issues, 1. Mask with 0x0f to get CPU extended family ID is bogus because CPU extended family ID takes 8 bits (bit 27:20). 2. Use CPU extended family ID fields without checking CPU family ID is risky. Because Intel SDM says, "The Extended Family ID needs to be examined only when the Family ID is 0FH." 3. Saving cpu family ID and cpu extended family ID separately doesn't align with Linux kernel. And it may bring extra complexity when making family specific changes in the future. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 652ef1f567ad..3f2573ecca76 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -26,7 +26,7 @@ static FILE *outf; static int cpu_model; static int cpu_stepping; -static int extended_family; +static int cpu_family; #define MAX_CPUS_IN_ONE_REQ 512 static short max_target_cpus; @@ -158,7 +158,7 @@ int is_icx_platform(void) static int is_dmr_plus_platform(void) { - if (extended_family == 0x04) + if (cpu_family == 19) return 1; return 0; @@ -167,13 +167,14 @@ static int is_dmr_plus_platform(void) static int update_cpu_model(void) { unsigned int ebx, ecx, edx; - unsigned int fms, family; + unsigned int fms; __cpuid(1, fms, ebx, ecx, edx); - family = (fms >> 8) & 0xf; - extended_family = (fms >> 20) & 0x0f; + cpu_family = (fms >> 8) & 0xf; + if (cpu_family == 0xf) + cpu_family += (fms >> 20) & 0xff; cpu_model = (fms >> 4) & 0xf; - if (family == 6 || family == 0xf) + if (cpu_family == 6 || cpu_family == 0xf) cpu_model += ((fms >> 16) & 0xf) << 4; cpu_stepping = fms & 0xf; From 3e244dd513e26728577f1e4deca6fdf749b6f244 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 19 Mar 2026 13:52:54 +0800 Subject: [PATCH 071/106] tools/power/x86/intel-speed-select: Fix some program return value When running the "intel-speed-select -h" command, it returns 1. 0 when using a version that is API incompatible. 2. 1 when using a version that is API compatible. And this is confusing. Fix the program to return 0 for "-h" parameter, and return 1 whenever "Incompatible API versions" is detected. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 3f2573ecca76..b1376411cfa4 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -1139,7 +1139,7 @@ static int isst_fill_platform_info(void) if (isst_platform_info.api_version > supported_api_ver) { printf("Incompatible API versions; Upgrade of tool is required\n"); - return -1; + exit(1); } set_platform_ops: @@ -3195,7 +3195,7 @@ static void usage(void) printf("\tTo get full turbo-freq information dump:\n"); printf("\t\tintel-speed-select turbo-freq info -l 0\n"); } - exit(1); + exit(0); } static void print_version(void) From 93f5b44b416c7419a76a5e1311fb750fca585638 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 19 Mar 2026 13:52:55 +0800 Subject: [PATCH 072/106] tools/power/x86/intel-speed-select: Print Version info when Incompatible API version is detected When running an old version intel-speed-select tool on newer platforms, even with "intel-speed-select -v", the tool only complains about "Incompatible API version", without giving the current version info. Print Version info whenever Incompatible API version is detected. Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index b1376411cfa4..1e156063141e 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -82,6 +82,11 @@ struct cpu_topology { static int read_only; +static void print_version(void) +{ + fprintf(outf, "Version %s\n", version_str); +} + static void check_privilege(void) { if (!read_only) @@ -1138,6 +1143,7 @@ static int isst_fill_platform_info(void) close(fd); if (isst_platform_info.api_version > supported_api_ver) { + print_version(); printf("Incompatible API versions; Upgrade of tool is required\n"); exit(1); } @@ -3198,12 +3204,6 @@ static void usage(void) exit(0); } -static void print_version(void) -{ - fprintf(outf, "Version %s\n", version_str); - exit(0); -} - static void cmdline(int argc, char **argv) { const char *pathname = "/dev/isst_interface"; @@ -3315,6 +3315,7 @@ static void cmdline(int argc, char **argv) break; case 'v': print_version(); + exit(0); break; case 'b': oob_mode = 1; From 1b25f03f3daf7c26c37050a7b2b5858ad5f99cfc Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 19 Mar 2026 13:52:56 +0800 Subject: [PATCH 073/106] tools/power/x86/intel-speed-select: Fix output when running on unsupported CLX platforms When running intel-speed-select on unsupported CLX platforms, it prints intel-speed-select: Invalid CPU model (85) : Success Because this is not a system error and errno is not set. Replace err() with exit(). Signed-off-by: Zhang Rui Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 1e156063141e..67878a08e116 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -3250,8 +3250,10 @@ static void cmdline(int argc, char **argv) } ret = update_cpu_model(); - if (ret) - err(-1, "Invalid CPU model (%d)\n", cpu_model); + if (ret) { + fprintf(stderr, "Invalid CPU model (%d)\n", cpu_model); + exit(1); + } printf("Intel(R) Speed Select Technology\n"); printf("Executing on CPU model:%d[0x%x]\n", cpu_model, cpu_model); From ee69d9e32bdb0044b9444f1ae12107ff8b5ff95f Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Sun, 5 Apr 2026 11:54:25 -0700 Subject: [PATCH 074/106] tools/power/x86/intel-speed-select: v1.26 release This version includes the following changes: - Setting current base frequency as maximum for SST-BF with kernel QOS changes - Harmonize extended family decoded with the rest of the kernel - Minor changes for error codes and messages Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index 67878a08e116..2faff1aead52 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -16,7 +16,7 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.25"; +static const char *version_str = "v1.26"; static const int supported_api_ver = 3; static struct isst_if_platform_info isst_platform_info; From 378500dc1313e2c06a2f675bb00ab5d7880433ba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:10:21 +0100 Subject: [PATCH 075/106] platform/x86: asus-laptop: 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/5082508.31r3eYUQgx@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-laptop.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index d96f6af26ff7..c927665dfa96 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1517,9 +1517,9 @@ static void asus_input_exit(struct asus_laptop *asus) /* * ACPI driver */ -static void asus_acpi_notify(struct acpi_device *device, u32 event) +static void asus_acpi_notify(acpi_handle handle, u32 event, void *data) { - struct asus_laptop *asus = acpi_driver_data(device); + struct asus_laptop *asus = data; u16 count; /* TODO Find a better way to handle events count. */ @@ -1881,6 +1881,11 @@ static int asus_acpi_add(struct acpi_device *device) if (result && result != -ENODEV) goto fail_pega_rfkill; + result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, + asus_acpi_notify, asus); + if (result) + goto fail_pega_rfkill; + asus_device_present = true; return 0; @@ -1906,6 +1911,7 @@ static void asus_acpi_remove(struct acpi_device *device) { struct asus_laptop *asus = acpi_driver_data(device); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, asus_acpi_notify); asus_backlight_exit(asus); asus_rfkill_exit(asus); asus_led_exit(asus); @@ -1932,7 +1938,6 @@ static struct acpi_driver asus_acpi_driver = { .ops = { .add = asus_acpi_add, .remove = asus_acpi_remove, - .notify = asus_acpi_notify, }, }; From ba19eb10170b50cd613aa18cfb0e1b4693db7edf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:11:10 +0100 Subject: [PATCH 076/106] platform/x86: asus-laptop: 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 Asus laptop ACPI driver to a platform one. 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/2402539.ElGaqSPkdT@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-laptop.c | 35 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index c927665dfa96..dbbb6292cd11 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1824,8 +1824,9 @@ static void asus_dmi_check(void) static bool asus_device_present; -static int asus_acpi_add(struct acpi_device *device) +static int asus_acpi_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct asus_laptop *asus; int result; @@ -1837,7 +1838,6 @@ static int asus_acpi_add(struct acpi_device *device) asus->handle = device->handle; strscpy(acpi_device_name(device), ASUS_LAPTOP_DEVICE_NAME); strscpy(acpi_device_class(device), ASUS_LAPTOP_CLASS); - device->driver_data = asus; asus->device = device; asus_dmi_check(); @@ -1846,6 +1846,8 @@ static int asus_acpi_add(struct acpi_device *device) if (result) goto fail_platform; + platform_set_drvdata(pdev, asus); + /* * Need platform type detection first, then the platform * device. It is used as a parent for the sub-devices below. @@ -1907,11 +1909,12 @@ fail_platform: return result; } -static void asus_acpi_remove(struct acpi_device *device) +static void asus_acpi_remove(struct platform_device *pdev) { - struct asus_laptop *asus = acpi_driver_data(device); + struct asus_laptop *asus = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, asus_acpi_notify); + acpi_dev_remove_notify_handler(asus->device, ACPI_DEVICE_NOTIFY, + asus_acpi_notify); asus_backlight_exit(asus); asus_rfkill_exit(asus); asus_led_exit(asus); @@ -1930,15 +1933,13 @@ static const struct acpi_device_id asus_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, asus_device_ids); -static struct acpi_driver asus_acpi_driver = { - .name = ASUS_LAPTOP_NAME, - .class = ASUS_LAPTOP_CLASS, - .ids = asus_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = asus_acpi_add, - .remove = asus_acpi_remove, - }, +static struct platform_driver asus_acpi_driver = { + .probe = asus_acpi_probe, + .remove = asus_acpi_remove, + .driver = { + .name = ASUS_LAPTOP_NAME, + .acpi_match_table = asus_device_ids, + }, }; static int __init asus_laptop_init(void) @@ -1949,7 +1950,7 @@ static int __init asus_laptop_init(void) if (result < 0) return result; - result = acpi_bus_register_driver(&asus_acpi_driver); + result = platform_driver_register(&asus_acpi_driver); if (result < 0) goto fail_acpi_driver; if (!asus_device_present) { @@ -1959,7 +1960,7 @@ static int __init asus_laptop_init(void) return 0; fail_no_device: - acpi_bus_unregister_driver(&asus_acpi_driver); + platform_driver_unregister(&asus_acpi_driver); fail_acpi_driver: platform_driver_unregister(&platform_driver); return result; @@ -1967,7 +1968,7 @@ fail_acpi_driver: static void __exit asus_laptop_exit(void) { - acpi_bus_unregister_driver(&asus_acpi_driver); + platform_driver_unregister(&asus_acpi_driver); platform_driver_unregister(&platform_driver); } From 4f52c97292587c0ad21f3bc78cb2345f7dfc6c89 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:12:02 +0100 Subject: [PATCH 077/106] platform/x86: asus-wireless: 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/1949745.tdWV9SEqCh@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wireless.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 41227bf95878..45d41875c515 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -108,9 +108,10 @@ static void led_state_set(struct led_classdev *led, enum led_brightness value) queue_work(data->wq, &data->led_work); } -static void asus_wireless_notify(struct acpi_device *adev, u32 event) +static void asus_wireless_notify(acpi_handle handle, u32 event, void *context) { - struct asus_wireless_data *data = acpi_driver_data(adev); + struct asus_wireless_data *data = context; + struct acpi_device *adev = data->adev; dev_dbg(&adev->dev, "event=%#x\n", event); if (event != 0x88) { @@ -166,8 +167,18 @@ static int asus_wireless_add(struct acpi_device *adev) data->led.default_trigger = "rfkill-none"; err = devm_led_classdev_register(&adev->dev, &data->led); if (err) - destroy_workqueue(data->wq); + goto err; + err = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, + asus_wireless_notify, data); + if (err) { + devm_led_classdev_unregister(&adev->dev, &data->led); + goto err; + } + return 0; + +err: + destroy_workqueue(data->wq); return err; } @@ -175,6 +186,8 @@ static void asus_wireless_remove(struct acpi_device *adev) { struct asus_wireless_data *data = acpi_driver_data(adev); + acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, + asus_wireless_notify); if (data->wq) { devm_led_classdev_unregister(&adev->dev, &data->led); destroy_workqueue(data->wq); @@ -188,7 +201,6 @@ static struct acpi_driver asus_wireless_driver = { .ops = { .add = asus_wireless_add, .remove = asus_wireless_remove, - .notify = asus_wireless_notify, }, }; module_acpi_driver(asus_wireless_driver); From f7e648027d7e1b5c06eb86d944b7e23926254cee Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 28 Feb 2026 16:12:48 +0100 Subject: [PATCH 078/106] platform/x86: asus-wireless: 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 Asus wireless ACPI driver to a platform one. After this change, the subordinate input and LED devices will be registered under the platform device used for driver binding instead of its ACPI companion. 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 Reviewed-by: Denis Benato Link: https://patch.msgid.link/13959361.uLZWGnKmhe@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wireless.c | 39 +++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/platform/x86/asus-wireless.c b/drivers/platform/x86/asus-wireless.c index 45d41875c515..2b494bf3cba8 100644 --- a/drivers/platform/x86/asus-wireless.c +++ b/drivers/platform/x86/asus-wireless.c @@ -12,6 +12,7 @@ #include #include #include +#include #include struct hswc_params { @@ -124,19 +125,22 @@ static void asus_wireless_notify(acpi_handle handle, u32 event, void *context) input_sync(data->idev); } -static int asus_wireless_add(struct acpi_device *adev) +static int asus_wireless_probe(struct platform_device *pdev) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct asus_wireless_data *data; const struct acpi_device_id *id; int err; - data = devm_kzalloc(&adev->dev, sizeof(*data), GFP_KERNEL); + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - adev->driver_data = data; + + platform_set_drvdata(pdev, data); + data->adev = adev; - data->idev = devm_input_allocate_device(&adev->dev); + data->idev = devm_input_allocate_device(&pdev->dev); if (!data->idev) return -ENOMEM; data->idev->name = "Asus Wireless Radio Control"; @@ -165,14 +169,14 @@ static int asus_wireless_add(struct acpi_device *adev) data->led.flags = LED_CORE_SUSPENDRESUME; data->led.max_brightness = 1; data->led.default_trigger = "rfkill-none"; - err = devm_led_classdev_register(&adev->dev, &data->led); + err = devm_led_classdev_register(&pdev->dev, &data->led); if (err) goto err; err = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, asus_wireless_notify, data); if (err) { - devm_led_classdev_unregister(&adev->dev, &data->led); + devm_led_classdev_unregister(&pdev->dev, &data->led); goto err; } return 0; @@ -182,28 +186,27 @@ err: return err; } -static void asus_wireless_remove(struct acpi_device *adev) +static void asus_wireless_remove(struct platform_device *pdev) { - struct asus_wireless_data *data = acpi_driver_data(adev); + struct asus_wireless_data *data = platform_get_drvdata(pdev); - acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(data->adev, ACPI_DEVICE_NOTIFY, asus_wireless_notify); if (data->wq) { - devm_led_classdev_unregister(&adev->dev, &data->led); + devm_led_classdev_unregister(&pdev->dev, &data->led); destroy_workqueue(data->wq); } } -static struct acpi_driver asus_wireless_driver = { - .name = "Asus Wireless Radio Control Driver", - .class = "hotkey", - .ids = device_ids, - .ops = { - .add = asus_wireless_add, - .remove = asus_wireless_remove, +static struct platform_driver asus_wireless_driver = { + .probe = asus_wireless_probe, + .remove = asus_wireless_remove, + .driver = { + .name = "Asus Wireless Radio Control Driver", + .acpi_match_table = device_ids, }, }; -module_acpi_driver(asus_wireless_driver); +module_platform_driver(asus_wireless_driver); MODULE_DESCRIPTION("Asus Wireless Radio Control Driver"); MODULE_AUTHOR("João Paulo Rechi Vita "); From 955165c3e537668b9a5a6eb26397281b88143002 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 24 Mar 2026 21:08:01 +0100 Subject: [PATCH 079/106] platform/x86: thinkpad_acpi: Drop ACPI driver registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in registering an ACPI driver that only has an empty .add() callback, which is done by the thinkpad_acpi driver, since after binding to an ACPI device it only sits there and does nothing. That binding only effectively causes the ACPI device's reference count to increase, but that can be achieved by using acpi_get_acpi_dev() instead of acpi_fetch_acpi_dev() in setup_acpi_notify(), and doing the corresponding cleanup in ibm_exit(). Update the code accordingly and get rid of the non-functional ACPI driver. No intentional functional impact beyond altering sysfs content. Signed-off-by: Rafael J. Wysocki Tested-by: Mark Pearson Reviewed-by: Mark Pearson Link: https://patch.msgid.link/3949487.kQq0lBPeGt@rafael.j.wysocki Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 62 ++------------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 90da69b0e417..1a9effcae5cd 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -299,7 +299,6 @@ struct ibm_struct; struct tp_acpi_drv_struct { const struct acpi_device_id *hid; - struct acpi_driver *driver; void (*notify) (struct ibm_struct *, u32); acpi_handle *handle; @@ -322,7 +321,6 @@ struct ibm_struct { struct tp_acpi_drv_struct *acpi; struct { - u8 acpi_driver_registered:1; u8 acpi_notify_installed:1; u8 proc_created:1; u8 init_called:1; @@ -832,9 +830,9 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) vdbg_printk(TPACPI_DBG_INIT, "setting up ACPI notify for %s\n", ibm->name); - ibm->acpi->device = acpi_fetch_acpi_dev(*ibm->acpi->handle); + ibm->acpi->device = acpi_get_acpi_dev(*ibm->acpi->handle); if (!ibm->acpi->device) { - pr_err("acpi_fetch_acpi_dev(%s) failed\n", ibm->name); + pr_err("acpi_get_acpi_dev(%s) failed\n", ibm->name); return -ENODEV; } @@ -859,44 +857,6 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm) return 0; } -static int __init tpacpi_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) -{ - int rc; - - dbg_printk(TPACPI_DBG_INIT, - "registering %s as an ACPI driver\n", ibm->name); - - BUG_ON(!ibm->acpi); - - ibm->acpi->driver = kzalloc_obj(struct acpi_driver); - if (!ibm->acpi->driver) { - pr_err("failed to allocate memory for ibm->acpi->driver\n"); - return -ENOMEM; - } - - sprintf(ibm->acpi->driver->name, "%s_%s", TPACPI_NAME, ibm->name); - ibm->acpi->driver->ids = ibm->acpi->hid; - - ibm->acpi->driver->ops.add = &tpacpi_device_add; - - rc = acpi_bus_register_driver(ibm->acpi->driver); - if (rc < 0) { - pr_err("acpi_bus_register_driver(%s) failed: %d\n", - ibm->name, rc); - kfree(ibm->acpi->driver); - ibm->acpi->driver = NULL; - } else if (!rc) - ibm->flags.acpi_driver_registered = 1; - - return rc; -} - - /**************************************************************************** **************************************************************************** * @@ -11560,6 +11520,8 @@ static void ibm_exit(struct ibm_struct *ibm) acpi_remove_notify_handler(*ibm->acpi->handle, ibm->acpi->type, dispatch_acpi_notify); + ibm->acpi->device->driver_data = NULL; + acpi_dev_put(ibm->acpi->device); ibm->flags.acpi_notify_installed = 0; } @@ -11570,16 +11532,6 @@ static void ibm_exit(struct ibm_struct *ibm) ibm->flags.proc_created = 0; } - if (ibm->flags.acpi_driver_registered) { - dbg_printk(TPACPI_DBG_EXIT, - "%s: acpi_bus_unregister_driver\n", ibm->name); - BUG_ON(!ibm->acpi); - acpi_bus_unregister_driver(ibm->acpi->driver); - kfree(ibm->acpi->driver); - ibm->acpi->driver = NULL; - ibm->flags.acpi_driver_registered = 0; - } - if (ibm->flags.init_called && ibm->exit) { ibm->exit(); ibm->flags.init_called = 0; @@ -11615,12 +11567,6 @@ static int __init ibm_init(struct ibm_init_struct *iibm) } if (ibm->acpi) { - if (ibm->acpi->hid) { - ret = register_tpacpi_subdriver(ibm); - if (ret) - goto err_out; - } - if (ibm->acpi->notify) { ret = setup_acpi_notify(ibm); if (ret == -ENODEV) { From 2d5821579d39621fcce1b3d19ea25a2a9cf59cd9 Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Tue, 31 Mar 2026 18:03:11 +0200 Subject: [PATCH 080/106] platform/x86: dell-wmi-sysman: Clean up security buffer helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In calculate_security_buffer(), call strlen() once and use ALIGN() to round up to an even size. In populate_security_buffer(), also avoid recomputing strlen(), rename the u32 pointer from 'seclen' to 'seclenp' to avoid confusion with the new length variable, and drop the memcpy() guard since calling it with size 0 is a no-op and therefore safe. Use 'const char *' for the read-only source string in both helpers. Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260331160310.608857-3-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../dell/dell-wmi-sysman/dell-wmi-sysman.h | 4 ++-- .../x86/dell/dell-wmi-sysman/sysman.c | 22 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) 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 817ee7ba07ca..5278a93fdaf7 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 @@ -189,8 +189,8 @@ void exit_bios_attr_set_interface(void); 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(char *authentication); -void populate_security_buffer(char *buffer, char *authentication); +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); int set_new_password(const char *password_type, const char *new); int init_bios_attr_pass_interface(void); diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 069cf958a90d..353881353a04 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -7,10 +7,12 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include #include +#include #include #include #include "dell-wmi-sysman.h" @@ -73,13 +75,9 @@ size_t calculate_string_buffer(const char *str) * * Currently only supported type is Admin password */ -size_t calculate_security_buffer(char *authentication) +size_t calculate_security_buffer(const char *authentication) { - if (strlen(authentication) > 0) { - return (sizeof(u32) * 2) + strlen(authentication) + - strlen(authentication) % 2; - } - return sizeof(u32) * 2; + return sizeof(u32) * 2 + ALIGN(strlen(authentication), 2); } /** @@ -89,18 +87,18 @@ size_t calculate_security_buffer(char *authentication) * * Currently only supported type is PLAIN TEXT */ -void populate_security_buffer(char *buffer, char *authentication) +void populate_security_buffer(char *buffer, const char *authentication) { + size_t seclen = strlen(authentication); char *auth = buffer + sizeof(u32) * 2; u32 *sectype = (u32 *) buffer; - u32 *seclen = sectype + 1; + u32 *seclenp = sectype + 1; - *sectype = strlen(authentication) > 0 ? 1 : 0; - *seclen = strlen(authentication); + *sectype = !!seclen; + *seclenp = seclen; /* plain text */ - if (strlen(authentication) > 0) - memcpy(auth, authentication, *seclen); + memcpy(auth, authentication, seclen); } /** From ec427398794bbc0e294225a094a3060cc4bff19c Mon Sep 17 00:00:00 2001 From: Thorsten Blum Date: Wed, 1 Apr 2026 11:43:19 +0200 Subject: [PATCH 081/106] platform/x86: dell-wmi-sysman: Fix typo in function comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit s/Fress/Frees/ Signed-off-by: Thorsten Blum Link: https://patch.msgid.link/20260401094318.658932-2-thorsten.blum@linux.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell-wmi-sysman/sysman.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c index 353881353a04..51d25fdc1389 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/sysman.c @@ -313,7 +313,7 @@ static int alloc_attributes_data(int attr_type) * destroy_attribute_objs() - Free a kset of kobjects * @kset: The kset to destroy * - * Fress kobjects created for each attribute_name under attribute type kset + * Frees kobjects created for each attribute_name under attribute type kset. */ static void destroy_attribute_objs(struct kset *kset) { From 7265b57fbc32782d02bdb8d865ba0d8efa209c8c Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:10 +0300 Subject: [PATCH 082/106] platform/x86: hp-wmi: fix ignored return values in fan settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hp_wmi_get_fan_count_userdefine_trigger() can fail, but its return value was silently ignored in hp_wmi_apply_fan_settings() for PWM_MODE_MAX/AUTO. Propagate these errors consistently. Additionally, handle the return value of hp_wmi_apply_fan_settings() in its callers by adding appropriate warnings on failure, and remove an unreachable "return 0" at the end of the function. Fixes: 46be1453e6e6 ("platform/x86: hp-wmi: add manual fan control for Victus S models") Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-2-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 988a0acc9622..eac39f68d762 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -2353,8 +2353,11 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) switch (priv->mode) { case PWM_MODE_MAX: - if (is_victus_s_thermal_profile()) - hp_wmi_get_fan_count_userdefine_trigger(); + if (is_victus_s_thermal_profile()) { + ret = hp_wmi_get_fan_count_userdefine_trigger(); + if (ret < 0) + return ret; + } ret = hp_wmi_fan_speed_max_set(1); if (ret < 0) return ret; @@ -2372,7 +2375,9 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) return 0; case PWM_MODE_AUTO: if (is_victus_s_thermal_profile()) { - hp_wmi_get_fan_count_userdefine_trigger(); + ret = hp_wmi_get_fan_count_userdefine_trigger(); + if (ret < 0) + return ret; ret = hp_wmi_fan_speed_max_reset(priv); } else { ret = hp_wmi_fan_speed_max_set(0); @@ -2385,8 +2390,6 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) /* shouldn't happen */ return -EINVAL; } - - return 0; } static umode_t hp_wmi_hwmon_is_visible(const void *data, @@ -2528,6 +2531,7 @@ static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work) { struct delayed_work *dwork; struct hp_wmi_hwmon_priv *priv; + int ret; dwork = to_delayed_work(work); priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork); @@ -2535,7 +2539,10 @@ static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work) * Re-apply the current hwmon context settings. * NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling. */ - hp_wmi_apply_fan_settings(priv); + ret = hp_wmi_apply_fan_settings(priv); + if (ret) + pr_warn_ratelimited("keep-alive failed to refresh fan settings: %d\n", + ret); } static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) @@ -2597,7 +2604,9 @@ static int hp_wmi_hwmon_init(void) INIT_DELAYED_WORK(&priv->keep_alive_dwork, hp_wmi_hwmon_keep_alive_handler); platform_set_drvdata(hp_wmi_platform_dev, priv); - hp_wmi_apply_fan_settings(priv); + ret = hp_wmi_apply_fan_settings(priv); + if (ret) + dev_warn(dev, "Failed to apply initial fan settings: %d\n", ret); return 0; } From 249ddba9c0ba4453c0a6bc0e3626e7864751d940 Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:11 +0300 Subject: [PATCH 083/106] platform/x86: hp-wmi: avoid cancel_delayed_work_sync from work handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hp_wmi_apply_fan_settings() uses cancel_delayed_work_sync() to stop the keep-alive timer in AUTO mode. However, since hp_wmi_apply_fan_settings() is also called from the keep-alive handler, a race condition with a sysfs write can cause the handler to wait on itself, leading to a deadlock. Replace cancel_delayed_work_sync() with cancel_delayed_work() in hp_wmi_apply_fan_settings() to avoid the self-flush deadlock. Fixes: c203c59fb5de ("platform/x86: hp-wmi: implement fan keep-alive") Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-3-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index eac39f68d762..79d6bc3cd223 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -2384,7 +2384,7 @@ static int hp_wmi_apply_fan_settings(struct hp_wmi_hwmon_priv *priv) } if (ret < 0) return ret; - cancel_delayed_work_sync(&priv->keep_alive_dwork); + cancel_delayed_work(&priv->keep_alive_dwork); return 0; default: /* shouldn't happen */ From 6297443beb0c5606399ec7d4f4b335e2e7379147 Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:12 +0300 Subject: [PATCH 084/106] platform/x86: hp-wmi: use mod_delayed_work to reset keep-alive timer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, schedule_delayed_work() is used to queue the 90s keep-alive timer. If a user manually changes the fan speed at T=85s, schedule_delayed_work() leaves the existing timer in place as it is a no-op if the work is already pending. This results in the keep-alive timer firing unnecessarily at T=90s, just 5 seconds after the user action. Replace schedule_delayed_work() with mod_delayed_work() to reset the 90s timer whenever fan settings are applied. This guarantees a full 90s delay after every user interaction, preventing redundant keep-alive executions and improving efficiency. Fixes: c203c59fb5de ("platform/x86: hp-wmi: implement fan keep-alive") Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-4-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 79d6bc3cd223..2932cab9aa78 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -2361,8 +2361,8 @@ 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; - schedule_delayed_work(&priv->keep_alive_dwork, - secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); + mod_delayed_work(system_wq, &priv->keep_alive_dwork, + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_MANUAL: if (!is_victus_s_thermal_profile()) @@ -2370,8 +2370,8 @@ 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; - schedule_delayed_work(&priv->keep_alive_dwork, - secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); + mod_delayed_work(system_wq, &priv->keep_alive_dwork, + secs_to_jiffies(KEEP_ALIVE_DELAY_SECS)); return 0; case PWM_MODE_AUTO: if (is_victus_s_thermal_profile()) { From cb4daa450f05447c1f914eaef75b2577c25a0fcd Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:13 +0300 Subject: [PATCH 085/106] platform/x86: hp-wmi: fix u8 underflow in gpu_delta calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gpu_delta was declared as u8. If the firmware specifies a GPU RPM lower than the CPU RPM, subtracting them causes an underflow (e.g. 10 - 20 = 246), which forces the GPU fan to remain clamped at U8_MAX (100% speed) during operation. Change gpu_delta to int and use signed arithmetic. Existing signed logic in hp_wmi_fan_speed_set() correctly handles negative deltas. Fixes: 46be1453e6e6 ("platform/x86: hp-wmi: add manual fan control for Victus S models") Suggested-by: Ilpo Järvinen Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-5-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 2932cab9aa78..4f49861d3fbe 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -455,7 +455,7 @@ enum pwm_modes { struct hp_wmi_hwmon_priv { u8 min_rpm; u8 max_rpm; - u8 gpu_delta; + int gpu_delta; u8 mode; u8 pwm; struct delayed_work keep_alive_dwork; @@ -2549,8 +2549,8 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) { u8 fan_data[128] = { 0 }; struct victus_s_fan_table *fan_table; - u8 min_rpm, max_rpm, gpu_delta; - int ret; + u8 min_rpm, max_rpm; + int gpu_delta, ret; /* Default behaviour on hwmon init is automatic mode */ priv->mode = PWM_MODE_AUTO; From 5969c55e2145368254194edbe0e64880314be69f Mon Sep 17 00:00:00 2001 From: Emre Cecanpunar Date: Tue, 7 Apr 2026 17:25:14 +0300 Subject: [PATCH 086/106] platform/x86: hp-wmi: add locking for concurrent hwmon access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hp_wmi_hwmon_priv.mode and .pwm are written by hp_wmi_hwmon_write() in sysfs context and read by hp_wmi_hwmon_keep_alive_handler() in a workqueue. A concurrent write and keep-alive expiry can observe an inconsistent mode/pwm pair (e.g. mode=MANUAL with a stale pwm). Add a mutex to hp_wmi_hwmon_priv protecting mode and pwm. Hold it in hp_wmi_hwmon_write() across the field update and apply call, and in hp_wmi_hwmon_keep_alive_handler() before calling apply. In hp_wmi_hwmon_read(), only the pwm_enable path reads priv->mode; use scoped_guard() there to avoid holding the lock across unrelated WMI calls. Fixes: c203c59fb5de ("platform/x86: hp-wmi: implement fan keep-alive") Suggested-by: Ilpo Järvinen Signed-off-by: Emre Cecanpunar Link: https://patch.msgid.link/20260407142515.20683-6-emreleno@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 4f49861d3fbe..470c6e48f8e9 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -453,6 +453,7 @@ enum pwm_modes { }; struct hp_wmi_hwmon_priv { + struct mutex lock; /* protects mode, pwm */ u8 min_rpm; u8 max_rpm; int gpu_delta; @@ -2422,6 +2423,7 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, { struct hp_wmi_hwmon_priv *priv; int rpm, ret; + u8 mode; priv = dev_get_drvdata(dev); switch (type) { @@ -2445,11 +2447,13 @@ static int hp_wmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, *val = rpm_to_pwm(rpm / 100, priv); return 0; } - switch (priv->mode) { + scoped_guard(mutex, &priv->lock) + mode = priv->mode; + switch (mode) { case PWM_MODE_MAX: case PWM_MODE_MANUAL: case PWM_MODE_AUTO: - *val = priv->mode; + *val = mode; return 0; default: /* shouldn't happen */ @@ -2467,6 +2471,7 @@ static int hp_wmi_hwmon_write(struct device *dev, enum hwmon_sensor_types type, int rpm; priv = dev_get_drvdata(dev); + guard(mutex)(&priv->lock); switch (type) { case hwmon_pwm: if (attr == hwmon_pwm_input) { @@ -2535,6 +2540,8 @@ static void hp_wmi_hwmon_keep_alive_handler(struct work_struct *work) dwork = to_delayed_work(work); priv = container_of(dwork, struct hp_wmi_hwmon_priv, keep_alive_dwork); + + guard(mutex)(&priv->lock); /* * Re-apply the current hwmon context settings. * NOTE: hp_wmi_apply_fan_settings will handle the re-scheduling. @@ -2591,6 +2598,10 @@ static int hp_wmi_hwmon_init(void) if (!priv) return -ENOMEM; + ret = devm_mutex_init(dev, &priv->lock); + if (ret) + return ret; + ret = hp_wmi_setup_fan_settings(priv); if (ret) return ret; From f8fd138c2363c0e2d3235c32bfb4fb5c6474e4ae Mon Sep 17 00:00:00 2001 From: Fedor Pchelkin Date: Fri, 3 Apr 2026 16:42:39 +0300 Subject: [PATCH 087/106] platform/x86: dell_rbu: avoid uninit value usage in packet_size_write() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure the temp value has been properly parsed from the user-provided buffer and initialized to be used in later operations. While at it, prefer a convenient kstrtoul() helper. Found by Linux Verification Center (linuxtesting.org) with Svace static analysis tool. Fixes: ad6ce87e5bd4 ("[PATCH] dell_rbu: changes in packet update mechanism") Signed-off-by: Fedor Pchelkin Link: https://patch.msgid.link/20260403134240.604837-1-pchelkin@ispras.ru [ij: add include] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/dell/dell_rbu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/dell/dell_rbu.c b/drivers/platform/x86/dell/dell_rbu.c index eb50f1d75d0c..3fa9de9aa47b 100644 --- a/drivers/platform/x86/dell/dell_rbu.c +++ b/drivers/platform/x86/dell/dell_rbu.c @@ -30,6 +30,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include +#include #include #include #include @@ -619,9 +620,12 @@ static ssize_t packet_size_write(struct file *filp, struct kobject *kobj, char *buffer, loff_t pos, size_t count) { unsigned long temp; + + if (kstrtoul(buffer, 10, &temp)) + return -EINVAL; + spin_lock(&rbu_data.lock); packet_empty_list(); - sscanf(buffer, "%lu", &temp); if (temp < 0xffffffff) rbu_data.packetsize = temp; From 6b0567dc4c9ad140044400e06dd97fdce12c204f Mon Sep 17 00:00:00 2001 From: Ethan Tidmore Date: Fri, 3 Apr 2026 02:09:28 -0500 Subject: [PATCH 088/106] platform/x86: uniwill-laptop: Fix signedness bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function sysfs_match_string() can return negative error codes and the variable assigned to it is the enum 'option'. Which could be an unsigned int due to different compiler implementations. Assign signed variable 'ret' to sysfs_match_string(), check for error, then assign ret to option. Detected by Smatch: drivers/platform/x86/uniwill/uniwill-acpi.c:919 usb_c_power_priority_store() warn: unsigned 'option' is never less than zero. Fixes: 03ae0a0d0973b ("platform/x86: uniwill-laptop: Implement USB-C power priority setting") Signed-off-by: Ethan Tidmore Link: https://patch.msgid.link/20260403070928.802196-1-ethantidmore06@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/uniwill/uniwill-acpi.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/uniwill/uniwill-acpi.c b/drivers/platform/x86/uniwill/uniwill-acpi.c index faade4cf08be..945df5092637 100644 --- a/drivers/platform/x86/uniwill/uniwill-acpi.c +++ b/drivers/platform/x86/uniwill/uniwill-acpi.c @@ -915,10 +915,11 @@ static ssize_t usb_c_power_priority_store(struct device *dev, unsigned int value; int ret; - option = sysfs_match_string(usb_c_power_priority_text, buf); - if (option < 0) - return option; + ret = sysfs_match_string(usb_c_power_priority_text, buf); + if (ret < 0) + return ret; + option = ret; value = usb_c_power_priority_value[option]; guard(mutex)(&data->usb_c_power_priority_lock); From e8c597368b8500a824c639bfb5ed0044068c6870 Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Fri, 3 Apr 2026 13:31:55 +0530 Subject: [PATCH 089/106] platform/x86: hp-wmi: Ignore backlight and FnLock events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On HP OmniBook 7 the keyboard backlight and FnLock keys are handled directly by the firmware. However, they still trigger WMI events which results in "Unknown key code" warnings in dmesg. Add these key codes to the keymap with KE_IGNORE to silence the warnings since no software action is needed. Tested-by: Artem S. Tashkinov Reported-by: Artem S. Tashkinov Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221181 Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260403080155.169653-1-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 470c6e48f8e9..851056bee614 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -399,6 +399,11 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x21a9, { KEY_TOUCHPAD_OFF } }, { KE_KEY, 0x121a9, { KEY_TOUCHPAD_ON } }, { KE_KEY, 0x231b, { KEY_HELP } }, + { KE_IGNORE, 0x21ab, }, /* FnLock on */ + { KE_IGNORE, 0x121ab, }, /* FnLock off */ + { KE_IGNORE, 0x30021aa, }, /* kbd backlight: level 2 -> off */ + { KE_IGNORE, 0x33221aa, }, /* kbd backlight: off -> level 1 */ + { KE_IGNORE, 0x36421aa, }, /* kbd backlight: level 1 -> level 2*/ { KE_END, 0 } }; From 3c34471c26abc52a37f5ad90949e2e4b8027eb14 Mon Sep 17 00:00:00 2001 From: Pengpeng Hou Date: Wed, 8 Apr 2026 08:38:21 +0800 Subject: [PATCH 090/106] platform/x86: dell-wmi-sysman: bound enumeration string aggregation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit populate_enum_data() aggregates firmware-provided value-modifier and possible-value strings into fixed 512-byte struct members. The current code bounds each individual source string but then appends every string and separator with raw strcat() and no remaining-space check. Switch the aggregation loops to a bounded append helper and reject enumeration packages whose combined strings do not fit in the destination buffers. Fixes: e8a60aa7404b ("platform/x86: Introduce support for Systems Management Driver over WMI for Dell Systems") Signed-off-by: Pengpeng Hou Link: https://patch.msgid.link/20260408084501.1-dell-wmi-sysman-v2-pengpeng@iscas.ac.cn [ij: add include] Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../dell/dell-wmi-sysman/enum-attributes.c | 34 +++++++++++++++---- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c b/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c index 09996fbdc707..a85639d8a076 100644 --- a/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c +++ b/drivers/platform/x86/dell/dell-wmi-sysman/enum-attributes.c @@ -6,10 +6,32 @@ * Copyright (c) 2020 Dell Inc. */ +#include + #include "dell-wmi-sysman.h" get_instance_id(enumeration); +static int append_enum_string(char *dest, const char *src) +{ + size_t dest_len = strlen(dest); + ssize_t copied; + + if (WARN_ON_ONCE(dest_len >= MAX_BUFF)) + return -EINVAL; + + copied = strscpy(dest + dest_len, src, MAX_BUFF - dest_len); + if (copied < 0) + return -EINVAL; + + dest_len += copied; + copied = strscpy(dest + dest_len, ";", MAX_BUFF - dest_len); + if (copied < 0) + return -EINVAL; + + return 0; +} + static ssize_t current_value_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int instance_id = get_enumeration_instance_id(kobj); @@ -176,9 +198,9 @@ int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, return -EINVAL; if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING)) return -EINVAL; - strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, - enumeration_obj[next_obj++].string.pointer); - strcat(wmi_priv.enumeration_data[instance_id].dell_value_modifier, ";"); + if (append_enum_string(wmi_priv.enumeration_data[instance_id].dell_value_modifier, + enumeration_obj[next_obj++].string.pointer)) + return -EINVAL; } if (next_obj >= enum_property_count) @@ -193,9 +215,9 @@ int populate_enum_data(union acpi_object *enumeration_obj, int instance_id, return -EINVAL; if (check_property_type(enumeration, next_obj, ACPI_TYPE_STRING)) return -EINVAL; - strcat(wmi_priv.enumeration_data[instance_id].possible_values, - enumeration_obj[next_obj++].string.pointer); - strcat(wmi_priv.enumeration_data[instance_id].possible_values, ";"); + if (append_enum_string(wmi_priv.enumeration_data[instance_id].possible_values, + enumeration_obj[next_obj++].string.pointer)) + return -EINVAL; } return sysfs_create_group(attr_name_kobj, &enumeration_attr_group); From a29b5cd42f5bc6ba1be6422f61f3f05bab707ce8 Mon Sep 17 00:00:00 2001 From: Daniil Bulgar Date: Tue, 7 Apr 2026 21:05:46 +0200 Subject: [PATCH 091/106] platform/x86: thinkpad_acpi: remove obsolete TODO comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch removes the obsolete TODO comment regarding fan speed presets in fan_write_cmd_speed. After discussion with the maintainers, it was decided that fixed presets (low/medium/high) are not suitable due to platform-specific variations. Signed-off-by: Daniil Bulgar Reviewed-by: Mark Pearson Link: https://patch.msgid.link/20260407190546.109900-1-bulgardaniil18@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/lenovo/thinkpad_acpi.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/platform/x86/lenovo/thinkpad_acpi.c b/drivers/platform/x86/lenovo/thinkpad_acpi.c index 1a9effcae5cd..e1cee42a1683 100644 --- a/drivers/platform/x86/lenovo/thinkpad_acpi.c +++ b/drivers/platform/x86/lenovo/thinkpad_acpi.c @@ -9235,9 +9235,6 @@ static int fan_write_cmd_speed(const char *cmd, int *rc) { int speed; - /* TODO: - * Support speed ? */ - if (sscanf(cmd, "speed %d", &speed) != 1) return 0; From d6116d86e58a04a9522e349c4a27145521c01ad7 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:35 +0200 Subject: [PATCH 092/106] platform/x86: int3472: Use local variable for LED struct access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a local struct int3472_pled pointer in the LED registration, unregistration, and brightness callback functions to avoid repeatedly dereferencing int3472->pled. In the brightness callback, use container_of() to get the int3472_pled struct directly instead of going through int3472_discrete_device. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Marco Nenciarini Link: https://patch.msgid.link/20260401203638.1601661-2-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/led.c | 43 ++++++++++++------------ 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index b1d84b968112..35abad900bf3 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -6,55 +6,56 @@ #include #include -static int int3472_pled_set(struct led_classdev *led_cdev, - enum led_brightness brightness) +static int int3472_pled_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct int3472_discrete_device *int3472 = - container_of(led_cdev, struct int3472_discrete_device, pled.classdev); + struct int3472_pled *led = container_of(led_cdev, struct int3472_pled, classdev); - gpiod_set_value_cansleep(int3472->pled.gpio, brightness); + gpiod_set_value_cansleep(led->gpio, brightness); return 0; } int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) { + struct int3472_pled *led = &int3472->pled; char *p; int ret; - if (int3472->pled.classdev.dev) + if (led->classdev.dev) return -EBUSY; - int3472->pled.gpio = gpio; + led->gpio = gpio; /* Generate the name, replacing the ':' in the ACPI devname with '_' */ - snprintf(int3472->pled.name, sizeof(int3472->pled.name), + snprintf(led->name, sizeof(led->name), "%s::privacy_led", acpi_dev_name(int3472->sensor)); - p = strchr(int3472->pled.name, ':'); + p = strchr(led->name, ':'); if (p) *p = '_'; - int3472->pled.classdev.name = int3472->pled.name; - int3472->pled.classdev.max_brightness = 1; - int3472->pled.classdev.brightness_set_blocking = int3472_pled_set; + led->classdev.name = led->name; + led->classdev.max_brightness = 1; + led->classdev.brightness_set_blocking = int3472_pled_set; - ret = led_classdev_register(int3472->dev, &int3472->pled.classdev); + ret = led_classdev_register(int3472->dev, &led->classdev); if (ret) return ret; - int3472->pled.lookup.provider = int3472->pled.name; - int3472->pled.lookup.dev_id = int3472->sensor_name; - int3472->pled.lookup.con_id = "privacy"; - led_add_lookup(&int3472->pled.lookup); + led->lookup.provider = led->name; + led->lookup.dev_id = int3472->sensor_name; + led->lookup.con_id = "privacy"; + led_add_lookup(&led->lookup); return 0; } void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) { - if (IS_ERR_OR_NULL(int3472->pled.classdev.dev)) + struct int3472_pled *led = &int3472->pled; + + if (IS_ERR_OR_NULL(led->classdev.dev)) return; - led_remove_lookup(&int3472->pled.lookup); - led_classdev_unregister(&int3472->pled.classdev); - gpiod_put(int3472->pled.gpio); + led_remove_lookup(&led->lookup); + led_classdev_unregister(&led->classdev); + gpiod_put(led->gpio); } From 39237e3208209d1bb35d939d6fee1f36b642f562 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:36 +0200 Subject: [PATCH 093/106] platform/x86: int3472: Rename pled to led in LED registration code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename the privacy LED type, struct member, and functions from "pled" to "led" in preparation for supporting additional LED types beyond just the privacy LED. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Marco Nenciarini Link: https://patch.msgid.link/20260401203638.1601661-3-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 4 ++-- drivers/platform/x86/intel/int3472/led.c | 14 +++++++------- include/linux/platform_data/x86/int3472.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 1c65ce87cde0..b03f61dab25f 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -354,7 +354,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_register_pled(int3472, gpio); + ret = skl_int3472_register_led(int3472, gpio); if (ret) err_msg = "Failed to register LED\n"; @@ -429,7 +429,7 @@ void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); - skl_int3472_unregister_pled(int3472); + skl_int3472_unregister_led(int3472); skl_int3472_unregister_regulator(int3472); } EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index 35abad900bf3..fe412cb938cf 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -6,17 +6,17 @@ #include #include -static int int3472_pled_set(struct led_classdev *led_cdev, enum led_brightness brightness) +static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct int3472_pled *led = container_of(led_cdev, struct int3472_pled, classdev); + struct int3472_led *led = container_of(led_cdev, struct int3472_led, classdev); gpiod_set_value_cansleep(led->gpio, brightness); return 0; } -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) { - struct int3472_pled *led = &int3472->pled; + struct int3472_led *led = &int3472->led; char *p; int ret; @@ -34,7 +34,7 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gp led->classdev.name = led->name; led->classdev.max_brightness = 1; - led->classdev.brightness_set_blocking = int3472_pled_set; + led->classdev.brightness_set_blocking = int3472_led_set; ret = led_classdev_register(int3472->dev, &led->classdev); if (ret) @@ -48,9 +48,9 @@ int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gp return 0; } -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472) +void skl_int3472_unregister_led(struct int3472_discrete_device *int3472) { - struct int3472_pled *led = &int3472->pled; + struct int3472_led *led = &int3472->led; if (IS_ERR_OR_NULL(led->classdev.dev)) return; diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index dbe745dc88d5..39a1938d77e1 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -122,12 +122,12 @@ struct int3472_discrete_device { u8 imgclk_index; } clock; - struct int3472_pled { + struct int3472_led { struct led_classdev classdev; struct led_lookup_data lookup; char name[INT3472_LED_MAX_NAME_LEN]; struct gpio_desc *gpio; - } pled; + } led; struct int3472_discrete_quirks quirks; @@ -161,7 +161,7 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_pled(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); -void skl_int3472_unregister_pled(struct int3472_discrete_device *int3472); +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); +void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); #endif From 218d3c44f5f0a3cc1647bc61a4e4eac663b37aa5 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:37 +0200 Subject: [PATCH 094/106] platform/x86: int3472: Parameterize LED con_id in registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a con_id parameter to skl_int3472_register_led() to allow callers to specify both the LED name suffix and lookup con_id instead of hardcoding "privacy". This prepares for registering additional LED types with different names. While at it, rename the privacy LED's GPIO con_id from "privacy-led" to "privacy" in int3472_get_con_id_and_polarity() and pass it directly to skl_int3472_register_led(), reducing churn when adding new LED types. No functional change. Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Signed-off-by: Marco Nenciarini Link: https://patch.msgid.link/20260401203638.1601661-4-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 4 ++-- drivers/platform/x86/intel/int3472/led.c | 7 ++++--- include/linux/platform_data/x86/int3472.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index b03f61dab25f..637e3821b496 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -212,7 +212,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_PRIVACY_LED: - *con_id = "privacy-led"; + *con_id = "privacy"; *gpio_flags = GPIO_ACTIVE_HIGH; break; case INT3472_GPIO_TYPE_HOTPLUG_DETECT: @@ -354,7 +354,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: - ret = skl_int3472_register_led(int3472, gpio); + ret = skl_int3472_register_led(int3472, gpio, con_id); if (ret) err_msg = "Failed to register LED\n"; diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index fe412cb938cf..22d0d6c5e6ce 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -14,7 +14,8 @@ static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness br return 0; } -int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio) +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id) { struct int3472_led *led = &int3472->led; char *p; @@ -27,7 +28,7 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi /* Generate the name, replacing the ':' in the ACPI devname with '_' */ snprintf(led->name, sizeof(led->name), - "%s::privacy_led", acpi_dev_name(int3472->sensor)); + "%s::%s_led", acpi_dev_name(int3472->sensor), con_id); p = strchr(led->name, ':'); if (p) *p = '_'; @@ -42,7 +43,7 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi led->lookup.provider = led->name; led->lookup.dev_id = int3472->sensor_name; - led->lookup.con_id = "privacy"; + led->lookup.con_id = con_id; led_add_lookup(&led->lookup); return 0; diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index 39a1938d77e1..ebf4d0637624 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -161,7 +161,8 @@ int skl_int3472_register_regulator(struct int3472_discrete_device *int3472, const char *second_sensor); void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); -int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio); +int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, + const char *con_id); void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); #endif From 0ec7f158dc01e354ba83d808e46346dba826e353 Mon Sep 17 00:00:00 2001 From: Marco Nenciarini Date: Wed, 1 Apr 2026 22:36:38 +0200 Subject: [PATCH 095/106] platform/x86: int3472: Add support for GPIO type 0x02 (IR flood LED) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for GPIO type 0x02, which controls an IR flood LED used for face authentication on some laptops (e.g. Dell Pro Max 16 Premium). Without this patch, the kernel logs "GPIO type 0x02 unknown; the sensor may not work" and IR sensors paired with a flood LED cannot function. The flood LED is registered through the LED subsystem like the existing privacy LED, including a lookup entry to allow future consumer drivers to find and control it via led_get(). To support multiple LEDs per INT3472 device, convert the single led struct member to an array with a counter. Signed-off-by: Marco Nenciarini Reviewed-by: Andy Shevchenko Reviewed-by: Hans de Goede Link: https://patch.msgid.link/20260401203638.1601661-5-mnencia@kcore.it Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/intel/int3472/discrete.c | 9 +++++++- drivers/platform/x86/intel/int3472/led.c | 23 ++++++++++--------- include/linux/platform_data/x86/int3472.h | 7 ++++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/intel/int3472/discrete.c b/drivers/platform/x86/intel/int3472/discrete.c index 637e3821b496..115bb37577a1 100644 --- a/drivers/platform/x86/intel/int3472/discrete.c +++ b/drivers/platform/x86/intel/int3472/discrete.c @@ -215,6 +215,10 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 *con_id = "privacy"; *gpio_flags = GPIO_ACTIVE_HIGH; break; + case INT3472_GPIO_TYPE_STROBE: + *con_id = "ir_flood"; + *gpio_flags = GPIO_ACTIVE_HIGH; + break; case INT3472_GPIO_TYPE_HOTPLUG_DETECT: *con_id = "hpd"; *gpio_flags = GPIO_ACTIVE_HIGH; @@ -252,6 +256,7 @@ static void int3472_get_con_id_and_polarity(struct int3472_discrete_device *int3 * * 0x00 Reset * 0x01 Power down + * 0x02 Strobe * 0x0b Power enable * 0x0c Clock enable * 0x0d Privacy LED @@ -336,6 +341,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_CLK_ENABLE: case INT3472_GPIO_TYPE_PRIVACY_LED: + case INT3472_GPIO_TYPE_STROBE: case INT3472_GPIO_TYPE_POWER_ENABLE: case INT3472_GPIO_TYPE_DOVDD: case INT3472_GPIO_TYPE_HANDSHAKE: @@ -354,6 +360,7 @@ static int skl_int3472_handle_gpio_resources(struct acpi_resource *ares, break; case INT3472_GPIO_TYPE_PRIVACY_LED: + case INT3472_GPIO_TYPE_STROBE: ret = skl_int3472_register_led(int3472, gpio, con_id); if (ret) err_msg = "Failed to register LED\n"; @@ -429,7 +436,7 @@ void int3472_discrete_cleanup(struct int3472_discrete_device *int3472) gpiod_remove_lookup_table(&int3472->gpios); skl_int3472_unregister_clock(int3472); - skl_int3472_unregister_led(int3472); + skl_int3472_unregister_leds(int3472); skl_int3472_unregister_regulator(int3472); } EXPORT_SYMBOL_NS_GPL(int3472_discrete_cleanup, "INTEL_INT3472_DISCRETE"); diff --git a/drivers/platform/x86/intel/int3472/led.c b/drivers/platform/x86/intel/int3472/led.c index 22d0d6c5e6ce..9b2573cc347b 100644 --- a/drivers/platform/x86/intel/int3472/led.c +++ b/drivers/platform/x86/intel/int3472/led.c @@ -17,13 +17,14 @@ static int int3472_led_set(struct led_classdev *led_cdev, enum led_brightness br int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, const char *con_id) { - struct int3472_led *led = &int3472->led; + struct int3472_led *led; char *p; int ret; - if (led->classdev.dev) - return -EBUSY; + if (int3472->n_leds >= INT3472_MAX_LEDS) + return -ENOSPC; + led = &int3472->leds[int3472->n_leds]; led->gpio = gpio; /* Generate the name, replacing the ':' in the ACPI devname with '_' */ @@ -46,17 +47,17 @@ int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpi led->lookup.con_id = con_id; led_add_lookup(&led->lookup); + int3472->n_leds++; return 0; } -void skl_int3472_unregister_led(struct int3472_discrete_device *int3472) +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472) { - struct int3472_led *led = &int3472->led; + for (unsigned int i = 0; i < int3472->n_leds; i++) { + struct int3472_led *led = &int3472->leds[i]; - if (IS_ERR_OR_NULL(led->classdev.dev)) - return; - - led_remove_lookup(&led->lookup); - led_classdev_unregister(&led->classdev); - gpiod_put(led->gpio); + led_remove_lookup(&led->lookup); + led_classdev_unregister(&led->classdev); + gpiod_put(led->gpio); + } } diff --git a/include/linux/platform_data/x86/int3472.h b/include/linux/platform_data/x86/int3472.h index ebf4d0637624..93f1e1fe09b4 100644 --- a/include/linux/platform_data/x86/int3472.h +++ b/include/linux/platform_data/x86/int3472.h @@ -23,6 +23,7 @@ /* PMIC GPIO Types */ #define INT3472_GPIO_TYPE_RESET 0x00 #define INT3472_GPIO_TYPE_POWERDOWN 0x01 +#define INT3472_GPIO_TYPE_STROBE 0x02 #define INT3472_GPIO_TYPE_POWER_ENABLE 0x0b #define INT3472_GPIO_TYPE_CLK_ENABLE 0x0c #define INT3472_GPIO_TYPE_PRIVACY_LED 0x0d @@ -32,6 +33,7 @@ #define INT3472_PDEV_MAX_NAME_LEN 23 #define INT3472_MAX_SENSOR_GPIOS 3 +#define INT3472_MAX_LEDS 2 #define INT3472_MAX_REGULATORS 3 /* E.g. "dovdd\0" */ @@ -127,11 +129,12 @@ struct int3472_discrete_device { struct led_lookup_data lookup; char name[INT3472_LED_MAX_NAME_LEN]; struct gpio_desc *gpio; - } led; + } leds[INT3472_MAX_LEDS]; struct int3472_discrete_quirks quirks; unsigned int ngpios; /* how many GPIOs have we seen */ + unsigned int n_leds; /* how many LEDs have we registered */ unsigned int n_sensor_gpios; /* how many have we mapped to sensor */ unsigned int n_regulator_gpios; /* how many have we mapped to a regulator */ struct gpiod_lookup_table gpios; @@ -163,6 +166,6 @@ void skl_int3472_unregister_regulator(struct int3472_discrete_device *int3472); int skl_int3472_register_led(struct int3472_discrete_device *int3472, struct gpio_desc *gpio, const char *con_id); -void skl_int3472_unregister_led(struct int3472_discrete_device *int3472); +void skl_int3472_unregister_leds(struct int3472_discrete_device *int3472); #endif From 7e2d964f417ec13763eecfecc5d2813f63cb8da0 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:32 +0200 Subject: [PATCH 096/106] platform/wmi: Add wmidev_invoke_procedure() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some WMI methods return no values, so the whole postprocessing of the result data is not needed for them. Add a special function for calling such WMI methods to prepare for future changes of the main wmidev_invoke_method() function. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../wmi/driver-development-guide.rst | 3 +- drivers/platform/wmi/core.c | 44 +++++++++++++++++++ include/linux/wmi.h | 3 ++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst index fbc2d9b12fe9..5b94402874c4 100644 --- a/Documentation/wmi/driver-development-guide.rst +++ b/Documentation/wmi/driver-development-guide.rst @@ -106,7 +106,8 @@ WMI method drivers WMI drivers can call WMI device methods using wmidev_invoke_method(). For each WMI method invocation the WMI driver needs to provide the instance number and the method ID, as well as -a buffer with the method arguments and optionally a buffer for the results. +a buffer with the method arguments and optionally a buffer for the results. When calling WMI +methods that do not return any values, wmidev_invoke_procedure() should be used instead. The layout of said buffers is device-specific and described by the Binary MOF data associated with a given WMI device. Said Binary MOF data also describes the method ID of a given WMI method diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index b8e6b9a421c6..7cc5ca11a60d 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -427,6 +427,50 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, } EXPORT_SYMBOL_GPL(wmidev_invoke_method); +/** + * wmidev_invoke_procedure - Invoke a WMI method that does not return values + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * @in: Mandatory WMI buffer containing input for the method call + * + * Invoke a WMI method that does not return any values. Use wmidev_invoke_method() + * for WMI methods that do return values. + * + * Return: 0 on success or negative error code on failure. + */ +int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in) +{ + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + struct acpi_buffer ain; + acpi_status status; + int ret; + + if (wblock->gblock.flags & ACPI_WMI_STRING) { + ret = wmi_marshal_string(in, &ain); + if (ret < 0) + return ret; + } else { + if (in->length > U32_MAX) + return -E2BIG; + + ain.length = in->length; + ain.pointer = in->data; + } + + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); + + if (wblock->gblock.flags & ACPI_WMI_STRING) + kfree(ain.pointer); + + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(wmidev_invoke_procedure); + static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) { diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 75cb0c7cfe57..b00950dc1231 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -70,6 +70,9 @@ ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in, struct wmi_buffer *out); +int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in); + int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out); int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in); From 578bc2a53ae286e438024d363ad0513f7105e6dc Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:33 +0200 Subject: [PATCH 097/106] platform/wmi: Convert drivers to use wmidev_invoke_procedure() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert users of wmidev_invoke_method() to wmidev_invoke_procedure() where applicable to prepare for future changes. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-3-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/bitland-mifs-wmi.c | 17 +++++++++-------- drivers/platform/x86/intel/wmi/thunderbolt.c | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c index 54380708b7b0..cd3cdd087511 100644 --- a/drivers/platform/x86/bitland-mifs-wmi.c +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -167,23 +167,24 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, struct bitland_mifs_output *output) { struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input }; + void *out_data __free(kfree) = NULL; struct wmi_buffer out_buf = { 0 }; int ret; guard(mutex)(&data->lock); - ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, output ? &out_buf : NULL); + if (!output) + return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf); + + ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf); if (ret) return ret; - if (output) { - void *out_data __free(kfree) = out_buf.data; + out_data = out_buf.data; + if (out_buf.length < sizeof(*output)) + return -EIO; - if (out_buf.length < sizeof(*output)) - return -EIO; - - memcpy(output, out_data, sizeof(*output)); - } + memcpy(output, out_data, sizeof(*output)); return 0; } diff --git a/drivers/platform/x86/intel/wmi/thunderbolt.c b/drivers/platform/x86/intel/wmi/thunderbolt.c index 47017f2d7597..9b1920d61674 100644 --- a/drivers/platform/x86/intel/wmi/thunderbolt.c +++ b/drivers/platform/x86/intel/wmi/thunderbolt.c @@ -34,7 +34,7 @@ static ssize_t force_power_store(struct device *dev, if (mode > 1) return -EINVAL; - ret = wmidev_invoke_method(to_wmi_device(dev), 0, 1, &buffer, NULL); + ret = wmidev_invoke_procedure(to_wmi_device(dev), 0, 1, &buffer); if (ret < 0) return ret; From 204b52fadf98b77eab8fd6cba4a7d55224bbf11b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:34 +0200 Subject: [PATCH 098/106] platform/wmi: Prepare to reject undersized unmarshalling results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Driver using the buffer-based WMI API usually reject buffers resulting from WMI method calls or block queries if they contain not enough data. Prepare the WMI core for assisting in this by automatically rejecting undersized unmarshalling results. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 6 ++--- drivers/platform/wmi/internal.h | 3 ++- drivers/platform/wmi/marshalling.c | 6 ++++- .../platform/wmi/tests/marshalling_kunit.c | 24 +++++++++++++++++-- 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 7cc5ca11a60d..66ec885bffd7 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -420,7 +420,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, return 0; } - ret = wmi_unmarshal_acpi_object(obj, out); + ret = wmi_unmarshal_acpi_object(obj, out, 0); kfree(obj); return ret; @@ -583,7 +583,7 @@ int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer * if (!obj) return -EIO; - ret = wmi_unmarshal_acpi_object(obj, out); + ret = wmi_unmarshal_acpi_object(obj, out, 0); kfree(obj); return ret; @@ -1416,7 +1416,7 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) return; } - ret = wmi_unmarshal_acpi_object(obj, &buffer); + ret = wmi_unmarshal_acpi_object(obj, &buffer, 0); if (ret < 0) { dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret); return; diff --git a/drivers/platform/wmi/internal.h b/drivers/platform/wmi/internal.h index 9a39ffa31ad1..c02908694563 100644 --- a/drivers/platform/wmi/internal.h +++ b/drivers/platform/wmi/internal.h @@ -11,7 +11,8 @@ union acpi_object; struct wmi_buffer; -int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer); +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer, + size_t min_size); int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out); #endif /* _WMI_INTERNAL_H_ */ diff --git a/drivers/platform/wmi/marshalling.c b/drivers/platform/wmi/marshalling.c index 63a92c4ebab5..87091832568e 100644 --- a/drivers/platform/wmi/marshalling.c +++ b/drivers/platform/wmi/marshalling.c @@ -151,7 +151,8 @@ static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer) return 0; } -int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer) +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer, + size_t min_size) { size_t length, alloc_length; u8 *data; @@ -161,6 +162,9 @@ int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *b if (ret < 0) return ret; + if (length < min_size) + return -ENODATA; + if (ARCH_KMALLOC_MINALIGN < 8) { /* * kmalloc() guarantees that the alignment of the resulting memory allocation is at diff --git a/drivers/platform/wmi/tests/marshalling_kunit.c b/drivers/platform/wmi/tests/marshalling_kunit.c index 0c7cd8774aa3..471963076d58 100644 --- a/drivers/platform/wmi/tests/marshalling_kunit.c +++ b/drivers/platform/wmi/tests/marshalling_kunit.c @@ -372,7 +372,7 @@ static void wmi_unmarshal_acpi_object_test(struct kunit *test) struct wmi_buffer result; int ret; - ret = wmi_unmarshal_acpi_object(¶m->obj, &result); + ret = wmi_unmarshal_acpi_object(¶m->obj, &result, param->buffer.length); if (ret < 0) KUNIT_FAIL_AND_ABORT(test, "Unmarshalling of ACPI object failed\n"); @@ -389,7 +389,7 @@ static void wmi_unmarshal_acpi_object_failure_test(struct kunit *test) struct wmi_buffer result; int ret; - ret = wmi_unmarshal_acpi_object(¶m->obj, &result); + ret = wmi_unmarshal_acpi_object(¶m->obj, &result, 0); if (ret < 0) return; @@ -427,6 +427,25 @@ static void wmi_marshal_string_failure_test(struct kunit *test) KUNIT_FAIL(test, "Invalid string was not rejected\n"); } +static void wmi_unmarshal_acpi_object_undersized_test(struct kunit *test) +{ + const union acpi_object obj = { + .integer = { + .type = ACPI_TYPE_INTEGER, + .value = 0xdeadbeef, + }, + }; + struct wmi_buffer result; + int ret; + + ret = wmi_unmarshal_acpi_object(&obj, &result, sizeof(expected_single_integer) + 1); + if (ret < 0) + return; + + kfree(result.data); + KUNIT_FAIL(test, "Undersized unmarshalling result was not rejected\n"); +} + static struct kunit_case wmi_marshalling_test_cases[] = { KUNIT_CASE_PARAM(wmi_unmarshal_acpi_object_test, wmi_unmarshal_acpi_object_gen_params), @@ -436,6 +455,7 @@ static struct kunit_case wmi_marshalling_test_cases[] = { wmi_unmarshal_acpi_object_failure_gen_params), KUNIT_CASE_PARAM(wmi_marshal_string_failure_test, wmi_marshal_string_failure_gen_params), + KUNIT_CASE(wmi_unmarshal_acpi_object_undersized_test), {} }; From 96b1b053e10d89f666a37b52be25ed4294e342be Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:35 +0200 Subject: [PATCH 099/106] platform/wmi: Extend wmidev_invoke_method() to reject undersized data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WMI drivers using the buffer-based WMI API are expected to reject undersized method return values. Extend wmidev_invoke_method() to enable the WMI driver core to perform this size check internally. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-5-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 23 ++++++++++------------- drivers/platform/x86/bitland-mifs-wmi.c | 11 +++-------- include/linux/wmi.h | 2 +- 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 66ec885bffd7..a1a612f33233 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -364,20 +364,23 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met EXPORT_SYMBOL_GPL(wmidev_evaluate_method); /** - * wmidev_invoke_method - Invoke a WMI method + * wmidev_invoke_method - Invoke a WMI method that returns values * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call * @in: Mandatory WMI buffer containing input for the method call - * @out: Optional WMI buffer to return the method results + * @out: Mandatory WMI buffer to return the method results + * @min_size: Minimum size of the method result data in bytes * - * Invoke a WMI method, the caller must free the resulting data inside @out. - * Said data is guaranteed to be aligned on a 8-byte boundary. + * Invoke a WMI method that returns values, the caller must free the resulting + * data inside @out using kfree(). Said data is guaranteed to be aligned on a + * 8-byte boundary. Use wmidev_invoke_procedure() for WMI methods that + * return no values. * * Return: 0 on success or negative error code on failure. */ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, - const struct wmi_buffer *in, struct wmi_buffer *out) + const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size) { struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -398,10 +401,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, ain.pointer = in->data; } - if (out) - status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); - else - status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); if (wblock->gblock.flags & ACPI_WMI_STRING) kfree(ain.pointer); @@ -409,9 +409,6 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, if (ACPI_FAILURE(status)) return -EIO; - if (!out) - return 0; - obj = aout.pointer; if (!obj) { out->length = 0; @@ -420,7 +417,7 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, return 0; } - ret = wmi_unmarshal_acpi_object(obj, out, 0); + ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret; diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c index cd3cdd087511..78639407d67e 100644 --- a/drivers/platform/x86/bitland-mifs-wmi.c +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -167,7 +166,6 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, struct bitland_mifs_output *output) { struct wmi_buffer in_buf = { .length = sizeof(*input), .data = (void *)input }; - void *out_data __free(kfree) = NULL; struct wmi_buffer out_buf = { 0 }; int ret; @@ -176,15 +174,12 @@ static int bitland_mifs_wmi_call(struct bitland_mifs_wmi_data *data, if (!output) return wmidev_invoke_procedure(data->wdev, 0, 1, &in_buf); - ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf); + ret = wmidev_invoke_method(data->wdev, 0, 1, &in_buf, &out_buf, sizeof(*output)); if (ret) return ret; - out_data = out_buf.data; - if (out_buf.length < sizeof(*output)) - return -EIO; - - memcpy(output, out_data, sizeof(*output)); + memcpy(output, out_buf.data, sizeof(*output)); + kfree(out_buf.data); return 0; } diff --git a/include/linux/wmi.h b/include/linux/wmi.h index b00950dc1231..858398beb01a 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -68,7 +68,7 @@ ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 size_t src_length); int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, - const struct wmi_buffer *in, struct wmi_buffer *out); + const struct wmi_buffer *in, struct wmi_buffer *out, size_t min_size); int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in); From 1aeded2f55f04fafb07b01e12142fd20c2a3d288 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:36 +0200 Subject: [PATCH 100/106] platform/wmi: Extend wmidev_query_block() to reject undersized data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WMI drivers using the buffer-based WMI API are expected to reject undersized query results. Extend wmidev_query_block() to enable the WMI driver core to perform this size check internally. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-6-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/core.c | 10 ++++++---- drivers/platform/x86/intel/wmi/sbl-fw-update.c | 7 +------ drivers/platform/x86/wmi-bmof.c | 2 +- include/linux/wmi.h | 3 ++- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index a1a612f33233..87b0e54dde5a 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -565,13 +565,15 @@ EXPORT_SYMBOL_GPL(wmidev_block_query); * @wdev: A wmi bus device from a driver * @instance: Instance index * @out: WMI buffer to fill + * @min_size: Minimum size of the result data in bytes * - * Query a WMI data block, the caller must free the resulting data inside @out. - * Said data is guaranteed to be aligned on a 8-byte boundary. + * Query a WMI data block, the caller must free the resulting data inside @out + * using kfree(). Said data is guaranteed to be aligned on a 8-byte boundary. * * Return: 0 on success or a negative error code on failure. */ -int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out) +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out, + size_t min_size) { union acpi_object *obj; int ret; @@ -580,7 +582,7 @@ int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer * if (!obj) return -EIO; - ret = wmi_unmarshal_acpi_object(obj, out, 0); + ret = wmi_unmarshal_acpi_object(obj, out, min_size); kfree(obj); return ret; diff --git a/drivers/platform/x86/intel/wmi/sbl-fw-update.c b/drivers/platform/x86/intel/wmi/sbl-fw-update.c index 3716ccaaed6a..62c9c7f1842b 100644 --- a/drivers/platform/x86/intel/wmi/sbl-fw-update.c +++ b/drivers/platform/x86/intel/wmi/sbl-fw-update.c @@ -28,15 +28,10 @@ static int get_fwu_request(struct device *dev, u32 *out) __le32 *result; int ret; - ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer); + ret = wmidev_query_block(to_wmi_device(dev), 0, &buffer, sizeof(*result)); if (ret < 0) return ret; - if (buffer.length < sizeof(*result)) { - kfree(buffer.data); - return -ENODATA; - } - result = buffer.data; *out = le32_to_cpu(*result); kfree(result); diff --git a/drivers/platform/x86/wmi-bmof.c b/drivers/platform/x86/wmi-bmof.c index e3a126de421b..6623cf60e4b4 100644 --- a/drivers/platform/x86/wmi-bmof.c +++ b/drivers/platform/x86/wmi-bmof.c @@ -62,7 +62,7 @@ static int wmi_bmof_probe(struct wmi_device *wdev, const void *context) if (!buffer) return -ENOMEM; - ret = wmidev_query_block(wdev, 0, buffer); + ret = wmidev_query_block(wdev, 0, buffer, 0); if (ret < 0) return ret; diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 858398beb01a..da94580572a9 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -73,7 +73,8 @@ int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, int wmidev_invoke_procedure(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in); -int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out); +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out, + size_t min_size); int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in); From 2e2a39149fe37327e0af225f09cad19526a90d48 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Mon, 6 Apr 2026 22:32:37 +0200 Subject: [PATCH 101/106] platform/wmi: Replace .no_notify_data with .min_event_size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WMI drivers using the buffer-based WMI API are expected to reject undersized event payloads. Extend the WMI driver core to allow such drivers to specify their minimum supported event payload size. Also remove the now redundant .no_notify_data field. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260406203237.2970-7-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/wmi/driver-development-guide.rst | 8 +++++--- drivers/platform/wmi/core.c | 12 ++++++++---- drivers/platform/x86/bitland-mifs-wmi.c | 8 ++------ drivers/platform/x86/dell/dell-wmi-base.c | 1 + drivers/platform/x86/lenovo/ideapad-laptop.c | 1 + drivers/platform/x86/lenovo/wmi-camera.c | 1 + drivers/platform/x86/lenovo/wmi-events.c | 1 + drivers/platform/x86/lenovo/ymc.c | 1 + drivers/platform/x86/lenovo/yogabook.c | 2 +- drivers/platform/x86/redmi-wmi.c | 1 + drivers/platform/x86/uniwill/uniwill-wmi.c | 1 + drivers/platform/x86/xiaomi-wmi.c | 1 + include/linux/wmi.h | 7 +++++-- 13 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Documentation/wmi/driver-development-guide.rst b/Documentation/wmi/driver-development-guide.rst index 5b94402874c4..387f508d57ad 100644 --- a/Documentation/wmi/driver-development-guide.rst +++ b/Documentation/wmi/driver-development-guide.rst @@ -71,7 +71,7 @@ to matching WMI devices using a struct wmi_device_id table: .remove = foo_remove, /* optional, devres is preferred */ .shutdown = foo_shutdown, /* optional, called during shutdown */ .notify_new = foo_notify, /* optional, for event handling */ - .no_notify_data = true, /* optional, enables events containing no additional data */ + .min_event_size = X, /* optional, simplifies event payload size verification */ .no_singleton = true, /* required for new WMI drivers */ }; module_wmi_driver(foo_driver); @@ -142,8 +142,10 @@ right before and after calling its remove() or shutdown() callback. However WMI driver developers should be aware that multiple WMI events can be received concurrently, so any locking (if necessary) needs to be provided by the WMI driver itself. -In order to be able to receive WMI events containing no additional event data, -the ``no_notify_data`` flag inside struct wmi_driver should be set to ``true``. +The WMI driver can furthermore instruct the WMI driver core to automatically reject WMI events +that contain a undersized event payload by populating the ``min_event_size`` field inside +struct wmi_driver. Setting this field to 0 will thus enable the WMI driver to receive WMI events +without any event payload. Take a look at drivers/platform/x86/xiaomi-wmi.c for an example WMI event driver. diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 87b0e54dde5a..7df50c8238e5 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -1040,7 +1040,7 @@ static int wmi_dev_probe(struct device *dev) } if (wdriver->notify || wdriver->notify_new) { - if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data) + if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && wdriver->min_event_size) return -ENODEV; } @@ -1398,10 +1398,14 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) { struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver); + struct wmi_buffer dummy = { + .length = 0, + .data = ZERO_SIZE_PTR, + }; struct wmi_buffer buffer; int ret; - if (!obj && !driver->no_notify_data) { + if (!obj && driver->min_event_size) { dev_warn(&wblock->dev.dev, "Event contains no event data\n"); return; } @@ -1411,11 +1415,11 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) if (driver->notify_new) { if (!obj) { - driver->notify_new(&wblock->dev, NULL); + driver->notify_new(&wblock->dev, &dummy); return; } - ret = wmi_unmarshal_acpi_object(obj, &buffer, 0); + ret = wmi_unmarshal_acpi_object(obj, &buffer, driver->min_event_size); if (ret < 0) { dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret); return; diff --git a/drivers/platform/x86/bitland-mifs-wmi.c b/drivers/platform/x86/bitland-mifs-wmi.c index 78639407d67e..b0d06a80e89e 100644 --- a/drivers/platform/x86/bitland-mifs-wmi.c +++ b/drivers/platform/x86/bitland-mifs-wmi.c @@ -734,15 +734,10 @@ static void bitland_mifs_wmi_notify(struct wmi_device *wdev, const struct wmi_buffer *buffer) { struct bitland_mifs_wmi_data *data = dev_get_drvdata(&wdev->dev); - const struct bitland_mifs_event *event; + const struct bitland_mifs_event *event = buffer->data; struct bitland_fan_notify_data fan_data; u8 brightness; - if (buffer->length < sizeof(*event)) - return; - - event = buffer->data; - /* Validate event type */ if (event->event_type != WMI_EVENT_TYPE_HOTKEY) return; @@ -830,6 +825,7 @@ static struct wmi_driver bitland_mifs_wmi_driver = { .pm = pm_sleep_ptr(&bitland_mifs_wmi_pm_ops), }, .id_table = bitland_mifs_wmi_id_table, + .min_event_size = sizeof(struct bitland_mifs_event), .probe = bitland_mifs_wmi_probe, .notify_new = bitland_mifs_wmi_notify, }; diff --git a/drivers/platform/x86/dell/dell-wmi-base.c b/drivers/platform/x86/dell/dell-wmi-base.c index e7a411ae9ca1..2a5804efd3ea 100644 --- a/drivers/platform/x86/dell/dell-wmi-base.c +++ b/drivers/platform/x86/dell/dell-wmi-base.c @@ -825,6 +825,7 @@ static struct wmi_driver dell_wmi_driver = { .name = "dell-wmi", }, .id_table = dell_wmi_id_table, + .min_event_size = sizeof(u16), .probe = dell_wmi_probe, .remove = dell_wmi_remove, .notify = dell_wmi_notify, diff --git a/drivers/platform/x86/lenovo/ideapad-laptop.c b/drivers/platform/x86/lenovo/ideapad-laptop.c index ae1ebb071fab..4fbc904f1fc3 100644 --- a/drivers/platform/x86/lenovo/ideapad-laptop.c +++ b/drivers/platform/x86/lenovo/ideapad-laptop.c @@ -2340,6 +2340,7 @@ static struct wmi_driver ideapad_wmi_driver = { .name = "ideapad_wmi", }, .id_table = ideapad_wmi_ids, + .min_event_size = sizeof(u32), .probe = ideapad_wmi_probe, .notify = ideapad_wmi_notify, }; diff --git a/drivers/platform/x86/lenovo/wmi-camera.c b/drivers/platform/x86/lenovo/wmi-camera.c index eb60fb9a5b3f..89ecbce60bf4 100644 --- a/drivers/platform/x86/lenovo/wmi-camera.c +++ b/drivers/platform/x86/lenovo/wmi-camera.c @@ -134,6 +134,7 @@ static struct wmi_driver lenovo_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = lenovo_wmi_id_table, + .min_event_size = sizeof(u8), .no_singleton = true, .probe = lenovo_wmi_probe, .notify = lenovo_wmi_notify, diff --git a/drivers/platform/x86/lenovo/wmi-events.c b/drivers/platform/x86/lenovo/wmi-events.c index 0994cd7dd504..4a6a2c82413a 100644 --- a/drivers/platform/x86/lenovo/wmi-events.c +++ b/drivers/platform/x86/lenovo/wmi-events.c @@ -183,6 +183,7 @@ static struct wmi_driver lwmi_events_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = lwmi_events_id_table, + .min_event_size = sizeof(u32), .probe = lwmi_events_probe, .notify = lwmi_events_notify, .no_singleton = true, diff --git a/drivers/platform/x86/lenovo/ymc.c b/drivers/platform/x86/lenovo/ymc.c index 470d53e3c9d2..1b73a55f1b89 100644 --- a/drivers/platform/x86/lenovo/ymc.c +++ b/drivers/platform/x86/lenovo/ymc.c @@ -153,6 +153,7 @@ static struct wmi_driver lenovo_ymc_driver = { .name = "lenovo-ymc", }, .id_table = lenovo_ymc_wmi_id_table, + .min_event_size = sizeof(u32), .probe = lenovo_ymc_probe, .notify = lenovo_ymc_notify, }; diff --git a/drivers/platform/x86/lenovo/yogabook.c b/drivers/platform/x86/lenovo/yogabook.c index 69887de36c9b..1a4b2ab1f35d 100644 --- a/drivers/platform/x86/lenovo/yogabook.c +++ b/drivers/platform/x86/lenovo/yogabook.c @@ -411,8 +411,8 @@ static struct wmi_driver yogabook_wmi_driver = { .name = "yogabook-wmi", .pm = pm_sleep_ptr(&yogabook_pm_ops), }, - .no_notify_data = true, .id_table = yogabook_wmi_id_table, + .min_event_size = 0, .probe = yogabook_wmi_probe, .remove = yogabook_wmi_remove, .notify = yogabook_wmi_notify, diff --git a/drivers/platform/x86/redmi-wmi.c b/drivers/platform/x86/redmi-wmi.c index e5cb348e3a39..58898630eda6 100644 --- a/drivers/platform/x86/redmi-wmi.c +++ b/drivers/platform/x86/redmi-wmi.c @@ -141,6 +141,7 @@ static struct wmi_driver redmi_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = redmi_wmi_id_table, + .min_event_size = 32, .probe = redmi_wmi_probe, .notify = redmi_wmi_notify, .no_singleton = true, diff --git a/drivers/platform/x86/uniwill/uniwill-wmi.c b/drivers/platform/x86/uniwill/uniwill-wmi.c index 31d9c39f14ab..097882f10b1e 100644 --- a/drivers/platform/x86/uniwill/uniwill-wmi.c +++ b/drivers/platform/x86/uniwill/uniwill-wmi.c @@ -77,6 +77,7 @@ static struct wmi_driver uniwill_wmi_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .id_table = uniwill_wmi_id_table, + .min_event_size = sizeof(u32), .notify = uniwill_wmi_notify, .no_singleton = true, }; diff --git a/drivers/platform/x86/xiaomi-wmi.c b/drivers/platform/x86/xiaomi-wmi.c index badf9e42e015..3874f3336a0d 100644 --- a/drivers/platform/x86/xiaomi-wmi.c +++ b/drivers/platform/x86/xiaomi-wmi.c @@ -83,6 +83,7 @@ static struct wmi_driver xiaomi_wmi_driver = { .name = "xiaomi-wmi", }, .id_table = xiaomi_wmi_id_table, + .min_event_size = 0, .probe = xiaomi_wmi_probe, .notify_new = xiaomi_wmi_notify, .no_singleton = true, diff --git a/include/linux/wmi.h b/include/linux/wmi.h index da94580572a9..2d242575a8b3 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -91,7 +91,7 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * struct wmi_driver - WMI driver structure * @driver: Driver model structure * @id_table: List of WMI GUIDs supported by this driver - * @no_notify_data: Driver supports WMI events which provide no event data + * @min_event_size: Minimum event payload size supported by this driver * @no_singleton: Driver can be instantiated multiple times * @probe: Callback for device binding * @remove: Callback for device unbinding @@ -101,11 +101,14 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * * This represents WMI drivers which handle WMI devices. The data inside the buffer * passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary. + * The minimum supported size for said buffer can be specified using @min_event_size. + * WMI drivers that still use the deprecated @notify callback can still set @min_event_size + * to 0 in order to signal that they support WMI events which provide no event data. */ struct wmi_driver { struct device_driver driver; const struct wmi_device_id *id_table; - bool no_notify_data; + size_t min_event_size; bool no_singleton; int (*probe)(struct wmi_device *wdev, const void *context); From 92c36b634b4586e02409488fa02a4908ee4ebea7 Mon Sep 17 00:00:00 2001 From: Shaun Varghese Date: Fri, 10 Apr 2026 23:15:21 +0530 Subject: [PATCH 102/106] platform/x86: hp-wmi: add Omen 14-fb0xxx (board 8C58) support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Board 8C58 (HP Omen Transcend Gaming Laptop 14-fb0xxx) appears to use the same thermal profile handling as other supported Omen systems, including board 8E41. Add it to omen_thermal_profile_boards so hp-wmi can expose the firmware thermal profiles through the platform_profile interface. Tested on Omen 14-fb0xxx: the profile handler exposes cool, balanced, and performance modes, and selecting performance increases sustained CPU package power under load. Signed-off-by: Shaun Varghese Link: https://patch.msgid.link/20260410174651.1424000-1-shaunvarghese43@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 851056bee614..77913373ed08 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -153,6 +153,7 @@ static const char * const omen_thermal_profile_boards[] = { "8900", "8901", "8902", "8912", "8917", "8918", "8949", "894A", "89EB", "8A15", "8A42", "8BAD", + "8C58", "8E41", }; From 9d317a54e46d3b6420567dc5b63e9d7ff5c064a3 Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Sat, 11 Apr 2026 00:40:36 +0530 Subject: [PATCH 103/106] platform/x86: hp-wmi: fix fan table parsing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For Victus S devices, the BIOS fan table header was being incorrectly parsed as: struct { u8 unknown; u8 num_entries; } The first field should be num_fans and the second should be unknown. It is pure coincidence that interpreting an "unknown" field as "num_entries" worked on multiple device, however for board 8D87 (in an upcoming patch), this assumption fails, and the hp-wmi driver fails to load. We fix this by correcting the header definition and compensating for num_entries by parsing each entry of the fan table until an all-NULL row is obtained, mirroring the behavior of OMEN Gaming Hub on Windows. Fixes: 46be1453e6e6 ("platform/x86: hp-wmi: add manual fan control for Victus S models") Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260410191039.125659-2-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 41 +++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 77913373ed08..205a7bf4cbf7 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -469,14 +469,14 @@ struct hp_wmi_hwmon_priv { }; struct victus_s_fan_table_header { + u8 num_fans; u8 unknown; - u8 num_entries; } __packed; struct victus_s_fan_table_entry { u8 cpu_rpm; u8 gpu_rpm; - u8 unknown; + u8 noise_db; } __packed; struct victus_s_fan_table { @@ -2563,7 +2563,9 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) u8 fan_data[128] = { 0 }; struct victus_s_fan_table *fan_table; u8 min_rpm, max_rpm; - int gpu_delta, ret; + u8 cpu_rpm, gpu_rpm, noise_db; + int gpu_delta, i, num_entries, ret; + size_t header_size, entry_size; /* Default behaviour on hwmon init is automatic mode */ priv->mode = PWM_MODE_AUTO; @@ -2578,13 +2580,36 @@ static int hp_wmi_setup_fan_settings(struct hp_wmi_hwmon_priv *priv) return ret; fan_table = (struct victus_s_fan_table *)fan_data; - if (fan_table->header.num_entries == 0 || - sizeof(struct victus_s_fan_table_header) + - sizeof(struct victus_s_fan_table_entry) * fan_table->header.num_entries > sizeof(fan_data)) + if (fan_table->header.num_fans == 0) + return -EINVAL; + + header_size = sizeof(struct victus_s_fan_table_header); + entry_size = sizeof(struct victus_s_fan_table_entry); + num_entries = (sizeof(fan_data) - header_size) / entry_size; + min_rpm = U8_MAX; + max_rpm = 0; + + for (i = 0 ; i < num_entries ; i++) { + cpu_rpm = fan_table->entries[i].cpu_rpm; + gpu_rpm = fan_table->entries[i].gpu_rpm; + noise_db = fan_table->entries[i].noise_db; + + /* + * On some devices, the fan table is truncated with an all-zero row, + * hence we stop parsing here. + */ + if (cpu_rpm == 0 && gpu_rpm == 0 && noise_db == 0) + break; + + if (cpu_rpm < min_rpm) + min_rpm = cpu_rpm; + if (cpu_rpm > max_rpm) + max_rpm = cpu_rpm; + } + + if (min_rpm == U8_MAX || max_rpm == 0) return -EINVAL; - min_rpm = fan_table->entries[0].cpu_rpm; - max_rpm = fan_table->entries[fan_table->header.num_entries - 1].cpu_rpm; gpu_delta = fan_table->entries[0].gpu_rpm - fan_table->entries[0].cpu_rpm; priv->min_rpm = min_rpm; priv->max_rpm = max_rpm; From 5badf5ebcd1476f4bb38c5909c5020e14384ad7d Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Sat, 11 Apr 2026 00:40:37 +0530 Subject: [PATCH 104/106] platform/x86: hp-wmi: Add support for OMEN MAX 16-ak0xxx (8D87) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP OMEN MAX 16-ak0xxx (8D87) requires the same WMI queries as other Victus S devices, hence it has been added to the corresponding list. For this reason, platform_profile_victus_s_get_ec() will be called during thermal_profile_setup() and victus_s_powersource_event() to obtain hardware state (platform profile) by reading from the Embedded Controller, however, this particular board's EC does not seem to expose the platform profile value, unlike other boards. Hence EC readback is disabled. Testing on board 8D87 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Jinyang Zhu Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260410191039.125659-3-krishna.chomal108@gmail.com Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/hp/hp-wmi.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c index 205a7bf4cbf7..9460e07202bd 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -48,6 +48,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45E9-BE91-3D44E2C707E4"); enum hp_ec_offsets { HP_EC_OFFSET_UNKNOWN = 0x00, + HP_NO_THERMAL_PROFILE_OFFSET = 0x01, HP_VICTUS_S_EC_THERMAL_PROFILE_OFFSET = 0x59, HP_OMEN_EC_THERMAL_PROFILE_FLAGS_OFFSET = 0x62, HP_OMEN_EC_THERMAL_PROFILE_TIMER_OFFSET = 0x63, @@ -127,6 +128,13 @@ static const struct thermal_profile_params omen_v1_legacy_thermal_params = { .ec_tp_offset = HP_OMEN_EC_THERMAL_PROFILE_OFFSET, }; +static const struct thermal_profile_params omen_v1_no_ec_thermal_params = { + .performance = HP_OMEN_V1_THERMAL_PROFILE_PERFORMANCE, + .balanced = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT, + .low_power = HP_OMEN_V1_THERMAL_PROFILE_DEFAULT, + .ec_tp_offset = HP_NO_THERMAL_PROFILE_OFFSET, +}; + /* * A generic pointer for the currently-active board's thermal profile * parameters. @@ -231,6 +239,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D41") }, .driver_data = (void *)&victus_s_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8D87") }, + .driver_data = (void *)&omen_v1_no_ec_thermal_params, + }, {}, }; @@ -1837,7 +1849,8 @@ static int platform_profile_victus_s_get_ec(enum platform_profile_option *profil const struct thermal_profile_params *params; params = active_thermal_profile_params; - if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { + if (params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || + params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { *profile = active_platform_profile; return 0; } @@ -2192,7 +2205,8 @@ static int thermal_profile_setup(struct platform_device *device) * behaves like a wrapper around active_platform_profile, to avoid using * uninitialized data, we default to PLATFORM_PROFILE_BALANCED. */ - if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN) { + if (active_thermal_profile_params->ec_tp_offset == HP_EC_OFFSET_UNKNOWN || + active_thermal_profile_params->ec_tp_offset == HP_NO_THERMAL_PROFILE_OFFSET) { active_platform_profile = PLATFORM_PROFILE_BALANCED; } else { err = platform_profile_victus_s_get_ec(&active_platform_profile); From 899225257e78585e2e10b0f7ba472b3c212a8d16 Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Sat, 11 Apr 2026 00:40:38 +0530 Subject: [PATCH 105/106] platform/x86: hp-wmi: Add support for Omen 16-n0xxx (8A44) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-n0xxx (board ID: 8A44) 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 8A44 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Prasoon Dev Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260410191039.125659-4-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 9460e07202bd..2185ac858b44 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -191,6 +191,10 @@ static const char * const victus_thermal_profile_boards[] = { /* DMI Board names of Victus 16-r and Victus 16-s laptops */ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst = { + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A44") }, + .driver_data = (void *)&omen_v1_legacy_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8A4D") }, .driver_data = (void *)&omen_v1_legacy_thermal_params, From 344bf523d441d44c75c429ea6cdcfa8f12efde4d Mon Sep 17 00:00:00 2001 From: Krishna Chomal Date: Sat, 11 Apr 2026 00:40:39 +0530 Subject: [PATCH 106/106] platform/x86: hp-wmi: Add support for Omen 16-wf1xxx (8C77) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HP Omen 16-wf1xxx (board ID: 8C77) 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_thermal_params. Testing on board 8C77 confirmed that platform profile is registered successfully and fan RPMs are readable and controllable. Tested-by: Thomas Arici Reported-by: Thomas Arici Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221300 Signed-off-by: Krishna Chomal Link: https://patch.msgid.link/20260410191039.125659-5-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 2185ac858b44..83614f2f9f22 100644 --- a/drivers/platform/x86/hp/hp-wmi.c +++ b/drivers/platform/x86/hp/hp-wmi.c @@ -227,6 +227,10 @@ static const struct dmi_system_id victus_s_thermal_profile_boards[] __initconst .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C76") }, .driver_data = (void *)&omen_v1_thermal_params, }, + { + .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C77") }, + .driver_data = (void *)&omen_v1_thermal_params, + }, { .matches = { DMI_MATCH(DMI_BOARD_NAME, "8C78") }, .driver_data = (void *)&omen_v1_thermal_params,