aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator/helpers.c
diff options
context:
space:
mode:
authorMatti Vaittinen <matti.vaittinen@fi.rohmeurope.com>2018-09-14 04:31:36 -0400
committerMark Brown <broonie@kernel.org>2018-09-28 09:57:03 -0400
commit18e4b55fbd2069cee51ef9660b35c65ec13bee6d (patch)
tree69c0b9d3796c30651bb669bd40cbd81ec9b3ad30 /drivers/regulator/helpers.c
parent01e17e5d8004f53044c6c9062e04ca118c420512 (diff)
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: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/regulator/helpers.c')
-rw-r--r--drivers/regulator/helpers.c232
1 files changed, 232 insertions, 0 deletions
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index 2ae7c3ac5940..d2b9fc359318 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -103,6 +103,128 @@ int regulator_disable_regmap(struct regulator_dev *rdev)
103} 103}
104EXPORT_SYMBOL_GPL(regulator_disable_regmap); 104EXPORT_SYMBOL_GPL(regulator_disable_regmap);
105 105
106static int regulator_range_selector_to_index(struct regulator_dev *rdev,
107 unsigned int rval)
108{
109 int i;
110
111 if (!rdev->desc->linear_range_selectors)
112 return -EINVAL;
113
114 rval &= rdev->desc->vsel_range_mask;
115
116 for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
117 if (rdev->desc->linear_range_selectors[i] == rval)
118 return i;
119 }
120 return -EINVAL;
121}
122
123/**
124 * regulator_get_voltage_sel_pickable_regmap - pickable range get_voltage_sel
125 *
126 * @rdev: regulator to operate on
127 *
128 * Regulators that use regmap for their register I/O and use pickable
129 * ranges can set the vsel_reg, vsel_mask, vsel_range_reg and vsel_range_mask
130 * fields in their descriptor and then use this as their get_voltage_vsel
131 * operation, saving some code.
132 */
133int regulator_get_voltage_sel_pickable_regmap(struct regulator_dev *rdev)
134{
135 unsigned int r_val;
136 int range;
137 unsigned int val;
138 int ret, i;
139 unsigned int voltages_in_range = 0;
140
141 if (!rdev->desc->linear_ranges)
142 return -EINVAL;
143
144 ret = regmap_read(rdev->regmap, rdev->desc->vsel_reg, &val);
145 if (ret != 0)
146 return ret;
147
148 ret = regmap_read(rdev->regmap, rdev->desc->vsel_range_reg, &r_val);
149 if (ret != 0)
150 return ret;
151
152 val &= rdev->desc->vsel_mask;
153 val >>= ffs(rdev->desc->vsel_mask) - 1;
154
155 range = regulator_range_selector_to_index(rdev, r_val);
156 if (range < 0)
157 return -EINVAL;
158
159 for (i = 0; i < range; i++)
160 voltages_in_range += (rdev->desc->linear_ranges[i].max_sel -
161 rdev->desc->linear_ranges[i].min_sel) + 1;
162
163 return val + voltages_in_range;
164}
165EXPORT_SYMBOL_GPL(regulator_get_voltage_sel_pickable_regmap);
166
167/**
168 * regulator_set_voltage_sel_pickable_regmap - pickable range set_voltage_sel
169 *
170 * @rdev: regulator to operate on
171 * @sel: Selector to set
172 *
173 * Regulators that use regmap for their register I/O and use pickable
174 * ranges can set the vsel_reg, vsel_mask, vsel_range_reg and vsel_range_mask
175 * fields in their descriptor and then use this as their set_voltage_vsel
176 * operation, saving some code.
177 */
178int regulator_set_voltage_sel_pickable_regmap(struct regulator_dev *rdev,
179 unsigned int sel)
180{
181 unsigned int range;
182 int ret, i;
183 unsigned int voltages_in_range = 0;
184
185 for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
186 voltages_in_range = (rdev->desc->linear_ranges[i].max_sel -
187 rdev->desc->linear_ranges[i].min_sel) + 1;
188 if (sel < voltages_in_range)
189 break;
190 sel -= voltages_in_range;
191 }
192
193 if (i == rdev->desc->n_linear_ranges)
194 return -EINVAL;
195
196 sel <<= ffs(rdev->desc->vsel_mask) - 1;
197 sel += rdev->desc->linear_ranges[i].min_sel;
198
199 range = rdev->desc->linear_range_selectors[i];
200
201 if (rdev->desc->vsel_reg == rdev->desc->vsel_range_reg) {
202 ret = regmap_update_bits(rdev->regmap,
203 rdev->desc->vsel_reg,
204 rdev->desc->vsel_range_mask |
205 rdev->desc->vsel_mask, sel | range);
206 } else {
207 ret = regmap_update_bits(rdev->regmap,
208 rdev->desc->vsel_range_reg,
209 rdev->desc->vsel_range_mask, range);
210 if (ret)
211 return ret;
212
213 ret = regmap_update_bits(rdev->regmap, rdev->desc->vsel_reg,
214 rdev->desc->vsel_mask, sel);
215 }
216
217 if (ret)
218 return ret;
219
220 if (rdev->desc->apply_bit)
221 ret = regmap_update_bits(rdev->regmap, rdev->desc->apply_reg,
222 rdev->desc->apply_bit,
223 rdev->desc->apply_bit);
224 return ret;
225}
226EXPORT_SYMBOL_GPL(regulator_set_voltage_sel_pickable_regmap);
227
106/** 228/**
107 * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users 229 * regulator_get_voltage_sel_regmap - standard get_voltage_sel for regmap users
108 * 230 *
@@ -337,6 +459,76 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev,
337EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); 459EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range);
338 460
339/** 461/**
462 * regulator_map_voltage_pickable_linear_range - map_voltage, pickable ranges
463 *
464 * @rdev: Regulator to operate on
465 * @min_uV: Lower bound for voltage
466 * @max_uV: Upper bound for voltage
467 *
468 * Drivers providing pickable linear_ranges in their descriptor can use
469 * this as their map_voltage() callback.
470 */
471int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev,
472 int min_uV, int max_uV)
473{
474 const struct regulator_linear_range *range;
475 int ret = -EINVAL;
476 int voltage, i;
477 unsigned int selector = 0;
478
479 if (!rdev->desc->n_linear_ranges) {
480 BUG_ON(!rdev->desc->n_linear_ranges);
481 return -EINVAL;
482 }
483
484 for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
485 int linear_max_uV;
486
487 range = &rdev->desc->linear_ranges[i];
488 linear_max_uV = range->min_uV +
489 (range->max_sel - range->min_sel) * range->uV_step;
490
491 if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV)) {
492 selector += (range->max_sel - range->min_sel + 1);
493 continue;
494 }
495
496 if (min_uV <= range->min_uV)
497 min_uV = range->min_uV;
498
499 /* range->uV_step == 0 means fixed voltage range */
500 if (range->uV_step == 0) {
501 ret = 0;
502 } else {
503 ret = DIV_ROUND_UP(min_uV - range->min_uV,
504 range->uV_step);
505 if (ret < 0)
506 return ret;
507 }
508
509 ret += selector;
510
511 voltage = rdev->desc->ops->list_voltage(rdev, ret);
512
513 /*
514 * Map back into a voltage to verify we're still in bounds.
515 * We may have overlapping voltage ranges. Hence we don't
516 * exit but retry until we have checked all ranges.
517 */
518 if (voltage < min_uV || voltage > max_uV)
519 selector += (range->max_sel - range->min_sel + 1);
520 else
521 break;
522 }
523
524 if (i == rdev->desc->n_linear_ranges)
525 return -EINVAL;
526
527 return ret;
528}
529EXPORT_SYMBOL_GPL(regulator_map_voltage_pickable_linear_range);
530
531/**
340 * regulator_list_voltage_linear - List voltages with simple calculation 532 * regulator_list_voltage_linear - List voltages with simple calculation
341 * 533 *
342 * @rdev: Regulator device 534 * @rdev: Regulator device
@@ -361,6 +553,46 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev,
361EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); 553EXPORT_SYMBOL_GPL(regulator_list_voltage_linear);
362 554
363/** 555/**
556 * regulator_list_voltage_pickable_linear_range - pickable range list voltages
557 *
558 * @rdev: Regulator device
559 * @selector: Selector to convert into a voltage
560 *
561 * list_voltage() operation, intended to be used by drivers utilizing pickable
562 * ranges helpers.
563 */
564int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev,
565 unsigned int selector)
566{
567 const struct regulator_linear_range *range;
568 int i;
569 unsigned int all_sels = 0;
570
571 if (!rdev->desc->n_linear_ranges) {
572 BUG_ON(!rdev->desc->n_linear_ranges);
573 return -EINVAL;
574 }
575
576 for (i = 0; i < rdev->desc->n_linear_ranges; i++) {
577 unsigned int sels_in_range;
578
579 range = &rdev->desc->linear_ranges[i];
580
581 sels_in_range = range->max_sel - range->min_sel;
582
583 if (all_sels + sels_in_range >= selector) {
584 selector -= all_sels;
585 return range->min_uV + (range->uV_step * selector);
586 }
587
588 all_sels += (sels_in_range + 1);
589 }
590
591 return -EINVAL;
592}
593EXPORT_SYMBOL_GPL(regulator_list_voltage_pickable_linear_range);
594
595/**
364 * regulator_list_voltage_linear_range - List voltages for linear ranges 596 * regulator_list_voltage_linear_range - List voltages for linear ranges
365 * 597 *
366 * @rdev: Regulator device 598 * @rdev: Regulator device