diff options
| -rw-r--r-- | drivers/regulator/core.c | 113 | ||||
| -rw-r--r-- | include/linux/regulator/consumer.h | 2 | ||||
| -rw-r--r-- | include/linux/regulator/driver.h | 9 | 
3 files changed, 124 insertions, 0 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75abcd8..da357a0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -692,6 +692,69 @@ static int set_machine_constraints(struct regulator_dev *rdev,  	else  		name = "regulator"; +	/* constrain machine-level voltage specs to fit +	 * the actual range supported by this regulator. +	 */ +	if (ops->list_voltage && rdev->desc->n_voltages) { +		int	count = rdev->desc->n_voltages; +		int	i; +		int	min_uV = INT_MAX; +		int	max_uV = INT_MIN; +		int	cmin = constraints->min_uV; +		int	cmax = constraints->max_uV; + +		/* it's safe to autoconfigure fixed-voltage supplies */ +		if (count == 1 && !cmin) { +			cmin = INT_MIN; +			cmax = INT_MAX; +		} + +		/* else require explicit machine-level constraints */ +		else if (cmin <= 0 || cmax <= 0 || cmax < cmin) { +			pr_err("%s: %s '%s' voltage constraints\n", +				       __func__, "invalid", name); +			ret = -EINVAL; +			goto out; +		} + +		/* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ +		for (i = 0; i < count; i++) { +			int	value; + +			value = ops->list_voltage(rdev, i); +			if (value <= 0) +				continue; + +			/* maybe adjust [min_uV..max_uV] */ +			if (value >= cmin && value < min_uV) +				min_uV = value; +			if (value <= cmax && value > max_uV) +				max_uV = value; +		} + +		/* final: [min_uV..max_uV] valid iff constraints valid */ +		if (max_uV < min_uV) { +			pr_err("%s: %s '%s' voltage constraints\n", +				       __func__, "unsupportable", name); +			ret = -EINVAL; +			goto out; +		} + +		/* use regulator's subset of machine constraints */ +		if (constraints->min_uV < min_uV) { +			pr_debug("%s: override '%s' %s, %d -> %d\n", +				       __func__, name, "min_uV", +					constraints->min_uV, min_uV); +			constraints->min_uV = min_uV; +		} +		if (constraints->max_uV > max_uV) { +			pr_debug("%s: override '%s' %s, %d -> %d\n", +				       __func__, name, "max_uV", +					constraints->max_uV, max_uV); +			constraints->max_uV = max_uV; +		} +	} +  	rdev->constraints = constraints;  	/* do we need to apply the constraint voltage */ @@ -1251,6 +1314,56 @@ int regulator_is_enabled(struct regulator *regulator)  EXPORT_SYMBOL_GPL(regulator_is_enabled);  /** + * regulator_count_voltages - count regulator_list_voltage() selectors + * @regulator: regulator source + * + * Returns number of selectors, or negative errno.  Selectors are + * numbered starting at zero, and typically correspond to bitfields + * in hardware registers. + */ +int regulator_count_voltages(struct regulator *regulator) +{ +	struct regulator_dev	*rdev = regulator->rdev; + +	return rdev->desc->n_voltages ? : -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_count_voltages); + +/** + * regulator_list_voltage - enumerate supported voltages + * @regulator: regulator source + * @selector: identify voltage to list + * Context: can sleep + * + * Returns a voltage that can be passed to @regulator_set_voltage(), + * zero if this selector code can't be used on this sytem, or a + * negative errno. + */ +int regulator_list_voltage(struct regulator *regulator, unsigned selector) +{ +	struct regulator_dev	*rdev = regulator->rdev; +	struct regulator_ops	*ops = rdev->desc->ops; +	int			ret; + +	if (!ops->list_voltage || selector >= rdev->desc->n_voltages) +		return -EINVAL; + +	mutex_lock(&rdev->mutex); +	ret = ops->list_voltage(rdev, selector); +	mutex_unlock(&rdev->mutex); + +	if (ret > 0) { +		if (ret < rdev->constraints->min_uV) +			ret = 0; +		else if (ret > rdev->constraints->max_uV) +			ret = 0; +	} + +	return ret; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage); + +/**   * regulator_set_voltage - set regulator output voltage   * @regulator: regulator source   * @min_uV: Minimum required voltage in uV diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index df6c4bc..277f4b9 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -142,6 +142,8 @@ int regulator_bulk_disable(int num_consumers,  void regulator_bulk_free(int num_consumers,  			 struct regulator_bulk_data *consumers); +int regulator_count_voltages(struct regulator *regulator); +int regulator_list_voltage(struct regulator *regulator, unsigned selector);  int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV);  int regulator_get_voltage(struct regulator *regulator);  int regulator_set_current_limit(struct regulator *regulator, diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 0cf37bc..2255468 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -45,6 +45,10 @@ enum regulator_status {   * @set_voltage: Set the voltage for the regulator within the range specified.   *               The driver should select the voltage closest to min_uV.   * @get_voltage: Return the currently configured voltage for the regulator. + * @list_voltage: Return one of the supported voltages, in microvolts; zero + *	if the selector indicates a voltage that is unusable on this system; + *	or negative errno.  Selectors range from zero to one less than + *	regulator_desc.n_voltages.  Voltages may be reported in any order.   *   * @set_current_limit: Configure a limit for a current-limited regulator.   * @get_current_limit: Get the limit for a current-limited regulator. @@ -66,6 +70,9 @@ enum regulator_status {   */  struct regulator_ops { +	/* enumerate supported voltages */ +	int (*list_voltage) (struct regulator_dev *, unsigned selector); +  	/* get/set regulator voltage */  	int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV);  	int (*get_voltage) (struct regulator_dev *); @@ -124,6 +131,7 @@ enum regulator_type {   *   * @name: Identifying name for the regulator.   * @id: Numerical identifier for the regulator. + * @n_voltages: Number of selectors available for ops.list_voltage().   * @ops: Regulator operations table.   * @irq: Interrupt number for the regulator.   * @type: Indicates if the regulator is a voltage or current regulator. @@ -132,6 +140,7 @@ enum regulator_type {  struct regulator_desc {  	const char *name;  	int id; +	unsigned n_voltages;  	struct regulator_ops *ops;  	int irq;  	enum regulator_type type;  | 
