From ba0b236736dde4059bdcb8e99beaa50d6e5b6e7e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:28:15 +0100 Subject: [PATCH 01/10] ACPI: x86: cmos_rtc: Clean up address space handler driver Make multiple changes that do not alter functionality to the CMOS RTC ACPI address space handler driver, including the following: - Drop the unused .detach() callback from cmos_rtc_handler. - Rename acpi_cmos_rtc_attach_handler() to acpi_cmos_rtc_attach(). - Rearrange acpi_cmos_rtc_space_handler() to reduce the number of redundant checks and make white space follow the coding style. - Adjust an error message in acpi_install_cmos_rtc_space_handler() and make the white space follow the coding style. - Rearrange acpi_remove_cmos_rtc_space_handler() and adjust an error message in it. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/5094429.31r3eYUQgx@rafael.j.wysocki --- drivers/acpi/x86/cmos_rtc.c | 63 +++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c index 51643ff6fe5f..977234da9fc1 100644 --- a/drivers/acpi/x86/cmos_rtc.c +++ b/drivers/acpi/x86/cmos_rtc.c @@ -24,31 +24,35 @@ static const struct acpi_device_id acpi_cmos_rtc_ids[] = { {} }; -static acpi_status -acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, - u32 bits, u64 *value64, - void *handler_context, void *region_context) +static acpi_status acpi_cmos_rtc_space_handler(u32 function, + acpi_physical_address address, + u32 bits, u64 *value64, + void *handler_context, + void *region_context) { - int i; + unsigned int i, bytes = DIV_ROUND_UP(bits, 8); u8 *value = (u8 *)value64; if (address > 0xff || !value64) return AE_BAD_PARAMETER; - if (function != ACPI_WRITE && function != ACPI_READ) - return AE_BAD_PARAMETER; + guard(spinlock_irq)(&rtc_lock); - spin_lock_irq(&rtc_lock); - - for (i = 0; i < DIV_ROUND_UP(bits, 8); ++i, ++address, ++value) - if (function == ACPI_READ) - *value = CMOS_READ(address); - else + if (function == ACPI_WRITE) { + for (i = 0; i < bytes; i++, address++, value++) CMOS_WRITE(*value, address); - spin_unlock_irq(&rtc_lock); + return AE_OK; + } - return AE_OK; + if (function == ACPI_READ) { + for (i = 0; i < bytes; i++, address++, value++) + *value = CMOS_READ(address); + + return AE_OK; + } + + return AE_BAD_PARAMETER; } int acpi_install_cmos_rtc_space_handler(acpi_handle handle) @@ -56,11 +60,11 @@ int acpi_install_cmos_rtc_space_handler(acpi_handle handle) acpi_status status; status = acpi_install_address_space_handler(handle, - ACPI_ADR_SPACE_CMOS, - &acpi_cmos_rtc_space_handler, - NULL, NULL); + ACPI_ADR_SPACE_CMOS, + acpi_cmos_rtc_space_handler, + NULL, NULL); if (ACPI_FAILURE(status)) { - pr_err("Error installing CMOS-RTC region handler\n"); + pr_err("Failed to install CMOS-RTC address space handler\n"); return -ENODEV; } @@ -70,26 +74,25 @@ EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler); void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) { - if (ACPI_FAILURE(acpi_remove_address_space_handler(handle, - ACPI_ADR_SPACE_CMOS, &acpi_cmos_rtc_space_handler))) - pr_err("Error removing CMOS-RTC region handler\n"); + acpi_status status; + + status = acpi_remove_address_space_handler(handle, + ACPI_ADR_SPACE_CMOS, + acpi_cmos_rtc_space_handler); + if (ACPI_FAILURE(status)) + pr_err("Failed to remove CMOS-RTC address space handler\n"); } EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler); -static int acpi_cmos_rtc_attach_handler(struct acpi_device *adev, const struct acpi_device_id *id) +static int acpi_cmos_rtc_attach(struct acpi_device *adev, + const struct acpi_device_id *id) { return acpi_install_cmos_rtc_space_handler(adev->handle); } -static void acpi_cmos_rtc_detach_handler(struct acpi_device *adev) -{ - acpi_remove_cmos_rtc_space_handler(adev->handle); -} - static struct acpi_scan_handler cmos_rtc_handler = { .ids = acpi_cmos_rtc_ids, - .attach = acpi_cmos_rtc_attach_handler, - .detach = acpi_cmos_rtc_detach_handler, + .attach = acpi_cmos_rtc_attach, }; void __init acpi_cmos_rtc_init(void) From 6cee29ad9d7e400d39ae0b1a54447fedcb62eecd Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:28:57 +0100 Subject: [PATCH 02/10] ACPI: x86: cmos_rtc: Improve coordination with ACPI TAD driver If a CMOS RTC (PNP0B00/PNP0B01/PNP0B02) device coexists with an ACPI TAD (timer and event alarm device, ACPI000E), the ACPI TAD driver will attempt to install the CMOS RTC address space hanlder that has been installed already and the TAD probing will fail. Avoid that by changing acpi_install_cmos_rtc_space_handler() to return zero and acpi_remove_cmos_rtc_space_handler() to do nothing if the CMOS RTC address space handler has been installed already. Fixes: 596ca52a56da ("ACPI: TAD: Install SystemCMOS address space handler for ACPI000E") Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2415111.ElGaqSPkdT@rafael.j.wysocki --- drivers/acpi/x86/cmos_rtc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c index 977234da9fc1..45db7e51cbe6 100644 --- a/drivers/acpi/x86/cmos_rtc.c +++ b/drivers/acpi/x86/cmos_rtc.c @@ -24,6 +24,8 @@ static const struct acpi_device_id acpi_cmos_rtc_ids[] = { {} }; +static bool cmos_rtc_space_handler_present __read_mostly; + static acpi_status acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, u32 bits, u64 *value64, @@ -59,6 +61,9 @@ int acpi_install_cmos_rtc_space_handler(acpi_handle handle) { acpi_status status; + if (cmos_rtc_space_handler_present) + return 0; + status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_CMOS, acpi_cmos_rtc_space_handler, @@ -68,6 +73,8 @@ int acpi_install_cmos_rtc_space_handler(acpi_handle handle) return -ENODEV; } + cmos_rtc_space_handler_present = true; + return 1; } EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler); @@ -76,6 +83,9 @@ void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) { acpi_status status; + if (cmos_rtc_space_handler_present) + return; + status = acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_CMOS, acpi_cmos_rtc_space_handler); @@ -87,7 +97,13 @@ EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler); static int acpi_cmos_rtc_attach(struct acpi_device *adev, const struct acpi_device_id *id) { - return acpi_install_cmos_rtc_space_handler(adev->handle); + int ret; + + ret = acpi_install_cmos_rtc_space_handler(adev->handle); + if (ret < 0) + return ret; + + return 1; } static struct acpi_scan_handler cmos_rtc_handler = { From 1ae2f435350ec05224a39995c3a680aa6fdae5a5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:29:37 +0100 Subject: [PATCH 03/10] ACPI: x86: cmos_rtc: Create a CMOS RTC platform device Make the CMOS RTC ACPI scan handler create a platform device that will be used subsequently by rtc-cmos for driver binding on x86 systems with ACPI and update add_rtc_cmos() to skip registering a fallback platform device for the CMOS RTC when the above one has been registered. Signed-off-by: Rafael J. Wysocki Acked-by: Dave Hansen # x86 Link: https://patch.msgid.link/1962427.tdWV9SEqCh@rafael.j.wysocki --- arch/x86/kernel/rtc.c | 4 ++++ drivers/acpi/x86/cmos_rtc.c | 8 ++++++++ include/linux/acpi.h | 4 ++++ 3 files changed, 16 insertions(+) diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 51a849a79c98..b112178e8185 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -2,6 +2,7 @@ /* * RTC related functions */ +#include #include #include #include @@ -146,6 +147,9 @@ static __init int add_rtc_cmos(void) } } #endif + if (cmos_rtc_platform_device_present) + return 0; + if (!x86_platform.legacy.rtc) return -ENODEV; diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c index 45db7e51cbe6..bdd66dfd4a44 100644 --- a/drivers/acpi/x86/cmos_rtc.c +++ b/drivers/acpi/x86/cmos_rtc.c @@ -24,6 +24,8 @@ static const struct acpi_device_id acpi_cmos_rtc_ids[] = { {} }; +bool cmos_rtc_platform_device_present; + static bool cmos_rtc_space_handler_present __read_mostly; static acpi_status acpi_cmos_rtc_space_handler(u32 function, @@ -103,6 +105,12 @@ static int acpi_cmos_rtc_attach(struct acpi_device *adev, if (ret < 0) return ret; + if (IS_ERR_OR_NULL(acpi_create_platform_device(adev, NULL))) { + pr_err("Failed to create CMOS-RTC platform device\n"); + return 0; + } else { + cmos_rtc_platform_device_present = true; + } return 1; } diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 4d2f0bed7a06..2bdb801cee01 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -791,6 +791,8 @@ const char *acpi_get_subsystem_id(acpi_handle handle); int acpi_mrrm_max_mem_region(void); #endif +extern bool cmos_rtc_platform_device_present; + #else /* !CONFIG_ACPI */ #define acpi_disabled 1 @@ -1116,6 +1118,8 @@ static inline int acpi_mrrm_max_mem_region(void) return 1; } +#define cmos_rtc_platform_device_present false + #endif /* !CONFIG_ACPI */ #ifdef CONFIG_ACPI_HMAT From 2a78e42104444f948698f1225deaf515e9b7224d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:30:21 +0100 Subject: [PATCH 04/10] ACPI: x86/rtc-cmos: Use platform device for driver binding Modify the rtc-cmos driver to bind to a platform device on systems with ACPI via acpi_match_table and advertise the CMOST RTC ACPI device IDs for driver auto-loading. Note that adding the requisite device IDs to it and exposing them via MODULE_DEVICE_TABLE() is sufficient for this purpose. Since the ACPI device IDs in question are the same as for the CMOS RTC ACPI scan handler, put them into a common header file and use the definition from there in both places. Additionally, to prevent a PNP device from being created for the CMOS RTC if a platform one is present already, make is_cmos_rtc_device() check cmos_rtc_platform_device_present introduced previously. Signed-off-by: Rafael J. Wysocki Acked-by: Alexandre Belloni Link: https://patch.msgid.link/13969123.uLZWGnKmhe@rafael.j.wysocki --- drivers/acpi/acpi_pnp.c | 2 +- drivers/acpi/x86/cmos_rtc.c | 5 +---- drivers/rtc/rtc-cmos.c | 10 ++++++++++ include/linux/acpi.h | 6 ++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 85d9f78619a2..4ad8f56d1a5d 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -368,7 +368,7 @@ static int is_cmos_rtc_device(struct acpi_device *adev) { "PNP0B02" }, {""}, }; - return !acpi_match_device_ids(adev, ids); + return !cmos_rtc_platform_device_present && !acpi_match_device_ids(adev, ids); } bool acpi_is_pnp_device(struct acpi_device *adev) diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c index bdd66dfd4a44..a6df5b991c96 100644 --- a/drivers/acpi/x86/cmos_rtc.c +++ b/drivers/acpi/x86/cmos_rtc.c @@ -18,10 +18,7 @@ #include "../internal.h" static const struct acpi_device_id acpi_cmos_rtc_ids[] = { - { "PNP0B00" }, - { "PNP0B01" }, - { "PNP0B02" }, - {} + ACPI_CMOS_RTC_IDS }; bool cmos_rtc_platform_device_present; diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 0743c6acd6e2..7457f42fd6f0 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -27,6 +27,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -1476,6 +1477,14 @@ static __init void cmos_of_init(struct platform_device *pdev) #else static inline void cmos_of_init(struct platform_device *pdev) {} #endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + ACPI_CMOS_RTC_IDS +}; +MODULE_DEVICE_TABLE(acpi, acpi_cmos_rtc_ids); +#endif + /*----------------------------------------------------------------*/ /* Platform setup should have set up an RTC device, when PNP is @@ -1530,6 +1539,7 @@ static struct platform_driver cmos_platform_driver = { .name = driver_name, .pm = &cmos_pm_ops, .of_match_table = of_match_ptr(of_cmos_match), + .acpi_match_table = ACPI_PTR(acpi_cmos_rtc_ids), } }; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 2bdb801cee01..5ecdcdaf31aa 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -791,6 +791,12 @@ const char *acpi_get_subsystem_id(acpi_handle handle); int acpi_mrrm_max_mem_region(void); #endif +#define ACPI_CMOS_RTC_IDS \ + { "PNP0B00", }, \ + { "PNP0B01", }, \ + { "PNP0B02", }, \ + { "", } + extern bool cmos_rtc_platform_device_present; #else /* !CONFIG_ACPI */ From d15f1c2e413e861270ca6aa5dc5d9da1bcd678ca Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:31:03 +0100 Subject: [PATCH 05/10] ACPI: PNP: Drop CMOS RTC PNP device support A previous change updated is_cmos_rtc_device() to effectively never allow PNP devices to be created for the CMOS RTC on x86 with ACPI and the PNP bus type is only used on x86, so the CMOS RTC device IDs can be dropped from acpi_pnp_device_ids[] and is_cmos_rtc_device() can go away completely. Update the code accordingly. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/3411834.aeNJFYEL58@rafael.j.wysocki --- drivers/acpi/acpi_pnp.c | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 4ad8f56d1a5d..da886923b008 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -125,10 +125,6 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"PNP0401"}, /* ECP Printer Port */ /* apple-gmux */ {"APP000B"}, - /* rtc_cmos */ - {"PNP0b00"}, - {"PNP0b01"}, - {"PNP0b02"}, /* c6xdigio */ {"PNP0400"}, /* Standard LPT Printer Port */ {"PNP0401"}, /* ECP Printer Port */ @@ -355,25 +351,9 @@ static struct acpi_scan_handler acpi_pnp_handler = { .attach = acpi_pnp_attach, }; -/* - * For CMOS RTC devices, the PNP ACPI scan handler does not work, because - * there is a CMOS RTC ACPI scan handler installed already, so we need to - * check those devices and enumerate them to the PNP bus directly. - */ -static int is_cmos_rtc_device(struct acpi_device *adev) -{ - static const struct acpi_device_id ids[] = { - { "PNP0B00" }, - { "PNP0B01" }, - { "PNP0B02" }, - {""}, - }; - return !cmos_rtc_platform_device_present && !acpi_match_device_ids(adev, ids); -} - bool acpi_is_pnp_device(struct acpi_device *adev) { - return adev->handler == &acpi_pnp_handler || is_cmos_rtc_device(adev); + return adev->handler == &acpi_pnp_handler; } EXPORT_SYMBOL_GPL(acpi_is_pnp_device); From 0139085310c40853cc429d5c38fd66e540c97d34 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:31:49 +0100 Subject: [PATCH 06/10] x86: rtc: Drop PNP device check Previous changes effectively prevented PNP devices from being created for the CMOS RTC on x86 with ACPI. Although in principle a CMOS RTC PNP device may exist on an x86 system without ACPI (that is, an x86 system where there is no ACPI at all, not one booted with ACPI disabled), such systems were there in the field ~30 years ago and most likely they would not be able to run a contemporary Linux kernel. For the above reasons, drop the PNP device check from add_rtc_cmos(). Signed-off-by: Rafael J. Wysocki Acked-by: Dave Hansen # x86 Link: https://patch.msgid.link/8660687.T7Z3S40VBb@rafael.j.wysocki --- arch/x86/kernel/rtc.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index b112178e8185..314b062a15de 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include @@ -134,19 +133,6 @@ static struct platform_device rtc_device = { static __init int add_rtc_cmos(void) { -#ifdef CONFIG_PNP - static const char * const ids[] __initconst = - { "PNP0b00", "PNP0b01", "PNP0b02", }; - struct pnp_dev *dev; - int i; - - pnp_for_each_dev(dev) { - for (i = 0; i < ARRAY_SIZE(ids); i++) { - if (compare_pnp_id(dev->id, ids[i]) != 0) - return 0; - } - } -#endif if (cmos_rtc_platform_device_present) return 0; @@ -154,8 +140,7 @@ static __init int add_rtc_cmos(void) return -ENODEV; platform_device_register(&rtc_device); - dev_info(&rtc_device.dev, - "registered platform RTC device (no PNP device found)\n"); + dev_info(&rtc_device.dev, "registered fallback platform RTC device\n"); return 0; } From 62bf102c9d47a4eeb7a4f82f5ba032187942dc7d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:32:29 +0100 Subject: [PATCH 07/10] rtc: cmos: Drop PNP device support Previous changes effectively prevented PNP devices from being created for the CMOS RTC on x86 with ACPI. Although in principle a CMOS RTC PNP device may exist on an x86 system without ACPI (that is, an x86 system where there is no ACPI at all, not one booted with ACPI disabled), such systems were there in the field ~30 years ago and most likely they would not be able to run a contemporary Linux kernel. For the above reasons, drop the PNP device support from the rtc-cmos driver. Signed-off-by: Rafael J. Wysocki Acked-by: Alexandre Belloni Link: https://patch.msgid.link/2355012.iZASKD2KPV@rafael.j.wysocki --- drivers/rtc/rtc-cmos.c | 115 ++++------------------------------------- 1 file changed, 9 insertions(+), 106 deletions(-) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 7457f42fd6f0..9ac5bab846c1 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -1370,85 +1370,6 @@ static int __maybe_unused cmos_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cmos_pm_ops, cmos_suspend, cmos_resume); -/*----------------------------------------------------------------*/ - -/* On non-x86 systems, a "CMOS" RTC lives most naturally on platform_bus. - * ACPI systems always list these as PNPACPI devices, and pre-ACPI PCs - * probably list them in similar PNPBIOS tables; so PNP is more common. - * - * We don't use legacy "poke at the hardware" probing. Ancient PCs that - * predate even PNPBIOS should set up platform_bus devices. - */ - -#ifdef CONFIG_PNP - -#include - -static int cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id) -{ - int irq; - - if (pnp_port_start(pnp, 0) == 0x70 && !pnp_irq_valid(pnp, 0)) { - irq = 0; -#ifdef CONFIG_X86 - /* Some machines contain a PNP entry for the RTC, but - * don't define the IRQ. It should always be safe to - * hardcode it on systems with a legacy PIC. - */ - if (nr_legacy_irqs()) - irq = RTC_IRQ; -#endif - } else { - irq = pnp_irq(pnp, 0); - } - - return cmos_do_probe(&pnp->dev, pnp_get_resource(pnp, IORESOURCE_IO, 0), irq); -} - -static void cmos_pnp_remove(struct pnp_dev *pnp) -{ - cmos_do_remove(&pnp->dev); -} - -static void cmos_pnp_shutdown(struct pnp_dev *pnp) -{ - struct device *dev = &pnp->dev; - struct cmos_rtc *cmos = dev_get_drvdata(dev); - - if (system_state == SYSTEM_POWER_OFF) { - int retval = cmos_poweroff(dev); - - if (cmos_aie_poweroff(dev) < 0 && !retval) - return; - } - - cmos_do_shutdown(cmos->irq); -} - -static const struct pnp_device_id rtc_ids[] = { - { .id = "PNP0b00", }, - { .id = "PNP0b01", }, - { .id = "PNP0b02", }, - { }, -}; -MODULE_DEVICE_TABLE(pnp, rtc_ids); - -static struct pnp_driver cmos_pnp_driver = { - .name = driver_name, - .id_table = rtc_ids, - .probe = cmos_pnp_probe, - .remove = cmos_pnp_remove, - .shutdown = cmos_pnp_shutdown, - - /* flag ensures resume() gets called, and stops syslog spam */ - .flags = PNP_DRIVER_RES_DO_NOT_CHANGE, - .driver = { - .pm = &cmos_pm_ops, - }, -}; - -#endif /* CONFIG_PNP */ - #ifdef CONFIG_OF static const struct of_device_id of_cmos_match[] = { { @@ -1543,45 +1464,27 @@ static struct platform_driver cmos_platform_driver = { } }; -#ifdef CONFIG_PNP -static bool pnp_driver_registered; -#endif static bool platform_driver_registered; static int __init cmos_init(void) { - int retval = 0; + int retval; -#ifdef CONFIG_PNP - retval = pnp_register_driver(&cmos_pnp_driver); - if (retval == 0) - pnp_driver_registered = true; -#endif - - if (!cmos_rtc.dev) { - retval = platform_driver_probe(&cmos_platform_driver, - cmos_platform_probe); - if (retval == 0) - platform_driver_registered = true; - } - - if (retval == 0) + if (cmos_rtc.dev) return 0; -#ifdef CONFIG_PNP - if (pnp_driver_registered) - pnp_unregister_driver(&cmos_pnp_driver); -#endif - return retval; + retval = platform_driver_probe(&cmos_platform_driver, cmos_platform_probe); + if (retval) + return retval; + + platform_driver_registered = true; + + return 0; } module_init(cmos_init); static void __exit cmos_exit(void) { -#ifdef CONFIG_PNP - if (pnp_driver_registered) - pnp_unregister_driver(&cmos_pnp_driver); -#endif if (platform_driver_registered) platform_driver_unregister(&cmos_platform_driver); } From e8d1eb65193ce93283f8f56a069eee5d548a6b70 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 23 Feb 2026 16:33:27 +0100 Subject: [PATCH 08/10] ACPI: TAD/x86: cmos_rtc: Consolidate address space handler setup On x86, as a rule the CMOS RTC address space handler is set up by the CMOS RTC ACPI scan handler attach callback, acpi_cmos_rtc_attach(), but if the ACPI namespace does not contain a CMOS RTC device object, the CMOS RTC address space handler installation is taken care of the ACPI TAD (Timer and Alarm Device) driver. This is not particularly straightforward and can be avoided by adding the ACPI TAD device ID to the CMOS RTC ACPI scan handler which will cause it to create a platform device for ACPI TAD after installing the CMOS RTC address space handler. One related detail that needs to be taken care of, though, is that the creation of an ACPI TAD platform device should not cause cmos_rtc_platform_device_present to be set, since this may cause add_rtc_cmos() to suppress the creation of a fallback CMOS RTC platform device which may not be the right thing to do (for instance, due to the fact that the ACPI TAD driver is missing an RTC class device interface). After doing the above, the CMOS RTC address space handler installation and removal can be dropped from the ACPI TAD driver (which allows it to be simplified quite a bit), acpi_remove_cmos_rtc_space_handler() can be dropped and acpi_install_cmos_rtc_space_handler() can be made static. Update the code as per the above. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/23028644.EfDdHjke4D@rafael.j.wysocki --- drivers/acpi/acpi_tad.c | 27 +++++---------------------- drivers/acpi/x86/cmos_rtc.c | 26 +++++--------------------- include/acpi/acpi_bus.h | 9 --------- 3 files changed, 10 insertions(+), 52 deletions(-) diff --git a/drivers/acpi/acpi_tad.c b/drivers/acpi/acpi_tad.c index 6d870d97ada6..4f5089fc023d 100644 --- a/drivers/acpi/acpi_tad.c +++ b/drivers/acpi/acpi_tad.c @@ -563,7 +563,6 @@ static int acpi_tad_disable_timer(struct device *dev, u32 timer_id) static void acpi_tad_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; - acpi_handle handle = ACPI_HANDLE(dev); struct acpi_tad_driver_data *dd = dev_get_drvdata(dev); device_init_wakeup(dev, false); @@ -587,7 +586,6 @@ static void acpi_tad_remove(struct platform_device *pdev) pm_runtime_suspend(dev); pm_runtime_disable(dev); - acpi_remove_cmos_rtc_space_handler(handle); } static int acpi_tad_probe(struct platform_device *pdev) @@ -599,11 +597,6 @@ static int acpi_tad_probe(struct platform_device *pdev) unsigned long long caps; int ret; - ret = acpi_install_cmos_rtc_space_handler(handle); - if (ret < 0) { - dev_info(dev, "Unable to install space handler\n"); - return -ENODEV; - } /* * Initialization failure messages are mostly about firmware issues, so * print them at the "info" level. @@ -611,27 +604,22 @@ static int acpi_tad_probe(struct platform_device *pdev) status = acpi_evaluate_integer(handle, "_GCP", NULL, &caps); if (ACPI_FAILURE(status)) { dev_info(dev, "Unable to get capabilities\n"); - ret = -ENODEV; - goto remove_handler; + return -ENODEV; } if (!(caps & ACPI_TAD_AC_WAKE)) { dev_info(dev, "Unsupported capabilities\n"); - ret = -ENODEV; - goto remove_handler; + return -ENODEV; } if (!acpi_has_method(handle, "_PRW")) { dev_info(dev, "Missing _PRW\n"); - ret = -ENODEV; - goto remove_handler; + return -ENODEV; } dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL); - if (!dd) { - ret = -ENOMEM; - goto remove_handler; - } + if (!dd) + return -ENOMEM; dd->capabilities = caps; dev_set_drvdata(dev, dd); @@ -673,11 +661,6 @@ static int acpi_tad_probe(struct platform_device *pdev) fail: acpi_tad_remove(pdev); - /* Don't fallthrough because cmos rtc space handler is removed in acpi_tad_remove() */ - return ret; - -remove_handler: - acpi_remove_cmos_rtc_space_handler(handle); return ret; } diff --git a/drivers/acpi/x86/cmos_rtc.c b/drivers/acpi/x86/cmos_rtc.c index a6df5b991c96..ced334e19896 100644 --- a/drivers/acpi/x86/cmos_rtc.c +++ b/drivers/acpi/x86/cmos_rtc.c @@ -18,13 +18,12 @@ #include "../internal.h" static const struct acpi_device_id acpi_cmos_rtc_ids[] = { + { "ACPI000E", 1 }, /* ACPI Time and Alarm Device (TAD) */ ACPI_CMOS_RTC_IDS }; bool cmos_rtc_platform_device_present; -static bool cmos_rtc_space_handler_present __read_mostly; - static acpi_status acpi_cmos_rtc_space_handler(u32 function, acpi_physical_address address, u32 bits, u64 *value64, @@ -56,8 +55,9 @@ static acpi_status acpi_cmos_rtc_space_handler(u32 function, return AE_BAD_PARAMETER; } -int acpi_install_cmos_rtc_space_handler(acpi_handle handle) +static int acpi_install_cmos_rtc_space_handler(acpi_handle handle) { + static bool cmos_rtc_space_handler_present __read_mostly; acpi_status status; if (cmos_rtc_space_handler_present) @@ -76,22 +76,6 @@ int acpi_install_cmos_rtc_space_handler(acpi_handle handle) return 1; } -EXPORT_SYMBOL_GPL(acpi_install_cmos_rtc_space_handler); - -void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) -{ - acpi_status status; - - if (cmos_rtc_space_handler_present) - return; - - status = acpi_remove_address_space_handler(handle, - ACPI_ADR_SPACE_CMOS, - acpi_cmos_rtc_space_handler); - if (ACPI_FAILURE(status)) - pr_err("Failed to remove CMOS-RTC address space handler\n"); -} -EXPORT_SYMBOL_GPL(acpi_remove_cmos_rtc_space_handler); static int acpi_cmos_rtc_attach(struct acpi_device *adev, const struct acpi_device_id *id) @@ -103,9 +87,9 @@ static int acpi_cmos_rtc_attach(struct acpi_device *adev, return ret; if (IS_ERR_OR_NULL(acpi_create_platform_device(adev, NULL))) { - pr_err("Failed to create CMOS-RTC platform device\n"); + pr_err("Failed to create a platform device for %s\n", (char *)id->id); return 0; - } else { + } else if (!id->driver_data) { cmos_rtc_platform_device_present = true; } return 1; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index aad1a95e6863..be6d9032a161 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -760,8 +760,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev); #ifdef CONFIG_X86 bool acpi_device_override_status(struct acpi_device *adev, unsigned long long *status); bool acpi_quirk_skip_acpi_ac_and_battery(void); -int acpi_install_cmos_rtc_space_handler(acpi_handle handle); -void acpi_remove_cmos_rtc_space_handler(acpi_handle handle); int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip); #else static inline bool acpi_device_override_status(struct acpi_device *adev, @@ -773,13 +771,6 @@ static inline bool acpi_quirk_skip_acpi_ac_and_battery(void) { return false; } -static inline int acpi_install_cmos_rtc_space_handler(acpi_handle handle) -{ - return 1; -} -static inline void acpi_remove_cmos_rtc_space_handler(acpi_handle handle) -{ -} static inline int acpi_quirk_skip_serdev_enumeration(struct device *controller_parent, bool *skip) { From db5dab0784a8e8477598b52f3de7bdfbed733b3f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 14 Mar 2026 13:11:20 +0100 Subject: [PATCH 09/10] rtc: cmos: Enable ACPI alarm if advertised in ACPI FADT If the ACPI_FADT_FIXED_RTC flag is unset, the platform is declaring that it supports the ACPI RTC fixed event which should be used instead of a dedicated CMOS RTC IRQ. However, the driver only enables it when is_hpet_enabled() returns true, which is questionable because there is no clear connection between enabled HPET and signaling wakeup via the ACPI RTC fixed event (for instance, the latter can be expected to work on systems that don't include a functional HPET). Moreover, since use_hpet_alarm() returns false if use_acpi_alarm is set, the ACPI RTC fixed event is effectively used instead of the HPET alarm if the latter is functional, but there is no particular reason why it could not be used otherwise. Accordingly, on x86 systems with ACPI, set use_acpi_alarm if ACPI_FADT_FIXED_RTC is unset without looking at whether or not HPET is enabled. Also, do the ACPI FADT check in use_acpi_alarm_quirks() before the DMI BIOS year checks which are more expensive and it's better to skip them if ACPI_FADT_FIXED_RTC is set. Signed-off-by: Rafael J. Wysocki Acked-by: Alexandre Belloni Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/9618535.CDJkKcVGEf@rafael.j.wysocki --- drivers/rtc/rtc-cmos.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 9ac5bab846c1..221e7525cd3f 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -817,6 +817,9 @@ static void rtc_wake_off(struct device *dev) #ifdef CONFIG_X86 static void use_acpi_alarm_quirks(void) { + if (acpi_gbl_FADT.flags & ACPI_FADT_FIXED_RTC) + return; + switch (boot_cpu_data.x86_vendor) { case X86_VENDOR_INTEL: if (dmi_get_bios_year() < 2015) @@ -830,8 +833,6 @@ static void use_acpi_alarm_quirks(void) default: return; } - if (!is_hpet_enabled()) - return; use_acpi_alarm = true; } From b48ee1f7404a17138785cb3ceb6a95e96b489c0d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 14 Mar 2026 13:12:44 +0100 Subject: [PATCH 10/10] rtc: cmos: Do not require IRQ if ACPI alarm is used If the ACPI RTC fixed event is used, a dedicated IRQ is not required for the CMOS RTC alarm to work, so allow the driver to use the alarm without a valid IRQ in that case. Signed-off-by: Rafael J. Wysocki Acked-by: Alexandre Belloni Reviewed-by: Mario Limonciello (AMD) Link: https://patch.msgid.link/6168746.MhkbZ0Pkbq@rafael.j.wysocki --- drivers/rtc/rtc-cmos.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 221e7525cd3f..acc064baefcd 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -216,6 +216,11 @@ static inline void cmos_write_bank2(unsigned char val, unsigned char addr) /*----------------------------------------------------------------*/ +static bool cmos_no_alarm(struct cmos_rtc *cmos) +{ + return !is_valid_irq(cmos->irq) && !cmos_use_acpi_alarm(); +} + static int cmos_read_time(struct device *dev, struct rtc_time *t) { int ret; @@ -287,7 +292,7 @@ static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t) }; /* This not only a rtc_op, but also called directly */ - if (!is_valid_irq(cmos->irq)) + if (cmos_no_alarm(cmos)) return -ETIMEDOUT; /* Basic alarms only support hour, minute, and seconds fields. @@ -520,7 +525,7 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) int ret; /* This not only a rtc_op, but also called directly */ - if (!is_valid_irq(cmos->irq)) + if (cmos_no_alarm(cmos)) return -EIO; ret = cmos_validate_alarm(dev, t); @@ -1096,7 +1101,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq); goto cleanup1; } - } else { + } else if (!cmos_use_acpi_alarm()) { clear_bit(RTC_FEATURE_ALARM, cmos_rtc.rtc->features); } @@ -1121,7 +1126,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) acpi_rtc_event_setup(dev); dev_info(dev, "%s%s, %d bytes nvram%s\n", - !is_valid_irq(rtc_irq) ? "no alarms" : + cmos_no_alarm(&cmos_rtc) ? "no alarms" : cmos_rtc.mon_alrm ? "alarms up to one year" : cmos_rtc.day_alrm ? "alarms up to one month" : "alarms up to one day", @@ -1147,7 +1152,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq) static void cmos_do_shutdown(int rtc_irq) { spin_lock_irq(&rtc_lock); - if (is_valid_irq(rtc_irq)) + if (!cmos_no_alarm(&cmos_rtc)) cmos_irq_disable(&cmos_rtc, RTC_IRQMASK); spin_unlock_irq(&rtc_lock); }