Skip to content

Commit ea398e2

Browse files
Boris Brezillonthierryreding
authored andcommitted
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>
1 parent d9070fd commit ea398e2

File tree

1 file changed

+82
-10
lines changed

1 file changed

+82
-10
lines changed

drivers/regulator/pwm-regulator.c

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,22 @@
2222
#include <linux/pwm.h>
2323
#include <linux/gpio/consumer.h>
2424

25+
struct pwm_continuous_reg_data {
26+
unsigned int min_uV_dutycycle;
27+
unsigned int max_uV_dutycycle;
28+
unsigned int dutycycle_unit;
29+
};
30+
2531
struct pwm_regulator_data {
2632
/* Shared */
2733
struct pwm_device *pwm;
2834

2935
/* Voltage table */
3036
struct pwm_voltages *duty_cycle_table;
3137

38+
/* Continuous mode info */
39+
struct pwm_continuous_reg_data continuous;
40+
3241
/* regulator descriptor */
3342
struct regulator_desc desc;
3443

@@ -145,32 +154,78 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
145154
static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
146155
{
147156
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;
148160
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;
150163
struct pwm_state pstate;
164+
unsigned int diff_duty;
165+
unsigned int voltage;
151166

152167
pwm_get_state(drvdata->pwm, &pstate);
153168

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;
155187
}
156188

157189
static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
158-
int min_uV, int max_uV,
159-
unsigned *selector)
190+
int req_min_uV, int req_max_uV,
191+
unsigned int *selector)
160192
{
161193
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;
162197
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;
164201
struct pwm_state pstate;
165-
unsigned int diff;
166202
int old_uV = pwm_regulator_get_voltage(rdev);
203+
unsigned int diff_duty;
204+
unsigned int dutycycle;
167205
int ret;
168206

169207
pwm_init_state(drvdata->pwm, &pstate);
170-
diff = rdev->constraints->max_uV - rdev->constraints->min_uV;
171208

172-
/* We pass diff as the scale to get a uV precision. */
173-
pwm_set_relative_duty_cycle(&pstate, req_diff, diff);
209+
/*
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);
174229

175230
ret = pwm_apply_state(drvdata->pwm, &pstate);
176231
if (ret) {
@@ -182,7 +237,7 @@ static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
182237
return 0;
183238

184239
/* 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);
186241
usleep_range(ramp_delay, ramp_delay + DIV_ROUND_UP(ramp_delay, 10));
187242

188243
return 0;
@@ -255,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
255310
static int pwm_regulator_init_continuous(struct platform_device *pdev,
256311
struct pwm_regulator_data *drvdata)
257312
{
313+
u32 dutycycle_range[2] = { 0, 100 };
314+
u32 dutycycle_unit = 100;
315+
258316
memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
259317
sizeof(drvdata->ops));
260318
drvdata->desc.ops = &drvdata->ops;
261319
drvdata->desc.continuous_voltage_range = true;
262320

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+
263335
return 0;
264336
}
265337

0 commit comments

Comments
 (0)