diff options
author | Kory Maincent <kory.maincent@bootlin.com> | 2025-01-15 15:41:57 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2025-01-17 17:48:54 +0000 |
commit | 42d7c87b4e1251f36eceac987e74623e7cda8577 (patch) | |
tree | d438d89f6edc45a9844c7cab351e47dd489663b4 | |
parent | c3ad22ad34f81a8906dba02ea8cc9756d2ce7b50 (diff) |
regulator: Add support for power budget
Introduce power budget management for the regulator device. Enable tracking
of available power capacity by providing helpers to request and release
power budget allocations.
Signed-off-by: Kory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20250115-feature_regulator_pw_budget-v2-1-0a44b949e6bc@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | drivers/regulator/core.c | 114 | ||||
-rw-r--r-- | drivers/regulator/of_regulator.c | 3 | ||||
-rw-r--r-- | include/linux/regulator/consumer.h | 21 | ||||
-rw-r--r-- | include/linux/regulator/driver.h | 2 | ||||
-rw-r--r-- | include/linux/regulator/machine.h | 2 |
5 files changed, 142 insertions, 0 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c092b78c5f12..6c0ef1182248 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -917,6 +917,26 @@ static ssize_t bypass_show(struct device *dev, } static DEVICE_ATTR_RO(bypass); +static ssize_t power_budget_milliwatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", rdev->constraints->pw_budget_mW); +} +static DEVICE_ATTR_RO(power_budget_milliwatt); + +static ssize_t power_requested_milliwatt_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_requested_mW); +} +static DEVICE_ATTR_RO(power_requested_milliwatt); + #define REGULATOR_ERROR_ATTR(name, bit) \ static ssize_t name##_show(struct device *dev, struct device_attribute *attr, \ char *buf) \ @@ -1149,6 +1169,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 +1651,9 @@ static int set_machine_constraints(struct regulator_dev *rdev) rdev->last_off = ktime_get(); } + if (!rdev->constraints->pw_budget_mW) + rdev->constraints->pw_budget_mW = INT_MAX; + print_constraints(rdev); return 0; } @@ -4602,6 +4629,87 @@ int regulator_get_current_limit(struct regulator *regulator) EXPORT_SYMBOL_GPL(regulator_get_current_limit); /** + * regulator_get_unclaimed_power_budget - get regulator unclaimed power budget + * @regulator: regulator source + * + * Return: Unclaimed power budget of the regulator in mW. + */ +int regulator_get_unclaimed_power_budget(struct regulator *regulator) +{ + return regulator->rdev->constraints->pw_budget_mW - + regulator->rdev->pw_requested_mW; +} +EXPORT_SYMBOL_GPL(regulator_get_unclaimed_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, pw_tot_req; + + regulator_lock(rdev); + if (rdev->supply) { + ret = regulator_request_power_budget(rdev->supply, pw_req); + if (ret < 0) + goto out; + } + + pw_tot_req = rdev->pw_requested_mW + pw_req; + if (pw_tot_req > rdev->constraints->pw_budget_mW) { + rdev_warn(rdev, "power requested %d mW out of budget %d mW", + pw_req, + rdev->constraints->pw_budget_mW - rdev->pw_requested_mW); + regulator_notifier_call_chain(rdev, + REGULATOR_EVENT_OVER_CURRENT_WARN, + NULL); + ret = -ERANGE; + goto out; + } + + rdev->pw_requested_mW = pw_tot_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; + int pw_tot_req; + + regulator_lock(rdev); + if (rdev->supply) + regulator_free_power_budget(rdev->supply, pw); + + pw_tot_req = rdev->pw_requested_mW - pw; + if (pw_tot_req >= 0) + rdev->pw_requested_mW = pw_tot_req; + else + rdev_warn(rdev, + "too much power freed %d mW (already requested %d mW)", + pw, rdev->pw_requested_mW); + + regulator_unlock(rdev); +} +EXPORT_SYMBOL_GPL(regulator_free_power_budget); + +/** * regulator_set_mode - set regulator operating mode * @regulator: regulator source * @mode: operating mode - one of the REGULATOR_MODE constants @@ -5239,6 +5347,8 @@ 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_milliwatt.attr, + &dev_attr_power_requested_milliwatt.attr, NULL }; @@ -5320,6 +5430,10 @@ 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_milliwatt.attr || + attr == &dev_attr_power_requested_milliwatt.attr) + return rdev->constraints->pw_budget_mW != INT_MAX ? mode : 0; + return mode; } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 6acbd5f2e796..d8597215289f 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 8c3c372ad735..8d1a6eca7eb9 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -258,6 +258,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_unclaimed_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); @@ -571,6 +576,22 @@ static inline int regulator_get_current_limit(struct regulator *regulator) return 0; } +static inline int regulator_get_unclaimed_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..4a216fdba354 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_requested_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 */ |