[PATCH] firmware/psci: Convert poweroff to the sys-off handler API

Diogo Ivo diogo.ivo at tecnico.ulisboa.pt
Wed Sep 23 07:00:00 PDT 2026



On 9/23/26 12:12, Diogo Ivo wrote:
> 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");

Sashiko flagged a valid point here, where if the first node containing
"system-power-controller" is actually disabled in DT we will still
demote PSCI to SYS_OFF_PRIO_DEFAULT - 1. To fix this I will send a v2
with the check
		if (np && of_device_is_available(np)) {
> +		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