[PATCH 3/5] regulator: core: Add mechanism to force disable a regulator at boot
From: Paul Louvel
Date: Wed Oct 21 2026 - 06:33:51 EST
Regulation activity may be enabled by default when the device powers up.
A consumer may query the regulator's state during probe and may disable
it if it is already enabled, potentially resulting in an unbalanced
disable operation:
ret = regulator_is_enabled();
if (ret < 0)
return ret;
if (ret)
ret = regulator_disable();
^^^^^^^^^^^^^^^^^^^^
Add the 'regulator-boot-force-off' property, which disable the regulator
when setting machine constraints.
Signed-off-by: Paul Louvel <paul.louvel@xxxxxxxxxxx>
---
drivers/regulator/core.c | 154 ++++++++++++++++++++------------------
drivers/regulator/of_regulator.c | 1 +
include/linux/regulator/machine.h | 3 +-
3 files changed, 84 insertions(+), 74 deletions(-)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 14422cfe80e2..b45048ec084a 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1450,6 +1450,81 @@ static int handle_notify_limits(struct regulator_dev *rdev,
return ret;
}
+
+/**
+ * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control
+ * @rdev: regulator_dev structure
+ * @enable: enable GPIO at initial use?
+ *
+ * GPIO is enabled in case of initial use. (enable_count is 0)
+ * GPIO is disabled when it is not shared any more. (enable_count <= 1)
+ *
+ * Return: 0 on success or a negative error number on failure.
+ */
+static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
+{
+ struct regulator_enable_gpio *pin = rdev->ena_pin;
+ int ret;
+
+ if (!pin)
+ return -EINVAL;
+
+ if (enable) {
+ /* Enable GPIO at initial use */
+ if (pin->enable_count == 0) {
+ ret = gpiod_set_value_cansleep(pin->gpiod, 1);
+ if (ret)
+ return ret;
+ }
+
+ pin->enable_count++;
+ } else {
+ if (pin->enable_count > 1) {
+ pin->enable_count--;
+ return 0;
+ }
+
+ /* Disable GPIO if not used */
+ if (pin->enable_count <= 1) {
+ ret = gpiod_set_value_cansleep(pin->gpiod, 0);
+ if (ret)
+ return ret;
+
+ pin->enable_count = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int _regulator_do_disable(struct regulator_dev *rdev)
+{
+ int ret;
+
+ trace_regulator_disable(rdev_get_name(rdev));
+
+ if (rdev->ena_pin) {
+ if (rdev->ena_gpio_state) {
+ ret = regulator_ena_gpio_ctrl(rdev, false);
+ if (ret < 0)
+ return ret;
+ rdev->ena_gpio_state = 0;
+ }
+
+ } else if (rdev->desc->ops->disable) {
+ ret = rdev->desc->ops->disable(rdev);
+ if (ret != 0)
+ return ret;
+ }
+
+ if (rdev->desc->off_on_delay)
+ rdev->last_off = ktime_get_boottime();
+
+ trace_regulator_disable_complete(rdev_get_name(rdev));
+
+ return 0;
+}
+
/**
* set_machine_constraints - sets regulator constraints
* @rdev: regulator source
@@ -1701,6 +1776,12 @@ static int set_machine_constraints(struct regulator_dev *rdev,
rdev->last_off = ktime_get();
}
+ if (rdev->constraints->boot_force_off) {
+ ret = _regulator_do_disable(rdev);
+ if (ret < 0)
+ return ret;
+ }
+
if (!rdev->constraints->pw_budget_mW)
rdev->constraints->pw_budget_mW = INT_MAX;
@@ -2910,51 +2991,6 @@ static void regulator_ena_gpio_free(struct regulator_dev *rdev)
rdev->ena_pin = NULL;
}
-/**
- * regulator_ena_gpio_ctrl - balance enable_count of each GPIO and actual GPIO pin control
- * @rdev: regulator_dev structure
- * @enable: enable GPIO at initial use?
- *
- * GPIO is enabled in case of initial use. (enable_count is 0)
- * GPIO is disabled when it is not shared any more. (enable_count <= 1)
- *
- * Return: 0 on success or a negative error number on failure.
- */
-static int regulator_ena_gpio_ctrl(struct regulator_dev *rdev, bool enable)
-{
- struct regulator_enable_gpio *pin = rdev->ena_pin;
- int ret;
-
- if (!pin)
- return -EINVAL;
-
- if (enable) {
- /* Enable GPIO at initial use */
- if (pin->enable_count == 0) {
- ret = gpiod_set_value_cansleep(pin->gpiod, 1);
- if (ret)
- return ret;
- }
-
- pin->enable_count++;
- } else {
- if (pin->enable_count > 1) {
- pin->enable_count--;
- return 0;
- }
-
- /* Disable GPIO if not used */
- if (pin->enable_count <= 1) {
- ret = gpiod_set_value_cansleep(pin->gpiod, 0);
- if (ret)
- return ret;
-
- pin->enable_count = 0;
- }
- }
-
- return 0;
-}
/**
* _regulator_check_status_enabled - check if regulator status can be
@@ -3226,34 +3262,6 @@ int regulator_enable(struct regulator *regulator)
}
EXPORT_SYMBOL_GPL(regulator_enable);
-static int _regulator_do_disable(struct regulator_dev *rdev)
-{
- int ret;
-
- trace_regulator_disable(rdev_get_name(rdev));
-
- if (rdev->ena_pin) {
- if (rdev->ena_gpio_state) {
- ret = regulator_ena_gpio_ctrl(rdev, false);
- if (ret < 0)
- return ret;
- rdev->ena_gpio_state = 0;
- }
-
- } else if (rdev->desc->ops->disable) {
- ret = rdev->desc->ops->disable(rdev);
- if (ret != 0)
- return ret;
- }
-
- if (rdev->desc->off_on_delay)
- rdev->last_off = ktime_get_boottime();
-
- trace_regulator_disable_complete(rdev_get_name(rdev));
-
- return 0;
-}
-
/* locks held by regulator_disable() */
static int _regulator_disable(struct regulator *regulator)
{
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 09e26febfc83..337629be58ca 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -132,6 +132,7 @@ static int of_get_regulation_constraints(struct device *dev,
constraints->always_on = of_property_read_bool(np, "regulator-always-on");
if (!constraints->always_on) /* status change should be possible. */
constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+ constraints->boot_force_off = of_property_read_bool(np, "regulator-boot-force-off");
constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
constraints->system_critical = of_property_read_bool(np,
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index 1fc440c5c4c7..35ab9de96e5c 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -227,7 +227,8 @@ struct regulation_constraints {
unsigned int active_discharge;
/* constraint flags */
- unsigned always_on:1; /* regulator never off when system is on */
+ unsigned always_on:1; /* regulator never off when system is on */
+ unsigned boot_force_off:1; /* force regulator off at boot */
unsigned boot_on:1; /* bootloader/firmware enabled regulator */
unsigned apply_uV:1; /* apply uV constraint if min == max */
unsigned ramp_disable:1; /* disable ramp delay */
--
2.56.0