aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/regulator
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-08-06 00:01:33 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-08-06 00:01:33 -0400
commita157b3aaa44829998d5a079174df989e5d8c20ff (patch)
tree35db2b0e47acebdc666fb58f185c84a219d78606 /drivers/regulator
parent32199ec3cf8db2de1709cec9339844555b55c16e (diff)
parent53de7c26ded7f5e954bfc202dffc43c0dd165337 (diff)
Merge tag 'pwm/for-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm
Pull pwm updates from Thierry Reding: "This set of changes improve some aspects of the atomic API as well as make use of this new API in the regulator framework to allow properly dealing with critical regulators controlled by a PWM. Aside from that there's a bunch of updates and cleanups for existing drivers, as well as the addition of new drivers for the Broadcom iProc, STMPE and ChromeOS EC controllers" * tag 'pwm/for-4.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm: (44 commits) regulator: pwm: Document pwm-dutycycle-unit and pwm-dutycycle-range regulator: pwm: Support extra continuous mode cases pwm: Add ChromeOS EC PWM driver dt-bindings: pwm: Add binding for ChromeOS EC PWM mfd: cros_ec: Add EC_PWM function definitions mfd: cros_ec: Add cros_ec_cmd_xfer_status() helper pwm: atmel: Use of_device_get_match_data() pwm: atmel: Fix checkpatch warnings pwm: atmel: Fix disabling of PWM channels dt-bindings: pwm: Add R-Car H3 device tree bindings pwm: rcar: Use ARCH_RENESAS pwm: tegra: Add support for Tegra186 dt-bindings: pwm: tegra: Add compatible string for Tegra186 pwm: tegra: Avoid overflow when calculating duty cycle pwm: tegra: Allow 100 % duty cycle pwm: tegra: Add support for reset control pwm: tegra: Rename mmio_base to regs pwm: tegra: Remove useless padding pwm: tegra: Drop NUM_PWM macro pwm: lpc32xx: Set PWM_PIN_LEVEL bit to default value ...
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/pwm-regulator.c162
1 files changed, 118 insertions, 44 deletions
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 666bc3bb52ef..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
@@ -37,9 +46,6 @@ struct pwm_regulator_data {
37 46
38 int state; 47 int state;
39 48
40 /* Continuous voltage */
41 int volt_uV;
42
43 /* Enable GPIO */ 49 /* Enable GPIO */
44 struct gpio_desc *enb_gpio; 50 struct gpio_desc *enb_gpio;
45}; 51};
@@ -52,10 +58,31 @@ struct pwm_voltages {
52/** 58/**
53 * Voltage table call-backs 59 * Voltage table call-backs
54 */ 60 */
61static void pwm_regulator_init_state(struct regulator_dev *rdev)
62{
63 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
64 struct pwm_state pwm_state;
65 unsigned int dutycycle;
66 int i;
67
68 pwm_get_state(drvdata->pwm, &pwm_state);
69 dutycycle = pwm_get_relative_duty_cycle(&pwm_state, 100);
70
71 for (i = 0; i < rdev->desc->n_voltages; i++) {
72 if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) {
73 drvdata->state = i;
74 return;
75 }
76 }
77}
78
55static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) 79static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
56{ 80{
57 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); 81 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
58 82
83 if (drvdata->state < 0)
84 pwm_regulator_init_state(rdev);
85
59 return drvdata->state; 86 return drvdata->state;
60} 87}
61 88
@@ -63,16 +90,14 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
63 unsigned selector) 90 unsigned selector)
64{ 91{
65 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); 92 struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
66 struct pwm_args pargs; 93 struct pwm_state pstate;
67 int dutycycle;
68 int ret; 94 int ret;
69 95
70 pwm_get_args(drvdata->pwm, &pargs); 96 pwm_init_state(drvdata->pwm, &pstate);
71 97 pwm_set_relative_duty_cycle(&pstate,
72 dutycycle = (pargs.period * 98 drvdata->duty_cycle_table[selector].dutycycle, 100);
73 drvdata->duty_cycle_table[selector].dutycycle) / 100;
74 99
75 ret = pwm_config(drvdata->pwm, dutycycle, pargs.period); 100 ret = pwm_apply_state(drvdata->pwm, &pstate);
76 if (ret) { 101 if (ret) {
77 dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); 102 dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
78 return ret; 103 return ret;
@@ -129,57 +154,90 @@ static int pwm_regulator_is_enabled(struct regulator_dev *dev)
129static int pwm_regulator_get_voltage(struct regulator_dev *rdev) 154static int pwm_regulator_get_voltage(struct regulator_dev *rdev)
130{ 155{
131 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;
160 int min_uV = rdev->constraints->min_uV;
161 int max_uV = rdev->constraints->max_uV;
162 int diff_uV = max_uV - min_uV;
163 struct pwm_state pstate;
164 unsigned int diff_duty;
165 unsigned int voltage;
166
167 pwm_get_state(drvdata->pwm, &pstate);
168
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 }
132 183
133 return drvdata->volt_uV; 184 voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty);
185
186 return voltage + min_uV;
134} 187}
135 188
136static int pwm_regulator_set_voltage(struct regulator_dev *rdev, 189static int pwm_regulator_set_voltage(struct regulator_dev *rdev,
137 int min_uV, int max_uV, 190 int req_min_uV, int req_max_uV,
138 unsigned *selector) 191 unsigned int *selector)
139{ 192{
140 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;
141 unsigned int ramp_delay = rdev->constraints->ramp_delay; 197 unsigned int ramp_delay = rdev->constraints->ramp_delay;
142 struct pwm_args pargs; 198 int min_uV = rdev->constraints->min_uV;
143 unsigned int req_diff = min_uV - rdev->constraints->min_uV; 199 int max_uV = rdev->constraints->max_uV;
144 unsigned int diff; 200 int diff_uV = max_uV - min_uV;
145 unsigned int duty_pulse; 201 struct pwm_state pstate;
146 u64 req_period;
147 u32 rem;
148 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;
149 int ret; 205 int ret;
150 206
151 pwm_get_args(drvdata->pwm, &pargs); 207 pwm_init_state(drvdata->pwm, &pstate);
152 diff = rdev->constraints->max_uV - rdev->constraints->min_uV;
153 208
154 /* First try to find out if we get the iduty cycle time which is 209 /*
155 * factor of PWM period time. If (request_diff_to_min * pwm_period) 210 * The dutycycle for min_uV might be greater than the one for max_uV.
156 * is perfect divided by voltage_range_diff then it is possible to 211 * This is happening when the user needs an inversed polarity, but the
157 * get duty cycle time which is factor of PWM period. This will help 212 * PWM device does not support inversing it in hardware.
158 * to get output voltage nearer to requested value as there is no
159 * calculation loss.
160 */ 213 */
161 req_period = req_diff * pargs.period; 214 if (max_uV_duty < min_uV_duty)
162 div_u64_rem(req_period, diff, &rem); 215 diff_duty = min_uV_duty - max_uV_duty;
163 if (!rem) { 216 else
164 do_div(req_period, diff); 217 diff_duty = max_uV_duty - min_uV_duty;
165 duty_pulse = (unsigned int)req_period; 218
166 } else { 219 dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) *
167 duty_pulse = (pargs.period / 100) * ((req_diff * 100) / diff); 220 diff_duty,
168 } 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);
169 229
170 ret = pwm_config(drvdata->pwm, duty_pulse, pargs.period); 230 ret = pwm_apply_state(drvdata->pwm, &pstate);
171 if (ret) { 231 if (ret) {
172 dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret); 232 dev_err(&rdev->dev, "Failed to configure PWM: %d\n", ret);
173 return ret; 233 return ret;
174 } 234 }
175 235
176 drvdata->volt_uV = min_uV;
177
178 if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev)) 236 if ((ramp_delay == 0) || !pwm_regulator_is_enabled(rdev))
179 return 0; 237 return 0;
180 238
181 /* Ramp delay is in uV/uS. Adjust to uS and delay */ 239 /* Ramp delay is in uV/uS. Adjust to uS and delay */
182 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);
183 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));
184 242
185 return 0; 243 return 0;
@@ -239,6 +297,7 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
239 return ret; 297 return ret;
240 } 298 }
241 299
300 drvdata->state = -EINVAL;
242 drvdata->duty_cycle_table = duty_cycle_table; 301 drvdata->duty_cycle_table = duty_cycle_table;
243 memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops, 302 memcpy(&drvdata->ops, &pwm_regulator_voltage_table_ops,
244 sizeof(drvdata->ops)); 303 sizeof(drvdata->ops));
@@ -251,11 +310,28 @@ static int pwm_regulator_init_table(struct platform_device *pdev,
251static int pwm_regulator_init_continuous(struct platform_device *pdev, 310static int pwm_regulator_init_continuous(struct platform_device *pdev,
252 struct pwm_regulator_data *drvdata) 311 struct pwm_regulator_data *drvdata)
253{ 312{
313 u32 dutycycle_range[2] = { 0, 100 };
314 u32 dutycycle_unit = 100;
315
254 memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops, 316 memcpy(&drvdata->ops, &pwm_regulator_voltage_continuous_ops,
255 sizeof(drvdata->ops)); 317 sizeof(drvdata->ops));
256 drvdata->desc.ops = &drvdata->ops; 318 drvdata->desc.ops = &drvdata->ops;
257 drvdata->desc.continuous_voltage_range = true; 319 drvdata->desc.continuous_voltage_range = true;
258 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
259 return 0; 335 return 0;
260} 336}
261 337
@@ -316,11 +392,9 @@ static int pwm_regulator_probe(struct platform_device *pdev)
316 return ret; 392 return ret;
317 } 393 }
318 394
319 /* 395 ret = pwm_adjust_config(drvdata->pwm);
320 * FIXME: pwm_apply_args() should be removed when switching to the 396 if (ret)
321 * atomic PWM API. 397 return ret;
322 */
323 pwm_apply_args(drvdata->pwm);
324 398
325 regulator = devm_regulator_register(&pdev->dev, 399 regulator = devm_regulator_register(&pdev->dev,
326 &drvdata->desc, &config); 400 &drvdata->desc, &config);