Add power budget management for the regulator device. Enable the regulator
to track available power capacity by providing helpers to request and
release power budget allocations.
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
---
drivers/regulator/core.c | 89 ++++++++++++++++++++++++++++++++++++++
drivers/regulator/of_regulator.c | 3 ++
include/linux/regulator/consumer.h | 21 +++++++++
include/linux/regulator/driver.h | 2 +
include/linux/regulator/machine.h | 2 +
5 files changed, 117 insertions(+)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index c092b78c5f12..c86092220b70 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -917,6 +917,15 @@ static ssize_t bypass_show(struct device *dev,
}
static DEVICE_ATTR_RO(bypass);
+static ssize_t power_budget_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", rdev->pw_available_mW);
+}
+static DEVICE_ATTR_RO(power_budget);
+
#define REGULATOR_ERROR_ATTR(name, bit) \
static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \
char *buf) \
@@ -1149,6 +1158,10 @@ static void print_constraints_debug(struct regulator_dev *rdev)
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
count += scnprintf(buf + count, len - count, "standby ");
+ if (constraints->pw_budget_mW)
+ count += scnprintf(buf + count, len - count, "%d mW budget",
+ constraints->pw_budget_mW);
+
if (!count)
count = scnprintf(buf, len, "no parameters");
else
@@ -1627,6 +1640,13 @@ static int set_machine_constraints(struct regulator_dev *rdev)
rdev->last_off = ktime_get();
}
+ if (rdev->constraints->pw_budget_mW)
+ rdev->pw_available_mW = rdev->constraints->pw_budget_mW;
+ else if (rdev->supply)
+ rdev->pw_available_mW = regulator_get_power_budget(rdev->supply);
+ else
+ rdev->pw_available_mW = INT_MAX;
+
print_constraints(rdev);
return 0;
}
@@ -4601,6 +4621,71 @@ int regulator_get_current_limit(struct regulator *regulator)
}
EXPORT_SYMBOL_GPL(regulator_get_current_limit);
+/**
+ * regulator_get_power_budget - get regulator total power budget
+ * @regulator: regulator source
+ *
+ * Return: Power budget of the regulator in mW.
+ */
+int regulator_get_power_budget(struct regulator *regulator)
+{
+ return regulator->rdev->pw_available_mW;
+}
+EXPORT_SYMBOL_GPL(regulator_get_power_budget);
+
+/**
+ * regulator_request_power_budget - request power budget on a regulator
+ * @regulator: regulator source
+ * @pw_req: Power requested
+ *
+ * Return: 0 on success or a negative error number on failure.
+ */
+int regulator_request_power_budget(struct regulator *regulator,
+ unsigned int pw_req)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret = 0;
+
+ regulator_lock(rdev);
+ if (rdev->supply) {
+ ret = regulator_request_power_budget(rdev->supply, pw_req);
+ if (ret < 0)
+ goto out;
+ }
+ if (pw_req > rdev->pw_available_mW) {
+ rdev_dbg(rdev, "power requested %d mW out of budget %d mW",
+ pw_req, rdev->pw_available_mW);
+ ret = -ERANGE;
+ goto out;
+ }
+
+ rdev->pw_available_mW -= pw_req;
+out:
+ regulator_unlock(rdev);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_request_power_budget);
+
+/**
+ * regulator_free_power_budget - free power budget on a regulator
+ * @regulator: regulator source
+ * @pw: Power to be released.
+ *
+ * Return: Power budget of the regulator in mW.
+ */
+void regulator_free_power_budget(struct regulator *regulator,
+ unsigned int pw)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+
+ regulator_lock(rdev);
+ if (rdev->supply)
+ regulator_free_power_budget(rdev->supply, pw);
+ rdev->pw_available_mW += pw;
+ regulator_unlock(rdev);
+}
+EXPORT_SYMBOL_GPL(regulator_free_power_budget);
+
/**
* regulator_set_mode - set regulator operating mode
* @regulator: regulator source
@@ -5239,6 +5324,7 @@ static struct attribute *regulator_dev_attrs[] = {
&dev_attr_suspend_standby_mode.attr,
&dev_attr_suspend_mem_mode.attr,
&dev_attr_suspend_disk_mode.attr,
+ &dev_attr_power_budget.attr,
NULL
};
@@ -5320,6 +5406,9 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj,
attr == &dev_attr_suspend_disk_mode.attr)
return ops->set_suspend_mode ? mode : 0;
+ if (attr == &dev_attr_power_budget.attr)
+ return rdev->pw_available_mW != INT_MAX ? mode : 0;
+
return mode;
}
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 6af8411679c7..011088c57891 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -125,6 +125,9 @@ static int of_get_regulation_constraints(struct device *dev,
if (constraints->min_uA != constraints->max_uA)
constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
+ if (!of_property_read_u32(np, "regulator-power-budget-milliwatt", &pval))
+ constraints->pw_budget_mW = pval;
+
constraints->boot_on = of_property_read_bool(np, "regulator-boot-on");
constraints->always_on = of_property_read_bool(np, "regulator-always-on");
if (!constraints->always_on) /* status change should be possible. */
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index bcba3935c6f9..ca78c539b94d 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -233,6 +233,11 @@ int regulator_sync_voltage(struct regulator *regulator);
int regulator_set_current_limit(struct regulator *regulator,
int min_uA, int max_uA);
int regulator_get_current_limit(struct regulator *regulator);
+int regulator_get_power_budget(struct regulator *regulator);
+int regulator_request_power_budget(struct regulator *regulator,
+ unsigned int pw_req);
+void regulator_free_power_budget(struct regulator *regulator,
+ unsigned int pw);
int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator);
@@ -526,6 +531,22 @@ static inline int regulator_get_current_limit(struct regulator *regulator)
return 0;
}
+static inline int regulator_get_power_budget(struct regulator *regulator)
+{
+ return INT_MAX;
+}
+
+static inline int regulator_request_power_budget(struct regulator *regulator,
+ unsigned int pw_req)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void regulator_free_power_budget(struct regulator *regulator,
+ unsigned int pw)
+{
+}
+
static inline int regulator_set_mode(struct regulator *regulator,
unsigned int mode)
{
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 5b66caf1695d..9e436b4d7c4c 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -656,6 +656,8 @@ struct regulator_dev {
int cached_err;
bool use_cached_err;
spinlock_t err_lock;
+
+ int pw_available_mW;
};
/*
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index b3db09a7429b..1fc440c5c4c7 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -113,6 +113,7 @@ struct notification_limit {
* @min_uA: Smallest current consumers may set.
* @max_uA: Largest current consumers may set.
* @ilim_uA: Maximum input current.
+ * @pw_budget_mW: Power budget for the regulator in mW.
* @system_load: Load that isn't captured by any consumer requests.
*
* @over_curr_limits: Limits for acting on over current.
@@ -185,6 +186,7 @@ struct regulation_constraints {
int max_uA;
int ilim_uA;
+ int pw_budget_mW;
int system_load;
/* used for coupled regulators */
--
2.34.1
On Mon, Jan 13, 2025 at 02:07:45PM +0100, Kory Maincent wrote: > + rdev->pw_available_mW -= pw_req; ... > + if (!of_property_read_u32(np, "regulator-power-budget-milliwatt", &pval)) > + constraints->pw_budget_mW = pval; > + This is only tracking the currently free power budget which both restricts what we can do for tracking things like mismatched or missing frees and means there's less information for diagnostic tools. I'd prefer to keep track of how much is in use and check against the budget when trying to increase it, allowing us to check for releasing more budget than was requested. There's also an interaction with hardware with support for enforcing power limits, either via alarms or by actually limiting. Current limiting/warning support is reasonably common, we should probably be joining it up with the power limiting. It's fortunately not used dynamically by anything at the minute so we could just remove that API and replace it by a power one, given that nobody uses it and there do appear to be users for the power based API. We do have some things that set current limits in constraints IIRC. We probably also need something explicit about how we handle baseline load from things like passive components, the assumption probably needs to be that it's negligable.
On Mon, 13 Jan 2025 14:11:16 +0000 Mark Brown <broonie@kernel.org> wrote: > On Mon, Jan 13, 2025 at 02:07:45PM +0100, Kory Maincent wrote: > > > + rdev->pw_available_mW -= pw_req; > > ... > > > + if (!of_property_read_u32(np, "regulator-power-budget-milliwatt", > > &pval)) > > + constraints->pw_budget_mW = pval; > > + > > This is only tracking the currently free power budget which both > restricts what we can do for tracking things like mismatched or missing > frees and means there's less information for diagnostic tools. I'd > prefer to keep track of how much is in use and check against the budget > when trying to increase it, allowing us to check for releasing more > budget than was requested. Ack. > There's also an interaction with hardware with support for enforcing > power limits, either via alarms or by actually limiting. Current > limiting/warning support is reasonably common, we should probably be > joining it up with the power limiting. It's fortunately not used > dynamically by anything at the minute so we could just remove that API > and replace it by a power one, given that nobody uses it and there do > appear to be users for the power based API. We do have some things that > set current limits in constraints IIRC. There is few users for the regulator_set_current_limit function. https://elixir.bootlin.com/linux/v6.12.6/A/ident/regulator_set_current_limit Not sure we could replace it to power limit that easily. > We probably also need something explicit about how we handle baseline > load from things like passive components, the assumption probably needs > to be that it's negligable. We could add a devicetree property on the consumer node, but lets keep it for later. Regards, -- Köry Maincent, Bootlin Embedded Linux and kernel engineering https://bootlin.com
On Mon, Jan 13, 2025 at 03:45:51PM +0100, Kory Maincent wrote: > Mark Brown <broonie@kernel.org> wrote: > > joining it up with the power limiting. It's fortunately not used > > dynamically by anything at the minute so we could just remove that API > > and replace it by a power one, given that nobody uses it and there do > > appear to be users for the power based API. We do have some things that > > set current limits in constraints IIRC. > There is few users for the regulator_set_current_limit function. > https://elixir.bootlin.com/linux/v6.12.6/A/ident/regulator_set_current_limit > Not sure we could replace it to power limit that easily. Huh, I wonder what tree I grepped in. The DRM usage is yet more broken usage of the regulator API, I'm not sure why it attracts this so much, but the others are legit. Still, we should be able to map between the two. > > We probably also need something explicit about how we handle baseline > > load from things like passive components, the assumption probably needs > > to be that it's negligable. > We could add a devicetree property on the consumer node, but lets keep it for > later. One problem is that there might not be a consumer node - things like random passives don't tend to get represented.
On Mon, 13 Jan 2025 14:51:53 +0000
Mark Brown <broonie@kernel.org> wrote:
> On Mon, Jan 13, 2025 at 03:45:51PM +0100, Kory Maincent wrote:
> > Mark Brown <broonie@kernel.org> wrote:
>
> > > joining it up with the power limiting. It's fortunately not used
> > > dynamically by anything at the minute so we could just remove that API
> > > and replace it by a power one, given that nobody uses it and there do
> > > appear to be users for the power based API. We do have some things that
> > > set current limits in constraints IIRC.
>
> > There is few users for the regulator_set_current_limit function.
> > https://elixir.bootlin.com/linux/v6.12.6/A/ident/regulator_set_current_limit
> >
>
> > Not sure we could replace it to power limit that easily.
>
> Huh, I wonder what tree I grepped in. The DRM usage is yet more broken
> usage of the regulator API, I'm not sure why it attracts this so much,
> but the others are legit. Still, we should be able to map between the
> two.
We could have something like that in regulator_request_power_budget()?
if (rdev->desc->ops->get_voltage && rdev->desc->ops->set_current_limit) {
ret = regulator_get_voltage(rdev);
if (ret < 0)
return ret;
tmp_64 = pw_req;
tmp_64 *= 1000000000ull;
/* uA = mW * 1000000000 / uV */
uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
ret = regulator_set_current_limit(rdev, uA);
if (ret)
return ret;
}
> > > We probably also need something explicit about how we handle baseline
> > > load from things like passive components, the assumption probably needs
> > > to be that it's negligable.
>
> > We could add a devicetree property on the consumer node, but lets keep it
> > for later.
>
> One problem is that there might not be a consumer node - things like
> random passives don't tend to get represented.
Mmh true, so indeed if they are not represented lets assume they are
negligible. ;)
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
On Mon, Jan 13, 2025 at 04:26:20PM +0100, Kory Maincent wrote:
> Mark Brown <broonie@kernel.org> wrote:
> > On Mon, Jan 13, 2025 at 03:45:51PM +0100, Kory Maincent wrote:
> > > Mark Brown <broonie@kernel.org> wrote:
> > but the others are legit. Still, we should be able to map between the
> > two.
> We could have something like that in regulator_request_power_budget()?
> if (rdev->desc->ops->get_voltage && rdev->desc->ops->set_current_limit) {
> ret = regulator_get_voltage(rdev);
> if (ret < 0)
> return ret;
>
> tmp_64 = pw_req;
> tmp_64 *= 1000000000ull;
> /* uA = mW * 1000000000 / uV */
> uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
> ret = regulator_set_current_limit(rdev, uA);
> if (ret)
> return ret;
Yup, indeed. That said I am wondering if it's safer to just configure
the constraint in the hardware rather than the currently requested
limit, considering what might happen in the case where there's multiple
consumers that have only been partially updated. If the hardware limits
or shuts down rather than warning it'll blow up badly so it might be
better to be conservative. Unfortunately we don't distinguish in the
ops. Possibly it should be a policy thing even but then that's better
at runtime...
On Mon, 13 Jan 2025 15:44:29 +0000
Mark Brown <broonie@kernel.org> wrote:
> On Mon, Jan 13, 2025 at 04:26:20PM +0100, Kory Maincent wrote:
> > Mark Brown <broonie@kernel.org> wrote:
> > > On Mon, Jan 13, 2025 at 03:45:51PM +0100, Kory Maincent wrote:
> [...]
>
> > > but the others are legit. Still, we should be able to map between the
> > > two.
>
> > We could have something like that in regulator_request_power_budget()?
>
> > if (rdev->desc->ops->get_voltage && rdev->desc->ops->set_current_limit) {
> > ret = regulator_get_voltage(rdev);
> > if (ret < 0)
> > return ret;
> >
> > tmp_64 = pw_req;
> > tmp_64 *= 1000000000ull;
> > /* uA = mW * 1000000000 / uV */
> > uA = DIV_ROUND_CLOSEST_ULL(tmp_64, uV);
> > ret = regulator_set_current_limit(rdev, uA);
> > if (ret)
> > return ret;
>
> Yup, indeed. That said I am wondering if it's safer to just configure
> the constraint in the hardware rather than the currently requested
> limit, considering what might happen in the case where there's multiple
> consumers that have only been partially updated. If the hardware limits
> or shuts down rather than warning it'll blow up badly so it might be
> better to be conservative. Unfortunately we don't distinguish in the
> ops. Possibly it should be a policy thing even but then that's better
> at runtime...
Indeed, should we begin without it and see later if we add it?
We could simply add an event for now:
regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL);
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
On Tue, Jan 14, 2025 at 02:53:28PM +0100, Kory Maincent wrote: > Mark Brown <broonie@kernel.org> wrote: > > Yup, indeed. That said I am wondering if it's safer to just configure > > the constraint in the hardware rather than the currently requested > > limit, considering what might happen in the case where there's multiple > > consumers that have only been partially updated. If the hardware limits > > or shuts down rather than warning it'll blow up badly so it might be > > better to be conservative. Unfortunately we don't distinguish in the > > ops. Possibly it should be a policy thing even but then that's better > > at runtime... > Indeed, should we begin without it and see later if we add it? I think so. > We could simply add an event for now: > regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); We should (TBH I thought that was there already) but part of the problem is that a bunch of the hardware will shut down or otherwise do something that we might not want when it hits the limit. Again something that could be addressed separately/incrementally.
© 2016 - 2025 Red Hat, Inc.