[PATCH] firmware/psci: Convert poweroff to the sys-off handler API
Diogo Ivo
diogo.ivo at tecnico.ulisboa.pt
Wed Sep 23 04:12:06 PDT 2026
The PSCI poweroff handler is currently registered via pm_power_off,
which fires as the last SYS_OFF_PRIO_DEFAULT handler. However, there
are two problems with this approach: PSCI is a firmware mechanism, so
its priority should be SYS_OFF_PRIO_FIRMWARE, and the point of the
sys-off API is to eliminate pm_power_off and make the ordering and
priority between shutdown mechanisms explicit.
This patch converts PSCI to register_sys_off_handler() so that its
position in the shutdown sequence is explicit and governed by priority.
In an ideal world PSCI would always be registered at SYS_OFF_PRIO_FIRMWARE
since it is a firmware mechanism. However, this approach would lead to
several problems:
- On platforms with ACPI/EFI and PSCI there would be a clash at FIRMWARE
level, and the first to register would prevail.
- Not all platforms have a functional PSCI SYSTEM_OFF callback. This
happens for example in some Tegra X1 platforms, where shutdown needs
to be handled by the PMIC driver, explicitly marked as a
system-power-controller.
As such, the approach here is:
- On ACPI systems EFI (SYS_OFF_PRIO_FIRMWARE + 1) and ACPI
(SYS_OFF_PRIO_FIRMWARE) already handle poweroff at higher
priorities. PSCI registers at SYS_OFF_PRIO_DEFAULT as a fallback
that runs after both.
- On DT systems with no system-power-controller, PSCI is the primary
firmware poweroff path and registers at SYS_OFF_PRIO_FIRMWARE.
- On DT systems where a system-power-controller node is present, a
dedicated platform driver is responsible for poweroff. PSCI
registers at SYS_OFF_PRIO_DEFAULT - 1 so that driver takes
precedence while PSCI still acts as a backstop.
Signed-off-by: Diogo Ivo <diogo.ivo at tecnico.ulisboa.pt>
---
---
drivers/firmware/psci/psci.c | 44 +++++++++++++++++++++++++++++++++++++++++---
1 file changed, 41 insertions(+), 3 deletions(-)
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index e73bae6cb23a..fc7986b7f12f 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -13,7 +13,6 @@
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
-#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
@@ -79,6 +78,8 @@ struct psci_0_1_function_ids get_psci_0_1_function_ids(void)
static u32 psci_cpu_suspend_feature;
static bool psci_system_reset2_supported;
static bool psci_system_off2_hibernate_supported;
+static bool psci_system_off_supported __initdata;
+static bool is_dt __initdata;
static inline bool psci_has_ext_power_state(void)
{
@@ -329,9 +330,11 @@ static struct notifier_block psci_sys_reset_nb = {
.priority = 129,
};
-static void psci_sys_poweroff(void)
+static int psci_sys_poweroff(struct sys_off_data *data)
{
invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
+
+ return NOTIFY_DONE;
}
#ifdef CONFIG_HIBERNATION
@@ -665,6 +668,40 @@ static void __init psci_init_smccc(void)
}
+static int __init psci_poweroff_init(void)
+{
+ int priority = is_dt ? SYS_OFF_PRIO_FIRMWARE : SYS_OFF_PRIO_DEFAULT;
+ struct sys_off_handler *handler;
+ struct device_node *np;
+
+ if (!psci_system_off_supported)
+ return 0;
+
+ if (is_dt) {
+ /*
+ * If a system-power-controller is designated in DT, a dedicated
+ * driver should handle power-off; in that case register PSCI at
+ * SYS_OFF_PRIO_DEFAULT - 1 as a fallback. This covers both the
+ * case where that driver fails to power-off the system and the
+ * case where it fails to register its own handler.
+ */
+ np = of_find_node_with_property(NULL, "system-power-controller");
+ if (np) {
+ priority = SYS_OFF_PRIO_DEFAULT - 1;
+ of_node_put(np);
+ }
+ }
+
+ handler = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF, priority,
+ psci_sys_poweroff, NULL);
+ if (IS_ERR(handler))
+ pr_err("Failed to register PSCI power-off handler: %ld\n",
+ PTR_ERR(handler));
+
+ return PTR_ERR_OR_ZERO(handler);
+}
+subsys_initcall(psci_poweroff_init);
+
static void __init psci_0_2_set_functions(void)
{
pr_info("Using standard PSCI v0.2 function IDs\n");
@@ -681,7 +718,7 @@ static void __init psci_0_2_set_functions(void)
register_restart_handler(&psci_sys_reset_nb);
- pm_power_off = psci_sys_poweroff;
+ psci_system_off_supported = true;
}
/*
@@ -822,6 +859,7 @@ int __init psci_dt_init(void)
init_fn = (psci_initcall_t)matched_np->data;
ret = init_fn(np);
+ is_dt = true;
of_node_put(np);
return ret;
}
---
base-commit: f0100363d8c374bd8e9ea7c9ba02744f0b802ca4
change-id: 20260530-firmware-psci-f4179901d35d
Best regards,
--
Diogo Ivo <diogo.ivo at tecnico.ulisboa.pt>
More information about the linux-arm-kernel
mailing list