diff options
author | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-06-14 05:13:21 -0400 |
---|---|---|
committer | Thierry Reding <thierry.reding@gmail.com> | 2016-07-25 09:34:06 -0400 |
commit | ea398e28739e25651ede7ddf5aeb57cbcbc8ca7d (patch) | |
tree | c01ed5831f0b7356c8106997f3787d0c83ac6cfb /drivers/regulator | |
parent | d9070fdbe40a04b61262bac0f7ff0c7c29a68015 (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.c | 92 |
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 | ||
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 | |||
25 | struct pwm_regulator_data { | 31 | struct 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) | |||
145 | static int pwm_regulator_get_voltage(struct regulator_dev *rdev) | 154 | static 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 | ||
157 | static int pwm_regulator_set_voltage(struct regulator_dev *rdev, | 189 | static 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, | |||
255 | static int pwm_regulator_init_continuous(struct platform_device *pdev, | 310 | static 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 | ||