[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250727-arm-psci-system_reset2-vendor-reboots-v13-5-6b8d23315898@oss.qualcomm.com>
Date: Sun, 27 Jul 2025 21:54:48 +0530
From: Shivendra Pratap <shivendra.pratap@....qualcomm.com>
To: Bartosz Golaszewski <bartosz.golaszewski@...aro.org>,
Bjorn Andersson <andersson@...nel.org>,
Sebastian Reichel <sre@...nel.org>, Rob Herring <robh@...nel.org>,
Sudeep Holla <sudeep.holla@....com>,
Souvik Chakravarty <Souvik.Chakravarty@....com>,
Krzysztof Kozlowski <krzk+dt@...nel.org>,
Conor Dooley <conor+dt@...nel.org>, Andy Yan <andy.yan@...k-chips.com>,
Mark Rutland <mark.rutland@....com>,
Lorenzo Pieralisi <lpieralisi@...nel.org>,
Arnd Bergmann <arnd@...db.de>, Konrad Dybcio <konradybcio@...nel.org>,
cros-qcom-dts-watchers@...omium.org, Vinod Koul <vkoul@...nel.org>,
Catalin Marinas <catalin.marinas@....com>,
Will Deacon <will@...nel.org>,
Florian Fainelli <florian.fainelli@...adcom.com>
Cc: Dmitry Baryshkov <dmitry.baryshkov@....qualcomm.com>,
Mukesh Ojha <mukesh.ojha@....qualcomm.com>,
Stephen Boyd <swboyd@...omium.org>,
Andre Draszik <andre.draszik@...aro.org>, linux-pm@...r.kernel.org,
linux-kernel@...r.kernel.org, devicetree@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org, linux-arm-msm@...r.kernel.org,
Elliot Berman <quic_eberman@...cinc.com>,
Shivendra Pratap <shivendra.pratap@....qualcomm.com>,
Srinivas Kandagatla <srini@...nel.org>
Subject: [PATCH v13 05/10] power: reset: reboot-mode: Expose sysfs for
registered reboot_modes
Currently, there is no standardized mechanism for userspace to
discover which reboot-modes are supported on a given platform.
This limitation forces tools and scripts to rely on hardcoded
assumptions about the supported reboot-modes.
Create a class 'reboot-mode' and a device under it to expose a
sysfs interface to show the available reboot mode arguments to
userspace.
Provision the register function with an additional parameter to
get an explicit driver_name. Create the device using this
driver_name. For platform drivers, use the driver_name configured
in dev node.
This results in the creation of:
/sys/class/reboot-mode/<driver>/reboot_modes
This read-only sysfs file will exposes the list of supported
reboot modes arguments provided by the driver, enabling userspace
to query the list of arguments.
Align the clean up path to maintain backward compatibility for
existing reboot-mode based drivers.
Signed-off-by: Shivendra Pratap <shivendra.pratap@....qualcomm.com>
---
drivers/power/reset/reboot-mode.c | 126 ++++++++++++++++++++++++++++++--------
include/linux/reboot-mode.h | 4 +-
2 files changed, 105 insertions(+), 25 deletions(-)
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index ac81b8b0a9b7fc31f8ef21024333a050087ce90f..4c23ff912fc6afbc6949bfdd6607781eccb4e85e 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) "reboot-mode: " fmt
#include <linux/device.h>
+#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -23,6 +24,8 @@ struct mode_info {
struct list_head list;
};
+static struct class *rb_class;
+
static u64 get_reboot_mode_magic(struct reboot_mode_driver *reboot, const char *cmd)
{
const char *normal = "normal";
@@ -75,26 +78,116 @@ static int reboot_mode_notify(struct notifier_block *this,
return NOTIFY_DONE;
}
+static void release_reboot_mode_device(struct device *dev, void *res);
+
+static ssize_t reboot_modes_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct reboot_mode_driver **devres_reboot;
+ struct reboot_mode_driver *reboot;
+ struct mode_info *info;
+ ssize_t size = 0;
+
+ devres_reboot = devres_find(dev, release_reboot_mode_device, NULL, NULL);
+ if (!devres_reboot || !(*devres_reboot))
+ return -ENODATA;
+
+ reboot = *devres_reboot;
+ mutex_lock(&reboot->rb_lock);
+ list_for_each_entry(info, &reboot->head, list) {
+ size += sprintf(buf + size, "%s,", info->mode);
+ }
+ mutex_unlock(&reboot->rb_lock);
+
+ if (size) {
+ size += sprintf(buf + size - 1, "\n");
+ return size;
+ }
+
+ return -ENODATA;
+}
+static DEVICE_ATTR_RO(reboot_modes);
+
+static void release_reboot_mode_device(struct device *dev, void *res)
+{
+ struct reboot_mode_driver *reboot = *(struct reboot_mode_driver **)res;
+ struct mode_info *info;
+ struct mode_info *next;
+
+ unregister_reboot_notifier(&reboot->reboot_notifier);
+
+ mutex_lock(&reboot->rb_lock);
+ list_for_each_entry_safe(info, next, &reboot->head, list) {
+ list_del(&info->list);
+ kfree_const(info->mode);
+ kfree(info);
+ }
+ mutex_unlock(&reboot->rb_lock);
+
+ device_remove_file(reboot->reboot_dev, &dev_attr_reboot_modes);
+}
+
+static int create_reboot_mode_device(struct reboot_mode_driver *reboot,
+ const char *dev_name)
+{
+ struct reboot_mode_driver **dr;
+ int ret = 0;
+
+ if (!rb_class) {
+ rb_class = class_create("reboot-mode");
+ if (IS_ERR(rb_class))
+ return PTR_ERR(rb_class);
+ }
+
+ reboot->reboot_dev = device_create(rb_class, NULL, 0, NULL, dev_name);
+ if (IS_ERR(reboot->reboot_dev))
+ return PTR_ERR(reboot->reboot_dev);
+
+ ret = device_create_file(reboot->reboot_dev, &dev_attr_reboot_modes);
+ if (ret)
+ return ret;
+
+ dr = devres_alloc(release_reboot_mode_device, sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ device_remove_file(reboot->reboot_dev, &dev_attr_reboot_modes);
+ return -ENOMEM;
+ }
+
+ *dr = reboot;
+ devres_add(reboot->reboot_dev, dr);
+
+ return ret;
+}
+
/**
* reboot_mode_register - register a reboot mode driver
* @reboot: reboot mode driver
* @np: Pointer to device tree node
+ * @driver_name: Name to use when exposing the sysfs interface
+ *
+ * Registers a reboot mode driver and sets up its sysfs entries
+ * under /sys/class/reboot-mode/<driver_name>/ to allow userspace
+ * interaction with available reboot modes. The DT node is used
+ * for parsing reboot-mode arguments.
*
* Returns: 0 on success or a negative error code on failure.
*/
-int reboot_mode_register(struct reboot_mode_driver *reboot, struct device_node *np)
+int reboot_mode_register(struct reboot_mode_driver *reboot, struct device_node *np,
+ const char *driver_name)
{
struct mode_info *info;
- struct mode_info *next;
struct property *prop;
size_t len = strlen(PREFIX);
u32 magic_arg1;
u32 magic_arg2;
int ret;
- if (!np)
+ if (!np || !driver_name)
return -EINVAL;
+ ret = create_reboot_mode_device(reboot, driver_name);
+ if (ret)
+ return ret;
+
INIT_LIST_HEAD(&reboot->head);
mutex_init(&reboot->rb_lock);
@@ -144,13 +237,8 @@ int reboot_mode_register(struct reboot_mode_driver *reboot, struct device_node *
return 0;
error:
- list_for_each_entry_safe(info, next, &reboot->head, list) {
- list_del(&info->list);
- kfree_const(info->mode);
- kfree(info);
- }
-
mutex_unlock(&reboot->rb_lock);
+ device_unregister(reboot->reboot_dev);
return ret;
}
EXPORT_SYMBOL_GPL(reboot_mode_register);
@@ -161,26 +249,16 @@ EXPORT_SYMBOL_GPL(reboot_mode_register);
*/
int reboot_mode_unregister(struct reboot_mode_driver *reboot)
{
- struct mode_info *info;
- struct mode_info *next;
-
- unregister_reboot_notifier(&reboot->reboot_notifier);
-
- mutex_lock(&reboot->rb_lock);
- list_for_each_entry_safe(info, next, &reboot->head, list) {
- list_del(&info->list);
- kfree_const(info->mode);
- kfree(info);
- }
- mutex_unlock(&reboot->rb_lock);
-
+ device_unregister(reboot->reboot_dev);
return 0;
}
EXPORT_SYMBOL_GPL(reboot_mode_unregister);
static void devm_reboot_mode_release(struct device *dev, void *res)
{
- reboot_mode_unregister(*(struct reboot_mode_driver **)res);
+ struct reboot_mode_driver *reboot = *(struct reboot_mode_driver **)res;
+
+ device_unregister(reboot->reboot_dev);
}
/**
@@ -200,7 +278,7 @@ int devm_reboot_mode_register(struct device *dev,
if (!dr)
return -ENOMEM;
- rc = reboot_mode_register(reboot, reboot->dev->of_node);
+ rc = reboot_mode_register(reboot, reboot->dev->of_node, reboot->dev->driver->name);
if (rc) {
devres_free(dr);
return rc;
diff --git a/include/linux/reboot-mode.h b/include/linux/reboot-mode.h
index 370228b5161963aac1d75af752ada0e8282b1078..0b9844a71224b3e4bcc0d1e396d6c9cb11ad8c97 100644
--- a/include/linux/reboot-mode.h
+++ b/include/linux/reboot-mode.h
@@ -7,6 +7,7 @@
struct reboot_mode_driver {
struct device *dev;
+ struct device *reboot_dev;
struct list_head head;
int (*write)(struct reboot_mode_driver *reboot, u64 magic);
struct notifier_block reboot_notifier;
@@ -14,7 +15,8 @@ struct reboot_mode_driver {
struct mutex rb_lock;
};
-int reboot_mode_register(struct reboot_mode_driver *reboot, struct device_node *np);
+int reboot_mode_register(struct reboot_mode_driver *reboot, struct device_node *np,
+ const char *driver_name);
int reboot_mode_unregister(struct reboot_mode_driver *reboot);
int devm_reboot_mode_register(struct device *dev,
struct reboot_mode_driver *reboot);
--
2.34.1
Powered by blists - more mailing lists