Commit 7b081a74 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull regulator updates from Mark Brown:
 "This was a very quiet release, aside from some smaller improvements we
  have:

   - Support for power budgeting on regulators, initially targeted at
     some still in review support for PSE controllers but generally
     useful

   - Support for error interrupts from ROHM BD96801 devices

   - Support for NXP PCA9452"

* tag 'regulator-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator:
  regulator: dt-bindings: Add regulator-power-budget-milliwatt property
  regulator: Add support for power budget
  regulator: core: Resolve supply using of_node from regulator_config
  regulator: of: Implement the unwind path of of_regulator_match()
  regulator: tps65219: Remove debugging helper function
  regulator: tps65219: Remove MODULE_ALIAS
  regulator: tps65219: Update driver name
  regulator: tps65219: Use dev_err_probe() instead of dev_err()
  regulator: dt-bindings: mt6315: Drop regulator-compatible property
  regulator: pca9450: Add PMIC pca9452 support
  regulator: dt-bindings: pca9450: Add pca9452 support
  regulator: pca9450: Use dev_err_probe() to simplify code
  regulator: pca9450: add enable_value for all bucks
  regulator: bd96801: Add ERRB IRQ
parents 6f10810c 367a8200
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -35,10 +35,6 @@ properties:
        $ref: regulator.yaml#
        unevaluatedProperties: false

        properties:
          regulator-compatible:
            pattern: "^vbuck[1-4]$"

    additionalProperties: false

required:
@@ -56,7 +52,6 @@ examples:

      regulators {
        vbuck1 {
          regulator-compatible = "vbuck1";
          regulator-min-microvolt = <300000>;
          regulator-max-microvolt = <1193750>;
          regulator-enable-ramp-delay = <256>;
@@ -64,7 +59,6 @@ examples:
        };

        vbuck3 {
          regulator-compatible = "vbuck3";
          regulator-min-microvolt = <300000>;
          regulator-max-microvolt = <1193750>;
          regulator-enable-ramp-delay = <256>;
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ properties:
      - nxp,pca9450b
      - nxp,pca9450c
      - nxp,pca9451a
      - nxp,pca9452

  reg:
    maxItems: 1
+3 −0
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@ properties:
  regulator-input-current-limit-microamp:
    description: maximum input current regulator allows

  regulator-power-budget-milliwatt:
    description: power budget of the regulator

  regulator-always-on:
    description: boolean, regulator should never be disabled
    type: boolean
+115 −15
Original line number Diff line number Diff line
@@ -5,12 +5,7 @@
/*
 * This version of the "BD86801 scalable PMIC"'s driver supports only very
 * basic set of the PMIC features. Most notably, there is no support for
 * the ERRB interrupt and the configurations which should be done when the
 * PMIC is in STBY mode.
 *
 * Supporting the ERRB interrupt would require dropping the regmap-IRQ
 * usage or working around (or accepting a presense of) a naming conflict
 * in debugFS IRQs.
 * the configurations which should be done when the PMIC is in STBY mode.
 *
 * Being able to reliably do the configurations like changing the
 * regulator safety limits (like limits for the over/under -voltages, over
@@ -22,16 +17,14 @@
 * be the need to configure these safety limits. Hence it's not simple to
 * come up with a generic solution.
 *
 * Users who require the ERRB handling and STBY state configurations can
 * have a look at the original RFC:
 * Users who require the STBY state configurations can have a look at the
 * original RFC:
 * https://lore.kernel.org/all/cover.1712920132.git.mazziesaccount@gmail.com/
 * which implements a workaround to debugFS naming conflict and some of
 * the safety limit configurations - but leaves the state change handling
 * and synchronization to be implemented.
 * which implements some of the safety limit configurations - but leaves the
 * state change handling and synchronization to be implemented.
 *
 * It would be great to hear (and receive a patch!) if you implement the
 * STBY configuration support or a proper fix to the debugFS naming
 * conflict in your downstream driver ;)
 * STBY configuration support in your downstream driver ;)
 */

#include <linux/cleanup.h>
@@ -728,6 +721,95 @@ static int initialize_pmic_data(struct device *dev,
	return 0;
}

static int bd96801_map_event_all(int irq, struct regulator_irq_data *rid,
			  unsigned long *dev_mask)
{
	int i;

	for (i = 0; i < rid->num_states; i++) {
		rid->states[i].notifs = REGULATOR_EVENT_FAIL;
		rid->states[i].errors = REGULATOR_ERROR_FAIL;
		*dev_mask |= BIT(i);
	}

	return 0;
}

static int bd96801_rdev_errb_irqs(struct platform_device *pdev,
				  struct regulator_dev *rdev)
{
	int i;
	void *retp;
	static const char * const single_out_errb_irqs[] = {
		"bd96801-%s-pvin-err", "bd96801-%s-ovp-err",
		"bd96801-%s-uvp-err", "bd96801-%s-shdn-err",
	};

	for (i = 0; i < ARRAY_SIZE(single_out_errb_irqs); i++) {
		struct regulator_irq_desc id = {
			.map_event = bd96801_map_event_all,
			.irq_off_ms = 1000,
		};
		struct regulator_dev *rdev_arr[1];
		char tmp[255];
		int irq;

		snprintf(tmp, 255, single_out_errb_irqs[i], rdev->desc->name);
		tmp[254] = 0;
		id.name = tmp;

		irq = platform_get_irq_byname(pdev, tmp);
		if (irq < 0)
			continue;

		rdev_arr[0] = rdev;
		retp = devm_regulator_irq_helper(&pdev->dev, &id, irq, 0,
						 REGULATOR_ERROR_FAIL, NULL,
						 rdev_arr, 1);
		if (IS_ERR(retp))
			return PTR_ERR(retp);

	}
	return 0;
}

static int bd96801_global_errb_irqs(struct platform_device *pdev,
				    struct regulator_dev **rdev, int num_rdev)
{
	int i, num_irqs;
	void *retp;
	static const char * const global_errb_irqs[] = {
		"bd96801-otp-err", "bd96801-dbist-err", "bd96801-eep-err",
		"bd96801-abist-err", "bd96801-prstb-err", "bd96801-drmoserr1",
		"bd96801-drmoserr2", "bd96801-slave-err", "bd96801-vref-err",
		"bd96801-tsd", "bd96801-uvlo-err", "bd96801-ovlo-err",
		"bd96801-osc-err", "bd96801-pon-err", "bd96801-poff-err",
		"bd96801-cmd-shdn-err", "bd96801-int-shdn-err"
	};

	num_irqs = ARRAY_SIZE(global_errb_irqs);
	for (i = 0; i < num_irqs; i++) {
		int irq;
		struct regulator_irq_desc id = {
			.name = global_errb_irqs[i],
			.map_event = bd96801_map_event_all,
			.irq_off_ms = 1000,
		};

		irq = platform_get_irq_byname(pdev, global_errb_irqs[i]);
		if (irq < 0)
			continue;

		retp = devm_regulator_irq_helper(&pdev->dev, &id, irq, 0,
						 REGULATOR_ERROR_FAIL, NULL,
						  rdev, num_rdev);
		if (IS_ERR(retp))
			return PTR_ERR(retp);
	}

	return 0;
}

static int bd96801_rdev_intb_irqs(struct platform_device *pdev,
				  struct bd96801_pmic_data *pdata,
				  struct bd96801_irqinfo *iinfo,
@@ -783,11 +865,10 @@ static int bd96801_rdev_intb_irqs(struct platform_device *pdev,
	return 0;
}



static int bd96801_probe(struct platform_device *pdev)
{
	struct regulator_dev *ldo_errs_rdev_arr[BD96801_NUM_LDOS];
	struct regulator_dev *all_rdevs[BD96801_NUM_REGULATORS];
	struct bd96801_regulator_data *rdesc;
	struct regulator_config config = {};
	int ldo_errs_arr[BD96801_NUM_LDOS];
@@ -795,6 +876,7 @@ static int bd96801_probe(struct platform_device *pdev)
	int temp_notif_ldos = 0;
	struct device *parent;
	int i, ret;
	bool use_errb;
	void *retp;

	parent = pdev->dev.parent;
@@ -819,6 +901,13 @@ static int bd96801_probe(struct platform_device *pdev)
	config.regmap = pdata->regmap;
	config.dev = parent;

	ret = of_property_match_string(pdev->dev.parent->of_node,
				       "interrupt-names", "errb");
	if (ret < 0)
		use_errb = false;
	else
		use_errb = true;

	ret = bd96801_walk_regulator_dt(&pdev->dev, pdata->regmap, rdesc,
					BD96801_NUM_REGULATORS);
	if (ret)
@@ -837,6 +926,7 @@ static int bd96801_probe(struct platform_device *pdev)
				rdesc[i].desc.name);
			return PTR_ERR(rdev);
		}
		all_rdevs[i] = rdev;
		/*
		 * LDOs don't have own temperature monitoring. If temperature
		 * notification was requested for this LDO from DT then we will
@@ -856,6 +946,12 @@ static int bd96801_probe(struct platform_device *pdev)
			if (ret)
				return ret;
		}
		/* Register per regulator ERRB notifiers */
		if (use_errb) {
			ret = bd96801_rdev_errb_irqs(pdev, rdev);
			if (ret)
				return ret;
		}
	}
	if (temp_notif_ldos) {
		int irq;
@@ -877,6 +973,10 @@ static int bd96801_probe(struct platform_device *pdev)
			return PTR_ERR(retp);
	}

	if (use_errb)
		return bd96801_global_errb_irqs(pdev, all_rdevs,
						ARRAY_SIZE(all_rdevs));

	return 0;
}

+142 −11
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;
}
@@ -1936,6 +1963,20 @@ static struct regulator_dev *regulator_lookup_by_name(const char *name)
	return dev ? dev_to_rdev(dev) : NULL;
}

static struct regulator_dev *regulator_dt_lookup(struct device *dev,
						 const char *supply)
{
	struct regulator_dev *r = NULL;

	if (dev_of_node(dev)) {
		r = of_regulator_dev_lookup(dev, dev_of_node(dev), supply);
		if (PTR_ERR(r) == -ENODEV)
			r = NULL;
	}

	return r;
}

/**
 * regulator_dev_lookup - lookup a regulator device.
 * @dev: device for regulator "consumer".
@@ -1960,17 +2001,10 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev,
	regulator_supply_alias(&dev, &supply);

	/* first do a dt based lookup */
	if (dev_of_node(dev)) {
		r = of_regulator_dev_lookup(dev, dev_of_node(dev), supply);
		if (!IS_ERR(r))
			return r;
		if (PTR_ERR(r) == -EPROBE_DEFER)
	r = regulator_dt_lookup(dev, supply);
	if (r)
		return r;

		if (PTR_ERR(r) == -ENODEV)
			r = NULL;
	}

	/* if not found, try doing it non-dt way */
	if (dev)
		devname = dev_name(dev);
@@ -2015,7 +2049,17 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
	if (rdev->supply)
		return 0;

	/* first do a dt based lookup on the node described in the virtual
	 * device.
	 */
	r = regulator_dt_lookup(&rdev->dev, rdev->supply_name);

	/* If regulator not found use usual search path in the parent
	 * device.
	 */
	if (!r)
		r = regulator_dev_lookup(dev, rdev->supply_name);

	if (IS_ERR(r)) {
		ret = PTR_ERR(r);

@@ -4584,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
@@ -5222,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
};

@@ -5303,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;
}

Loading