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@xxxxxxxxxxx> --- 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