The reboot-mode framework so far only reads a value from a backing store
on boot (get/set) and maps it to an environment variable. Triggering a
reset into a specific mode on demand - e.g. rebooting a Qualcomm SoC into
EDL via a PSCI SYSTEM_RESET2 vendor reset - needs a different approach.

Add an trigger() op to struct reboot_mode_ops. It takes the
decoded magic cells for a mode and resets the system immediately; on
success it does not return. Backing-store drivers (nvmem, gpio, rtc)
leave it NULL and are unaffected.

Add two uclass helpers built on top of it:

  - reboot_mode_request(name) looks up a mode by name across all
    UCLASS_REBOOT_MODE devices and, if the owning device can trigger,
    resets into it. Returns -ENOENT if no triggerable mode matches.

  - reboot_mode_list() enumerates every triggerable mode registered with
    the framework, so a command can present the available modes to the
    user rather than hard-coding them.

Signed-off-by: Balaji Selvanathan <[email protected]>
---
 drivers/reboot-mode/reboot-mode-uclass.c | 48 ++++++++++++++++++++++++++++++++
 include/reboot-mode/reboot-mode.h        | 40 ++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)

diff --git a/drivers/reboot-mode/reboot-mode-uclass.c 
b/drivers/reboot-mode/reboot-mode-uclass.c
index 80aabf54750..866a56ecb7b 100644
--- a/drivers/reboot-mode/reboot-mode-uclass.c
+++ b/drivers/reboot-mode/reboot-mode-uclass.c
@@ -54,6 +54,54 @@ int dm_reboot_mode_update(struct udevice *dev)
        return 0;
 }
 
+int reboot_mode_request(const char *name)
+{
+       const struct reboot_mode_uclass_platdata *plat_data;
+       struct reboot_mode_ops *ops;
+       struct udevice *dev;
+       int i;
+
+       uclass_foreach_dev_probe(UCLASS_REBOOT_MODE, dev) {
+               ops = reboot_mode_get_ops(dev);
+               if (!ops || !ops->trigger)
+                       continue;
+
+               plat_data = dev_get_uclass_plat(dev);
+               for (i = 0; i < plat_data->count; i++) {
+                       if (strcmp(plat_data->modes[i].mode_name, name))
+                               continue;
+
+                       /* Does not return on success. */
+                       return ops->trigger(dev, plat_data->modes[i].magic,
+                                           plat_data->modes[i].count);
+               }
+       }
+
+       return -ENOENT;
+}
+
+int reboot_mode_list(void)
+{
+       const struct reboot_mode_uclass_platdata *plat_data;
+       struct reboot_mode_ops *ops;
+       struct udevice *dev;
+       int i;
+
+       printf("Available reset modes:\n");
+
+       uclass_foreach_dev_probe(UCLASS_REBOOT_MODE, dev) {
+               ops = reboot_mode_get_ops(dev);
+               if (!ops || !ops->trigger)
+                       continue;
+
+               plat_data = dev_get_uclass_plat(dev);
+               for (i = 0; i < plat_data->count; i++)
+                       printf("  %s\n", plat_data->modes[i].mode_name);
+       }
+
+       return 0;
+}
+
 int dm_reboot_mode_pre_probe(struct udevice *dev)
 {
        struct reboot_mode_uclass_platdata *plat_data;
diff --git a/include/reboot-mode/reboot-mode.h 
b/include/reboot-mode/reboot-mode.h
index 54a8c09650e..67be5e4b3eb 100644
--- a/include/reboot-mode/reboot-mode.h
+++ b/include/reboot-mode/reboot-mode.h
@@ -46,6 +46,22 @@ struct reboot_mode_ops {
         * @rebootmode: New reboot mode value to store
         */
        int (*set)(struct udevice *dev, u32 rebootmode);
+
+       /**
+        * trigger() - reset the system into the given mode now
+        *
+        * Unlike get()/set(), which persist a value for the next boot to
+        * consume, trigger() performs the reset immediately. On success it
+        * does not return. Drivers with a backing store (nvmem, gpio, rtc)
+        * leave this NULL.
+        *
+        * @dev:        Device to trigger
+        * @magic:      Array of @count 32-bit magic cells describing the mode
+        * @count:      Number of valid cells in @magic (1 to
+        *              REBOOT_MODE_MAX_MAGIC)
+        * Return: does not return on success; -ve on error
+        */
+       int (*trigger)(struct udevice *dev, const u32 *magic, int count);
 };
 
 /* Access the operations for a reboot mode device */
@@ -59,4 +75,28 @@ struct reboot_mode_ops {
  */
 int dm_reboot_mode_update(struct udevice *dev);
 
+/**
+ * reboot_mode_request() - reset the system into a named mode
+ *
+ * Search every UCLASS_REBOOT_MODE device for a mode named @name and, if the
+ * owning device supports triggering, reset into it. On success this does not
+ * return.
+ *
+ * @name:      Mode name (without the device tree "mode-" prefix)
+ * Return: does not return on success; -ENOENT if no device declares a
+ *        triggerable mode called @name; other -ve on error
+ */
+int reboot_mode_request(const char *name);
+
+/**
+ * reboot_mode_list() - print all triggerable reboot modes
+ *
+ * Enumerate every UCLASS_REBOOT_MODE device and print the name of each mode
+ * that can be triggered (i.e. whose device implements the trigger op). Modes
+ * that only exist to be read from a backing store on boot are not listed.
+ *
+ * Return: 0 if OK, -ve on error
+ */
+int reboot_mode_list(void);
+
 #endif /* REBOOT_MODE_REBOOT_MODE_H__ */

-- 
2.34.1

Reply via email to