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. Introduce a late_initcall to register PSCI vendor-specific resets as reboot-mode arguments like reset_type and cookie. For a SoC where, PSCI vendor-specific system_reset2 is supported, the appropriate value gets filled to reset_type and cookie during this reboot-mode hook registration. If the secure firmware supports PSCI system_reset2, restart notifier will make secure call to trigger appropriate requested reset type. By using the above implementation, usespace 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 as “reset-types” and are based on the reboot-mode based commands. Signed-off-by: Shivendra Pratap <shivendra.pratap@xxxxxxxxxxxxxxxx> --- drivers/firmware/psci/Kconfig | 1 + drivers/firmware/psci/psci.c | 53 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig index 97944168b5e66aea1e38a7eb2d4ced8348fce64b..9d65fe7b06a6429de8a26d06f9384e5c93f36e5f 100644 --- a/drivers/firmware/psci/Kconfig +++ b/drivers/firmware/psci/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config ARM_PSCI_FW bool + 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..87293f78ed83eb33ba67ded73728729811693ea3 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -13,10 +13,13 @@ #include <linux/errno.h> #include <linux/linkage.h> #include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.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 +54,14 @@ 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; + bool psci_tos_resident_on(int cpu) { return cpu == resident_cpu; @@ -309,7 +320,14 @@ 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) { + /* + * if vendor_reset.valid is true call sys reset2 with + * the vendor_reset(reset_type and cookie). + */ + 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 +565,39 @@ 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, + u32 reset_type, u32 cookie) +{ + if (psci_system_reset2_supported) { + vendor_reset.reset_type = PSCI_1_1_RESET_TYPE_VENDOR_START | reset_type; + vendor_reset.cookie = cookie; + vendor_reset.valid = true; + } + + return NOTIFY_DONE; +} + +static int __init psci_init_vendor_reset(void) +{ + struct reboot_mode_driver *reboot; + struct device_node *np; + + np = of_find_node_by_name(NULL, "reset-types"); + if (!np) + return -ENODEV; + + reboot = kzalloc(sizeof(*reboot), GFP_KERNEL); + if (!reboot) { + of_node_put(np); + return -ENOMEM; + } + + reboot->write_with_cookie = psci_set_vendor_sys_reset2; + + return reboot_mode_register(reboot, np); +} +late_initcall(psci_init_vendor_reset) + static void __init psci_init_system_reset2(void) { int ret; -- 2.34.1