Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

regulator: implement selector stepping

Some regulators require that the requested voltage be reached gradually
by setting all or some of the intermediate values. Implement a new field
in the regulator description struct that allows users to specify the
number of selectors by which the regulator API should step when ramping
the voltage up/down.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Link: https://lore.kernel.org/r/20190703161035.31808-2-brgl@bgdev.pl
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Bartosz Golaszewski and committed by
Mark Brown
2da8d947 ba2bf340

+69
+63
drivers/regulator/core.c
··· 3106 3106 return ret; 3107 3107 } 3108 3108 3109 + static int _regulator_set_voltage_sel_step(struct regulator_dev *rdev, 3110 + int uV, int new_selector) 3111 + { 3112 + const struct regulator_ops *ops = rdev->desc->ops; 3113 + int diff, old_sel, curr_sel, ret; 3114 + 3115 + /* Stepping is only needed if the regulator is enabled. */ 3116 + if (!_regulator_is_enabled(rdev)) 3117 + goto final_set; 3118 + 3119 + if (!ops->get_voltage_sel) 3120 + return -EINVAL; 3121 + 3122 + old_sel = ops->get_voltage_sel(rdev); 3123 + if (old_sel < 0) 3124 + return old_sel; 3125 + 3126 + diff = new_selector - old_sel; 3127 + if (diff == 0) 3128 + return 0; /* No change needed. */ 3129 + 3130 + if (diff > 0) { 3131 + /* Stepping up. */ 3132 + for (curr_sel = old_sel + rdev->desc->vsel_step; 3133 + curr_sel < new_selector; 3134 + curr_sel += rdev->desc->vsel_step) { 3135 + /* 3136 + * Call the callback directly instead of using 3137 + * _regulator_call_set_voltage_sel() as we don't 3138 + * want to notify anyone yet. Same in the branch 3139 + * below. 3140 + */ 3141 + ret = ops->set_voltage_sel(rdev, curr_sel); 3142 + if (ret) 3143 + goto try_revert; 3144 + } 3145 + } else { 3146 + /* Stepping down. */ 3147 + for (curr_sel = old_sel - rdev->desc->vsel_step; 3148 + curr_sel > new_selector; 3149 + curr_sel -= rdev->desc->vsel_step) { 3150 + ret = ops->set_voltage_sel(rdev, curr_sel); 3151 + if (ret) 3152 + goto try_revert; 3153 + } 3154 + } 3155 + 3156 + final_set: 3157 + /* The final selector will trigger the notifiers. */ 3158 + return _regulator_call_set_voltage_sel(rdev, uV, new_selector); 3159 + 3160 + try_revert: 3161 + /* 3162 + * At least try to return to the previous voltage if setting a new 3163 + * one failed. 3164 + */ 3165 + (void)ops->set_voltage_sel(rdev, old_sel); 3166 + return ret; 3167 + } 3168 + 3109 3169 static int _regulator_set_voltage_time(struct regulator_dev *rdev, 3110 3170 int old_uV, int new_uV) 3111 3171 { ··· 3239 3179 selector = ret; 3240 3180 if (old_selector == selector) 3241 3181 ret = 0; 3182 + else if (rdev->desc->vsel_step) 3183 + ret = _regulator_set_voltage_sel_step( 3184 + rdev, best_val, selector); 3242 3185 else 3243 3186 ret = _regulator_call_set_voltage_sel( 3244 3187 rdev, best_val, selector);
+6
include/linux/regulator/driver.h
··· 286 286 * @vsel_range_mask: Mask for register bitfield used for range selector 287 287 * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_ 288 288 * @vsel_mask: Mask for register bitfield used for selector 289 + * @vsel_step: Specify the resolution of selector stepping when setting 290 + * voltage. If 0, then no stepping is done (requested selector is 291 + * set directly), if >0 then the regulator API will ramp the 292 + * voltage up/down gradually each time increasing/decreasing the 293 + * selector by the specified step value. 289 294 * @csel_reg: Register for current limit selector using regmap set_current_limit 290 295 * @csel_mask: Mask for register bitfield used for current limit selector 291 296 * @apply_reg: Register for initiate voltage change on the output when ··· 365 360 unsigned int vsel_range_mask; 366 361 unsigned int vsel_reg; 367 362 unsigned int vsel_mask; 363 + unsigned int vsel_step; 368 364 unsigned int csel_reg; 369 365 unsigned int csel_mask; 370 366 unsigned int apply_reg;