Add a PSCI backend for the reboot-mode framework that triggers a
SYSTEM_RESET2 vendor-specific reset described by a "mode-*" property under
a "reboot-mode" subnode of the PSCI device tree node (for example
"mode-edl = <0x80000000 0x00000001>" to enter Qualcomm EDL/download mode).

All device tree parsing and name matching already live in the reboot-mode
uclass; this driver only turns a decoded set of magic cells into a
firmware call.

U-Boot's psci_system_reset2() client takes only a 32-bit cookie and ORs in
PSCI_RESET2_TYPE_VENDOR itself, so the trigger folds the low cells into a
cookie and rejects a 3-cell mode whose cookie_hi is non-zero rather than
silently truncating it.

psci_bind() binds the driver to the "reboot-mode" subnode when
CONFIG_REBOOT_MODE_PSCI is enabled, using device_bind_driver_to_node().

Signed-off-by: Balaji Selvanathan <[email protected]>
---
 drivers/firmware/psci.c                | 16 ++++++++++
 drivers/reboot-mode/Kconfig            | 11 +++++++
 drivers/reboot-mode/Makefile           |  1 +
 drivers/reboot-mode/reboot-mode-psci.c | 58 ++++++++++++++++++++++++++++++++++
 4 files changed, 86 insertions(+)

diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 2e3223e1c32..781a848e350 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -188,6 +188,22 @@ static int psci_bind(struct udevice *dev)
                        pr_debug("PSCI System Reset was not bound.\n");
        }
 
+       /*
+        * Bind the PSCI reboot-mode driver to the "reboot-mode" subnode so it
+        * can trigger SYSTEM_RESET2 vendor resets described by "mode-*"
+        * properties there (for example EDL/download mode).
+        */
+       if (CONFIG_IS_ENABLED(REBOOT_MODE_PSCI)) {
+               ofnode rm_node = ofnode_find_subnode(dev_ofnode(dev),
+                                                    "reboot-mode");
+
+               if (ofnode_valid(rm_node) &&
+                   device_bind_driver_to_node(dev, "reboot-mode-psci",
+                                              "reboot-mode-psci", rm_node,
+                                              NULL))
+                       pr_warn("PSCI reboot-mode was not bound.\n");
+       }
+
        /* From PSCI v1.0 onward we can discover services through 
ARM_SMCCC_FEATURE */
        if (IS_ENABLED(CONFIG_ARM_SMCCC_FEATURES) && device_is_compatible(dev, 
"arm,psci-1.0"))
                dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND);
diff --git a/drivers/reboot-mode/Kconfig b/drivers/reboot-mode/Kconfig
index 3fdb4218a8b..109a196a3c9 100644
--- a/drivers/reboot-mode/Kconfig
+++ b/drivers/reboot-mode/Kconfig
@@ -39,4 +39,15 @@ config REBOOT_MODE_NVMEM
          Use any kind of non-volatile memory (EEPROM, RTC, etc) to control the
          reboot mode.
 
+config REBOOT_MODE_PSCI
+       bool "Use PSCI SYSTEM_RESET2 vendor resets as reboot modes"
+       depends on DM_REBOOT_MODE && ARM_SMCCC
+       help
+         Trigger PSCI SYSTEM_RESET2 vendor-specific resets (for example
+         rebooting a Qualcomm SoC into EDL/download mode) via the reboot-mode
+         framework. Each reset is described by a "mode-*" property under the
+         "reboot-mode" subnode of the psci device tree node, so the magic
+         values are not hard-coded in the driver. The modes can then be
+         triggered from the "reset" command by name.
+
 endmenu
diff --git a/drivers/reboot-mode/Makefile b/drivers/reboot-mode/Makefile
index 48c8ab7fe71..56c7078258b 100644
--- a/drivers/reboot-mode/Makefile
+++ b/drivers/reboot-mode/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode-uclass.o
 obj-$(CONFIG_DM_REBOOT_MODE_GPIO) += reboot-mode-gpio.o
 obj-$(CONFIG_DM_REBOOT_MODE_RTC) += reboot-mode-rtc.o
 obj-$(CONFIG_REBOOT_MODE_NVMEM) += reboot-mode-nvmem.o
+obj-$(CONFIG_REBOOT_MODE_PSCI) += reboot-mode-psci.o
diff --git a/drivers/reboot-mode/reboot-mode-psci.c 
b/drivers/reboot-mode/reboot-mode-psci.c
new file mode 100644
index 00000000000..88133c68ab4
--- /dev/null
+++ b/drivers/reboot-mode/reboot-mode-psci.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <reboot-mode/reboot-mode.h>
+#include <asm/psci.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+static int psci_reboot_mode_trigger(struct udevice *dev, const u32 *magic,
+                                   int count)
+{
+       u32 reset_type = magic[0];
+       u64 cookie = 0;
+       int i;
+
+       if (count < 1 || count > REBOOT_MODE_MAX_MAGIC)
+               return -EINVAL;
+
+       /*
+        * U-Boot's psci_system_reset2() takes only a 32-bit cookie, whereas
+        * the binding allows a 64-bit cookie in the 3-cell form (magic[1] is
+        * cookie_hi, magic[2] is cookie_lo). Reject a non-zero high half
+        * rather than silently truncating it.
+        */
+       if (count == REBOOT_MODE_MAX_MAGIC && magic[1]) {
+               dev_err(dev, "64-bit reset cookie is not supported\n");
+               return -EINVAL;
+       }
+
+       for (i = 1; i < count; i++)
+               cookie = (cookie << 32) | magic[i];
+
+       if (psci_features(ARM_PSCI_1_1_FN64_SYSTEM_RESET2) !=
+           ARM_PSCI_RET_SUCCESS) {
+               dev_err(dev, "SYSTEM_RESET2 is not supported by firmware\n");
+               return -EPROTONOSUPPORT;
+       }
+
+       /* Does not return on success. */
+       psci_system_reset2(reset_type, (u32)cookie);
+
+       return -EINPROGRESS;
+}
+
+static const struct reboot_mode_ops psci_reboot_mode_ops = {
+       .trigger = psci_reboot_mode_trigger,
+};
+
+U_BOOT_DRIVER(psci_reboot_mode) = {
+       .name = "reboot-mode-psci",
+       .id = UCLASS_REBOOT_MODE,
+       .ops = &psci_reboot_mode_ops,
+};

-- 
2.34.1

Reply via email to