aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorBoris Brezillon <boris.brezillon@free-electrons.com>2016-06-14 05:13:21 -0400
committerThierry Reding <thierry.reding@gmail.com>2016-07-25 09:34:06 -0400
commitea398e28739e25651ede7ddf5aeb57cbcbc8ca7d (patch)
treec01ed5831f0b7356c8106997f3787d0c83ac6cfb /drivers/regulator
parentd9070fdbe40a04b61262bac0f7ff0c7c29a68015 (diff)
regulator: pwm: Support extra continuous mode cases
The continuous mode allows one to declare a PWM regulator without having to declare the voltage <-> dutycycle association table. It works fine as long as your voltage(dutycycle) function is linear, but also has the following constraints: - dutycycle for min_uV = 0% - dutycycle for max_uV = 100% - dutycycle for min_uV < dutycycle for max_uV While the linearity constraint is acceptable for now, we sometimes need to restrict of the PWM range (to limit the maximum/minimum voltage for example) or have a min_uV_dutycycle > max_uV_dutycycle (this could be tweaked with PWM polarity, but not all PWMs support inverted polarity). Add the pwm-dutycycle-range and pwm-dutycycle-unit DT properties to define such constraints. If those properties are not defined, the PWM regulator use the default pwm-dutycycle-range = <0 100> and pwm-dutycycle-unit = <100> values (existing behavior). Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> Reviewed-by: Brian Norris <briannorris@chromium.org> Tested-by: Brian Norris <briannorris@chromium.org> Tested-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Mark Brown <broonie@kernel.org> Signed-off-by: Thierry Reding <thierry.reding@gmail.com>
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/pwm-regulator.c92
1 files changed, 82 insertions, 10 deletions
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index ae0bebbfda9d..c24524242da2 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -22,6 +22,12 @@
22#include <linux/pwm.h> 22#include <linux/pwm.h>
23#include <linux/gpio/consumer.h> 23#include <linux/gpio/consumer.h>
24 24
25struct pwm_continuous_reg_data {
26 unsigned int min_uV_dutycycle;
27 unsigned int max_uV_dutycycle;
28 unsigned int dutycycle_unit;
29};
30
25struct pwm_regulator_data { 31struct pwm_regulator_data {
26 /* Shared */ 32 /* Shared */
27 struct pwm_device *pwm; 33 struct pwm_device *pwm;
@@ -29,6 +35,9 @@ struct pwm_regulator_data {
29 /* Voltage table */ 35 /* Voltage table */
30 struct pwm_voltages *duty_cycle_table; 36 struct pwm_voltages *duty_cycle_table;
31 37
38 /* Continuous mode info */
39 struct pwm_continuous_reg_data continuous;
40
32 /* regulator descriptor */ 41 /* regulator descriptor */
33 struct regulator_desc desc; 42 struct regulator_desc desc;
34 43
@@ -145,32 +154,78 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
145static int pwm_regulator_get_voltage(struct regulator_dev *rdev) 154static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
146{ 155{
147 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); 156 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
157 unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
158 unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
159 unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
148 int min_uV = rdev->constraints->min_uV; 160 int min_uV = rdev->constraints->min_uV;
149 int diff = rdev->constraints->max_uV - min_uV; 161 int max_uV = rdev->constraints->max_uV;
162 int diff_uV = max_uV - min_uV;
150 struct pwm_state pstate; 163 struct pwm_state pstate;
164 unsigned int diff_duty;
165 unsigned int voltage;
151 166
152 pwm_get_state(drvdata->pwm, &pstate); 167 pwm_get_state(drvdata->pwm, &pstate);
153 168
154 return min_uV + pwm_get_relative_duty_cycle(&pstate, diff); 169 voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit);
170
171 /*
172 * The dutycycle for min_uV might be greater than the one for max_uV.
173 * This is happening when the user needs an inversed polarity, but the
174 * PWM device does not support inversing it in hardware.
175 */
176 if (max_uV_duty < min_uV_duty) {
177 voltage = min_uV_duty - voltage;
178 diff_duty = min_uV_duty - max_uV_duty;
179 } else {
180 voltage = voltage - min_uV_duty;
181 diff_duty = max_uV_duty - min_uV_duty;
182 }
183
184 voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty);
185
186 return voltage + min_uV;
155} 187}
156 188
157static int pwm_regulator_set_voltage(struct regulator_dev *rdev, 189static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
158 int min_uV, int max_uV, 190 int req_min_uV, int req_max_uV,
159 unsigned *selector) 191 unsigned int *selector)
160{ 192{
161 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); 193 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
194 unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle;
195 unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle;
196 unsigned int duty_unit = drvdata->continuous.dutycycle_unit;
162 unsigned int ramp_delay = rdev->constraints->ramp_delay; 197 unsigned int ramp_delay = rdev->constraints->ramp_delay;
163 unsigned int req_diff = min_uV - rdev->constraints->min_uV; 198 int min_uV = rdev->constraints->min_uV;
199 int max_uV = rdev->constraints->max_uV;
200 int diff_uV = max_uV - min_uV;
164 struct pwm_state pstate; 201 struct pwm_state pstate;
165 unsigned int diff;
166 int old_uV = pwm_regulator_get_voltage(rdev); 202 int old_uV = pwm_regulator_get_voltage(rdev);
203 unsigned int diff_duty;
204 unsigned int dutycycle;
167 int ret; 205 int ret;
168 206
169 pwm_init_state(drvdata->pwm, &pstate); 207 pwm_init_state(drvdata->pwm, &pstate);
170 diff = rdev->constraints->max_uV - rdev->constraints->min_uV;
171 208
172 /* We pass diff as the scale to get a uV precision. */ 209 /*
173 pwm_set_relative_duty_cycle(&pstate, req_diff, diff); 210 * The dutycycle for min_uV might be greater than the one for max_uV.
211 * This is happening when the user needs an inversed polarity, but the
212 * PWM device does not support inversing it in hardware.
213 */
214 if (max_uV_duty < min_uV_duty)
215 diff_duty = min_uV_duty - max_uV_duty;
216 else
217 diff_duty = max_uV_duty - min_uV_duty;
218
219 dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) *
220 diff_duty,
221 diff_uV);
222
223 if (max_uV_duty < min_uV_duty)
224 dutycycle = min_uV_duty - dutycycle;
225 else
226 dutycycle = min_uV_duty + dutycycle;
227
228 pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit);
174 229
175 ret = pwm_apply_state(drvdata->pwm, &pstate); 230 ret = pwm_apply_state(drvdata->pwm, &pstate);
176 if (ret) { 231 if (ret) {
@@ -182,7 +237,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
182 return 0; 237 return 0;
183 238
184 /* Ramp delay is in uV/uS. Adjust to uS and delay */ 239 /* Ramp delay is in uV/uS. Adjust to uS and delay */
185 ramp_delay = DIV_ROUND_UP(abs(min_uV - old_uV), ramp_delay); 240 ramp_delay = DIV_ROUND_UP(abs(req_min_uV - old_uV), ramp_delay);
186 usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10)); 241 usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
187 242
188 return 0; 243 return 0;
@@ -255,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
255static int pwm_regulator_init_continuous(struct platform_device *pdev, 310static int pwm_regulator_init_continuous(struct platform_device *pdev,
256 struct pwm_regulator_data *drvdata) 311 struct pwm_regulator_data *drvdata)
257{ 312{
313 u32 dutycycle_range[2] = { 0, 100 };
314 u32 dutycycle_unit = 100;
315
258 memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops, 316 memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
259 sizeof(drvdata->ops)); 317 sizeof(drvdata->ops));
260 drvdata->desc.ops = &drvdata->ops; 318 drvdata->desc.ops = &drvdata->ops;
261 drvdata->desc.continuous_voltage_range = true; 319 drvdata->desc.continuous_voltage_range = true;
262 320
321 of_property_read_u32_array(pdev->dev.of_node,
322 "pwm-dutycycle-range",
323 dutycycle_range, 2);
324 of_property_read_u32(pdev->dev.of_node, "pwm-dutycycle-unit",
325 &dutycycle_unit);
326
327 if (dutycycle_range[0] > dutycycle_unit ||
328 dutycycle_range[1] > dutycycle_unit)
329 return -EINVAL;
330
331 drvdata->continuous.dutycycle_unit = dutycycle_unit;
332 drvdata->continuous.min_uV_dutycycle = dutycycle_range[0];
333 drvdata->continuous.max_uV_dutycycle = dutycycle_range[1];
334
263 return 0; 335 return 0;
264} 336}
265 337