diff options
author | Mark Brown <broonie@kernel.org> | 2018-09-28 10:07:30 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2018-09-28 10:07:30 -0400 |
commit | 5451521409ce1c04d56c03854008fe8861893b05 (patch) | |
tree | 8d8ee36c9021cd511fdf2f0416664d05b5ec406d /drivers/regulator/helpers.c | |
parent | 5a7d7d0f9f791b1e13f26dbbb07c86482912ad62 (diff) | |
parent | 2ece646c90c5b45dd76c76ea207a3f3459f2c472 (diff) |
Merge tag 'bd71847-support' into regulator-4.20
regulator/mfd: Support for the ROHM BD71847
This adds support for the BD71847 which touches both MFD and regulator.
There's a few other bits and pieces included as some dependency patches
had already been applied so would've required rebasing.
Diffstat (limited to 'drivers/regulator/helpers.c')
-rw-r--r-- | drivers/regulator/helpers.c | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index ef09021dc46e..5686a1335bd3 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 | } |
104 | EXPORT_SYMBOL_GPL(regulator_disable_regmap); | 104 | EXPORT_SYMBOL_GPL(regulator_disable_regmap); |
105 | 105 | ||
106 | static 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 | */ | ||
133 | int 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 | } | ||
165 | EXPORT_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 | */ | ||
178 | int 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 | } | ||
226 | EXPORT_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 | * |
@@ -338,6 +460,76 @@ int regulator_map_voltage_linear_range(struct regulator_dev *rdev, | |||
338 | EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); | 460 | EXPORT_SYMBOL_GPL(regulator_map_voltage_linear_range); |
339 | 461 | ||
340 | /** | 462 | /** |
463 | * regulator_map_voltage_pickable_linear_range - map_voltage, pickable ranges | ||
464 | * | ||
465 | * @rdev: Regulator to operate on | ||
466 | * @min_uV: Lower bound for voltage | ||
467 | * @max_uV: Upper bound for voltage | ||
468 | * | ||
469 | * Drivers providing pickable linear_ranges in their descriptor can use | ||
470 | * this as their map_voltage() callback. | ||
471 | */ | ||
472 | int regulator_map_voltage_pickable_linear_range(struct regulator_dev *rdev, | ||
473 | int min_uV, int max_uV) | ||
474 | { | ||
475 | const struct regulator_linear_range *range; | ||
476 | int ret = -EINVAL; | ||
477 | int voltage, i; | ||
478 | unsigned int selector = 0; | ||
479 | |||
480 | if (!rdev->desc->n_linear_ranges) { | ||
481 | BUG_ON(!rdev->desc->n_linear_ranges); | ||
482 | return -EINVAL; | ||
483 | } | ||
484 | |||
485 | for (i = 0; i < rdev->desc->n_linear_ranges; i++) { | ||
486 | int linear_max_uV; | ||
487 | |||
488 | range = &rdev->desc->linear_ranges[i]; | ||
489 | linear_max_uV = range->min_uV + | ||
490 | (range->max_sel - range->min_sel) * range->uV_step; | ||
491 | |||
492 | if (!(min_uV <= linear_max_uV && max_uV >= range->min_uV)) { | ||
493 | selector += (range->max_sel - range->min_sel + 1); | ||
494 | continue; | ||
495 | } | ||
496 | |||
497 | if (min_uV <= range->min_uV) | ||
498 | min_uV = range->min_uV; | ||
499 | |||
500 | /* range->uV_step == 0 means fixed voltage range */ | ||
501 | if (range->uV_step == 0) { | ||
502 | ret = 0; | ||
503 | } else { | ||
504 | ret = DIV_ROUND_UP(min_uV - range->min_uV, | ||
505 | range->uV_step); | ||
506 | if (ret < 0) | ||
507 | return ret; | ||
508 | } | ||
509 | |||
510 | ret += selector; | ||
511 | |||
512 | voltage = rdev->desc->ops->list_voltage(rdev, ret); | ||
513 | |||
514 | /* | ||
515 | * Map back into a voltage to verify we're still in bounds. | ||
516 | * We may have overlapping voltage ranges. Hence we don't | ||
517 | * exit but retry until we have checked all ranges. | ||
518 | */ | ||
519 | if (voltage < min_uV || voltage > max_uV) | ||
520 | selector += (range->max_sel - range->min_sel + 1); | ||
521 | else | ||
522 | break; | ||
523 | } | ||
524 | |||
525 | if (i == rdev->desc->n_linear_ranges) | ||
526 | return -EINVAL; | ||
527 | |||
528 | return ret; | ||
529 | } | ||
530 | EXPORT_SYMBOL_GPL(regulator_map_voltage_pickable_linear_range); | ||
531 | |||
532 | /** | ||
341 | * regulator_list_voltage_linear - List voltages with simple calculation | 533 | * regulator_list_voltage_linear - List voltages with simple calculation |
342 | * | 534 | * |
343 | * @rdev: Regulator device | 535 | * @rdev: Regulator device |
@@ -362,6 +554,46 @@ int regulator_list_voltage_linear(struct regulator_dev *rdev, | |||
362 | EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); | 554 | EXPORT_SYMBOL_GPL(regulator_list_voltage_linear); |
363 | 555 | ||
364 | /** | 556 | /** |
557 | * regulator_list_voltage_pickable_linear_range - pickable range list voltages | ||
558 | * | ||
559 | * @rdev: Regulator device | ||
560 | * @selector: Selector to convert into a voltage | ||
561 | * | ||
562 | * list_voltage() operation, intended to be used by drivers utilizing pickable | ||
563 | * ranges helpers. | ||
564 | */ | ||
565 | int regulator_list_voltage_pickable_linear_range(struct regulator_dev *rdev, | ||
566 | unsigned int selector) | ||
567 | { | ||
568 | const struct regulator_linear_range *range; | ||
569 | int i; | ||
570 | unsigned int all_sels = 0; | ||
571 | |||
572 | if (!rdev->desc->n_linear_ranges) { | ||
573 | BUG_ON(!rdev->desc->n_linear_ranges); | ||
574 | return -EINVAL; | ||
575 | } | ||
576 | |||
577 | for (i = 0; i < rdev->desc->n_linear_ranges; i++) { | ||
578 | unsigned int sels_in_range; | ||
579 | |||
580 | range = &rdev->desc->linear_ranges[i]; | ||
581 | |||
582 | sels_in_range = range->max_sel - range->min_sel; | ||
583 | |||
584 | if (all_sels + sels_in_range >= selector) { | ||
585 | selector -= all_sels; | ||
586 | return range->min_uV + (range->uV_step * selector); | ||
587 | } | ||
588 | |||
589 | all_sels += (sels_in_range + 1); | ||
590 | } | ||
591 | |||
592 | return -EINVAL; | ||
593 | } | ||
594 | EXPORT_SYMBOL_GPL(regulator_list_voltage_pickable_linear_range); | ||
595 | |||
596 | /** | ||
365 | * regulator_list_voltage_linear_range - List voltages for linear ranges | 597 | * regulator_list_voltage_linear_range - List voltages for linear ranges |
366 | * | 598 | * |
367 | * @rdev: Regulator device | 599 | * @rdev: Regulator device |