[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250922-arm-psci-system_reset2-vendor-reboots-v15-5-7ce3a08878f1@oss.qualcomm.com>
Date: Mon, 22 Sep 2025 19:10:15 +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>,
Moritz Fischer <moritz.fischer@...us.com>,
John Stultz <john.stultz@...aro.org>,
Matthias Brugger <matthias.bgg@...il.com>,
Krzysztof Kozlowski <krzk@...nel.org>
Cc: Dmitry Baryshkov <dmitry.baryshkov@....qualcomm.com>,
Mukesh Ojha <mukesh.ojha@....qualcomm.com>,
Stephen Boyd <swboyd@...omium.org>,
Andre Draszik <andre.draszik@...aro.org>,
Kathiravan Thirumoorthy <kathiravan.thirumoorthy@....qualcomm.com>,
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 v15 05/14] 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. Use the driver_name field of the struct
reboot_mode_driver to create the device. For device-based
drivers, configure the device driver name as driver_name.
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 | 127 ++++++++++++++++++++++++++++++--------
include/linux/reboot-mode.h | 2 +
2 files changed, 103 insertions(+), 26 deletions(-)
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index 79763a839c9b0161b4acb6afb625f50a880971cc..1e78eb3d0fe513c934b37bf7f0829e1f9f4634f0 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";
@@ -69,6 +72,89 @@ 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;
+ scoped_guard(mutex, &reboot->rb_lock) {
+ list_for_each_entry(info, &reboot->head, list)
+ size += sysfs_emit_at(buf, size, "%s ", info->mode);
+ }
+
+ if (size) {
+ size += sysfs_emit_at(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);
+
+ scoped_guard(mutex, &reboot->rb_lock) {
+ list_for_each_entry_safe(info, next, &reboot->head, list) {
+ list_del(&info->list);
+ kfree_const(info->mode);
+ kfree(info);
+ }
+ }
+
+ device_remove_file(reboot->reboot_dev, &dev_attr_reboot_modes);
+}
+
+static int create_reboot_mode_device(struct reboot_mode_driver *reboot)
+{
+ 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, reboot->driver_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)
+ goto create_file_err;
+
+ dr = devres_alloc(release_reboot_mode_device, sizeof(*dr), GFP_KERNEL);
+ if (!dr) {
+ ret = -ENOMEM;
+ goto devres_alloc_error;
+ }
+
+ *dr = reboot;
+ devres_add(reboot->reboot_dev, dr);
+ return ret;
+
+devres_alloc_error:
+ device_remove_file(reboot->reboot_dev, &dev_attr_reboot_modes);
+create_file_err:
+ device_unregister(reboot->reboot_dev);
+ return ret;
+}
+
/**
* reboot_mode_register - register a reboot mode driver
* @reboot: reboot mode driver
@@ -79,7 +165,6 @@ static int reboot_mode_notify(struct notifier_block *this,
int reboot_mode_register(struct reboot_mode_driver *reboot, struct fwnode_handle *fwnode)
{
struct mode_info *info;
- struct mode_info *next;
struct device_node *np;
struct property *prop;
size_t len = strlen(PREFIX);
@@ -87,13 +172,17 @@ int reboot_mode_register(struct reboot_mode_driver *reboot, struct fwnode_handle
u32 magic_arg2;
int ret;
- if (!fwnode)
+ if (!fwnode || !reboot->driver_name)
return -EINVAL;
np = to_of_node(fwnode);
if (!np)
return -EINVAL;
+ ret = create_reboot_mode_device(reboot);
+ if (ret)
+ return ret;
+
INIT_LIST_HEAD(&reboot->head);
mutex_init(&reboot->rb_lock);
@@ -136,20 +225,15 @@ int reboot_mode_register(struct reboot_mode_driver *reboot, struct fwnode_handle
list_add_tail(&info->list, &reboot->head);
}
+ }
- reboot->reboot_notifier.notifier_call = reboot_mode_notify;
- register_reboot_notifier(&reboot->reboot_notifier);
+ reboot->reboot_notifier.notifier_call = reboot_mode_notify;
+ register_reboot_notifier(&reboot->reboot_notifier);
- return 0;
+ return 0;
error:
- list_for_each_entry_safe(info, next, &reboot->head, list) {
- list_del(&info->list);
- kfree_const(info->mode);
- kfree(info);
- }
- }
-
+ device_unregister(reboot->reboot_dev);
return ret;
}
EXPORT_SYMBOL_GPL(reboot_mode_register);
@@ -160,26 +244,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);
-
- scoped_guard(mutex, &reboot->rb_lock) {
- list_for_each_entry_safe(info, next, &reboot->head, list) {
- list_del(&info->list);
- kfree_const(info->mode);
- kfree(info);
- }
- }
-
+ 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);
}
/**
@@ -202,6 +276,7 @@ int devm_reboot_mode_register(struct device *dev,
if (!dr)
return -ENOMEM;
+ reboot->driver_name = reboot->dev->driver->name;
rc = reboot_mode_register(reboot, of_fwnode_handle(reboot->dev->of_node));
if (rc) {
devres_free(dr);
diff --git a/include/linux/reboot-mode.h b/include/linux/reboot-mode.h
index 3a14df2ddd1db4181ea76f99ef447ed8368a3594..c68a671f6947f2346e1e6a0ce3c6ebc18722b98e 100644
--- a/include/linux/reboot-mode.h
+++ b/include/linux/reboot-mode.h
@@ -8,6 +8,8 @@
struct reboot_mode_driver {
struct device *dev;
+ struct device *reboot_dev;
+ const char *driver_name;
struct list_head head;
int (*write)(struct reboot_mode_driver *reboot, u64 magic);
struct notifier_block reboot_notifier;
--
2.34.1
Powered by blists - more mailing lists