[PATCH v19 06/10] power: reset: Add psci-reboot-mode driver

Shivendra Pratap posted 10 patches 1 month, 1 week ago
[PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Shivendra Pratap 1 month, 1 week ago
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 <shivendra.pratap@oss.qualcomm.com>
---
 drivers/power/reset/Kconfig            |  10 +++
 drivers/power/reset/Makefile           |   1 +
 drivers/power/reset/psci-reboot-mode.c | 111 +++++++++++++++++++++++++++++++++
 3 files changed, 122 insertions(+)

diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index f6c1bcbb57deff3568d6b1b326454add3b3bbf06..529d6c7d3555601f7b7e6199acd29838030fcef2 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 0e4ae6f6b5c55729cf60846d47e6fe0fec24f3cc..49774b42cdf61fd57a5b70f286c65c9d66bbc0cb 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 0000000000000000000000000000000000000000..499cf504071e88022fa5b5b32e26b7a674da8691
--- /dev/null
+++ b/drivers/power/reset/psci-reboot-mode.c
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/device/faux.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/psci.h>
+#include <linux/reboot.h>
+#include <linux/reboot-mode.h>
+#include <linux/types.h>
+
+/*
+ * Predefined reboot-modes:
+ * reset_type(arg1) is zero; cookie(arg2) is stored in magic.
+ * psci_reboot_mode_set_predefined_modes to move values to higher 32 bit of magic.
+ */
+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++) {
+		/* Move values to higher 32 bit of magic */
+		psci_resets[i].magic = FIELD_PREP(GENMASK_ULL(63, 32), psci_resets[i].magic);
+		INIT_LIST_HEAD(&psci_resets[i].list);
+		list_add_tail(&psci_resets[i].list, &reboot->predefined_modes);
+	}
+}
+
+/*
+ * magic is 64 bit.
+ * arg1 - reset_type(Low 32 bit of magic).
+ * arg2 - cookie(High 32 bit of magic).
+ * arg2(cookie) decides the mode, If arg1(reset_type) is 0;
+ */
+static int psci_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic)
+{
+	u32 reset_type = FIELD_GET(GENMASK_ULL(31, 0), magic);
+	u32 cookie = FIELD_GET(GENMASK_ULL(63, 32), magic);
+
+	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 faux_device *fdev)
+{
+	struct reboot_mode_driver *reboot;
+	struct device_node *psci_np;
+	struct device_node *np;
+	int ret;
+
+	psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
+	if (!psci_np)
+		return -ENODEV;
+
+	/*
+	 * Find the psci:reboot-mode node.
+	 * If NULL, continue to register predefined modes.
+	 * np refcount to be handled by dev;
+	 * psci_np refcount is decremented by of_find_node_by_name;
+	 */
+	np = of_find_node_by_name(psci_np, "reboot-mode");
+	fdev->dev.of_node = np;
+
+	reboot = devm_kzalloc(&fdev->dev, sizeof(*reboot), GFP_KERNEL);
+	if (!reboot)
+		return -ENOMEM;
+
+	psci_reboot_mode_set_predefined_modes(reboot);
+	reboot->write = psci_reboot_mode_write;
+	reboot->dev = &fdev->dev;
+
+	ret = devm_reboot_mode_register(&fdev->dev, reboot);
+	if (ret) {
+		dev_err(&fdev->dev, "devm_reboot_mode_register failed %d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct faux_device_ops psci_reboot_mode_ops = {
+	.probe = psci_reboot_mode_probe,
+};
+
+static int __init psci_reboot_mode_init(void)
+{
+	struct faux_device *fdev;
+
+	fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
+	if (!fdev)
+		return -ENODEV;
+
+	return 0;
+}
+device_initcall(psci_reboot_mode_init);

-- 
2.34.1
Re: [PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Bartosz Golaszewski 1 month, 1 week ago
On Sun, Dec 28, 2025 at 6:21 PM Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> wrote:
>

[snip]

> +
> +static int psci_reboot_mode_probe(struct faux_device *fdev)
> +{
> +       struct reboot_mode_driver *reboot;
> +       struct device_node *psci_np;
> +       struct device_node *np;
> +       int ret;
> +
> +       psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
> +       if (!psci_np)
> +               return -ENODEV;
> +
> +       /*
> +        * Find the psci:reboot-mode node.
> +        * If NULL, continue to register predefined modes.
> +        * np refcount to be handled by dev;
> +        * psci_np refcount is decremented by of_find_node_by_name;
> +        */

Can you make this comment into full sentences, I had trouble parsing
the meaning for a minute.

> +       np = of_find_node_by_name(psci_np, "reboot-mode");
> +       fdev->dev.of_node = np;

The logic of the device assigning its own node is a bit sketchy,
ideally this should be done before the device probes.

> +
> +       reboot = devm_kzalloc(&fdev->dev, sizeof(*reboot), GFP_KERNEL);
> +       if (!reboot)
> +               return -ENOMEM;
> +
> +       psci_reboot_mode_set_predefined_modes(reboot);
> +       reboot->write = psci_reboot_mode_write;
> +       reboot->dev = &fdev->dev;
> +
> +       ret = devm_reboot_mode_register(&fdev->dev, reboot);
> +       if (ret) {
> +               dev_err(&fdev->dev, "devm_reboot_mode_register failed %d\n", ret);
> +               return ret;

Use dev_err_probe().

> +       }
> +
> +       return 0;
> +}
> +
> +static struct faux_device_ops psci_reboot_mode_ops = {
> +       .probe = psci_reboot_mode_probe,
> +};
> +
> +static int __init psci_reboot_mode_init(void)
> +{
> +       struct faux_device *fdev;
> +
> +       fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
> +       if (!fdev)
> +               return -ENODEV;

This will always create this device for everyone who includes this
module. Move the of_find_compatible_node(NULL, NULL, "arm,psci-1.0")
call from probe() here instead and don't create the device if it
fails.

Bart

> +
> +       return 0;
> +}
> +device_initcall(psci_reboot_mode_init);
>
> --
> 2.34.1
>
Re: [PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Shivendra Pratap 1 month ago

On 1/2/2026 5:27 PM, Bartosz Golaszewski wrote:
> On Sun, Dec 28, 2025 at 6:21 PM Shivendra Pratap
> <shivendra.pratap@oss.qualcomm.com> wrote:
>>
> 
> [snip]
> 
>> +
>> +static int psci_reboot_mode_probe(struct faux_device *fdev)
>> +{
>> +       struct reboot_mode_driver *reboot;
>> +       struct device_node *psci_np;
>> +       struct device_node *np;
>> +       int ret;
>> +
>> +       psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
>> +       if (!psci_np)
>> +               return -ENODEV;
>> +
>> +       /*
>> +        * Find the psci:reboot-mode node.
>> +        * If NULL, continue to register predefined modes.
>> +        * np refcount to be handled by dev;
>> +        * psci_np refcount is decremented by of_find_node_by_name;
>> +        */
> 
> Can you make this comment into full sentences, I had trouble parsing
> the meaning for a minute.

Ack.

> 
>> +       np = of_find_node_by_name(psci_np, "reboot-mode");
>> +       fdev->dev.of_node = np;
> 
> The logic of the device assigning its own node is a bit sketchy,
> ideally this should be done before the device probes.

Will move it to init. thanks.

> 
>> +
>> +       reboot = devm_kzalloc(&fdev->dev, sizeof(*reboot), GFP_KERNEL);
>> +       if (!reboot)
>> +               return -ENOMEM;
>> +
>> +       psci_reboot_mode_set_predefined_modes(reboot);
>> +       reboot->write = psci_reboot_mode_write;
>> +       reboot->dev = &fdev->dev;
>> +
>> +       ret = devm_reboot_mode_register(&fdev->dev, reboot);
>> +       if (ret) {
>> +               dev_err(&fdev->dev, "devm_reboot_mode_register failed %d\n", ret);
>> +               return ret;
> 
> Use dev_err_probe().

Ack.

> 
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static struct faux_device_ops psci_reboot_mode_ops = {
>> +       .probe = psci_reboot_mode_probe,
>> +};
>> +
>> +static int __init psci_reboot_mode_init(void)
>> +{
>> +       struct faux_device *fdev;
>> +
>> +       fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
>> +       if (!fdev)
>> +               return -ENODEV;
> 
> This will always create this device for everyone who includes this
> module. Move the of_find_compatible_node(NULL, NULL, "arm,psci-1.0")
> call from probe() here instead and don't create the device if it
> fails.

Ack. 
Will move both calls to init before creating the faux device. 

psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
and 
np = of_find_node_by_name(psci_np, "reboot-mode");
--

thanks,
Shivendra
Re: [PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Bartosz Golaszewski 1 month ago
On Mon, Jan 5, 2026 at 7:06 PM Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> wrote:
>
> >> +static int __init psci_reboot_mode_init(void)
> >> +{
> >> +       struct faux_device *fdev;
> >> +
> >> +       fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
> >> +       if (!fdev)
> >> +               return -ENODEV;
> >
> > This will always create this device for everyone who includes this
> > module. Move the of_find_compatible_node(NULL, NULL, "arm,psci-1.0")
> > call from probe() here instead and don't create the device if it
> > fails.
>
> Ack.
> Will move both calls to init before creating the faux device.
>
> psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
> and
> np = of_find_node_by_name(psci_np, "reboot-mode");
> --
>

On a second glance - and I may be totally wrong - would it be possible
to switch to using the auxiliary bus and create this device from
drivers/firmware/psci/psci.c? That would be even cleaner.

Bart
Re: [PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Shivendra Pratap 1 month ago

On 1/6/2026 6:04 PM, Bartosz Golaszewski wrote:
> On Mon, Jan 5, 2026 at 7:06 PM Shivendra Pratap
> <shivendra.pratap@oss.qualcomm.com> wrote:
>>
>>>> +static int __init psci_reboot_mode_init(void)
>>>> +{
>>>> +       struct faux_device *fdev;
>>>> +
>>>> +       fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
>>>> +       if (!fdev)
>>>> +               return -ENODEV;
>>>
>>> This will always create this device for everyone who includes this
>>> module. Move the of_find_compatible_node(NULL, NULL, "arm,psci-1.0")
>>> call from probe() here instead and don't create the device if it
>>> fails.
>>
>> Ack.
>> Will move both calls to init before creating the faux device.
>>
>> psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
>> and
>> np = of_find_node_by_name(psci_np, "reboot-mode");
>> --
>>
> 
> On a second glance - and I may be totally wrong - would it be possible
> to switch to using the auxiliary bus and create this device from
> drivers/firmware/psci/psci.c? That would be even cleaner.

Till v17, device was being created in psci.c. Lorenzo wanted to move it
outside psci similar to design of cpuidle-psci.
 
https://lore.kernel.org/all/aRIfc9iuC2b9DqI+@lpieralisi/

thanks,
Shivendra
Re: [PATCH v19 06/10] power: reset: Add psci-reboot-mode driver
Posted by Bartosz Golaszewski 1 month ago
On Tue, Jan 6, 2026 at 3:45 PM Shivendra Pratap
<shivendra.pratap@oss.qualcomm.com> wrote:
>
> On 1/6/2026 6:04 PM, Bartosz Golaszewski wrote:
> > On Mon, Jan 5, 2026 at 7:06 PM Shivendra Pratap
> > <shivendra.pratap@oss.qualcomm.com> wrote:
> >>
> >>>> +static int __init psci_reboot_mode_init(void)
> >>>> +{
> >>>> +       struct faux_device *fdev;
> >>>> +
> >>>> +       fdev = faux_device_create("psci-reboot-mode", NULL, &psci_reboot_mode_ops);
> >>>> +       if (!fdev)
> >>>> +               return -ENODEV;
> >>>
> >>> This will always create this device for everyone who includes this
> >>> module. Move the of_find_compatible_node(NULL, NULL, "arm,psci-1.0")
> >>> call from probe() here instead and don't create the device if it
> >>> fails.
> >>
> >> Ack.
> >> Will move both calls to init before creating the faux device.
> >>
> >> psci_np = of_find_compatible_node(NULL, NULL, "arm,psci-1.0");
> >> and
> >> np = of_find_node_by_name(psci_np, "reboot-mode");
> >> --
> >>
> >
> > On a second glance - and I may be totally wrong - would it be possible
> > to switch to using the auxiliary bus and create this device from
> > drivers/firmware/psci/psci.c? That would be even cleaner.
>
> Till v17, device was being created in psci.c. Lorenzo wanted to move it
> outside psci similar to design of cpuidle-psci.
>
> https://lore.kernel.org/all/aRIfc9iuC2b9DqI+@lpieralisi/
>

Thanks for the link. Right, there's no actual psci driver binding to a
struct device, rather we only have a set of functions called very
early into the boot process.

Nevermind this comment

Bart