]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
regulator: Add support for power budget
authorKory Maincent <kory.maincent@bootlin.com>
Wed, 15 Jan 2025 14:41:57 +0000 (15:41 +0100)
committerMark Brown <broonie@kernel.org>
Fri, 17 Jan 2025 17:48:54 +0000 (17:48 +0000)
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>
drivers/regulator/core.c
drivers/regulator/of_regulator.c
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/regulator/machine.h

index c092b78c5f1247f040520063ef420a30116fa2f9..6c0ef1182248d19e0d2d285c77ccee04cb7d8f61 100644 (file)
@@ -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;
 }
@@ -4601,6 +4628,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
@@ -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;
 }
 
index 6acbd5f2e7963ddebaa88f8bcb956ecae608b35f..d8597215289f4c1ec248237ff1d053281d642d19 100644 (file)
@@ -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. */
index 8c3c372ad735e5e33bc0a02770d42a21dd67e37d..8d1a6eca7eb959af54b2ca320668258ac3bc4e02 100644 (file)
@@ -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)
 {
index 5b66caf1695db2ad27ed78cf6beb8ae55052b5b7..4a216fdba354f88e702c4f07c2ad72be2eea1953 100644 (file)
@@ -656,6 +656,8 @@ struct regulator_dev {
        int cached_err;
        bool use_cached_err;
        spinlock_t err_lock;
+
+       int pw_requested_mW;
 };
 
 /*
index b3db09a7429beeb64a413a0ba2f785a4c877c018..1fc440c5c4c7e861f5724ae11789582f6545b21a 100644 (file)
@@ -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 */