Blame view
drivers/regulator/pwm-regulator.c
10.6 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
aa66cc663 regulator: pwm-re... |
2 3 4 5 6 7 |
/* * Regulator driver for PWM Regulators * * Copyright (C) 2014 - STMicroelectronics Inc. * * Author: Lee Jones <lee.jones@linaro.org> |
aa66cc663 regulator: pwm-re... |
8 9 10 11 12 13 14 15 16 17 18 |
*/ #include <linux/module.h> #include <linux/init.h> #include <linux/err.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> #include <linux/regulator/of_regulator.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/pwm.h> |
27bfa8893 regulator: pwm: S... |
19 |
#include <linux/gpio/consumer.h> |
aa66cc663 regulator: pwm-re... |
20 |
|
ea398e287 regulator: pwm: S... |
21 22 23 24 25 |
struct pwm_continuous_reg_data { unsigned int min_uV_dutycycle; unsigned int max_uV_dutycycle; unsigned int dutycycle_unit; }; |
aa66cc663 regulator: pwm-re... |
26 |
struct pwm_regulator_data { |
4773be185 regulator: pwm-re... |
27 |
/* Shared */ |
aa66cc663 regulator: pwm-re... |
28 |
struct pwm_device *pwm; |
4773be185 regulator: pwm-re... |
29 30 31 |
/* Voltage table */ struct pwm_voltages *duty_cycle_table; |
f907a0a94 regulator: pwm: A... |
32 |
|
ea398e287 regulator: pwm: S... |
33 34 |
/* Continuous mode info */ struct pwm_continuous_reg_data continuous; |
f907a0a94 regulator: pwm: A... |
35 36 |
/* regulator descriptor */ struct regulator_desc desc; |
aa66cc663 regulator: pwm-re... |
37 |
int state; |
4773be185 regulator: pwm-re... |
38 |
|
27bfa8893 regulator: pwm: S... |
39 40 |
/* Enable GPIO */ struct gpio_desc *enb_gpio; |
aa66cc663 regulator: pwm-re... |
41 42 43 44 45 46 |
}; struct pwm_voltages { unsigned int uV; unsigned int dutycycle; }; |
4e773e739 regulator: pwm-re... |
47 |
/* |
4773be185 regulator: pwm-re... |
48 49 |
* Voltage table call-backs */ |
87248991a regulator: pwm: P... |
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
static void pwm_regulator_init_state(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); struct pwm_state pwm_state; unsigned int dutycycle; int i; pwm_get_state(drvdata->pwm, &pwm_state); dutycycle = pwm_get_relative_duty_cycle(&pwm_state, 100); for (i = 0; i < rdev->desc->n_voltages; i++) { if (dutycycle == drvdata->duty_cycle_table[i].dutycycle) { drvdata->state = i; return; } } } |
ab101e354 regulator: pwm-re... |
67 |
static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev) |
aa66cc663 regulator: pwm-re... |
68 |
{ |
ab101e354 regulator: pwm-re... |
69 |
struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); |
aa66cc663 regulator: pwm-re... |
70 |
|
87248991a regulator: pwm: P... |
71 72 |
if (drvdata->state < 0) pwm_regulator_init_state(rdev); |
aa66cc663 regulator: pwm-re... |
73 74 |
return drvdata->state; } |
ab101e354 regulator: pwm-re... |
75 |
static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev, |
aa66cc663 regulator: pwm-re... |
76 77 |
unsigned selector) { |
ab101e354 regulator: pwm-re... |
78 |
struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); |
3f4eb39be regulator: pwm: S... |
79 |
struct pwm_state pstate; |
aa66cc663 regulator: pwm-re... |
80 |
int ret; |
3f4eb39be regulator: pwm: S... |
81 82 83 |
pwm_init_state(drvdata->pwm, &pstate); pwm_set_relative_duty_cycle(&pstate, drvdata->duty_cycle_table[selector].dutycycle, 100); |
aa66cc663 regulator: pwm-re... |
84 |
|
3f4eb39be regulator: pwm: S... |
85 |
ret = pwm_apply_state(drvdata->pwm, &pstate); |
aa66cc663 regulator: pwm-re... |
86 |
if (ret) { |
5bf59bd5e regulator: pwm: P... |
87 88 |
dev_err(&rdev->dev, "Failed to configure PWM: %d ", ret); |
aa66cc663 regulator: pwm-re... |
89 90 91 92 |
return ret; } drvdata->state = selector; |
aa66cc663 regulator: pwm-re... |
93 94 |
return 0; } |
ab101e354 regulator: pwm-re... |
95 |
static int pwm_regulator_list_voltage(struct regulator_dev *rdev, |
aa66cc663 regulator: pwm-re... |
96 97 |
unsigned selector) { |
ab101e354 regulator: pwm-re... |
98 |
struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); |
aa66cc663 regulator: pwm-re... |
99 |
|
ab101e354 regulator: pwm-re... |
100 |
if (selector >= rdev->desc->n_voltages) |
aa66cc663 regulator: pwm-re... |
101 102 103 104 |
return -EINVAL; return drvdata->duty_cycle_table[selector].uV; } |
4773be185 regulator: pwm-re... |
105 |
|
1de7d8024 regulator: pwm: i... |
106 107 108 |
static int pwm_regulator_enable(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); |
a4aae5afc regulator: pwm-re... |
109 |
gpiod_set_value_cansleep(drvdata->enb_gpio, 1); |
27bfa8893 regulator: pwm: S... |
110 |
|
1de7d8024 regulator: pwm: i... |
111 112 113 114 115 116 117 118 |
return pwm_enable(drvdata->pwm); } static int pwm_regulator_disable(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); pwm_disable(drvdata->pwm); |
a4aae5afc regulator: pwm-re... |
119 |
gpiod_set_value_cansleep(drvdata->enb_gpio, 0); |
27bfa8893 regulator: pwm: S... |
120 |
|
1de7d8024 regulator: pwm: i... |
121 122 123 124 125 126 |
return 0; } static int pwm_regulator_is_enabled(struct regulator_dev *dev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev); |
27bfa8893 regulator: pwm: S... |
127 128 |
if (drvdata->enb_gpio && !gpiod_get_value_cansleep(drvdata->enb_gpio)) return false; |
1de7d8024 regulator: pwm: i... |
129 130 |
return pwm_is_enabled(drvdata->pwm); } |
4773be185 regulator: pwm-re... |
131 132 133 |
static int pwm_regulator_get_voltage(struct regulator_dev *rdev) { struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); |
ea398e287 regulator: pwm: S... |
134 135 136 |
unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle; unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle; unsigned int duty_unit = drvdata->continuous.dutycycle_unit; |
d9070fdbe regulator: pwm: R... |
137 |
int min_uV = rdev->constraints->min_uV; |
ea398e287 regulator: pwm: S... |
138 139 |
int max_uV = rdev->constraints->max_uV; int diff_uV = max_uV - min_uV; |
d9070fdbe regulator: pwm: R... |
140 |
struct pwm_state pstate; |
ea398e287 regulator: pwm: S... |
141 142 |
unsigned int diff_duty; unsigned int voltage; |
4773be185 regulator: pwm-re... |
143 |
|
d9070fdbe regulator: pwm: R... |
144 |
pwm_get_state(drvdata->pwm, &pstate); |
ea398e287 regulator: pwm: S... |
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
voltage = pwm_get_relative_duty_cycle(&pstate, duty_unit); /* * The dutycycle for min_uV might be greater than the one for max_uV. * This is happening when the user needs an inversed polarity, but the * PWM device does not support inversing it in hardware. */ if (max_uV_duty < min_uV_duty) { voltage = min_uV_duty - voltage; diff_duty = min_uV_duty - max_uV_duty; } else { voltage = voltage - min_uV_duty; diff_duty = max_uV_duty - min_uV_duty; } voltage = DIV_ROUND_CLOSEST_ULL((u64)voltage * diff_uV, diff_duty); return voltage + min_uV; |
4773be185 regulator: pwm-re... |
163 164 165 |
} static int pwm_regulator_set_voltage(struct regulator_dev *rdev, |
ea398e287 regulator: pwm: S... |
166 167 |
int req_min_uV, int req_max_uV, unsigned int *selector) |
4773be185 regulator: pwm-re... |
168 169 |
{ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev); |
ea398e287 regulator: pwm: S... |
170 171 172 |
unsigned int min_uV_duty = drvdata->continuous.min_uV_dutycycle; unsigned int max_uV_duty = drvdata->continuous.max_uV_dutycycle; unsigned int duty_unit = drvdata->continuous.dutycycle_unit; |
ea398e287 regulator: pwm: S... |
173 174 175 |
int min_uV = rdev->constraints->min_uV; int max_uV = rdev->constraints->max_uV; int diff_uV = max_uV - min_uV; |
3f4eb39be regulator: pwm: S... |
176 |
struct pwm_state pstate; |
ea398e287 regulator: pwm: S... |
177 178 |
unsigned int diff_duty; unsigned int dutycycle; |
4773be185 regulator: pwm-re... |
179 |
int ret; |
3f4eb39be regulator: pwm: S... |
180 |
pwm_init_state(drvdata->pwm, &pstate); |
fd786fb02 regulator: pwm: T... |
181 |
|
ea398e287 regulator: pwm: S... |
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
/* * The dutycycle for min_uV might be greater than the one for max_uV. * This is happening when the user needs an inversed polarity, but the * PWM device does not support inversing it in hardware. */ if (max_uV_duty < min_uV_duty) diff_duty = min_uV_duty - max_uV_duty; else diff_duty = max_uV_duty - min_uV_duty; dutycycle = DIV_ROUND_CLOSEST_ULL((u64)(req_min_uV - min_uV) * diff_duty, diff_uV); if (max_uV_duty < min_uV_duty) dutycycle = min_uV_duty - dutycycle; else dutycycle = min_uV_duty + dutycycle; pwm_set_relative_duty_cycle(&pstate, dutycycle, duty_unit); |
4773be185 regulator: pwm-re... |
202 |
|
3f4eb39be regulator: pwm: S... |
203 |
ret = pwm_apply_state(drvdata->pwm, &pstate); |
4773be185 regulator: pwm-re... |
204 |
if (ret) { |
5bf59bd5e regulator: pwm: P... |
205 206 |
dev_err(&rdev->dev, "Failed to configure PWM: %d ", ret); |
4773be185 regulator: pwm-re... |
207 208 |
return ret; } |
4773be185 regulator: pwm-re... |
209 210 |
return 0; } |
638aef7a7 regulator: pwm: N... |
211 |
static const struct regulator_ops pwm_regulator_voltage_table_ops = { |
aa66cc663 regulator: pwm-re... |
212 213 214 215 |
.set_voltage_sel = pwm_regulator_set_voltage_sel, .get_voltage_sel = pwm_regulator_get_voltage_sel, .list_voltage = pwm_regulator_list_voltage, .map_voltage = regulator_map_voltage_iterate, |
1de7d8024 regulator: pwm: i... |
216 217 218 |
.enable = pwm_regulator_enable, .disable = pwm_regulator_disable, .is_enabled = pwm_regulator_is_enabled, |
aa66cc663 regulator: pwm-re... |
219 |
}; |
638aef7a7 regulator: pwm: N... |
220 |
static const struct regulator_ops pwm_regulator_voltage_continuous_ops = { |
4773be185 regulator: pwm-re... |
221 222 |
.get_voltage = pwm_regulator_get_voltage, .set_voltage = pwm_regulator_set_voltage, |
1de7d8024 regulator: pwm: i... |
223 224 225 |
.enable = pwm_regulator_enable, .disable = pwm_regulator_disable, .is_enabled = pwm_regulator_is_enabled, |
4773be185 regulator: pwm-re... |
226 |
}; |
638aef7a7 regulator: pwm: N... |
227 |
static const struct regulator_desc pwm_regulator_desc = { |
aa66cc663 regulator: pwm-re... |
228 |
.name = "pwm-regulator", |
aa66cc663 regulator: pwm-re... |
229 230 231 232 |
.type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, .supply_name = "pwm", }; |
f9178dad6 regulator: pwm-re... |
233 234 235 236 237 |
static int pwm_regulator_init_table(struct platform_device *pdev, struct pwm_regulator_data *drvdata) { struct device_node *np = pdev->dev.of_node; struct pwm_voltages *duty_cycle_table; |
60cb65ebf regulator: pwm-re... |
238 |
unsigned int length = 0; |
f9178dad6 regulator: pwm-re... |
239 240 241 242 243 244 |
int ret; of_find_property(np, "voltage-table", &length); if ((length < sizeof(*duty_cycle_table)) || (length % sizeof(*duty_cycle_table))) { |
5bf59bd5e regulator: pwm: P... |
245 246 |
dev_err(&pdev->dev, "voltage-table length(%d) is invalid ", |
f9178dad6 regulator: pwm-re... |
247 248 249 250 251 252 253 254 255 256 257 258 |
length); return -EINVAL; } duty_cycle_table = devm_kzalloc(&pdev->dev, length, GFP_KERNEL); if (!duty_cycle_table) return -ENOMEM; ret = of_property_read_u32_array(np, "voltage-table", (u32 *)duty_cycle_table, length / sizeof(u32)); if (ret) { |
5bf59bd5e regulator: pwm: P... |
259 260 |
dev_err(&pdev->dev, "Failed to read voltage-table: %d ", ret); |
f9178dad6 regulator: pwm-re... |
261 262 |
return ret; } |
59ae97a7a regulator: pwm: F... |
263 |
drvdata->state = -ENOTRECOVERABLE; |
f9178dad6 regulator: pwm-re... |
264 |
drvdata->duty_cycle_table = duty_cycle_table; |
638aef7a7 regulator: pwm: N... |
265 |
drvdata->desc.ops = &pwm_regulator_voltage_table_ops; |
f907a0a94 regulator: pwm: A... |
266 |
drvdata->desc.n_voltages = length / sizeof(*duty_cycle_table); |
f9178dad6 regulator: pwm-re... |
267 268 269 |
return 0; } |
4773be185 regulator: pwm-re... |
270 271 272 |
static int pwm_regulator_init_continuous(struct platform_device *pdev, struct pwm_regulator_data *drvdata) { |
ea398e287 regulator: pwm: S... |
273 274 |
u32 dutycycle_range[2] = { 0, 100 }; u32 dutycycle_unit = 100; |
638aef7a7 regulator: pwm: N... |
275 |
drvdata->desc.ops = &pwm_regulator_voltage_continuous_ops; |
f907a0a94 regulator: pwm: A... |
276 |
drvdata->desc.continuous_voltage_range = true; |
4773be185 regulator: pwm-re... |
277 |
|
ea398e287 regulator: pwm: S... |
278 279 280 281 282 283 284 285 286 287 288 289 290 |
of_property_read_u32_array(pdev->dev.of_node, "pwm-dutycycle-range", dutycycle_range, 2); of_property_read_u32(pdev->dev.of_node, "pwm-dutycycle-unit", &dutycycle_unit); if (dutycycle_range[0] > dutycycle_unit || dutycycle_range[1] > dutycycle_unit) return -EINVAL; drvdata->continuous.dutycycle_unit = dutycycle_unit; drvdata->continuous.min_uV_dutycycle = dutycycle_range[0]; drvdata->continuous.max_uV_dutycycle = dutycycle_range[1]; |
4773be185 regulator: pwm-re... |
291 292 |
return 0; } |
aa66cc663 regulator: pwm-re... |
293 294 |
static int pwm_regulator_probe(struct platform_device *pdev) { |
5ad2cb14f regulator: pwm-re... |
295 |
const struct regulator_init_data *init_data; |
aa66cc663 regulator: pwm-re... |
296 |
struct pwm_regulator_data *drvdata; |
aa66cc663 regulator: pwm-re... |
297 298 299 |
struct regulator_dev *regulator; struct regulator_config config = { }; struct device_node *np = pdev->dev.of_node; |
27bfa8893 regulator: pwm: S... |
300 |
enum gpiod_flags gpio_flags; |
f9178dad6 regulator: pwm-re... |
301 |
int ret; |
aa66cc663 regulator: pwm-re... |
302 303 304 305 306 307 308 309 310 311 |
if (!np) { dev_err(&pdev->dev, "Device Tree node missing "); return -EINVAL; } drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); if (!drvdata) return -ENOMEM; |
f907a0a94 regulator: pwm: A... |
312 |
memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(drvdata->desc)); |
4773be185 regulator: pwm-re... |
313 |
if (of_find_property(np, "voltage-table", NULL)) |
f9178dad6 regulator: pwm-re... |
314 |
ret = pwm_regulator_init_table(pdev, drvdata); |
4773be185 regulator: pwm-re... |
315 316 317 318 |
else ret = pwm_regulator_init_continuous(pdev, drvdata); if (ret) return ret; |
aa66cc663 regulator: pwm-re... |
319 |
|
5ad2cb14f regulator: pwm-re... |
320 |
init_data = of_get_regulator_init_data(&pdev->dev, np, |
f907a0a94 regulator: pwm: A... |
321 |
&drvdata->desc); |
5ad2cb14f regulator: pwm-re... |
322 |
if (!init_data) |
aa66cc663 regulator: pwm-re... |
323 324 325 326 327 |
return -ENOMEM; config.of_node = np; config.dev = &pdev->dev; config.driver_data = drvdata; |
5ad2cb14f regulator: pwm-re... |
328 |
config.init_data = init_data; |
aa66cc663 regulator: pwm-re... |
329 330 331 |
drvdata->pwm = devm_pwm_get(&pdev->dev, NULL); if (IS_ERR(drvdata->pwm)) { |
5bf59bd5e regulator: pwm: P... |
332 |
ret = PTR_ERR(drvdata->pwm); |
0cd71b9a4 regulator: pwm: D... |
333 334 335 336 337 338 339 |
if (ret == -EPROBE_DEFER) dev_dbg(&pdev->dev, "Failed to get PWM, deferring probe "); else dev_err(&pdev->dev, "Failed to get PWM: %d ", ret); |
5bf59bd5e regulator: pwm: P... |
340 |
return ret; |
aa66cc663 regulator: pwm-re... |
341 |
} |
27bfa8893 regulator: pwm: S... |
342 343 344 345 346 347 348 349 350 351 352 353 |
if (init_data->constraints.boot_on || init_data->constraints.always_on) gpio_flags = GPIOD_OUT_HIGH; else gpio_flags = GPIOD_OUT_LOW; drvdata->enb_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", gpio_flags); if (IS_ERR(drvdata->enb_gpio)) { ret = PTR_ERR(drvdata->enb_gpio); dev_err(&pdev->dev, "Failed to get enable GPIO: %d ", ret); return ret; } |
fd4f99c4c regulator: pwm: A... |
354 355 356 |
ret = pwm_adjust_config(drvdata->pwm); if (ret) return ret; |
8c12ad8e9 regulator: pwm: U... |
357 |
|
aa66cc663 regulator: pwm-re... |
358 |
regulator = devm_regulator_register(&pdev->dev, |
f907a0a94 regulator: pwm: A... |
359 |
&drvdata->desc, &config); |
aa66cc663 regulator: pwm-re... |
360 |
if (IS_ERR(regulator)) { |
5bf59bd5e regulator: pwm: P... |
361 362 363 364 365 |
ret = PTR_ERR(regulator); dev_err(&pdev->dev, "Failed to register regulator %s: %d ", drvdata->desc.name, ret); return ret; |
aa66cc663 regulator: pwm-re... |
366 367 368 369 |
} return 0; } |
dc8c5ea35 regulator: pwm: F... |
370 |
static const struct of_device_id __maybe_unused pwm_of_match[] = { |
aa66cc663 regulator: pwm-re... |
371 372 373 374 375 376 377 378 |
{ .compatible = "pwm-regulator" }, { }, }; MODULE_DEVICE_TABLE(of, pwm_of_match); static struct platform_driver pwm_regulator_driver = { .driver = { .name = "pwm-regulator", |
aa66cc663 regulator: pwm-re... |
379 380 381 382 383 384 385 386 387 388 389 |
.of_match_table = of_match_ptr(pwm_of_match), }, .probe = pwm_regulator_probe, }; module_platform_driver(pwm_regulator_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); MODULE_DESCRIPTION("PWM Regulator Driver"); MODULE_ALIAS("platform:pwm-regulator"); |