SoC vendors have different types of resets which are controlled
through various hardware registers. For instance, Qualcomm SoC
may have a requirement that reboot with “bootloader” command
should reboot the device to bootloader flashing mode and reboot
with “edl” should reboot the device into Emergency flashing mode.
Setting up such reboots on Qualcomm devices can be inconsistent
across SoC platforms and may require setting different HW
registers, where some of these registers may not be accessible to
HLOS. These knobs evolve over product generations and require
more drivers. PSCI spec defines, SYSTEM_RESET2, vendor-specific
reset which can help align this requirement. Add support for PSCI
SYSTEM_RESET2, vendor-specific resets and align the implementation
to allow user-space initiated reboots to trigger these resets.
Implement the PSCI vendor-specific resets by registering to the
reboot-mode framework. As psci init is done at early kernel init,
reboot-mode registration cannot be done at the time of psci init.
This is because reboot-mode creates a “reboot-mode” class for
exposing sysfs, which can fail at early kernel init. To overcome
this, introduce a late_initcall to register PSCI vendor-specific
resets as reboot modes. Implement a reboot-mode write function
that sets reset_type and cookie values during the reboot notifier
callback. Introduce a firmware-based call for SYSTEM_RESET2
vendor-specific reset in the psci_sys_reset path, using
reset_type and cookie if supported by secure firmware. Register a
panic notifier and clear vendor_reset valid status during panic.
This is needed for any kernel panic that occurs post
reboot_notifiers.
By using the above implementation, userspace will be able to issue
such resets using the reboot() system call with the "*arg"
parameter as a string based command. The commands can be defined
in PSCI device tree node under “reboot-mode” and are based on the
reboot-mode based commands.
Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com>
---
drivers/firmware/psci/Kconfig | 2 +
drivers/firmware/psci/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig
index 97944168b5e66aea1e38a7eb2d4ced8348fce64b..93ff7b071a0c364a376699733e6bc5654d56a17f 100644
--- a/drivers/firmware/psci/Kconfig
+++ b/drivers/firmware/psci/Kconfig
@@ -1,6 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
config ARM_PSCI_FW
bool
+ select POWER_RESET
+ select REBOOT_MODE
config ARM_PSCI_CHECKER
bool "ARM PSCI checker"
diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c
index 38ca190d4a22d6e7e0f06420e8478a2b0ec2fe6f..40a27bc2cc3a3393acc14c7b2314155540ed06c9 100644
--- a/drivers/firmware/psci/psci.c
+++ b/drivers/firmware/psci/psci.c
@@ -13,10 +13,12 @@
#include <linux/errno.h>
#include <linux/linkage.h>
#include <linux/of.h>
+#include <linux/panic_notifier.h>
#include <linux/pm.h>
#include <linux/printk.h>
#include <linux/psci.h>
#include <linux/reboot.h>
+#include <linux/reboot-mode.h>
#include <linux/slab.h>
#include <linux/suspend.h>
@@ -51,6 +53,24 @@ static int resident_cpu = -1;
struct psci_operations psci_ops;
static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE;
+struct psci_vendor_sysreset2 {
+ u32 reset_type;
+ u32 cookie;
+ bool valid;
+};
+
+static struct psci_vendor_sysreset2 vendor_reset;
+
+static int psci_panic_event(struct notifier_block *nb, unsigned long v, void *p)
+{
+ vendor_reset.valid = false;
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block psci_panic_block = {
+ .notifier_call = psci_panic_event
+};
+
bool psci_tos_resident_on(int cpu)
{
return cpu == resident_cpu;
@@ -309,7 +329,10 @@ static int get_set_conduit_method(const struct device_node *np)
static int psci_sys_reset(struct notifier_block *nb, unsigned long action,
void *data)
{
- if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
+ if (vendor_reset.valid && psci_system_reset2_supported) {
+ invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), vendor_reset.reset_type,
+ vendor_reset.cookie, 0);
+ } else if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) &&
psci_system_reset2_supported) {
/*
* reset_type[31] = 0 (architectural)
@@ -547,6 +570,70 @@ static const struct platform_suspend_ops psci_suspend_ops = {
.enter = psci_system_suspend_enter,
};
+static int psci_set_vendor_sys_reset2(struct reboot_mode_driver *reboot, u64 magic)
+{
+ u32 magic_32;
+
+ if (psci_system_reset2_supported) {
+ magic_32 = magic & 0xffffffff;
+ vendor_reset.reset_type = PSCI_1_1_RESET_TYPE_VENDOR_START | magic_32;
+ vendor_reset.cookie = (magic >> 32) & 0xffffffff;
+ vendor_reset.valid = true;
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int __init psci_init_vendor_reset(void)
+{
+ struct reboot_mode_driver *reboot;
+ struct device_node *psci_np;
+ struct device_node *np;
+ int ret;
+
+ if (!psci_system_reset2_supported)
+ return -EINVAL;
+
+ psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
+ if (!psci_np)
+ return -ENODEV;
+
+ np = of_find_node_by_name(psci_np, "reboot-mode");
+ if (!np) {
+ of_node_put(psci_np);
+ return -ENODEV;
+ }
+
+ ret = atomic_notifier_chain_register(&panic_notifier_list, &psci_panic_block);
+ if (ret)
+ goto err_notifier;
+
+ reboot = kzalloc(sizeof(*reboot), GFP_KERNEL);
+ if (!reboot) {
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ reboot->write = psci_set_vendor_sys_reset2;
+ reboot->driver_name = "psci";
+
+ ret = reboot_mode_register(reboot, of_fwnode_handle(np));
+ if (ret)
+ goto err_register;
+
+ return 0;
+
+err_register:
+ kfree(reboot);
+err_kzalloc:
+ atomic_notifier_chain_unregister(&panic_notifier_list, &psci_panic_block);
+err_notifier:
+ of_node_put(psci_np);
+ of_node_put(np);
+ return ret;
+}
+late_initcall(psci_init_vendor_reset)
+
static void __init psci_init_system_reset2(void)
{
int ret;
--
2.34.1
On 9/22/2025 7:10 PM, Shivendra Pratap wrote: > SoC vendors have different types of resets which are controlled > through various hardware registers. For instance, Qualcomm SoC > may have a requirement that reboot with “bootloader” command > should reboot the device to bootloader flashing mode and reboot > with “edl” should reboot the device into Emergency flashing mode. > Setting up such reboots on Qualcomm devices can be inconsistent > across SoC platforms and may require setting different HW > registers, where some of these registers may not be accessible to > HLOS. These knobs evolve over product generations and require > more drivers. PSCI spec defines, SYSTEM_RESET2, vendor-specific > reset which can help align this requirement. Add support for PSCI > SYSTEM_RESET2, vendor-specific resets and align the implementation > to allow user-space initiated reboots to trigger these resets. > > Implement the PSCI vendor-specific resets by registering to the > reboot-mode framework. As psci init is done at early kernel init, > reboot-mode registration cannot be done at the time of psci init. > This is because reboot-mode creates a “reboot-mode” class for > exposing sysfs, which can fail at early kernel init. To overcome > this, introduce a late_initcall to register PSCI vendor-specific > resets as reboot modes. Implement a reboot-mode write function > that sets reset_type and cookie values during the reboot notifier > callback. Introduce a firmware-based call for SYSTEM_RESET2 > vendor-specific reset in the psci_sys_reset path, using > reset_type and cookie if supported by secure firmware. Register a > panic notifier and clear vendor_reset valid status during panic. > This is needed for any kernel panic that occurs post > reboot_notifiers. > > By using the above implementation, userspace will be able to issue > such resets using the reboot() system call with the "*arg" > parameter as a string based command. The commands can be defined > in PSCI device tree node under “reboot-mode” and are based on the > reboot-mode based commands. > > Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com> > --- > drivers/firmware/psci/Kconfig | 2 + > drivers/firmware/psci/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++- > 2 files changed, 90 insertions(+), 1 deletion(-) > > diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig > index 97944168b5e66aea1e38a7eb2d4ced8348fce64b..93ff7b071a0c364a376699733e6bc5654d56a17f 100644 > --- a/drivers/firmware/psci/Kconfig > +++ b/drivers/firmware/psci/Kconfig > @@ -1,6 +1,8 @@ > # SPDX-License-Identifier: GPL-2.0-only > config ARM_PSCI_FW > bool > + select POWER_RESET > + select REBOOT_MODE > > config ARM_PSCI_CHECKER > bool "ARM PSCI checker" > diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c > index 38ca190d4a22d6e7e0f06420e8478a2b0ec2fe6f..40a27bc2cc3a3393acc14c7b2314155540ed06c9 100644 > --- a/drivers/firmware/psci/psci.c > +++ b/drivers/firmware/psci/psci.c > @@ -13,10 +13,12 @@ > #include <linux/errno.h> > #include <linux/linkage.h> > #include <linux/of.h> > +#include <linux/panic_notifier.h> > #include <linux/pm.h> > #include <linux/printk.h> > #include <linux/psci.h> > #include <linux/reboot.h> > +#include <linux/reboot-mode.h> > #include <linux/slab.h> > #include <linux/suspend.h> > > @@ -51,6 +53,24 @@ static int resident_cpu = -1; > struct psci_operations psci_ops; > static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE; > > +struct psci_vendor_sysreset2 { > + u32 reset_type; > + u32 cookie; > + bool valid; > +}; > + > +static struct psci_vendor_sysreset2 vendor_reset; > + > +static int psci_panic_event(struct notifier_block *nb, unsigned long v, void *p) > +{ > + vendor_reset.valid = false; > + return NOTIFY_DONE; > +} > + > +static struct notifier_block psci_panic_block = { > + .notifier_call = psci_panic_event > +}; > + > bool psci_tos_resident_on(int cpu) > { > return cpu == resident_cpu; > @@ -309,7 +329,10 @@ static int get_set_conduit_method(const struct device_node *np) > static int psci_sys_reset(struct notifier_block *nb, unsigned long action, > void *data) > { > - if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && > + if (vendor_reset.valid && psci_system_reset2_supported) { > + invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), vendor_reset.reset_type, > + vendor_reset.cookie, 0); > + } else if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && > psci_system_reset2_supported) { > /* > * reset_type[31] = 0 (architectural) > @@ -547,6 +570,70 @@ static const struct platform_suspend_ops psci_suspend_ops = { > .enter = psci_system_suspend_enter, > }; > > +static int psci_set_vendor_sys_reset2(struct reboot_mode_driver *reboot, u64 magic) > +{ > + u32 magic_32; > + > + if (psci_system_reset2_supported) { > + magic_32 = magic & 0xffffffff; > + vendor_reset.reset_type = PSCI_1_1_RESET_TYPE_VENDOR_START | magic_32; > + vendor_reset.cookie = (magic >> 32) & 0xffffffff; Minor Nit: Can we use GENMASK(31, 0) instead of 0xffffffff? Apart from this, change LGTM. With that, Reviewed-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com> > + vendor_reset.valid = true; > + } > + > + return NOTIFY_DONE; > +} > + > +static int __init psci_init_vendor_reset(void) > +{ > + struct reboot_mode_driver *reboot; > + struct device_node *psci_np; > + struct device_node *np; > + int ret; > + > + if (!psci_system_reset2_supported) > + return -EINVAL; > + > + psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0"); > + if (!psci_np) > + return -ENODEV; > + > + np = of_find_node_by_name(psci_np, "reboot-mode"); > + if (!np) { > + of_node_put(psci_np); > + return -ENODEV; > + } > + > + ret = atomic_notifier_chain_register(&panic_notifier_list, &psci_panic_block); > + if (ret) > + goto err_notifier; > + > + reboot = kzalloc(sizeof(*reboot), GFP_KERNEL); > + if (!reboot) { > + ret = -ENOMEM; > + goto err_kzalloc; > + } > + > + reboot->write = psci_set_vendor_sys_reset2; > + reboot->driver_name = "psci"; > + > + ret = reboot_mode_register(reboot, of_fwnode_handle(np)); > + if (ret) > + goto err_register; > + > + return 0; > + > +err_register: > + kfree(reboot); > +err_kzalloc: > + atomic_notifier_chain_unregister(&panic_notifier_list, &psci_panic_block); > +err_notifier: > + of_node_put(psci_np); > + of_node_put(np); > + return ret; > +} > +late_initcall(psci_init_vendor_reset) > + > static void __init psci_init_system_reset2(void) > { > int ret; >
On 9/30/2025 11:15 AM, Kathiravan Thirumoorthy wrote: > > On 9/22/2025 7:10 PM, Shivendra Pratap wrote: >> SoC vendors have different types of resets which are controlled >> through various hardware registers. For instance, Qualcomm SoC >> may have a requirement that reboot with “bootloader” command >> should reboot the device to bootloader flashing mode and reboot >> with “edl” should reboot the device into Emergency flashing mode. >> Setting up such reboots on Qualcomm devices can be inconsistent >> across SoC platforms and may require setting different HW >> registers, where some of these registers may not be accessible to >> HLOS. These knobs evolve over product generations and require >> more drivers. PSCI spec defines, SYSTEM_RESET2, vendor-specific >> reset which can help align this requirement. Add support for PSCI >> SYSTEM_RESET2, vendor-specific resets and align the implementation >> to allow user-space initiated reboots to trigger these resets. >> >> Implement the PSCI vendor-specific resets by registering to the >> reboot-mode framework. As psci init is done at early kernel init, >> reboot-mode registration cannot be done at the time of psci init. >> This is because reboot-mode creates a “reboot-mode” class for >> exposing sysfs, which can fail at early kernel init. To overcome >> this, introduce a late_initcall to register PSCI vendor-specific >> resets as reboot modes. Implement a reboot-mode write function >> that sets reset_type and cookie values during the reboot notifier >> callback. Introduce a firmware-based call for SYSTEM_RESET2 >> vendor-specific reset in the psci_sys_reset path, using >> reset_type and cookie if supported by secure firmware. Register a >> panic notifier and clear vendor_reset valid status during panic. >> This is needed for any kernel panic that occurs post >> reboot_notifiers. >> >> By using the above implementation, userspace will be able to issue >> such resets using the reboot() system call with the "*arg" >> parameter as a string based command. The commands can be defined >> in PSCI device tree node under “reboot-mode” and are based on the >> reboot-mode based commands. >> >> Signed-off-by: Shivendra Pratap <shivendra.pratap@oss.qualcomm.com> >> --- >> drivers/firmware/psci/Kconfig | 2 + >> drivers/firmware/psci/psci.c | 89 ++++++++++++++++++++++++++++++++++++++++++- >> 2 files changed, 90 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig >> index 97944168b5e66aea1e38a7eb2d4ced8348fce64b..93ff7b071a0c364a376699733e6bc5654d56a17f 100644 >> --- a/drivers/firmware/psci/Kconfig >> +++ b/drivers/firmware/psci/Kconfig >> @@ -1,6 +1,8 @@ >> # SPDX-License-Identifier: GPL-2.0-only >> config ARM_PSCI_FW >> bool >> + select POWER_RESET >> + select REBOOT_MODE >> config ARM_PSCI_CHECKER >> bool "ARM PSCI checker" >> diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c >> index 38ca190d4a22d6e7e0f06420e8478a2b0ec2fe6f..40a27bc2cc3a3393acc14c7b2314155540ed06c9 100644 >> --- a/drivers/firmware/psci/psci.c >> +++ b/drivers/firmware/psci/psci.c >> @@ -13,10 +13,12 @@ >> #include <linux/errno.h> >> #include <linux/linkage.h> >> #include <linux/of.h> >> +#include <linux/panic_notifier.h> >> #include <linux/pm.h> >> #include <linux/printk.h> >> #include <linux/psci.h> >> #include <linux/reboot.h> >> +#include <linux/reboot-mode.h> >> #include <linux/slab.h> >> #include <linux/suspend.h> >> @@ -51,6 +53,24 @@ static int resident_cpu = -1; >> struct psci_operations psci_ops; >> static enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE; >> +struct psci_vendor_sysreset2 { >> + u32 reset_type; >> + u32 cookie; >> + bool valid; >> +}; >> + >> +static struct psci_vendor_sysreset2 vendor_reset; >> + >> +static int psci_panic_event(struct notifier_block *nb, unsigned long v, void *p) >> +{ >> + vendor_reset.valid = false; >> + return NOTIFY_DONE; >> +} >> + >> +static struct notifier_block psci_panic_block = { >> + .notifier_call = psci_panic_event >> +}; >> + >> bool psci_tos_resident_on(int cpu) >> { >> return cpu == resident_cpu; >> @@ -309,7 +329,10 @@ static int get_set_conduit_method(const struct device_node *np) >> static int psci_sys_reset(struct notifier_block *nb, unsigned long action, >> void *data) >> { >> - if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && >> + if (vendor_reset.valid && psci_system_reset2_supported) { >> + invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), vendor_reset.reset_type, >> + vendor_reset.cookie, 0); >> + } else if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && >> psci_system_reset2_supported) { >> /* >> * reset_type[31] = 0 (architectural) >> @@ -547,6 +570,70 @@ static const struct platform_suspend_ops psci_suspend_ops = { >> .enter = psci_system_suspend_enter, >> }; >> +static int psci_set_vendor_sys_reset2(struct reboot_mode_driver *reboot, u64 magic) >> +{ >> + u32 magic_32; >> + >> + if (psci_system_reset2_supported) { >> + magic_32 = magic & 0xffffffff; >> + vendor_reset.reset_type = PSCI_1_1_RESET_TYPE_VENDOR_START | magic_32; >> + vendor_reset.cookie = (magic >> 32) & 0xffffffff; > > > Minor Nit: Can we use GENMASK(31, 0) instead of 0xffffffff? Apart from this, change LGTM. With that, Ack. Will update this. > > Reviewed-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com> thanks for review. thanks, Shivendra
© 2016 - 2025 Red Hat, Inc.