Unverified Commit 42d7c87b authored by Kory Maincent's avatar Kory Maincent Committed by Mark Brown
Browse files

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: default avatarKory Maincent <kory.maincent@bootlin.com>
Link: https://patch.msgid.link/20250115-feature_regulator_pw_budget-v2-1-0a44b949e6bc@bootlin.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent c3ad22ad
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
@@ -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;
}

+3 −0
Original line number Diff line number Diff line
@@ -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. */
+21 −0
Original line number Diff line number Diff line
@@ -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)
{
+2 −0
Original line number Diff line number Diff line
@@ -656,6 +656,8 @@ struct regulator_dev {
	int cached_err;
	bool use_cached_err;
	spinlock_t err_lock;

	int pw_requested_mW;
};

/*
+2 −0
Original line number Diff line number Diff line
@@ -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 */