[PATCH v2 3/5] power: reset: syscon-reboot-mode: enable supplies for the next stage
Alexey Charkov
alchark at flipper.net
Tue Sep 29 03:28:20 PDT 2026
Requesting a boot mode on some SoCs such as the Rockchip RK3576 hands the
request to the boot ROM in a register that a full reset would clear, so
any reset that carries it has to leave the PMIC untouched. This means that
the regulators stay in whichever power state the running OS left them,
which may be unsuitable for the early boot code. E.g. on RK3576 the DDR
initialization blob runs on the way back and accesses registers which
Linux normally leaves powered down, resulting in an unrecoverable abort.
Get every supply named in the reboot mode node and enable it when a mode
is written, which is when a mode is being requested. The supplies are
deliberately left enabled afterwards: the system is on its way down, and
what runs next cannot turn them on.
Signed-off-by: Alexey Charkov <alchark at flipper.net>
------
Current implementation of of_regulator_bulk_get_all() never writes the
supply names, instead leaving them uninitialized, while the regulator
core uses the field in error paths to provide meaningful error messages
to the user. This has been highlighted by Sashiko on v1, and is addressed
separately in [1]
[1] https://lore.kernel.org/all/20260929-regulator-get-all-v1-1-e887c66a47f1@flipper.net/
---
drivers/power/reset/syscon-reboot-mode.c | 41 ++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/drivers/power/reset/syscon-reboot-mode.c b/drivers/power/reset/syscon-reboot-mode.c
index e0772c9f70f7..5ef882d81816 100644
--- a/drivers/power/reset/syscon-reboot-mode.c
+++ b/drivers/power/reset/syscon-reboot-mode.c
@@ -10,6 +10,8 @@
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <linux/mfd/syscon.h>
#include <linux/reboot-mode.h>
@@ -18,6 +20,8 @@ struct syscon_reboot_mode {
struct reboot_mode_driver reboot;
u32 offset;
u32 mask;
+ struct regulator_bulk_data *supplies;
+ int num_supplies;
};
static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
@@ -28,6 +32,20 @@ static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
syscon_rbm = container_of(reboot, struct syscon_reboot_mode, reboot);
+ /*
+ * Whatever acts on the mode (e.g. boot ROM) runs before the operating
+ * system, and may need supplies that the running system had powered
+ * down. Enable them here, and deliberately leave them enabled: the
+ * system is on its way down, and what runs next may not know how to
+ * turn them on.
+ */
+ if (syscon_rbm->num_supplies) {
+ ret = regulator_bulk_enable(syscon_rbm->num_supplies,
+ syscon_rbm->supplies);
+ if (ret < 0)
+ dev_err(reboot->dev, "enabling reboot mode supplies failed\n");
+ }
+
ret = regmap_update_bits(syscon_rbm->map, syscon_rbm->offset,
syscon_rbm->mask, magic);
if (ret < 0)
@@ -36,6 +54,14 @@ static int syscon_reboot_mode_write(struct reboot_mode_driver *reboot,
return ret;
}
+static void syscon_reboot_mode_put_supplies(void *data)
+{
+ struct syscon_reboot_mode *syscon_rbm = data;
+
+ regulator_bulk_free(syscon_rbm->num_supplies, syscon_rbm->supplies);
+ kfree(syscon_rbm->supplies);
+}
+
static int syscon_reboot_mode_probe(struct platform_device *pdev)
{
int ret;
@@ -59,6 +85,21 @@ static int syscon_reboot_mode_probe(struct platform_device *pdev)
of_property_read_u32(pdev->dev.of_node, "mask", &syscon_rbm->mask);
+ ret = of_regulator_bulk_get_all(&pdev->dev, pdev->dev.of_node,
+ &syscon_rbm->supplies);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "can't get reboot mode supplies\n");
+
+ syscon_rbm->num_supplies = ret;
+ if (syscon_rbm->num_supplies) {
+ ret = devm_add_action_or_reset(&pdev->dev,
+ syscon_reboot_mode_put_supplies,
+ syscon_rbm);
+ if (ret)
+ return ret;
+ }
+
ret = devm_reboot_mode_register(&pdev->dev, &syscon_rbm->reboot);
if (ret)
dev_err(&pdev->dev, "can't register reboot mode\n");
--
2.55.0
More information about the Linux-rockchip
mailing list