From f3df4211e39cc0f97c98f7b7cf835cee62b068fc Mon Sep 17 00:00:00 2001 From: Shivendra Pratap Date: Fri, 27 Mar 2026 19:56:10 +0530 Subject: [PATCH 1/3] power: reset: Add psci-reboot-mode driver PSCI supports different types of resets like COLD reset, ARCH WARM reset, vendor-specific resets. Currently there is no common driver that handles all supported psci resets at one place. Additionally, there is no common mechanism to issue the supported psci resets from userspace. Add a PSCI reboot mode driver and define two types of PSCI resets in the driver as reboot-modes: predefined resets controlled by Linux reboot_mode and customizable resets defined by SoC vendors in their device tree under the psci:reboot-mode node. Register the driver with the reboot-mode framework to interface these resets to userspace. When userspace initiates a supported command, pass the reset arguments to the PSCI driver to enable command-based reset. This change allows userspace to issue supported PSCI reset commands using the standard reboot system calls while enabling SoC vendors to define their specific resets for PSCI. Signed-off-by: Shivendra Pratap --- drivers/power/reset/Kconfig | 10 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/psci-reboot-mode.c | 92 ++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 drivers/power/reset/psci-reboot-mode.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f6c1bcbb57def..529d6c7d35556 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -348,6 +348,16 @@ config NVMEM_REBOOT_MODE then the bootloader can read it and take different action according to the mode. +config PSCI_REBOOT_MODE + bool "PSCI reboot mode driver" + depends on OF && ARM_PSCI_FW + select REBOOT_MODE + help + Say y here will enable PSCI reboot mode driver. This gets + the PSCI reboot mode arguments and passes them to psci + driver. psci driver uses these arguments for issuing + device reset into different boot states. + config POWER_MLXBF tristate "Mellanox BlueField power handling driver" depends on (GPIO_MLXBF2 || GPIO_MLXBF3) && ACPI diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 0e4ae6f6b5c55..49774b42cdf61 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -40,4 +40,5 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o +obj-$(CONFIG_PSCI_REBOOT_MODE) += psci-reboot-mode.o obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o diff --git a/drivers/power/reset/psci-reboot-mode.c b/drivers/power/reset/psci-reboot-mode.c new file mode 100644 index 0000000000000..1c92db5d0f69a --- /dev/null +++ b/drivers/power/reset/psci-reboot-mode.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Predefined reboot-modes are defined as per the values + * of enum reboot_mode defined in the kernel: reboot.c. + */ +static struct mode_info psci_resets[] = { + { .mode = "warm", .magic = REBOOT_WARM}, + { .mode = "soft", .magic = REBOOT_SOFT}, + { .mode = "cold", .magic = REBOOT_COLD}, +}; + +static void psci_reboot_mode_set_predefined_modes(struct reboot_mode_driver *reboot) +{ + INIT_LIST_HEAD(&reboot->predefined_modes); + for (u32 i = 0; i < ARRAY_SIZE(psci_resets); i++) { + /* Prepare the magic with arg1 as 0 and arg2 as per pre-defined mode */ + psci_resets[i].magic = REBOOT_MODE_MAGIC(0, psci_resets[i].magic); + INIT_LIST_HEAD(&psci_resets[i].list); + list_add_tail(&psci_resets[i].list, &reboot->predefined_modes); + } +} + +/* + * arg1 is reset_type(Low 32 bit of magic). + * arg2 is cookie(High 32 bit of magic). + * If reset_type is 0, cookie will be used to decide the reset command. + */ +static int psci_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic) +{ + u32 reset_type = REBOOT_MODE_ARG1(magic); + u32 cookie = REBOOT_MODE_ARG2(magic); + + pr_err("DEBUG: PSCI write called"); + pr_err("DEBUG: PSCI write called"); + pr_err("DEBUG: PSCI write called"); + if (reset_type == 0) { + if (cookie == REBOOT_WARM || cookie == REBOOT_SOFT) + psci_set_reset_cmd(true, 0, 0); + else + psci_set_reset_cmd(false, 0, 0); + } else { + psci_set_reset_cmd(true, reset_type, cookie); + } + + return NOTIFY_DONE; +} + +static int psci_reboot_mode_probe(struct platform_device *pdev) +{ + struct reboot_mode_driver *reboot; + int ret; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + psci_reboot_mode_set_predefined_modes(reboot); + reboot->write = psci_reboot_mode_write; + reboot->dev = &pdev->dev; + + ret = devm_reboot_mode_register(&pdev->dev, reboot); + if (ret) { + dev_err_probe(&pdev->dev, ret, "devm_reboot_mode_register failed %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver psci_reboot_mode_driver = { + .probe = psci_reboot_mode_probe, + .driver = { + .name = "psci-reboot-mode", + }, +}; + +module_platform_driver(psci_reboot_mode_driver); From 495aadfa2dc3968442003ffd52b221a9b9aa869f Mon Sep 17 00:00:00 2001 From: Shivendra Pratap Date: Wed, 25 Mar 2026 19:42:52 +0530 Subject: [PATCH 2/3] mfd: psci-mfd: Introduce psci mfd driver for cpuidle-psci-domain cell Add a new PSCI MFD driver that binds to arm,psci-1.0 and registers cpuidle-psci-domain as a child cell. Since cpuidle-psci-domain now probes without its own of_node, use the parent PSCI of_node to traverse the power domain. Signed-off-by: Shivendra Pratap --- drivers/cpuidle/Kconfig.arm | 1 + drivers/cpuidle/cpuidle-psci-domain.c | 2 +- drivers/mfd/Kconfig | 9 ++++++ drivers/mfd/Makefile | 2 ++ drivers/mfd/psci-mfd.c | 42 +++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/psci-mfd.c diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index a1ee475d180da..e7242f29781ba 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -37,6 +37,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN depends on ARM_PSCI_CPUIDLE depends on PM_GENERIC_DOMAINS_OF select DT_IDLE_GENPD + select MFD_PSCI default y help Select this to enable the PSCI based CPUidle driver to use PM domains, diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index b9e4ad7d43a33..916318bc22665 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -129,7 +129,7 @@ static const struct of_device_id psci_of_match[] = { static int psci_cpuidle_domain_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.parent->of_node; bool use_osi = psci_has_osi_support(); int ret = 0, pd_count = 0; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268e..733de4a55ba77 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2355,6 +2355,15 @@ config MFD_ATC260X_I2C and ATC2609A chip variants, additional drivers must be enabled in order to use the functionality of the device. +config MFD_PSCI + bool "PSCI MFD for psci child cells" + depends on ARM_PSCI_FW + help + PSCI MFD registers PSCI child cells and exposes them as + platform devices. Child drivers are probed only if enabled in the + kernel configuration. Select this option whenever a supported PSCI + child driver is selected. + config MFD_KHADAS_MCU tristate "Support for Khadas System control Microcontroller" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28af..36e872b11b995 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_MFD_MACSMC) += macsmc.o +obj-$(CONFIG_MFD_PSCI) += psci-mfd.o + obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o diff --git a/drivers/mfd/psci-mfd.c b/drivers/mfd/psci-mfd.c new file mode 100644 index 0000000000000..fc508adf69b93 --- /dev/null +++ b/drivers/mfd/psci-mfd.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +static const struct mfd_cell psci_cells[] = { + { + .name = "psci-cpuidle-domain", + }, +}; + +static int psci_mfd_probe(struct platform_device *pdev) +{ + return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, psci_cells, + ARRAY_SIZE(psci_cells), NULL, 0, NULL); +} + +static const struct of_device_id psci_mfd_of_match[] = { + { .compatible = "arm,psci-1.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, psci_mfd_of_match); + +static struct platform_driver psci_mfd_driver = { + .probe = psci_mfd_probe, + .driver = { + .name = "psci-mfd", + .of_match_table = psci_mfd_of_match, + }, +}; + +static int __init psci_mfd_init(void) +{ + return platform_driver_register(&psci_mfd_driver); +} + +core_initcall(psci_mfd_init); From 07bac492ff8d0a80845ebe729bfe1d848f6974b0 Mon Sep 17 00:00:00 2001 From: Shivendra Pratap Date: Fri, 27 Mar 2026 20:04:45 +0530 Subject: [PATCH 3/3] mfd: psci-mfd: Add psci-reboot-mode as psci-mfd cell Add a psci-reboot-mode cell to the psci-mfd driver and bind the psci:reboot-mode node to it. Signed-off-by: Shivendra Pratap --- drivers/mfd/psci-mfd.c | 55 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/drivers/mfd/psci-mfd.c b/drivers/mfd/psci-mfd.c index fc508adf69b93..c997498ee5d19 100644 --- a/drivers/mfd/psci-mfd.c +++ b/drivers/mfd/psci-mfd.c @@ -4,20 +4,71 @@ */ #include +#include #include #include #include +#include static const struct mfd_cell psci_cells[] = { { .name = "psci-cpuidle-domain", }, + { + .name = "psci-reboot-mode", + }, }; +static int psci_mfd_match_pdev_name(struct device *dev, const void *data) +{ + struct platform_device *pdev; + + if (dev->bus != &platform_bus_type) + return 0; + + pdev = to_platform_device(dev); + + return !strcmp(pdev->name, data); +} + +static void psci_mfd_bind_reboot_mode_node(struct platform_device *pdev) +{ + struct device_node *np; + struct device *child; + + if (!pdev->dev.of_node) + return; + + np = of_get_child_by_name(pdev->dev.of_node, "reboot-mode"); + if (!np) + return; + + child = device_find_child(&pdev->dev, "psci-reboot-mode", + psci_mfd_match_pdev_name); + if (!child) { + dev_dbg(&pdev->dev, "psci-reboot-mode child not found\n"); + of_node_put(np); + return; + } + + device_set_node(child, of_fwnode_handle(np)); + put_device(child); + of_node_put(np); +} + static int psci_mfd_probe(struct platform_device *pdev) { - return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, psci_cells, - ARRAY_SIZE(psci_cells), NULL, 0, NULL); + int ret; + + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, psci_cells, + ARRAY_SIZE(psci_cells), NULL, 0, NULL); + if (ret) + goto out; + + psci_mfd_bind_reboot_mode_node(pdev); + +out: + return ret; } static const struct of_device_id psci_mfd_of_match[] = {