Commit 18e4b55f authored by Matti Vaittinen's avatar Matti Vaittinen Committed by Mark Brown

regulator: Support regulators where voltage ranges are selectable

For example ROHM BD71837 and ROHM BD71847 Power management ICs have
regulators which provide multiple linear ranges. Ranges can be
selected by individual non contagious bit in vsel register. Add
regmap helper functions for selecting ranges.
Signed-off-by: default avatarMatti Vaittinen <matti.vaittinen@fi.rohmeurope.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 01e17e5d
...@@ -2783,6 +2783,11 @@ static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV, ...@@ -2783,6 +2783,11 @@ static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV,
if (desc->ops->list_voltage == regulator_list_voltage_linear_range) if (desc->ops->list_voltage == regulator_list_voltage_linear_range)
return regulator_map_voltage_linear_range(rdev, min_uV, max_uV); return regulator_map_voltage_linear_range(rdev, min_uV, max_uV);
if (desc->ops->list_voltage ==
regulator_list_voltage_pickable_linear_range)
return regulator_map_voltage_pickable_linear_range(rdev,
min_uV, max_uV);
return regulator_map_voltage_iterate(rdev, min_uV, max_uV); return regulator_map_voltage_iterate(rdev, min_uV, max_uV);
} }
......
...@@ -103,6 +103,128 @@ int regulator_disable_regmap(struct regulator_dev *rdev) ...@@ -103,6 +103,128 @@ int regulator_disable_regmap(struct regulator_dev *rdev)
} }
EXPORT_SYMBOL_GPL(regulator_disable_regmap); EXPORT_SYMBOL_GPL(regulator_disable_regmap);
static int regulator_range_selector_to_index(struct regulator_dev *rdev,
unsigned int rval)
{
int i;
if (!rdev->desc->linear_range_selectors)
return -EINVAL;
rval &= rdev->desc->vsel_range_mask;
for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
if (rdev->desc->linear_range_selectors[i] == rval)
return i;
}
return -EINVAL;
}
/**
* regulator_get_voltage_sel_pickable_regmap - pickable range get_voltage_sel
*
* @rdev: regulator to operate on
*
* Regulators that use regmap for their register I/O and use pickable
* ranges can set the vsel_reg, vsel_mask, vsel_range_reg and vsel_range_mask
* fields in their descriptor and then use this as their get_voltage_vsel
* operation, saving some code.
*/
int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
{
unsigned int r_val;
int range;
unsigned int val;
int ret, i;
unsigned int voltages_in_range = 0;
if (!rdev->desc->linear_ranges)
return -EINVAL;
ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
if (ret != 0)
return ret;
ret = regmap_read(rdev->regmap, rdev->desc->vsel_range_reg, &r_val);
if (ret != 0)
return ret;
val &= rdev->desc->vsel_mask;
val >>= ffs(rdev->desc->vsel_mask) - 1;
range = regulator_range_selector_to_index(rdev, r_val);
if (range < 0)
return -EINVAL;
for (i = 0; i < range; i++)
voltages_in_range += (rdev->desc->linear_ranges[i].max_sel -
rdev->desc->linear_ranges[i].min_sel) + 1;
return val + voltages_in_range;
}
EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_pickable_regmap);
/**
* regulator_set_voltage_sel_pickable_regmap - pickable range set_voltage_sel
*
* @rdev: regulator to operate on
* @sel: Selector to set
*
* Regulators that use regmap for their register I/O and use pickable
* ranges can set the vsel_reg, vsel_mask, vsel_range_reg and vsel_range_mask
* fields in their descriptor and then use this as their set_voltage_vsel
* operation, saving some code.
*/
int regulator_set_voltage_sel_pickable_regmap(struct regulator_dev *rdev,
unsigned int sel)
{
unsigned int range;
int ret, i;
unsigned int voltages_in_range = 0;
for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
voltages_in_range = (rdev->desc->linear_ranges[i].max_sel -
rdev->desc->linear_ranges[i].min_sel) + 1;
if (sel < voltages_in_range)
break;
sel -= voltages_in_range;
}
if (i == rdev->desc->n_linear_ranges)
return -EINVAL;
sel <<= ffs(rdev->desc->vsel_mask) - 1;
sel += rdev->desc->linear_ranges[i].min_sel;
range = rdev->desc->linear_range_selectors[i];
if (rdev->desc->vsel_reg == rdev->desc->vsel_range_reg) {
ret = regmap_update_bits(rdev->regmap,
rdev->desc->vsel_reg,
rdev->desc->vsel_range_mask |
rdev->desc->vsel_mask, sel | range);
} else {
ret = regmap_update_bits(rdev->regmap,
rdev->desc->vsel_range_reg,
rdev->desc->vsel_range_mask, range);
if (ret)
return ret;
ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
rdev->desc->vsel_mask, sel);
}
if (ret)
return ret;
if (rdev->desc->apply_bit)
ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
rdev->desc->apply_bit,
rdev->desc->apply_bit);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_pickable_regmap);
/** /**
* regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users
* *
...@@ -336,6 +458,76 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev, ...@@ -336,6 +458,76 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
} }
EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range);
/**
* regulator_map_voltage_pickable_linear_range - map_voltage, pickable ranges
*
* @rdev: Regulator to operate on
* @min_uV: Lower bound for voltage
* @max_uV: Upper bound for voltage
*
* Drivers providing pickable linear_ranges in their descriptor can use
* this as their map_voltage() callback.
*/
int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
const struct regulator_linear_range *range;
int ret = -EINVAL;
int voltage, i;
unsigned int selector = 0;
if (!rdev->desc->n_linear_ranges) {
BUG_ON(!rdev->desc->n_linear_ranges);
return -EINVAL;
}
for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
int linear_max_uV;
range = &rdev->desc->linear_ranges[i];
linear_max_uV = range->min_uV +
(range->max_sel - range->min_sel) * range->uV_step;
if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV)) {
selector += (range->max_sel - range->min_sel + 1);
continue;
}
if (min_uV <= range->min_uV)
min_uV = range->min_uV;
/* range->uV_step == 0 means fixed voltage range */
if (range->uV_step == 0) {
ret = 0;
} else {
ret = DIV_ROUND_UP(min_uV - range->min_uV,
range->uV_step);
if (ret < 0)
return ret;
}
ret += selector;
voltage = rdev->desc->ops->list_voltage(rdev, ret);
/*
* Map back into a voltage to verify we're still in bounds.
* We may have overlapping voltage ranges. Hence we don't
* exit but retry until we have checked all ranges.
*/
if (voltage < min_uV || voltage > max_uV)
selector += (range->max_sel - range->min_sel + 1);
else
break;
}
if (i == rdev->desc->n_linear_ranges)
return -EINVAL;
return ret;
}
EXPORT_SYMBOL_GPL(regulator_map_voltage_pickable_linear_range);
/** /**
* regulator_list_voltage_linear - List voltages with simple calculation * regulator_list_voltage_linear - List voltages with simple calculation
* *
...@@ -360,6 +552,46 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev, ...@@ -360,6 +552,46 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev,
} }
EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); EXPORT_SYMBOL_GPL(regulator_list_voltage_linear);
/**
* regulator_list_voltage_pickable_linear_range - pickable range list voltages
*
* @rdev: Regulator device
* @selector: Selector to convert into a voltage
*
* list_voltage() operation, intended to be used by drivers utilizing pickable
* ranges helpers.
*/
int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev,
unsigned int selector)
{
const struct regulator_linear_range *range;
int i;
unsigned int all_sels = 0;
if (!rdev->desc->n_linear_ranges) {
BUG_ON(!rdev->desc->n_linear_ranges);
return -EINVAL;
}
for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
unsigned int sels_in_range;
range = &rdev->desc->linear_ranges[i];
sels_in_range = range->max_sel - range->min_sel;
if (all_sels + sels_in_range >= selector) {
selector -= all_sels;
return range->min_uV + (range->uV_step * selector);
}
all_sels += (sels_in_range + 1);
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(regulator_list_voltage_pickable_linear_range);
/** /**
* regulator_list_voltage_linear_range - List voltages for linear ranges * regulator_list_voltage_linear_range - List voltages for linear ranges
* *
......
...@@ -271,9 +271,16 @@ enum regulator_type { ...@@ -271,9 +271,16 @@ enum regulator_type {
* @ramp_delay: Time to settle down after voltage change (unit: uV/us) * @ramp_delay: Time to settle down after voltage change (unit: uV/us)
* @min_dropout_uV: The minimum dropout voltage this regulator can handle * @min_dropout_uV: The minimum dropout voltage this regulator can handle
* @linear_ranges: A constant table of possible voltage ranges. * @linear_ranges: A constant table of possible voltage ranges.
* @n_linear_ranges: Number of entries in the @linear_ranges table. * @linear_range_selectors: A constant table of voltage range selectors.
* If pickable ranges are used each range must
* have corresponding selector here.
* @n_linear_ranges: Number of entries in the @linear_ranges (and in
* linear_range_selectors if used) table(s).
* @volt_table: Voltage mapping table (if table based mapping) * @volt_table: Voltage mapping table (if table based mapping)
* *
* @vsel_range_reg: Register for range selector when using pickable ranges
* and regulator_regmap_X_voltage_X_pickable functions.
* @vsel_range_mask: Mask for register bitfield used for range selector
* @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_
* @vsel_mask: Mask for register bitfield used for selector * @vsel_mask: Mask for register bitfield used for selector
* @csel_reg: Register for TPS65218 LS3 current regulator * @csel_reg: Register for TPS65218 LS3 current regulator
...@@ -338,10 +345,14 @@ struct regulator_desc { ...@@ -338,10 +345,14 @@ struct regulator_desc {
int min_dropout_uV; int min_dropout_uV;
const struct regulator_linear_range *linear_ranges; const struct regulator_linear_range *linear_ranges;
const unsigned int *linear_range_selectors;
int n_linear_ranges; int n_linear_ranges;
const unsigned int *volt_table; const unsigned int *volt_table;
unsigned int vsel_range_reg;
unsigned int vsel_range_mask;
unsigned int vsel_reg; unsigned int vsel_reg;
unsigned int vsel_mask; unsigned int vsel_mask;
unsigned int csel_reg; unsigned int csel_reg;
...@@ -498,18 +509,25 @@ int regulator_mode_to_status(unsigned int); ...@@ -498,18 +509,25 @@ int regulator_mode_to_status(unsigned int);
int regulator_list_voltage_linear(struct regulator_dev *rdev, int regulator_list_voltage_linear(struct regulator_dev *rdev,
unsigned int selector); unsigned int selector);
int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev,
unsigned int selector);
int regulator_list_voltage_linear_range(struct regulator_dev *rdev, int regulator_list_voltage_linear_range(struct regulator_dev *rdev,
unsigned int selector); unsigned int selector);
int regulator_list_voltage_table(struct regulator_dev *rdev, int regulator_list_voltage_table(struct regulator_dev *rdev,
unsigned int selector); unsigned int selector);
int regulator_map_voltage_linear(struct regulator_dev *rdev, int regulator_map_voltage_linear(struct regulator_dev *rdev,
int min_uV, int max_uV); int min_uV, int max_uV);
int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
int min_uV, int max_uV);
int regulator_map_voltage_linear_range(struct regulator_dev *rdev, int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
int min_uV, int max_uV); int min_uV, int max_uV);
int regulator_map_voltage_iterate(struct regulator_dev *rdev, int regulator_map_voltage_iterate(struct regulator_dev *rdev,
int min_uV, int max_uV); int min_uV, int max_uV);
int regulator_map_voltage_ascend(struct regulator_dev *rdev, int regulator_map_voltage_ascend(struct regulator_dev *rdev,
int min_uV, int max_uV); int min_uV, int max_uV);
int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev);
int regulator_set_voltage_sel_pickable_regmap(struct regulator_dev *rdev,
unsigned int sel);
int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev); int regulator_get_voltage_sel_regmap(struct regulator_dev *rdev);
int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel); int regulator_set_voltage_sel_regmap(struct regulator_dev *rdev, unsigned sel);
int regulator_is_enabled_regmap(struct regulator_dev *rdev); int regulator_is_enabled_regmap(struct regulator_dev *rdev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment