From 03667e146f815d5de8d2d8c85ced39791fb2b373 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 14 Dec 2025 19:25:37 +0100 Subject: [PATCH 01/25] ACPI: NFIT: core: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI NFIT core 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. This change was mostly developed by Michal Wilczynski [1]. Linu: https://lore.kernel.org/linux-acpi/20231011083334.3987477-6-michal.wilczynski@intel.com/ [1] Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/6221453.lOV4Wx5bFT@rafael.j.wysocki Acked-by: Ira Weiny Tested-by: Ira Weiny --- drivers/acpi/nfit/core.c | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index 3eb56b77cb6d..e19aba02b800 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2,6 +2,7 @@ /* * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. */ +#include #include #include #include @@ -89,15 +90,22 @@ static const guid_t *to_nfit_bus_uuid(int family) static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc) { struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc; + struct acpi_device *adev; - /* - * If provider == 'ACPI.NFIT' we can assume 'dev' is a struct - * acpi_device. - */ + /* If provider == 'ACPI.NFIT', a struct acpi_device is there. */ if (!nd_desc->provider_name || strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0) return NULL; + /* + * But it can be the ACPI companion of acpi_desc->dev when it cones from + * acpi_nfit_probe(). + */ + adev = ACPI_COMPANION(acpi_desc->dev); + if (adev) + return adev; + + /* Or it is acpi_desc->dev itself when it comes from nfit_ctl_test(). */ return to_acpi_device(acpi_desc->dev); } @@ -3283,11 +3291,11 @@ static void acpi_nfit_put_table(void *table) static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *adev = data; + struct device *dev = data; - device_lock(&adev->dev); - __acpi_nfit_notify(&adev->dev, handle, event); - device_unlock(&adev->dev); + device_lock(dev); + __acpi_nfit_notify(dev, handle, event); + device_unlock(dev); } static void acpi_nfit_remove_notify_handler(void *data) @@ -3328,18 +3336,19 @@ void acpi_nfit_shutdown(void *data) } EXPORT_SYMBOL_GPL(acpi_nfit_shutdown); -static int acpi_nfit_add(struct acpi_device *adev) +static int acpi_nfit_probe(struct platform_device *pdev) { struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_nfit_desc *acpi_desc; - struct device *dev = &adev->dev; + struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct acpi_table_header *tbl; acpi_status status = AE_OK; acpi_size sz; int rc = 0; rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY, - acpi_nfit_notify, adev); + acpi_nfit_notify, dev); if (rc) return rc; @@ -3369,7 +3378,7 @@ static int acpi_nfit_add(struct acpi_device *adev) acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); if (!acpi_desc) return -ENOMEM; - acpi_nfit_desc_init(acpi_desc, &adev->dev); + acpi_nfit_desc_init(acpi_desc, dev); /* Save the acpi header for exporting the revision via sysfs */ acpi_desc->acpi_header = *tbl; @@ -3474,11 +3483,11 @@ static const struct acpi_device_id acpi_nfit_ids[] = { }; MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids); -static struct acpi_driver acpi_nfit_driver = { - .name = KBUILD_MODNAME, - .ids = acpi_nfit_ids, - .ops = { - .add = acpi_nfit_add, +static struct platform_driver acpi_nfit_driver = { + .probe = acpi_nfit_probe, + .driver = { + .name = "acpi-nfit", + .acpi_match_table = acpi_nfit_ids, }, }; @@ -3516,7 +3525,7 @@ static __init int nfit_init(void) return -ENOMEM; nfit_mce_register(); - ret = acpi_bus_register_driver(&acpi_nfit_driver); + ret = platform_driver_register(&acpi_nfit_driver); if (ret) { nfit_mce_unregister(); destroy_workqueue(nfit_wq); @@ -3529,7 +3538,7 @@ static __init int nfit_init(void) static __exit void nfit_exit(void) { nfit_mce_unregister(); - acpi_bus_unregister_driver(&acpi_nfit_driver); + platform_driver_unregister(&acpi_nfit_driver); destroy_workqueue(nfit_wq); WARN_ON(!list_empty(&acpi_descs)); } From ab06eb9204010c61e754bf4e71ee6f554584714d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:52:45 +0100 Subject: [PATCH 02/25] ACPI: scan: Register platform devices for fixed event buttons On platforms using ACPI, power and sleep buttons may be so called "fixed event devices" in which case they are hooked up directly to the Fixed Events register in the platform via dedicated lines and there are no corresponding device objects in the ACPI namespace. Nevertheless, in Linux they get corresponding struct acpi_device objects with special device IDs, either LNXPWRBN or LNXSLPBN, which are then used for driver binding in a ususal way. However, the function creating those struct acpi_device objects for "fixed event device" buttons, acpi_bus_scan_fixed(), does not register platform devices for them, unlike the generic code handling device enumeration based on the ACPI namespace. Consequently, if an ACPI power or sleep button is represented by a device object in the ACPI namespace, it will get a corresponding platform device, but if it is a "fixed event device", it will not get one, which is inconsistent and prevents the ACPI power button driver from being converted into a platform driver. For the sake of consistency and to allow the ACPI power button driver to become a platform one going forward, modify acpi_bus_scan_fixed() to register platform devices for "fixed event device" buttons and update ACPI platform device registration code to work with non-device ACPI object types, so it can handle the buttons in question. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3731144.R56niFO833@rafael.j.wysocki --- drivers/acpi/acpi_platform.c | 40 ++++++++++++++++++++---------------- drivers/acpi/scan.c | 4 ++++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 48d15dd785f6..52c8d602f3a5 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -114,10 +114,8 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, struct platform_device *pdev = NULL; struct platform_device_info pdevinfo; const struct acpi_device_id *match; - struct resource_entry *rentry; - struct list_head resource_list; struct resource *resources = NULL; - int count; + int count = 0; /* If the ACPI node already has a physical device attached, skip it. */ if (adev->physical_node_count) @@ -137,22 +135,28 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, } } - INIT_LIST_HEAD(&resource_list); - count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); - if (count < 0) - return NULL; - if (count > 0) { - resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); - if (!resources) { - acpi_dev_free_resource_list(&resource_list); - return ERR_PTR(-ENOMEM); - } - count = 0; - list_for_each_entry(rentry, &resource_list, node) - acpi_platform_fill_resource(adev, rentry->res, - &resources[count++]); + if (adev->device_type == ACPI_BUS_TYPE_DEVICE) { + struct list_head resource_list = LIST_HEAD_INIT(resource_list); - acpi_dev_free_resource_list(&resource_list); + count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); + if (count < 0) + return ERR_PTR(-ENODATA); + + if (count > 0) { + struct resource_entry *rentry; + + resources = kcalloc(count, sizeof(*resources), GFP_KERNEL); + if (!resources) { + acpi_dev_free_resource_list(&resource_list); + return ERR_PTR(-ENOMEM); + } + count = 0; + list_for_each_entry(rentry, &resource_list, node) + acpi_platform_fill_resource(adev, rentry->res, + &resources[count++]); + + acpi_dev_free_resource_list(&resource_list); + } } memset(&pdevinfo, 0, sizeof(pdevinfo)); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8a895d377e21..da4da565f257 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2772,6 +2772,8 @@ static void acpi_bus_scan_fixed(void) device_init_wakeup(&adev->dev, true); else dev_dbg(&adev->dev, "No driver\n"); + + acpi_default_enumeration(adev); } } @@ -2784,6 +2786,8 @@ static void acpi_bus_scan_fixed(void) adev->flags.match_driver = true; if (device_attach(&adev->dev) < 0) dev_dbg(&adev->dev, "No driver\n"); + + acpi_default_enumeration(adev); } } } From ddfebb7537cb422eca26ebdc3fe3426b60582c25 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:54:17 +0100 Subject: [PATCH 03/25] ACPI: scan: Reduce code duplication related to fixed event devices Move duplicate fixed event device registration code from acpi_bus_scan_fixed() into a new function called acpi_bus_add_fixed_device_object() and make acpi_bus_scan_fixed() invoke that function as needed. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/1916860.atdPhlSkOF@rafael.j.wysocki --- drivers/acpi/scan.c | 48 +++++++++++++++++++-------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index da4da565f257..0ae36ab7b6ee 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2759,37 +2759,29 @@ int acpi_bus_register_early_device(int type) } EXPORT_SYMBOL_GPL(acpi_bus_register_early_device); +static void acpi_bus_add_fixed_device_object(enum acpi_bus_device_type type) +{ + struct acpi_device *adev = NULL; + + acpi_add_single_object(&adev, NULL, type, false); + if (adev) { + adev->flags.match_driver = true; + if (device_attach(&adev->dev) >= 0) + device_init_wakeup(&adev->dev, true); + else + dev_dbg(&adev->dev, "No driver\n"); + + acpi_default_enumeration(adev); + } +} + static void acpi_bus_scan_fixed(void) { - if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) { - struct acpi_device *adev = NULL; + if (!(acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON)) + acpi_bus_add_fixed_device_object(ACPI_BUS_TYPE_POWER_BUTTON); - acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_POWER_BUTTON, - false); - if (adev) { - adev->flags.match_driver = true; - if (device_attach(&adev->dev) >= 0) - device_init_wakeup(&adev->dev, true); - else - dev_dbg(&adev->dev, "No driver\n"); - - acpi_default_enumeration(adev); - } - } - - if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) { - struct acpi_device *adev = NULL; - - acpi_add_single_object(&adev, NULL, ACPI_BUS_TYPE_SLEEP_BUTTON, - false); - if (adev) { - adev->flags.match_driver = true; - if (device_attach(&adev->dev) < 0) - dev_dbg(&adev->dev, "No driver\n"); - - acpi_default_enumeration(adev); - } - } + if (!(acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON)) + acpi_bus_add_fixed_device_object(ACPI_BUS_TYPE_SLEEP_BUTTON); } static void __init acpi_get_spcr_uart_addr(void) From 93dc5db6d47aaa3b4b458ddfddfa3369c24e22f4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:55:09 +0100 Subject: [PATCH 04/25] ACPI: button: Adjust event notification routines Adjust the event notification routines in the ACPI button driver to take a struct acpi_button pointer as an argument istead of a struct acpi_device one where applicable, which allows the use of acpi_driver_data() to be limited and will facilitate subsequent changes. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2260995.Icojqenx9y@rafael.j.wysocki --- drivers/acpi/button.c | 67 +++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 3c6dd9b4ba0a..09a6e4ffe9f2 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -169,6 +169,7 @@ static struct acpi_driver acpi_button_driver = { }; struct acpi_button { + struct acpi_device *adev; unsigned int type; struct input_dev *input; char phys[32]; /* for input device */ @@ -202,9 +203,9 @@ static int acpi_lid_evaluate_state(struct acpi_device *device) return lid_state ? 1 : 0; } -static int acpi_lid_notify_state(struct acpi_device *device, int state) +static int acpi_lid_notify_state(struct acpi_button *button, int state) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; ktime_t next_report; bool do_update; @@ -287,18 +288,18 @@ static int acpi_lid_notify_state(struct acpi_device *device, int state) static int __maybe_unused acpi_button_state_seq_show(struct seq_file *seq, void *offset) { - struct acpi_device *device = seq->private; + struct acpi_button *button = seq->private; int state; - state = acpi_lid_evaluate_state(device); + state = acpi_lid_evaluate_state(button->adev); seq_printf(seq, "state: %s\n", state < 0 ? "unsupported" : (state ? "open" : "closed")); return 0; } -static int acpi_button_add_fs(struct acpi_device *device) +static int acpi_button_add_fs(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; struct proc_dir_entry *entry = NULL; int ret = 0; @@ -333,7 +334,7 @@ static int acpi_button_add_fs(struct acpi_device *device) /* create /proc/acpi/button/lid/LID/state */ entry = proc_create_single_data(ACPI_BUTTON_FILE_STATE, S_IRUGO, acpi_device_dir(device), acpi_button_state_seq_show, - device); + button); if (!entry) { ret = -ENODEV; goto remove_dev_dir; @@ -355,9 +356,9 @@ remove_button_dir: goto done; } -static int acpi_button_remove_fs(struct acpi_device *device) +static int acpi_button_remove_fs(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = button->adev; if (button->type != ACPI_BUTTON_TYPE_LID) return 0; @@ -385,9 +386,10 @@ int acpi_lid_open(void) } EXPORT_SYMBOL(acpi_lid_open); -static int acpi_lid_update_state(struct acpi_device *device, +static int acpi_lid_update_state(struct acpi_button *button, bool signal_wakeup) { + struct acpi_device *device = button->adev; int state; state = acpi_lid_evaluate_state(device); @@ -397,19 +399,17 @@ static int acpi_lid_update_state(struct acpi_device *device, if (state && signal_wakeup) acpi_pm_wakeup_event(&device->dev); - return acpi_lid_notify_state(device, state); + return acpi_lid_notify_state(button, state); } -static void acpi_lid_initialize_state(struct acpi_device *device) +static void acpi_lid_initialize_state(struct acpi_button *button) { - struct acpi_button *button = acpi_driver_data(device); - switch (lid_init_state) { case ACPI_BUTTON_LID_INIT_OPEN: - (void)acpi_lid_notify_state(device, 1); + (void)acpi_lid_notify_state(button, 1); break; case ACPI_BUTTON_LID_INIT_METHOD: - (void)acpi_lid_update_state(device, false); + (void)acpi_lid_update_state(button, false); break; case ACPI_BUTTON_LID_INIT_IGNORE: default: @@ -421,8 +421,8 @@ static void acpi_lid_initialize_state(struct acpi_device *device) static void acpi_lid_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_button *button; + struct acpi_button *button = data; + struct acpi_device *device = button->adev; if (event != ACPI_BUTTON_NOTIFY_STATUS) { acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", @@ -430,17 +430,16 @@ static void acpi_lid_notify(acpi_handle handle, u32 event, void *data) return; } - button = acpi_driver_data(device); if (!button->lid_state_initialized) return; - acpi_lid_update_state(device, true); + acpi_lid_update_state(button, true); } static void acpi_button_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_button *button; + struct acpi_button *button = data; + struct acpi_device *device = button->adev; struct input_dev *input; int keycode; @@ -457,7 +456,6 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) acpi_pm_wakeup_event(&device->dev); - button = acpi_driver_data(device); if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE) return; @@ -505,7 +503,7 @@ static int acpi_button_resume(struct device *dev) if (button->type == ACPI_BUTTON_TYPE_LID) { button->last_state = !!acpi_lid_evaluate_state(device); button->last_time = ktime_get(); - acpi_lid_initialize_state(device); + acpi_lid_initialize_state(button); } if (button->type == ACPI_BUTTON_TYPE_POWER) { @@ -521,12 +519,12 @@ static int acpi_button_resume(struct device *dev) static int acpi_lid_input_open(struct input_dev *input) { - struct acpi_device *device = input_get_drvdata(input); - struct acpi_button *button = acpi_driver_data(device); + struct acpi_button *button = input_get_drvdata(input); + struct acpi_device *device = button->adev; button->last_state = !!acpi_lid_evaluate_state(device); button->last_time = ktime_get(); - acpi_lid_initialize_state(device); + acpi_lid_initialize_state(button); return 0; } @@ -551,6 +549,7 @@ static int acpi_button_add(struct acpi_device *device) device->driver_data = button; + button->adev = device; button->input = input = input_allocate_device(); if (!input) { error = -ENOMEM; @@ -587,7 +586,7 @@ static int acpi_button_add(struct acpi_device *device) } if (!error) - error = acpi_button_add_fs(device); + error = acpi_button_add_fs(button); if (error) { input_free_device(input); @@ -617,7 +616,7 @@ static int acpi_button_add(struct acpi_device *device) break; } - input_set_drvdata(input, device); + input_set_drvdata(input, button); error = input_register_device(input); if (error) { input_free_device(input); @@ -628,17 +627,17 @@ static int acpi_button_add(struct acpi_device *device) case ACPI_BUS_TYPE_POWER_BUTTON: status = acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_button_event, - device); + button); break; case ACPI_BUS_TYPE_SLEEP_BUTTON: status = acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON, acpi_button_event, - device); + button); break; default: status = acpi_install_notify_handler(device->handle, ACPI_ALL_NOTIFY, handler, - device); + button); break; } if (ACPI_FAILURE(status)) { @@ -661,7 +660,7 @@ static int acpi_button_add(struct acpi_device *device) err_input_unregister: input_unregister_device(input); err_remove_fs: - acpi_button_remove_fs(device); + acpi_button_remove_fs(button); err_free_button: kfree(button); return error; @@ -689,7 +688,7 @@ static void acpi_button_remove(struct acpi_device *device) } acpi_os_wait_events_complete(); - acpi_button_remove_fs(device); + acpi_button_remove_fs(button); input_unregister_device(button->input); kfree(button); } From 52d86401963666423cb9a56d117136c846093db0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:57:57 +0100 Subject: [PATCH 05/25] ACPI: button: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI button 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. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2461734.NG923GbCHz@rafael.j.wysocki --- drivers/acpi/button.c | 61 +++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index 09a6e4ffe9f2..b899b8745fed 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #define ACPI_BUTTON_CLASS "button" @@ -145,8 +146,8 @@ static const struct dmi_system_id dmi_lid_quirks[] = { {} }; -static int acpi_button_add(struct acpi_device *device); -static void acpi_button_remove(struct acpi_device *device); +static int acpi_button_probe(struct platform_device *pdev); +static void acpi_button_remove(struct platform_device *pdev); #ifdef CONFIG_PM_SLEEP static int acpi_button_suspend(struct device *dev); @@ -157,19 +158,19 @@ static int acpi_button_resume(struct device *dev); #endif static SIMPLE_DEV_PM_OPS(acpi_button_pm, acpi_button_suspend, acpi_button_resume); -static struct acpi_driver acpi_button_driver = { - .name = "button", - .class = ACPI_BUTTON_CLASS, - .ids = button_device_ids, - .ops = { - .add = acpi_button_add, - .remove = acpi_button_remove, +static struct platform_driver acpi_button_driver = { + .probe = acpi_button_probe, + .remove = acpi_button_remove, + .driver = { + .name = "acpi-button", + .acpi_match_table = button_device_ids, + .pm = &acpi_button_pm, }, - .drv.pm = &acpi_button_pm, }; struct acpi_button { struct acpi_device *adev; + struct platform_device *pdev; unsigned int type; struct input_dev *input; char phys[32]; /* for input device */ @@ -397,7 +398,7 @@ static int acpi_lid_update_state(struct acpi_button *button, return state; if (state && signal_wakeup) - acpi_pm_wakeup_event(&device->dev); + acpi_pm_wakeup_event(&button->pdev->dev); return acpi_lid_notify_state(button, state); } @@ -454,7 +455,7 @@ static void acpi_button_notify(acpi_handle handle, u32 event, void *data) return; } - acpi_pm_wakeup_event(&device->dev); + acpi_pm_wakeup_event(&button->pdev->dev); if (button->suspended || event == ACPI_BUTTON_NOTIFY_WAKE) return; @@ -486,8 +487,7 @@ static u32 acpi_button_event(void *data) #ifdef CONFIG_PM_SLEEP static int acpi_button_suspend(struct device *dev) { - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); + struct acpi_button *button = dev_get_drvdata(dev); button->suspended = true; return 0; @@ -495,9 +495,9 @@ static int acpi_button_suspend(struct device *dev) static int acpi_button_resume(struct device *dev) { + struct acpi_button *button = dev_get_drvdata(dev); + struct acpi_device *device = ACPI_COMPANION(dev); struct input_dev *input; - struct acpi_device *device = to_acpi_device(dev); - struct acpi_button *button = acpi_driver_data(device); button->suspended = false; if (button->type == ACPI_BUTTON_TYPE_LID) { @@ -529,8 +529,9 @@ static int acpi_lid_input_open(struct input_dev *input) return 0; } -static int acpi_button_add(struct acpi_device *device) +static int acpi_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_notify_handler handler; struct acpi_button *button; struct input_dev *input; @@ -547,8 +548,9 @@ static int acpi_button_add(struct acpi_device *device) if (!button) return -ENOMEM; - device->driver_data = button; + platform_set_drvdata(pdev, button); + button->pdev = pdev; button->adev = device; button->input = input = input_allocate_device(); if (!input) { @@ -599,7 +601,7 @@ static int acpi_button_add(struct acpi_device *device) input->phys = button->phys; input->id.bustype = BUS_HOST; input->id.product = button->type; - input->dev.parent = &device->dev; + input->dev.parent = &pdev->dev; switch (button->type) { case ACPI_BUTTON_TYPE_POWER: @@ -653,7 +655,7 @@ static int acpi_button_add(struct acpi_device *device) lid_device = device; } - device_init_wakeup(&device->dev, true); + device_init_wakeup(&pdev->dev, true); pr_info("%s [%s]\n", name, acpi_device_bid(device)); return 0; @@ -666,9 +668,10 @@ err_free_button: return error; } -static void acpi_button_remove(struct acpi_device *device) +static void acpi_button_remove(struct platform_device *pdev) { - struct acpi_button *button = acpi_driver_data(device); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_button *button = platform_get_drvdata(pdev); switch (device->device_type) { case ACPI_BUS_TYPE_POWER_BUTTON: @@ -727,7 +730,7 @@ module_param_call(lid_init_state, NULL, 0644); MODULE_PARM_DESC(lid_init_state, "Behavior for reporting LID initial state"); -static int acpi_button_register_driver(struct acpi_driver *driver) +static int __init acpi_button_init(void) { const struct dmi_system_id *dmi_id; @@ -743,20 +746,20 @@ static int acpi_button_register_driver(struct acpi_driver *driver) * Modules such as nouveau.ko and i915.ko have a link time dependency * on acpi_lid_open(), and would therefore not be loadable on ACPI * capable kernels booted in non-ACPI mode if the return value of - * acpi_bus_register_driver() is returned from here with ACPI disabled + * platform_driver_register() is returned from here with ACPI disabled * when this driver is built as a module. */ if (acpi_disabled) return 0; - return acpi_bus_register_driver(driver); + return platform_driver_register(&acpi_button_driver); } -static void acpi_button_unregister_driver(struct acpi_driver *driver) +static void __exit acpi_button_exit(void) { if (!acpi_disabled) - acpi_bus_unregister_driver(driver); + platform_driver_unregister(&acpi_button_driver); } -module_driver(acpi_button_driver, acpi_button_register_driver, - acpi_button_unregister_driver); +module_init(acpi_button_init); +module_exit(acpi_button_exit); From f4203ec64e1155c07c5d95bddc62201af8598c67 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 14:59:00 +0100 Subject: [PATCH 06/25] ACPI: tiny-power-button: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI tiny-power-button 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. Signed-off-by: Rafael J. Wysocki [ rjw: White space fixup ] Link: https://patch.msgid.link/5629403.Sb9uPGUboI@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/tiny-power-button.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/tiny-power-button.c b/drivers/acpi/tiny-power-button.c index 6353be6fec69..531e65b01bcb 100644 --- a/drivers/acpi/tiny-power-button.c +++ b/drivers/acpi/tiny-power-button.c @@ -1,7 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later -#include -#include #include +#include +#include +#include #include MODULE_AUTHOR("Josh Triplett"); @@ -35,8 +36,9 @@ static u32 acpi_tiny_power_button_event(void *not_used) return ACPI_INTERRUPT_HANDLED; } -static int acpi_tiny_power_button_add(struct acpi_device *device) +static int acpi_tiny_power_button_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_status status; if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { @@ -55,8 +57,10 @@ static int acpi_tiny_power_button_add(struct acpi_device *device) return 0; } -static void acpi_tiny_power_button_remove(struct acpi_device *device) +static void acpi_tiny_power_button_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + if (device->device_type == ACPI_BUS_TYPE_POWER_BUTTON) { acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON, acpi_tiny_power_button_event); @@ -67,14 +71,13 @@ static void acpi_tiny_power_button_remove(struct acpi_device *device) acpi_os_wait_events_complete(); } -static struct acpi_driver acpi_tiny_power_button_driver = { - .name = "tiny-power-button", - .class = "tiny-power-button", - .ids = tiny_power_button_device_ids, - .ops = { - .add = acpi_tiny_power_button_add, - .remove = acpi_tiny_power_button_remove, +static struct platform_driver acpi_tiny_power_button_driver = { + .probe = acpi_tiny_power_button_probe, + .remove = acpi_tiny_power_button_remove, + .driver = { + .name = "acpi-tiny-power-button", + .acpi_match_table = tiny_power_button_device_ids, }, }; -module_acpi_driver(acpi_tiny_power_button_driver); +module_platform_driver(acpi_tiny_power_button_driver); From 2cf321ef4e8225108b5680dcdfc9429ed965aa30 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 15:00:01 +0100 Subject: [PATCH 07/25] ACPI: scan: Do not bind ACPI drivers to fixed event buttons Both ACPI button drivers have been converted to platform ones, so there is no reason to attempt to bind an ACPI driver to a struct acpi_device representing a fixed event device button. Update the relevant code accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2213073.OBFZWjSADL@rafael.j.wysocki --- drivers/acpi/scan.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0ae36ab7b6ee..1bc470019298 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2764,15 +2764,8 @@ static void acpi_bus_add_fixed_device_object(enum acpi_bus_device_type type) struct acpi_device *adev = NULL; acpi_add_single_object(&adev, NULL, type, false); - if (adev) { - adev->flags.match_driver = true; - if (device_attach(&adev->dev) >= 0) - device_init_wakeup(&adev->dev, true); - else - dev_dbg(&adev->dev, "No driver\n"); - + if (adev) acpi_default_enumeration(adev); - } } static void acpi_bus_scan_fixed(void) From 91ba8de81bcb8cea922ee9427e54798a16dfd724 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 15 Dec 2025 15:00:57 +0100 Subject: [PATCH 08/25] ACPI: scan: Do not mark button ACPI devices as wakeup-capable It is generally questionable to mark struct acpi_device "devices" as wakeup-capable because they represent firmware entities that by themselves have no wakeup capabilities. It was done for struct acpi_device "devices" corresponding to buttons because the ACPI button driver was binding to them directly, but now that corresponding platform devices are created for the buttons and they are marked as wakeup-capable by the ACPI button driver, there is no reason to continue doing it. Update acpi_wakeup_gpe_init() accordingly. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2891119.BEx9A2HvPv@rafael.j.wysocki --- drivers/acpi/scan.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1bc470019298..37f06d7e97c5 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1023,7 +1023,6 @@ static bool acpi_wakeup_gpe_init(struct acpi_device *device) wakeup->sleep_state == ACPI_STATE_S5) wakeup->sleep_state = ACPI_STATE_S4; acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); - device_set_wakeup_capable(&device->dev, true); return true; } From d27ccaebab98a77c236494e8fc6857c629b5ffdd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Dec 2025 15:42:21 +0100 Subject: [PATCH 09/25] ACPI: scan: Register platform devices for thermal zones Currently, platform devices are not registered for ACPI thermal zones because they are not represented as device objects in the ACPI namespace. Instead, they are represented as thermal zone objects, so in particular the platform_id flag is not set for them during enumeration because it is only set for objects of type ACPI_BUS_TYPE_DEVICE, but otherwise they are handled similarly at the ACPI core level. To facilitate converting the ACPI thermal zone driver into a platform one, modify acpi_set_pnp_ids() to set the platform_id flag for thermal zones in analogy with device objects to cause platform devices to be registered for them. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: lihuisong@huawei.com Link: https://patch.msgid.link/4701463.LvFx2qVVIh@rafael.j.wysocki --- drivers/acpi/scan.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 37f06d7e97c5..7e4dbec8f77c 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -1469,6 +1469,7 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, break; case ACPI_BUS_TYPE_THERMAL: acpi_add_id(pnp, ACPI_THERMAL_HID); + pnp->type.platform_id = 1; break; case ACPI_BUS_TYPE_POWER_BUTTON: acpi_add_id(pnp, ACPI_BUTTON_HID_POWERF); From a4975385997a6fa1a69c3e5004a48430830f960f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Dec 2025 15:43:11 +0100 Subject: [PATCH 10/25] ACPI: thermal: Adjust event notification routine Adjust acpi_thermal_notify() to cast its "data" argument to a struct acpi_thermal pointer istead of a struct acpi_device one, which allows the use of acpi_driver_data() to be limited and will facilitate subsequent changes. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Acked-by: lihuisong@huawei.com Link: https://patch.msgid.link/5035876.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/thermal.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index a511f9ea0267..cad19e62537e 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -670,8 +670,7 @@ static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_thermal *tz = acpi_driver_data(device); + struct acpi_thermal *tz = data; if (!tz) return; @@ -685,8 +684,8 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_trips_update(tz, event); break; default: - acpi_handle_debug(device->handle, "Unsupported event [0x%x]\n", - event); + acpi_handle_debug(tz->device->handle, + "Unsupported event [0x%x]\n", event); break; } } @@ -881,7 +880,7 @@ static int acpi_thermal_add(struct acpi_device *device) acpi_device_bid(device), deci_kelvin_to_celsius(tz->temp_dk)); result = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_thermal_notify, device); + acpi_thermal_notify, tz); if (result) goto flush_wq; From d1db160da0d1c4711267d311d2461127d7c9a2ba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Dec 2025 15:43:45 +0100 Subject: [PATCH 11/25] ACPI: thermal: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI thermal zone 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. Signed-off-by: Rafael J. Wysocki Tested-by: lihuisong@huawei.com Link: https://patch.msgid.link/2249483.irdbgypaU6@rafael.j.wysocki --- drivers/acpi/thermal.c | 48 +++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index cad19e62537e..25c75dad8ec4 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -776,9 +777,10 @@ static void acpi_thermal_free_thermal_zone(struct acpi_thermal *tz) kfree(tz); } -static int acpi_thermal_add(struct acpi_device *device) +static int acpi_thermal_probe(struct platform_device *pdev) { struct thermal_trip trip_table[ACPI_THERMAL_MAX_NR_TRIPS] = { 0 }; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_thermal_trip *acpi_trip; struct thermal_trip *trip; struct acpi_thermal *tz; @@ -794,11 +796,12 @@ static int acpi_thermal_add(struct acpi_device *device) if (!tz) return -ENOMEM; + platform_set_drvdata(pdev, tz); + tz->device = device; strscpy(tz->name, device->pnp.bus_id); strscpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_THERMAL_CLASS); - device->driver_data = tz; acpi_thermal_aml_dependency_fix(tz); @@ -895,16 +898,11 @@ free_memory: return result; } -static void acpi_thermal_remove(struct acpi_device *device) +static void acpi_thermal_remove(struct platform_device *pdev) { - struct acpi_thermal *tz; + struct acpi_thermal *tz = platform_get_drvdata(pdev); - if (!device || !acpi_driver_data(device)) - return; - - tz = acpi_driver_data(device); - - acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, + acpi_dev_remove_notify_handler(tz->device, ACPI_DEVICE_NOTIFY, acpi_thermal_notify); flush_workqueue(acpi_thermal_pm_queue); @@ -922,16 +920,9 @@ static int acpi_thermal_suspend(struct device *dev) static int acpi_thermal_resume(struct device *dev) { - struct acpi_thermal *tz; + struct acpi_thermal *tz = dev_get_drvdata(dev); int i, j; - if (!dev) - return -EINVAL; - - tz = acpi_driver_data(to_acpi_device(dev)); - if (!tz) - return -EINVAL; - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { struct acpi_thermal_trip *acpi_trip = &tz->trips.active[i].trip; @@ -958,15 +949,14 @@ static const struct acpi_device_id thermal_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, thermal_device_ids); -static struct acpi_driver acpi_thermal_driver = { - .name = "thermal", - .class = ACPI_THERMAL_CLASS, - .ids = thermal_device_ids, - .ops = { - .add = acpi_thermal_add, - .remove = acpi_thermal_remove, - }, - .drv.pm = &acpi_thermal_pm, +static struct platform_driver acpi_thermal_driver = { + .probe = acpi_thermal_probe, + .remove = acpi_thermal_remove, + .driver = { + .name = "acpi-thermal", + .acpi_match_table = thermal_device_ids, + .pm = &acpi_thermal_pm, + }, }; static int thermal_act(const struct dmi_system_id *d) @@ -1064,7 +1054,7 @@ static int __init acpi_thermal_init(void) if (!acpi_thermal_pm_queue) return -ENODEV; - result = acpi_bus_register_driver(&acpi_thermal_driver); + result = platform_driver_register(&acpi_thermal_driver); if (result < 0) { destroy_workqueue(acpi_thermal_pm_queue); return -ENODEV; @@ -1075,7 +1065,7 @@ static int __init acpi_thermal_init(void) static void __exit acpi_thermal_exit(void) { - acpi_bus_unregister_driver(&acpi_thermal_driver); + platform_driver_unregister(&acpi_thermal_driver); destroy_workqueue(acpi_thermal_pm_queue); } From 6cba60361b8922b140ee460220f291d45448d537 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 10 Dec 2025 15:44:30 +0100 Subject: [PATCH 12/25] ACPI: thermal: Rework system suspend and resume handling In the process of handling system resume, acpi_thermal_resume() attempts to power up active cooling devices to guarantee that they will be operational when the ACPI thermal check queued by it runs. However, the only kind of cooling devices that can be bound to ACPI thermal zones is fans and they are already powered up by the ACPI fan driver resume, which additionally takes "ACPI 4" fans that don't need to be powered up into account. For this reason, remove the part of acpi_thermal_resume() related to fans and in order to ensure that it will run after powering up all fans, rename it to acpi_thermal_complete() and point the .complete() driver callback to it. Analogously, rename acpi_thermal_suspend() to acpi_thermal_prepare() and point the .prepare() driver callback to it. Signed-off-by: Rafael J. Wysocki Acked-by: lihuisong@huawei.com Link: https://patch.msgid.link/3024049.e9J7NaK4W3@rafael.j.wysocki --- drivers/acpi/thermal.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 25c75dad8ec4..e9d3ab18b404 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -911,37 +911,26 @@ static void acpi_thermal_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int acpi_thermal_suspend(struct device *dev) +static int acpi_thermal_prepare(struct device *dev) { /* Make sure the previously queued thermal check work has been done */ flush_workqueue(acpi_thermal_pm_queue); return 0; } -static int acpi_thermal_resume(struct device *dev) +static void acpi_thermal_complete(struct device *dev) { - struct acpi_thermal *tz = dev_get_drvdata(dev); - int i, j; - - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - struct acpi_thermal_trip *acpi_trip = &tz->trips.active[i].trip; - - if (!acpi_thermal_trip_valid(acpi_trip)) - break; - - for (j = 0; j < acpi_trip->devices.count; j++) - acpi_bus_update_power(acpi_trip->devices.handles[j], NULL); - } - - acpi_queue_thermal_check(tz); - - return AE_OK; + acpi_queue_thermal_check(dev_get_drvdata(dev)); } -#else -#define acpi_thermal_suspend NULL -#define acpi_thermal_resume NULL -#endif -static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, acpi_thermal_suspend, acpi_thermal_resume); + +static const struct dev_pm_ops acpi_thermal_pm_ops = { + .prepare = acpi_thermal_prepare, + .complete = acpi_thermal_complete, +}; +#define ACPI_THERMAL_PM &acpi_thermal_pm_ops +#else /* !CONFIG_PM_SLEEP */ +#define ACPI_THERMAL_PM NULL +#endif /* CONFIG_PM_SLEEP */ static const struct acpi_device_id thermal_device_ids[] = { {ACPI_THERMAL_HID, 0}, @@ -955,7 +944,7 @@ static struct platform_driver acpi_thermal_driver = { .driver = { .name = "acpi-thermal", .acpi_match_table = thermal_device_ids, - .pm = &acpi_thermal_pm, + .pm = ACPI_THERMAL_PM, }, }; From 6e35ab507c88c358274439745d5e574a7e05d7a1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 11 Dec 2025 15:19:08 +0100 Subject: [PATCH 13/25] ACPI: HED: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI hardware error device (HED) 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. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/8620378.T7Z3S40VBb@rafael.j.wysocki --- drivers/acpi/hed.c | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/acpi/hed.c b/drivers/acpi/hed.c index 3499f86c411e..4d5e12ed6f3c 100644 --- a/drivers/acpi/hed.c +++ b/drivers/acpi/hed.c @@ -13,6 +13,7 @@ #include #include #include +#include #include static const struct acpi_device_id acpi_hed_ids[] = { @@ -47,8 +48,9 @@ static void acpi_hed_notify(acpi_handle handle, u32 event, void *data) blocking_notifier_call_chain(&acpi_hed_notify_list, 0, NULL); } -static int acpi_hed_add(struct acpi_device *device) +static int acpi_hed_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); int err; /* Only one hardware error device */ @@ -64,26 +66,27 @@ static int acpi_hed_add(struct acpi_device *device) return err; } -static void acpi_hed_remove(struct acpi_device *device) +static void acpi_hed_remove(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_hed_notify); hed_handle = NULL; } -static struct acpi_driver acpi_hed_driver = { - .name = "hardware_error_device", - .class = "hardware_error", - .ids = acpi_hed_ids, - .ops = { - .add = acpi_hed_add, - .remove = acpi_hed_remove, +static struct platform_driver acpi_hed_driver = { + .probe = acpi_hed_probe, + .remove = acpi_hed_remove, + .driver = { + .name = "acpi-hardware-error-device", + .acpi_match_table = acpi_hed_ids, }, }; static int __init acpi_hed_driver_init(void) { - return acpi_bus_register_driver(&acpi_hed_driver); + return platform_driver_register(&acpi_hed_driver); } subsys_initcall(acpi_hed_driver_init); From fe9542b8b53cd72cd6304278052a3d7db3f6c824 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 11 Dec 2025 15:16:37 +0100 Subject: [PATCH 14/25] ACPI: EC: Register a platform device for ECDT EC To facilitate converting the ACPI EC driver into a platform one, modify acpi_bus_register_early_device(), used by acpi_ec_ecdt_start() for creating a struct acpi_device to represent the "early" EC based on the ECDT ACPI table, to carry out the default ACPI enumeration for the given device which will cause a platform device to be registered for it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2397353.ElGaqSPkdT@rafael.j.wysocki --- drivers/acpi/scan.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7e4dbec8f77c..a67d1a2d0a2a 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2754,6 +2754,8 @@ int acpi_bus_register_early_device(int type) if (result) return result; + acpi_default_enumeration(device); + device->flags.match_driver = true; return device_attach(&device->dev); } From db65a06d10b3bf7153ba80cde6e447d440412b9f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 11 Dec 2025 15:17:23 +0100 Subject: [PATCH 15/25] ACPI: EC: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI embedded controller (EC) driver to a platform one. After this conversion, acpi_bus_register_early_device() does not need to attempt to bind an ACPI driver to the struct acpi_device created by it, so update it accordingly. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Signed-off-by: Rafael J. Wysocki [ rjw: Removed excess semicolon ] Link: https://patch.msgid.link/1946304.tdWV9SEqCh@rafael.j.wysocki Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 54 ++++++++++++++++++--------------------------- drivers/acpi/scan.c | 4 +--- 2 files changed, 22 insertions(+), 36 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 59b3d50ff01e..bf4d86571b0d 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1674,8 +1675,9 @@ static int acpi_ec_setup(struct acpi_ec *ec, struct acpi_device *device, bool ca return ret; } -static int acpi_ec_add(struct acpi_device *device) +static int acpi_ec_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_ec *ec; int ret; @@ -1730,6 +1732,8 @@ static int acpi_ec_add(struct acpi_device *device) acpi_handle_info(ec->handle, "EC: Used to handle transactions and events\n"); + platform_set_drvdata(pdev, ec); + /* This is needed for the SMBUS HC driver to work. */ device->driver_data = ec; ret = !!request_region(ec->data_addr, 1, "EC data"); @@ -1750,14 +1754,11 @@ err: return ret; } -static void acpi_ec_remove(struct acpi_device *device) +static void acpi_ec_remove(struct platform_device *pdev) { - struct acpi_ec *ec; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_ec *ec = platform_get_drvdata(pdev); - if (!device) - return; - - ec = acpi_driver_data(device); release_region(ec->data_addr, 1); release_region(ec->command_addr, 1); device->driver_data = NULL; @@ -2095,8 +2096,7 @@ out: #ifdef CONFIG_PM_SLEEP static int acpi_ec_suspend(struct device *dev) { - struct acpi_ec *ec = - acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); if (!pm_suspend_no_platform() && ec_freeze_events) acpi_ec_disable_event(ec); @@ -2105,7 +2105,7 @@ static int acpi_ec_suspend(struct device *dev) static int acpi_ec_suspend_noirq(struct device *dev) { - struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); /* * The SCI handler doesn't run at this point, so the GPE can be @@ -2122,7 +2122,7 @@ static int acpi_ec_suspend_noirq(struct device *dev) static int acpi_ec_resume_noirq(struct device *dev) { - struct acpi_ec *ec = acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); acpi_ec_leave_noirq(ec); @@ -2135,8 +2135,7 @@ static int acpi_ec_resume_noirq(struct device *dev) static int acpi_ec_resume(struct device *dev) { - struct acpi_ec *ec = - acpi_driver_data(to_acpi_device(dev)); + struct acpi_ec *ec = dev_get_drvdata(dev); acpi_ec_enable_event(ec); return 0; @@ -2265,15 +2264,14 @@ module_param_call(ec_event_clearing, param_set_event_clearing, param_get_event_c NULL, 0644); MODULE_PARM_DESC(ec_event_clearing, "Assumed SCI_EVT clearing timing"); -static struct acpi_driver acpi_ec_driver = { - .name = "ec", - .class = ACPI_EC_CLASS, - .ids = ec_device_ids, - .ops = { - .add = acpi_ec_add, - .remove = acpi_ec_remove, - }, - .drv.pm = &acpi_ec_pm, +static struct platform_driver acpi_ec_driver = { + .probe = acpi_ec_probe, + .remove = acpi_ec_remove, + .driver = { + .name = "acpi-ec", + .acpi_match_table = ec_device_ids, + .pm = &acpi_ec_pm, + }, }; static void acpi_ec_destroy_workqueues(void) @@ -2378,17 +2376,7 @@ void __init acpi_ec_init(void) } /* Driver must be registered after acpi_ec_init_workqueues(). */ - acpi_bus_register_driver(&acpi_ec_driver); + platform_driver_register(&acpi_ec_driver); acpi_ec_ecdt_start(); } - -/* EC driver currently not unloadable */ -#if 0 -static void __exit acpi_ec_exit(void) -{ - - acpi_bus_unregister_driver(&acpi_ec_driver); - acpi_ec_destroy_workqueues(); -} -#endif /* 0 */ diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a67d1a2d0a2a..d0479ac40856 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2755,9 +2755,7 @@ int acpi_bus_register_early_device(int type) return result; acpi_default_enumeration(device); - - device->flags.match_driver = true; - return device_attach(&device->dev); + return 0; } EXPORT_SYMBOL_GPL(acpi_bus_register_early_device); From 6d2590533cdd057cacb3dc5a022fbe7a631bb99a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 11 Dec 2025 15:17:58 +0100 Subject: [PATCH 16/25] ACPI: SMBUS HC: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI SMBUS HC driver to a platform one. After this conversion, acpi_ec_probe() does not need to populate the driver_data pointer of the EC platform device's ACPI companion any more, so update it accordingly. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/13909645.uLZWGnKmhe@rafael.j.wysocki --- drivers/acpi/ec.c | 2 -- drivers/acpi/sbshc.c | 43 +++++++++++++++++++++---------------------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index bf4d86571b0d..197970339edc 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1733,8 +1733,6 @@ static int acpi_ec_probe(struct platform_device *pdev) "EC: Used to handle transactions and events\n"); platform_set_drvdata(pdev, ec); - /* This is needed for the SMBUS HC driver to work. */ - device->driver_data = ec; ret = !!request_region(ec->data_addr, 1, "EC data"); WARN(!ret, "Could not request EC data io port 0x%lx", ec->data_addr); diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index 1a2bf520be23..b2892037e5d2 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -13,6 +13,8 @@ #include #include #include +#include + #include "sbshc.h" #include "internal.h" @@ -30,8 +32,8 @@ struct acpi_smb_hc { bool done; }; -static int acpi_smbus_hc_add(struct acpi_device *device); -static void acpi_smbus_hc_remove(struct acpi_device *device); +static int acpi_smbus_hc_probe(struct platform_device *pdev); +static void acpi_smbus_hc_remove(struct platform_device *pdev); static const struct acpi_device_id sbs_device_ids[] = { {"ACPI0001", 0}, @@ -41,14 +43,13 @@ static const struct acpi_device_id sbs_device_ids[] = { MODULE_DEVICE_TABLE(acpi, sbs_device_ids); -static struct acpi_driver acpi_smb_hc_driver = { - .name = "smbus_hc", - .class = ACPI_SMB_HC_CLASS, - .ids = sbs_device_ids, - .ops = { - .add = acpi_smbus_hc_add, - .remove = acpi_smbus_hc_remove, - }, +static struct platform_driver acpi_smb_hc_driver = { + .probe = acpi_smbus_hc_probe, + .remove = acpi_smbus_hc_remove, + .driver = { + .name = "acpi-smbus-hc", + .acpi_match_table = sbs_device_ids, + }, }; union acpi_smb_status { @@ -237,15 +238,13 @@ static int smbus_alarm(void *context) return 0; } -static int acpi_smbus_hc_add(struct acpi_device *device) +static int acpi_smbus_hc_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); int status; unsigned long long val; struct acpi_smb_hc *hc; - if (!device) - return -EINVAL; - status = acpi_evaluate_integer(device->handle, "_EC", NULL, &val); if (ACPI_FAILURE(status)) { pr_err("error obtaining _EC.\n"); @@ -261,9 +260,12 @@ static int acpi_smbus_hc_add(struct acpi_device *device) mutex_init(&hc->lock); init_waitqueue_head(&hc->wait); - hc->ec = acpi_driver_data(acpi_dev_parent(device)); + platform_set_drvdata(pdev, hc); + + hc->ec = dev_get_drvdata(pdev->dev.parent); hc->offset = (val >> 8) & 0xff; hc->query_bit = val & 0xff; + /* This is needed for the SBS driver to work. */ device->driver_data = hc; acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); @@ -273,21 +275,18 @@ static int acpi_smbus_hc_add(struct acpi_device *device) return 0; } -static void acpi_smbus_hc_remove(struct acpi_device *device) +static void acpi_smbus_hc_remove(struct platform_device *pdev) { - struct acpi_smb_hc *hc; + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); + struct acpi_smb_hc *hc = platform_get_drvdata(pdev); - if (!device) - return; - - hc = acpi_driver_data(device); acpi_ec_remove_query_handler(hc->ec, hc->query_bit); acpi_os_wait_events_complete(); kfree(hc); device->driver_data = NULL; } -module_acpi_driver(acpi_smb_hc_driver); +module_platform_driver(acpi_smb_hc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexey Starikovskiy"); From 9460eaae2ee42e7f2267ea12b26aa59c391996a7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 11 Dec 2025 15:18:33 +0100 Subject: [PATCH 17/25] ACPI: SBS: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI smart battery subsystem (SBS) driver to a platform one. After this conversion, acpi_smbus_hc_probe() does not need to populate the driver_data pointer of the SMBUS HC platform device's ACPI companion any more, so update it accordingly. While this is not expected to alter functionality, it changes sysfs layout and so it will be visible to user space. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3390477.aeNJFYEL58@rafael.j.wysocki --- drivers/acpi/sbs.c | 48 +++++++++++++++++++------------------------- drivers/acpi/sbshc.c | 2 -- 2 files changed, 21 insertions(+), 29 deletions(-) diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index d3edc3bcbf01..85160e475c97 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -95,7 +96,7 @@ struct acpi_sbs { #define to_acpi_sbs(x) power_supply_get_drvdata(x) -static void acpi_sbs_remove(struct acpi_device *device); +static void acpi_sbs_remove(struct platform_device *pdev); static int acpi_battery_get_state(struct acpi_battery *battery); static inline int battery_scale(int log) @@ -628,8 +629,9 @@ static void acpi_sbs_callback(void *context) } } -static int acpi_sbs_add(struct acpi_device *device) +static int acpi_sbs_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_sbs *sbs; int result = 0; int id; @@ -642,11 +644,12 @@ static int acpi_sbs_add(struct acpi_device *device) mutex_init(&sbs->lock); - sbs->hc = acpi_driver_data(acpi_dev_parent(device)); + platform_set_drvdata(pdev, sbs); + + sbs->hc = dev_get_drvdata(pdev->dev.parent); sbs->device = device; strscpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); strscpy(acpi_device_class(device), ACPI_SBS_CLASS); - device->driver_data = sbs; result = acpi_charger_add(sbs); if (result && result != -ENODEV) @@ -670,20 +673,15 @@ static int acpi_sbs_add(struct acpi_device *device) acpi_smbus_register_callback(sbs->hc, acpi_sbs_callback, sbs); end: if (result) - acpi_sbs_remove(device); + acpi_sbs_remove(pdev); return result; } -static void acpi_sbs_remove(struct acpi_device *device) +static void acpi_sbs_remove(struct platform_device *pdev) { - struct acpi_sbs *sbs; + struct acpi_sbs *sbs = platform_get_drvdata(pdev); int id; - if (!device) - return; - sbs = acpi_driver_data(device); - if (!sbs) - return; mutex_lock(&sbs->lock); acpi_smbus_unregister_callback(sbs->hc); for (id = 0; id < MAX_SBS_BAT; ++id) @@ -697,11 +695,7 @@ static void acpi_sbs_remove(struct acpi_device *device) #ifdef CONFIG_PM_SLEEP static int acpi_sbs_resume(struct device *dev) { - struct acpi_sbs *sbs; - if (!dev) - return -EINVAL; - sbs = to_acpi_device(dev)->driver_data; - acpi_sbs_callback(sbs); + acpi_sbs_callback(dev_get_drvdata(dev)); return 0; } #else @@ -710,14 +704,14 @@ static int acpi_sbs_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume); -static struct acpi_driver acpi_sbs_driver = { - .name = "sbs", - .class = ACPI_SBS_CLASS, - .ids = sbs_device_ids, - .ops = { - .add = acpi_sbs_add, - .remove = acpi_sbs_remove, - }, - .drv.pm = &acpi_sbs_pm, +static struct platform_driver acpi_sbs_driver = { + .probe = acpi_sbs_probe, + .remove = acpi_sbs_remove, + .driver = { + .name = "acpi-sbs", + .acpi_match_table = sbs_device_ids, + .pm = &acpi_sbs_pm, + }, }; -module_acpi_driver(acpi_sbs_driver); + +module_platform_driver(acpi_sbs_driver); diff --git a/drivers/acpi/sbshc.c b/drivers/acpi/sbshc.c index b2892037e5d2..7fc8ae7396d3 100644 --- a/drivers/acpi/sbshc.c +++ b/drivers/acpi/sbshc.c @@ -265,8 +265,6 @@ static int acpi_smbus_hc_probe(struct platform_device *pdev) hc->ec = dev_get_drvdata(pdev->dev.parent); hc->offset = (val >> 8) & 0xff; hc->query_bit = val & 0xff; - /* This is needed for the SBS driver to work. */ - device->driver_data = hc; acpi_ec_add_query_handler(hc->ec, hc->query_bit, NULL, smbus_alarm, hc); dev_info(&device->dev, "SBS HC: offset = 0x%0x, query_bit = 0x%0x\n", From 336aae5c4e1a473018136524b18a74877310c0a3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Jan 2026 12:50:57 +0100 Subject: [PATCH 18/25] ACPI: scan: Register platform devices for backlight device objects ACPI device objects associated with backlight interfaces are special because they are ACPI companions of PCI devices (GPUs), but the interfaces exposed by them resemble platform device one. Currently, the ACPI video driver binds to them with the help of a special "synthetic" device ID regardless of the pairing with the PCI devices, but since it is generally better to use platform drivers for handling such interfaces, the plan is to convert that drviver into a platform one. However, for this purpose, platform devices corresponding to the ACPI backlight device objects need to be registered, so update acpi_bus_attach() to apply the default ACPI enumeration to them and modify acpi_create_platform_device() to avoid bailing out early if a "physical" device is already attached to a backlight ACPI device object. In addition, update acpi_companion_match() to return a valid struct acpi_device pointer if the ACPI companion of the given device is a backlight ACPI device object, which will facilitate driver matching for platform devices corresponding to those objects. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Armin Wolf Link: https://patch.msgid.link/5081593.31r3eYUQgx@rafael.j.wysocki --- drivers/acpi/acpi_platform.c | 4 ++-- drivers/acpi/bus.c | 3 +++ drivers/acpi/scan.c | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 52c8d602f3a5..377ca8324121 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -118,7 +118,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, int count = 0; /* If the ACPI node already has a physical device attached, skip it. */ - if (adev->physical_node_count) + if (adev->physical_node_count && !adev->pnp.type.backlight) return NULL; match = acpi_match_acpi_device(forbidden_id_list, adev); @@ -135,7 +135,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, } } - if (adev->device_type == ACPI_BUS_TYPE_DEVICE) { + if (adev->device_type == ACPI_BUS_TYPE_DEVICE && !adev->pnp.type.backlight) { struct list_head resource_list = LIST_HEAD_INIT(resource_list); count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index a984ccd4a2a0..574e190f6bf9 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -761,6 +761,9 @@ const struct acpi_device *acpi_companion_match(const struct device *dev) if (list_empty(&adev->pnp.ids)) return NULL; + if (adev->pnp.type.backlight) + return adev; + return acpi_primary_dev_companion(adev, dev); } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d0479ac40856..360d82378e56 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2350,7 +2350,8 @@ static int acpi_bus_attach(struct acpi_device *device, void *first_pass) if (ret < 0) return 0; - if (device->pnp.type.platform_id || device->flags.enumeration_by_parent) + if (device->pnp.type.platform_id || device->pnp.type.backlight || + device->flags.enumeration_by_parent) acpi_default_enumeration(device); else acpi_device_set_enumerated(device); From d91a624a69631f852efc2ec7d910d5439277e47f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Jan 2026 12:52:15 +0100 Subject: [PATCH 19/25] ACPI: video: Adjust event notification routine Adjust acpi_video_bus_notify() to cast its "data" argument to a struct acpi_video_bus pointer instead of a struct acpi_device one, which allows the use of acpi_driver_data() to be limited and will facilitate subsequent changes. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Armin Wolf Link: https://patch.msgid.link/2409089.ElGaqSPkdT@rafael.j.wysocki --- drivers/acpi/acpi_video.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index be8e7e18abca..54e09e7f8e77 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1540,14 +1540,11 @@ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video) static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *device = data; - struct acpi_video_bus *video = acpi_driver_data(device); + struct acpi_video_bus *video = data; + struct acpi_device *device = video->device; struct input_dev *input; int keycode = 0; - if (!video || !video->input) - return; - input = video->input; switch (event) { @@ -2076,7 +2073,7 @@ static int acpi_video_bus_add(struct acpi_device *device) goto err_del; error = acpi_dev_install_notify_handler(device, ACPI_DEVICE_NOTIFY, - acpi_video_bus_notify, device); + acpi_video_bus_notify, video); if (error) goto err_remove; From 02c057ddefef592a882c5815f22630901d9ab344 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 2 Jan 2026 12:53:53 +0100 Subject: [PATCH 20/25] ACPI: video: Convert the driver to a platform one While binding drivers directly to struct acpi_device objects allows basic functionality to be provided, at least in the majority of cases, there are some problems with it, related to general consistency, sysfs layout, power management operation ordering, and code cleanliness. Overall, it is better to bind drivers to platform devices than to their ACPI companions, so convert the ACPI video 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. Signed-off-by: Rafael J. Wysocki Reviewed-by: Armin Wolf Link: https://patch.msgid.link/1957556.tdWV9SEqCh@rafael.j.wysocki --- drivers/acpi/acpi_video.c | 47 +++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 54e09e7f8e77..944aba605c1a 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -76,8 +77,8 @@ static int register_count; static DEFINE_MUTEX(register_count_mutex); static DEFINE_MUTEX(video_list_lock); static LIST_HEAD(video_bus_head); -static int acpi_video_bus_add(struct acpi_device *device); -static void acpi_video_bus_remove(struct acpi_device *device); +static int acpi_video_bus_probe(struct platform_device *pdev); +static void acpi_video_bus_remove(struct platform_device *pdev); static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data); /* @@ -98,14 +99,13 @@ static const struct acpi_device_id video_device_ids[] = { }; MODULE_DEVICE_TABLE(acpi, video_device_ids); -static struct acpi_driver acpi_video_bus = { - .name = "video", - .class = ACPI_VIDEO_CLASS, - .ids = video_device_ids, - .ops = { - .add = acpi_video_bus_add, - .remove = acpi_video_bus_remove, - }, +static struct platform_driver acpi_video_bus = { + .probe = acpi_video_bus_probe, + .remove = acpi_video_bus_remove, + .driver = { + .name = "acpi-video", + .acpi_match_table = video_device_ids, + }, }; struct acpi_video_bus_flags { @@ -1888,7 +1888,8 @@ static void acpi_video_dev_add_notify_handler(struct acpi_video_device *device) device->flags.notify = 1; } -static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) +static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video, + struct platform_device *pdev) { struct input_dev *input; struct acpi_video_device *dev; @@ -1911,7 +1912,7 @@ static int acpi_video_bus_add_notify_handler(struct acpi_video_bus *video) input->phys = video->phys; input->id.bustype = BUS_HOST; input->id.product = 0x06; - input->dev.parent = &video->device->dev; + input->dev.parent = &pdev->dev; input->evbit[0] = BIT(EV_KEY); set_bit(KEY_SWITCHVIDEOMODE, input->keybit); set_bit(KEY_VIDEO_NEXT, input->keybit); @@ -1983,8 +1984,9 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video) static int instance; -static int acpi_video_bus_add(struct acpi_device *device) +static int acpi_video_bus_probe(struct platform_device *pdev) { + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); struct acpi_video_bus *video; bool auto_detect; int error; @@ -2021,6 +2023,8 @@ static int acpi_video_bus_add(struct acpi_device *device) instance++; } + platform_set_drvdata(pdev, video); + video->device = device; strscpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); strscpy(acpi_device_class(device), ACPI_VIDEO_CLASS); @@ -2068,7 +2072,7 @@ static int acpi_video_bus_add(struct acpi_device *device) !auto_detect) acpi_video_bus_register_backlight(video); - error = acpi_video_bus_add_notify_handler(video); + error = acpi_video_bus_add_notify_handler(video, pdev); if (error) goto err_del; @@ -2096,15 +2100,10 @@ err_free_video: return error; } -static void acpi_video_bus_remove(struct acpi_device *device) +static void acpi_video_bus_remove(struct platform_device *pdev) { - struct acpi_video_bus *video = NULL; - - - if (!device || !acpi_driver_data(device)) - return; - - video = acpi_driver_data(device); + struct acpi_video_bus *video = platform_get_drvdata(pdev); + struct acpi_device *device = ACPI_COMPANION(&pdev->dev); acpi_dev_remove_notify_handler(device, ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); @@ -2167,7 +2166,7 @@ int acpi_video_register(void) dmi_check_system(video_dmi_table); - ret = acpi_bus_register_driver(&acpi_video_bus); + ret = platform_driver_register(&acpi_video_bus); if (ret) goto leave; @@ -2187,7 +2186,7 @@ void acpi_video_unregister(void) { mutex_lock(®ister_count_mutex); if (register_count) { - acpi_bus_unregister_driver(&acpi_video_bus); + platform_driver_unregister(&acpi_video_bus); register_count = 0; may_report_brightness_keys = false; } From b8c8a8ea18ad679e29b6970ae0b5707bc061b5e7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 6 Jan 2026 13:27:14 +0100 Subject: [PATCH 21/25] ACPI: Documentation: driver-api: Disapprove of using ACPI drivers Sadly, there is quite a bit of technical debt related to the kernel's ACPI support subsystem and one of the most significant pieces of it is the existence and use of ACPI drivers represented by struct acpi_driver objects. Those drivers are bound directly to struct acpi_device objects, also referred to as "ACPI device nodes", representing device objects in the ACPI namespace defined as: A hierarchical tree structure in OS-controlled memory that contains named objects. These objects may be data objects, control method objects, bus/device package objects, and so on. according to the ACPI specification [1]. The above definition implies, although rather indirectly, that the objects in question don't really represent hardware. They are just "device package objects" containing some information on the devices present in the given platform that is known to the platform firmware. Although the platform firmware can be the only source of information on some devices, the information provided by it alone may be insufficient for device enumeration in general. If that is the case, binding a driver directly to a given ACPI device node clearly doesn't make sense. If the device in question is enumerated through a hardware interface, it will be represented by a device object matching that interface, like a struct pci_dev, and the ACPI device node corresponding to it will be treated as its "ACPI companions" whose role is to amend the "native" enumeratiom mechanism. For the sake of consistency and confusion avoidance, it is better to treat ACPI device nodes in general as ACPI companions of other device objects representing hardware. In some cases though it appeared easier to take a shortcut and use an ACPI driver binding directly to an ACPI device node. Moreover, there were corner cases in which that was the only choice, but they all have been addressed now. In all cases in which an ACPI driver might be used, the ACPI device node it might bind to is an ACPI companion of another device object representing a piece of hardware. It is thus better to use a driver binding to the latter than to use an ACPI driver and leave the other device object alone, not just because doing so is more consistent and less confusing, but also because using ACPI drivers may lead to potential functional deficiencies, like possible ordering issues related to power management. Unfortunately, there are quite a few ACPI drivers in use and, as a rule, they bind to ACPI device nodes that are ACPI companions of platform devices, so in fact they play the role of platform drivers although in a kind of convoluted way. An effort has been under way to replace them with platform drivers, which is relatively straightforward in the vast majority of cases, but it has not been pursued very aggressively so far, mostly due to the existence of the corner cases mentioned above. However, since those corner cases are gone now, it makes sense to spend more time on driver conversions with the ultimate goal to get rid of struct acpi_driver and the related code from the kernel. To that end, add a document explaining why using ACPI drivers is not a good idea, so it need not be explained from scratch on every attempt to convert an ACPI driver to a platform one. Link: https://uefi.org/specs/ACPI/6.6/02_Definition_of_Terms.html#term-ACPI-Namespace [1] Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko Reviewed-by: Armin Wolf Reviewed-by: Randy Dunlap Reviewed-by: Danilo Krummrich Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/2396510.ElGaqSPkdT@rafael.j.wysocki --- .../driver-api/acpi/acpi-drivers.rst | 80 +++++++++++++++++++ Documentation/driver-api/acpi/index.rst | 1 + 2 files changed, 81 insertions(+) create mode 100644 Documentation/driver-api/acpi/acpi-drivers.rst diff --git a/Documentation/driver-api/acpi/acpi-drivers.rst b/Documentation/driver-api/acpi/acpi-drivers.rst new file mode 100644 index 000000000000..b1fbbddb8b4f --- /dev/null +++ b/Documentation/driver-api/acpi/acpi-drivers.rst @@ -0,0 +1,80 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: + +========================================= +Why using ACPI drivers is not a good idea +========================================= + +:Copyright: |copy| 2026, Intel Corporation + +:Author: Rafael J. Wysocki + +Even though binding drivers directly to struct acpi_device objects, also +referred to as "ACPI device nodes", allows basic functionality to be provided +at least in some cases, there are problems with it, related to general +consistency, sysfs layout, power management operation ordering, and code +cleanliness. + +First of all, ACPI device nodes represent firmware entities rather than +hardware and in many cases they provide auxiliary information on devices +enumerated independently (like PCI devices or CPUs). It is therefore generally +questionable to assign resources to them because the entities represented by +them do not decode addresses in the memory or I/O address spaces and do not +generate interrupts or similar (all of that is done by hardware). + +Second, as a general rule, a struct acpi_device can only be a parent of another +struct acpi_device. If that is not the case, the location of the child device +in the device hierarchy is at least confusing and it may not be straightforward +to identify the piece of hardware providing functionality represented by it. +However, binding a driver directly to an ACPI device node may cause that to +happen if the given driver registers input devices or wakeup sources under it, +for example. + +Next, using system suspend and resume callbacks directly on ACPI device nodes +is also questionable because it may cause ordering problems to appear. Namely, +ACPI device nodes are registered before enumerating hardware corresponding to +them and they land on the PM list in front of the majority of other device +objects. Consequently, the execution ordering of their PM callbacks may be +different from what is generally expected. Also, in general, dependencies +returned by _DEP objects do not affect ACPI device nodes themselves, but the +"physical" devices associated with them, which potentially is one more source +of inconsistency related to treating ACPI device nodes as "real" device +representation. + +All of the above means that binding drivers to ACPI device nodes should +generally be avoided and so struct acpi_driver objects should not be used. + +Moreover, a device ID is necessary to bind a driver directly to an ACPI device +node, but device IDs are not generally associated with all of them. Some of +them contain alternative information allowing the corresponding pieces of +hardware to be identified, for example represeted by an _ADR object return +value, and device IDs are not used in those cases. In consequence, confusingly +enough, binding an ACPI driver to an ACPI device node may even be impossible. + +When that happens, the piece of hardware corresponding to the given ACPI device +node is represented by another device object, like a struct pci_dev, and the +ACPI device node is the "ACPI companion" of that device, accessible through its +fwnode pointer used by the ACPI_COMPANION() macro. The ACPI companion holds +additional information on the device configuration and possibly some "recipes" +on device manipulation in the form of AML (ACPI Machine Language) bytecode +provided by the platform firmware. Thus the role of the ACPI device node is +similar to the role of a struct device_node on a system where Device Tree is +used for platform description. + +For consistency, this approach has been extended to the cases in which ACPI +device IDs are used. Namely, in those cases, an additional device object is +created to represent the piece of hardware corresponding to a given ACPI device +node. By default, it is a platform device, but it may also be a PNP device, a +CPU device, or another type of device, depending on what the given piece of +hardware actually is. There are even cases in which multiple devices are +"backed" or "accompanied" by one ACPI device node (e.g. ACPI device nodes +corresponding to GPUs that may provide firmware interfaces for backlight +brightness control in addition to GPU configuration information). + +This means that it really should never be necessary to bind a driver directly to +an ACPI device node because there is a "proper" device object representing the +corresponding piece of hardware that can be bound to by a "proper" driver using +the given ACPI device node as the device's ACPI companion. Thus, in principle, +there is no reason to use ACPI drivers and if they all were replaced with other +driver types (for example, platform drivers), some code could be dropped and +some complexity would go away. diff --git a/Documentation/driver-api/acpi/index.rst b/Documentation/driver-api/acpi/index.rst index ace0008e54c2..2b10d83f9994 100644 --- a/Documentation/driver-api/acpi/index.rst +++ b/Documentation/driver-api/acpi/index.rst @@ -7,3 +7,4 @@ ACPI Support linuxized-acpica scan_handlers + acpi-drivers From 88fad6ce090b395af4c654594a54589a386bf24b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 10 Jan 2026 12:57:00 +0100 Subject: [PATCH 22/25] ACPI: PM: Let acpi_dev_pm_attach() skip devices without ACPI PM It is pointless to attach the generic ACPI PM domain to devices whose ACPI companions don't support ACPI power management and don't have a wakeup GPE, so update acpi_dev_pm_attach() to skip such devices. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5050298.GXAFRqVoOG@rafael.j.wysocki --- drivers/acpi/device_pm.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 4e0583274b8f..c7d64ab1fe5d 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -1457,6 +1457,15 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on) if (!adev || !acpi_match_device_ids(adev, special_pm_ids)) return 0; + /* + * Skip devices whose ACPI companions don't support power management and + * don't have a wakeup GPE. + */ + if (!acpi_device_power_manageable(adev) && !acpi_device_can_wakeup(adev)) { + dev_dbg(dev, "No ACPI power management or wakeup GPE\n"); + return 0; + } + /* * Only attach the power domain to the first device if the * companion is shared by multiple. This is to prevent doing power From 57c31e6d620f132dcf610b2c52b4cdbd203c6f4a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 10 Jan 2026 12:58:38 +0100 Subject: [PATCH 23/25] ACPI: scan: Use acpi_setup_gpe_for_wake() for buttons After starting to use platform devices for representing buttons enumerated via ACPI, acpi_mark_gpe_for_wake() is insufficient for preparing their GPEs to wake up the system from sleep because it does not change the "dispatch type" of the given GPE to ACPI_GPE_DISPATCH_NOTIFY. Subsequently, this causes acpi_enable_gpe() in __acpi_device_wakeup_enable() to fail and system suspend transitions to be aborted. Address this by updating acpi_wakeup_gpe_init() to use acpi_setup_gpe_for_wake() for buttons like for any other devices. This allows acpi_setup_gpe_for_wake() to be simplified further because buttons are not a special case in it any more, so do that as well. Fixes: 52d864019636 ("ACPI: button: Convert the driver to a platform one") Signed-off-by: Rafael J. Wysocki Tested-by: Xi Pardee Link: https://patch.msgid.link/2259694.irdbgypaU6@rafael.j.wysocki --- drivers/acpi/scan.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 360d82378e56..b4301c797701 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -999,15 +999,11 @@ static int acpi_bus_extract_wakeup_device_power_package(struct acpi_device *dev) return err; } -/* Do not use a button for S5 wakeup */ -#define ACPI_AVOID_WAKE_FROM_S5 BIT(0) - static bool acpi_wakeup_gpe_init(struct acpi_device *device) { static const struct acpi_device_id button_device_ids[] = { - {"PNP0C0C", 0}, /* Power button */ - {"PNP0C0D", ACPI_AVOID_WAKE_FROM_S5}, /* Lid */ - {"PNP0C0E", ACPI_AVOID_WAKE_FROM_S5}, /* Sleep button */ + {"PNP0C0D", 0}, /* Lid */ + {"PNP0C0E", 0}, /* Sleep button */ {"", 0}, }; struct acpi_device_wakeup *wakeup = &device->wakeup; @@ -1016,15 +1012,9 @@ static bool acpi_wakeup_gpe_init(struct acpi_device *device) wakeup->flags.notifier_present = 0; - /* Power button, Lid switch always enable wakeup */ match = acpi_match_acpi_device(button_device_ids, device); - if (match) { - if ((match->driver_data & ACPI_AVOID_WAKE_FROM_S5) && - wakeup->sleep_state == ACPI_STATE_S5) - wakeup->sleep_state = ACPI_STATE_S4; - acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); - return true; - } + if (match && wakeup->sleep_state == ACPI_STATE_S5) + wakeup->sleep_state = ACPI_STATE_S4; status = acpi_setup_gpe_for_wake(device->handle, wakeup->gpe_device, wakeup->gpe_number); From b862e66a9c2e3e1b55d2192b0dc8f80535e11eee Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 10 Jan 2026 12:50:54 +0100 Subject: [PATCH 24/25] ACPI: scan: Clean up after recent changes Use LIST_HEAD() for initializing an on-stack list head in two places and remove an empty code line added by mistake. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/12825056.O9o76ZdvQC@rafael.j.wysocki --- drivers/acpi/acpi_platform.c | 2 +- drivers/acpi/scan.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index 377ca8324121..0ec1afc744f5 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -136,7 +136,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, } if (adev->device_type == ACPI_BUS_TYPE_DEVICE && !adev->pnp.type.backlight) { - struct list_head resource_list = LIST_HEAD_INIT(resource_list); + LIST_HEAD(resource_list); count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); if (count < 0) diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index b4301c797701..582ccf4596ff 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -2594,8 +2594,8 @@ static void acpi_scan_postponed(void) static void acpi_scan_claim_resources(struct acpi_device *adev) { - struct list_head resource_list = LIST_HEAD_INIT(resource_list); struct resource_entry *rentry; + LIST_HEAD(resource_list); unsigned int count = 0; const char *regionid; @@ -2652,7 +2652,6 @@ exit: acpi_dev_free_resource_list(&resource_list); } - static int __init acpi_reserve_motherboard_resources(void) { struct acpi_scan_system_dev *sd, *tmp; From 5315c0ddbefe415abba4c89568ecfd27f79c5aba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Jan 2026 09:46:36 +0100 Subject: [PATCH 25/25] ACPI: video: simplify code with acpi_get_local_u64_address() Now we have a helper so there's no need to open-code. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260121084654.2227037-1-andriy.shevchenko@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpi_video.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_video.c b/drivers/acpi/acpi_video.c index 944aba605c1a..69469757b965 100644 --- a/drivers/acpi/acpi_video.c +++ b/drivers/acpi/acpi_video.c @@ -1134,13 +1134,11 @@ static int acpi_video_bus_get_one_device(struct acpi_device *device, void *arg) struct acpi_video_bus *video = arg; struct acpi_video_device_attrib *attribute; struct acpi_video_device *data; - unsigned long long device_id; - acpi_status status; int device_type; + u64 device_id; - status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); /* Skip devices without _ADR instead of failing. */ - if (ACPI_FAILURE(status)) + if (acpi_get_local_u64_address(device->handle, &device_id)) goto exit; data = kzalloc(sizeof(struct acpi_video_device), GFP_KERNEL);