]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
af41e8db PM |
2 | /* |
3 | * Copyright (C) 2014-2015 Samsung Electronics | |
4 | * Przemyslaw Marczak <[email protected]> | |
af41e8db | 5 | */ |
f15cd4f1 | 6 | |
af41e8db | 7 | #include <common.h> |
af41e8db PM |
8 | #include <errno.h> |
9 | #include <dm.h> | |
f7ae49fc | 10 | #include <log.h> |
af41e8db | 11 | #include <dm/uclass-internal.h> |
c05ed00a | 12 | #include <linux/delay.h> |
af41e8db PM |
13 | #include <power/pmic.h> |
14 | #include <power/regulator.h> | |
15 | ||
af41e8db PM |
16 | int regulator_mode(struct udevice *dev, struct dm_regulator_mode **modep) |
17 | { | |
18 | struct dm_regulator_uclass_platdata *uc_pdata; | |
19 | ||
20 | *modep = NULL; | |
21 | ||
22 | uc_pdata = dev_get_uclass_platdata(dev); | |
23 | if (!uc_pdata) | |
24 | return -ENXIO; | |
25 | ||
26 | *modep = uc_pdata->mode; | |
27 | return uc_pdata->mode_count; | |
28 | } | |
29 | ||
30 | int regulator_get_value(struct udevice *dev) | |
31 | { | |
32 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
33 | ||
34 | if (!ops || !ops->get_value) | |
35 | return -ENOSYS; | |
36 | ||
37 | return ops->get_value(dev); | |
38 | } | |
39 | ||
e66d1cb3 KK |
40 | static void regulator_set_value_ramp_delay(struct udevice *dev, int old_uV, |
41 | int new_uV, unsigned int ramp_delay) | |
42 | { | |
43 | int delay = DIV_ROUND_UP(abs(new_uV - old_uV), ramp_delay); | |
44 | ||
45 | debug("regulator %s: delay %u us (%d uV -> %d uV)\n", dev->name, delay, | |
46 | old_uV, new_uV); | |
47 | ||
48 | udelay(delay); | |
49 | } | |
50 | ||
af41e8db PM |
51 | int regulator_set_value(struct udevice *dev, int uV) |
52 | { | |
53 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
eaadcf38 | 54 | struct dm_regulator_uclass_platdata *uc_pdata; |
e66d1cb3 | 55 | int ret, old_uV = uV, is_enabled = 0; |
eaadcf38 K |
56 | |
57 | uc_pdata = dev_get_uclass_platdata(dev); | |
58 | if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) | |
59 | return -EINVAL; | |
60 | if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV) | |
61 | return -EINVAL; | |
af41e8db PM |
62 | |
63 | if (!ops || !ops->set_value) | |
64 | return -ENOSYS; | |
65 | ||
e66d1cb3 KK |
66 | if (uc_pdata->ramp_delay) { |
67 | is_enabled = regulator_get_enable(dev); | |
68 | old_uV = regulator_get_value(dev); | |
69 | } | |
70 | ||
71 | ret = ops->set_value(dev, uV); | |
72 | ||
73 | if (!ret) { | |
74 | if (uc_pdata->ramp_delay && old_uV > 0 && is_enabled) | |
75 | regulator_set_value_ramp_delay(dev, old_uV, uV, | |
76 | uc_pdata->ramp_delay); | |
77 | } | |
78 | ||
79 | return ret; | |
af41e8db | 80 | } |
2f5d532f | 81 | |
11406b8f JC |
82 | int regulator_set_suspend_value(struct udevice *dev, int uV) |
83 | { | |
84 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
85 | struct dm_regulator_uclass_platdata *uc_pdata; | |
86 | ||
87 | uc_pdata = dev_get_uclass_platdata(dev); | |
88 | if (uc_pdata->min_uV != -ENODATA && uV < uc_pdata->min_uV) | |
89 | return -EINVAL; | |
90 | if (uc_pdata->max_uV != -ENODATA && uV > uc_pdata->max_uV) | |
91 | return -EINVAL; | |
92 | ||
93 | if (!ops->set_suspend_value) | |
94 | return -ENOSYS; | |
95 | ||
96 | return ops->set_suspend_value(dev, uV); | |
97 | } | |
98 | ||
99 | int regulator_get_suspend_value(struct udevice *dev) | |
100 | { | |
101 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
102 | ||
103 | if (!ops->get_suspend_value) | |
104 | return -ENOSYS; | |
105 | ||
106 | return ops->get_suspend_value(dev); | |
107 | } | |
108 | ||
2f5d532f K |
109 | /* |
110 | * To be called with at most caution as there is no check | |
111 | * before setting the actual voltage value. | |
112 | */ | |
113 | int regulator_set_value_force(struct udevice *dev, int uV) | |
114 | { | |
115 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
116 | ||
117 | if (!ops || !ops->set_value) | |
118 | return -ENOSYS; | |
119 | ||
120 | return ops->set_value(dev, uV); | |
121 | } | |
af41e8db PM |
122 | |
123 | int regulator_get_current(struct udevice *dev) | |
124 | { | |
125 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
126 | ||
127 | if (!ops || !ops->get_current) | |
128 | return -ENOSYS; | |
129 | ||
130 | return ops->get_current(dev); | |
131 | } | |
132 | ||
133 | int regulator_set_current(struct udevice *dev, int uA) | |
134 | { | |
135 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
5483456e K |
136 | struct dm_regulator_uclass_platdata *uc_pdata; |
137 | ||
138 | uc_pdata = dev_get_uclass_platdata(dev); | |
139 | if (uc_pdata->min_uA != -ENODATA && uA < uc_pdata->min_uA) | |
140 | return -EINVAL; | |
141 | if (uc_pdata->max_uA != -ENODATA && uA > uc_pdata->max_uA) | |
142 | return -EINVAL; | |
af41e8db PM |
143 | |
144 | if (!ops || !ops->set_current) | |
145 | return -ENOSYS; | |
146 | ||
147 | return ops->set_current(dev, uA); | |
148 | } | |
149 | ||
06bdf600 | 150 | int regulator_get_enable(struct udevice *dev) |
af41e8db PM |
151 | { |
152 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
153 | ||
154 | if (!ops || !ops->get_enable) | |
155 | return -ENOSYS; | |
156 | ||
157 | return ops->get_enable(dev); | |
158 | } | |
159 | ||
160 | int regulator_set_enable(struct udevice *dev, bool enable) | |
161 | { | |
162 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
4f86a724 | 163 | struct dm_regulator_uclass_platdata *uc_pdata; |
e66d1cb3 | 164 | int ret, old_enable = 0; |
af41e8db PM |
165 | |
166 | if (!ops || !ops->set_enable) | |
167 | return -ENOSYS; | |
168 | ||
4f86a724 PD |
169 | uc_pdata = dev_get_uclass_platdata(dev); |
170 | if (!enable && uc_pdata->always_on) | |
f93fab31 | 171 | return -EACCES; |
4f86a724 | 172 | |
e66d1cb3 KK |
173 | if (uc_pdata->ramp_delay) |
174 | old_enable = regulator_get_enable(dev); | |
175 | ||
176 | ret = ops->set_enable(dev, enable); | |
177 | if (!ret) { | |
178 | if (uc_pdata->ramp_delay && !old_enable && enable) { | |
179 | int uV = regulator_get_value(dev); | |
180 | ||
181 | if (uV > 0) { | |
182 | regulator_set_value_ramp_delay(dev, 0, uV, | |
183 | uc_pdata->ramp_delay); | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | return ret; | |
af41e8db PM |
189 | } |
190 | ||
cc4a224a LV |
191 | int regulator_set_enable_if_allowed(struct udevice *dev, bool enable) |
192 | { | |
193 | int ret; | |
194 | ||
195 | ret = regulator_set_enable(dev, enable); | |
196 | if (ret == -ENOSYS || ret == -EACCES) | |
197 | return 0; | |
198 | ||
199 | return ret; | |
200 | } | |
201 | ||
11406b8f JC |
202 | int regulator_set_suspend_enable(struct udevice *dev, bool enable) |
203 | { | |
204 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
205 | ||
206 | if (!ops->set_suspend_enable) | |
207 | return -ENOSYS; | |
208 | ||
209 | return ops->set_suspend_enable(dev, enable); | |
210 | } | |
211 | ||
212 | int regulator_get_suspend_enable(struct udevice *dev) | |
213 | { | |
214 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
215 | ||
216 | if (!ops->get_suspend_enable) | |
217 | return -ENOSYS; | |
218 | ||
219 | return ops->get_suspend_enable(dev); | |
220 | } | |
221 | ||
af41e8db PM |
222 | int regulator_get_mode(struct udevice *dev) |
223 | { | |
224 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
225 | ||
226 | if (!ops || !ops->get_mode) | |
227 | return -ENOSYS; | |
228 | ||
229 | return ops->get_mode(dev); | |
230 | } | |
231 | ||
232 | int regulator_set_mode(struct udevice *dev, int mode) | |
233 | { | |
234 | const struct dm_regulator_ops *ops = dev_get_driver_ops(dev); | |
235 | ||
236 | if (!ops || !ops->set_mode) | |
237 | return -ENOSYS; | |
238 | ||
239 | return ops->set_mode(dev, mode); | |
240 | } | |
241 | ||
3b880757 | 242 | int regulator_get_by_platname(const char *plat_name, struct udevice **devp) |
af41e8db PM |
243 | { |
244 | struct dm_regulator_uclass_platdata *uc_pdata; | |
245 | struct udevice *dev; | |
3b880757 | 246 | int ret; |
af41e8db PM |
247 | |
248 | *devp = NULL; | |
249 | ||
3b880757 PM |
250 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; |
251 | ret = uclass_find_next_device(&dev)) { | |
422f04b6 SG |
252 | if (ret) { |
253 | debug("regulator %s, ret=%d\n", dev->name, ret); | |
3b880757 | 254 | continue; |
422f04b6 | 255 | } |
3b880757 | 256 | |
af41e8db PM |
257 | uc_pdata = dev_get_uclass_platdata(dev); |
258 | if (!uc_pdata || strcmp(plat_name, uc_pdata->name)) | |
259 | continue; | |
260 | ||
261 | return uclass_get_device_tail(dev, 0, devp); | |
262 | } | |
263 | ||
422f04b6 | 264 | debug("%s: can't find: %s, ret=%d\n", __func__, plat_name, ret); |
af41e8db PM |
265 | |
266 | return -ENODEV; | |
267 | } | |
268 | ||
3b880757 | 269 | int regulator_get_by_devname(const char *devname, struct udevice **devp) |
af41e8db PM |
270 | { |
271 | return uclass_get_device_by_name(UCLASS_REGULATOR, devname, devp); | |
272 | } | |
273 | ||
7c816e24 PM |
274 | int device_get_supply_regulator(struct udevice *dev, const char *supply_name, |
275 | struct udevice **devp) | |
276 | { | |
277 | return uclass_get_device_by_phandle(UCLASS_REGULATOR, dev, | |
278 | supply_name, devp); | |
279 | } | |
280 | ||
3b55d30f | 281 | int regulator_autoset(struct udevice *dev) |
af41e8db | 282 | { |
3b55d30f SG |
283 | struct dm_regulator_uclass_platdata *uc_pdata; |
284 | int ret = 0; | |
af41e8db | 285 | |
3b55d30f | 286 | uc_pdata = dev_get_uclass_platdata(dev); |
11406b8f JC |
287 | |
288 | ret = regulator_set_suspend_enable(dev, uc_pdata->suspend_on); | |
289 | if (!ret && uc_pdata->suspend_on) { | |
290 | ret = regulator_set_suspend_value(dev, uc_pdata->suspend_uV); | |
291 | if (!ret) | |
292 | return ret; | |
293 | } | |
294 | ||
3b55d30f SG |
295 | if (!uc_pdata->always_on && !uc_pdata->boot_on) |
296 | return -EMEDIUMTYPE; | |
af41e8db | 297 | |
1164c546 SS |
298 | if (uc_pdata->type == REGULATOR_TYPE_FIXED) |
299 | return regulator_set_enable(dev, true); | |
300 | ||
3b55d30f SG |
301 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) |
302 | ret = regulator_set_value(dev, uc_pdata->min_uV); | |
11406b8f JC |
303 | if (uc_pdata->init_uV > 0) |
304 | ret = regulator_set_value(dev, uc_pdata->init_uV); | |
3b55d30f SG |
305 | if (!ret && (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA)) |
306 | ret = regulator_set_current(dev, uc_pdata->min_uA); | |
af41e8db PM |
307 | |
308 | if (!ret) | |
3b55d30f | 309 | ret = regulator_set_enable(dev, true); |
af41e8db PM |
310 | |
311 | return ret; | |
312 | } | |
313 | ||
3b55d30f | 314 | static void regulator_show(struct udevice *dev, int ret) |
af41e8db PM |
315 | { |
316 | struct dm_regulator_uclass_platdata *uc_pdata; | |
af41e8db PM |
317 | |
318 | uc_pdata = dev_get_uclass_platdata(dev); | |
3b880757 | 319 | |
3b55d30f SG |
320 | printf("%s@%s: ", dev->name, uc_pdata->name); |
321 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UV) | |
322 | printf("set %d uV", uc_pdata->min_uV); | |
323 | if (uc_pdata->flags & REGULATOR_FLAG_AUTOSET_UA) | |
324 | printf("; set %d uA", uc_pdata->min_uA); | |
325 | printf("; enabling"); | |
326 | if (ret) | |
7d577999 | 327 | printf(" (ret: %d)", ret); |
3b55d30f SG |
328 | printf("\n"); |
329 | } | |
af41e8db | 330 | |
3b55d30f SG |
331 | int regulator_autoset_by_name(const char *platname, struct udevice **devp) |
332 | { | |
333 | struct udevice *dev; | |
334 | int ret; | |
af41e8db | 335 | |
3b55d30f | 336 | ret = regulator_get_by_platname(platname, &dev); |
af41e8db PM |
337 | if (devp) |
338 | *devp = dev; | |
3b55d30f | 339 | if (ret) { |
422f04b6 | 340 | debug("Can get the regulator: %s (err=%d)\n", platname, ret); |
3b55d30f SG |
341 | return ret; |
342 | } | |
3b880757 | 343 | |
3b55d30f | 344 | return regulator_autoset(dev); |
af41e8db PM |
345 | } |
346 | ||
3b880757 PM |
347 | int regulator_list_autoset(const char *list_platname[], |
348 | struct udevice *list_devp[], | |
349 | bool verbose) | |
af41e8db PM |
350 | { |
351 | struct udevice *dev; | |
3b880757 | 352 | int error = 0, i = 0, ret; |
af41e8db | 353 | |
3b880757 | 354 | while (list_platname[i]) { |
3b55d30f SG |
355 | ret = regulator_autoset_by_name(list_platname[i], &dev); |
356 | if (ret != -EMEDIUMTYPE && verbose) | |
357 | regulator_show(dev, ret); | |
3b880757 PM |
358 | if (ret & !error) |
359 | error = ret; | |
360 | ||
361 | if (list_devp) | |
362 | list_devp[i] = dev; | |
363 | ||
364 | i++; | |
365 | } | |
af41e8db | 366 | |
3b880757 PM |
367 | return error; |
368 | } | |
369 | ||
370 | static bool regulator_name_is_unique(struct udevice *check_dev, | |
371 | const char *check_name) | |
372 | { | |
373 | struct dm_regulator_uclass_platdata *uc_pdata; | |
374 | struct udevice *dev; | |
375 | int check_len = strlen(check_name); | |
376 | int ret; | |
377 | int len; | |
378 | ||
379 | for (ret = uclass_find_first_device(UCLASS_REGULATOR, &dev); dev; | |
380 | ret = uclass_find_next_device(&dev)) { | |
381 | if (ret || dev == check_dev) | |
af41e8db PM |
382 | continue; |
383 | ||
3b880757 PM |
384 | uc_pdata = dev_get_uclass_platdata(dev); |
385 | len = strlen(uc_pdata->name); | |
386 | if (len != check_len) | |
387 | continue; | |
388 | ||
389 | if (!strcmp(uc_pdata->name, check_name)) | |
390 | return false; | |
af41e8db PM |
391 | } |
392 | ||
3b880757 | 393 | return true; |
af41e8db PM |
394 | } |
395 | ||
396 | static int regulator_post_bind(struct udevice *dev) | |
397 | { | |
398 | struct dm_regulator_uclass_platdata *uc_pdata; | |
3b880757 | 399 | const char *property = "regulator-name"; |
af41e8db PM |
400 | |
401 | uc_pdata = dev_get_uclass_platdata(dev); | |
af41e8db PM |
402 | |
403 | /* Regulator's mandatory constraint */ | |
f15cd4f1 | 404 | uc_pdata->name = dev_read_string(dev, property); |
af41e8db | 405 | if (!uc_pdata->name) { |
f15cd4f1 SG |
406 | debug("%s: dev '%s' has no property '%s'\n", |
407 | __func__, dev->name, property); | |
408 | uc_pdata->name = dev_read_name(dev); | |
cf260011 PF |
409 | if (!uc_pdata->name) |
410 | return -EINVAL; | |
af41e8db PM |
411 | } |
412 | ||
3b880757 PM |
413 | if (regulator_name_is_unique(dev, uc_pdata->name)) |
414 | return 0; | |
415 | ||
f15cd4f1 | 416 | debug("'%s' of dev: '%s', has nonunique value: '%s\n", |
3b880757 PM |
417 | property, dev->name, uc_pdata->name); |
418 | ||
419 | return -EINVAL; | |
af41e8db PM |
420 | } |
421 | ||
422 | static int regulator_pre_probe(struct udevice *dev) | |
423 | { | |
424 | struct dm_regulator_uclass_platdata *uc_pdata; | |
11406b8f | 425 | ofnode node; |
af41e8db PM |
426 | |
427 | uc_pdata = dev_get_uclass_platdata(dev); | |
428 | if (!uc_pdata) | |
429 | return -ENXIO; | |
430 | ||
431 | /* Regulator's optional constraints */ | |
f15cd4f1 SG |
432 | uc_pdata->min_uV = dev_read_u32_default(dev, "regulator-min-microvolt", |
433 | -ENODATA); | |
434 | uc_pdata->max_uV = dev_read_u32_default(dev, "regulator-max-microvolt", | |
435 | -ENODATA); | |
11406b8f JC |
436 | uc_pdata->init_uV = dev_read_u32_default(dev, "regulator-init-microvolt", |
437 | -ENODATA); | |
f15cd4f1 SG |
438 | uc_pdata->min_uA = dev_read_u32_default(dev, "regulator-min-microamp", |
439 | -ENODATA); | |
440 | uc_pdata->max_uA = dev_read_u32_default(dev, "regulator-max-microamp", | |
441 | -ENODATA); | |
442 | uc_pdata->always_on = dev_read_bool(dev, "regulator-always-on"); | |
443 | uc_pdata->boot_on = dev_read_bool(dev, "regulator-boot-on"); | |
e66d1cb3 KK |
444 | uc_pdata->ramp_delay = dev_read_u32_default(dev, "regulator-ramp-delay", |
445 | 0); | |
af41e8db | 446 | |
11406b8f JC |
447 | node = dev_read_subnode(dev, "regulator-state-mem"); |
448 | if (ofnode_valid(node)) { | |
449 | uc_pdata->suspend_on = !ofnode_read_bool(node, "regulator-off-in-suspend"); | |
450 | if (ofnode_read_u32(node, "regulator-suspend-microvolt", &uc_pdata->suspend_uV)) | |
451 | uc_pdata->suspend_uV = uc_pdata->max_uV; | |
452 | } else { | |
453 | uc_pdata->suspend_on = true; | |
454 | uc_pdata->suspend_uV = uc_pdata->max_uV; | |
455 | } | |
456 | ||
7837ceab SG |
457 | /* Those values are optional (-ENODATA if unset) */ |
458 | if ((uc_pdata->min_uV != -ENODATA) && | |
459 | (uc_pdata->max_uV != -ENODATA) && | |
460 | (uc_pdata->min_uV == uc_pdata->max_uV)) | |
461 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UV; | |
462 | ||
463 | /* Those values are optional (-ENODATA if unset) */ | |
464 | if ((uc_pdata->min_uA != -ENODATA) && | |
465 | (uc_pdata->max_uA != -ENODATA) && | |
466 | (uc_pdata->min_uA == uc_pdata->max_uA)) | |
467 | uc_pdata->flags |= REGULATOR_FLAG_AUTOSET_UA; | |
468 | ||
b7adcdd0 LM |
469 | if (uc_pdata->boot_on) |
470 | regulator_set_enable(dev, uc_pdata->boot_on); | |
471 | ||
af41e8db PM |
472 | return 0; |
473 | } | |
474 | ||
083fc83a SG |
475 | int regulators_enable_boot_on(bool verbose) |
476 | { | |
477 | struct udevice *dev; | |
478 | struct uclass *uc; | |
479 | int ret; | |
480 | ||
481 | ret = uclass_get(UCLASS_REGULATOR, &uc); | |
482 | if (ret) | |
483 | return ret; | |
484 | for (uclass_first_device(UCLASS_REGULATOR, &dev); | |
3f603cbb | 485 | dev; |
083fc83a SG |
486 | uclass_next_device(&dev)) { |
487 | ret = regulator_autoset(dev); | |
d08504d1 SG |
488 | if (ret == -EMEDIUMTYPE) { |
489 | ret = 0; | |
083fc83a | 490 | continue; |
d08504d1 | 491 | } |
083fc83a SG |
492 | if (verbose) |
493 | regulator_show(dev, ret); | |
364809de SG |
494 | if (ret == -ENOSYS) |
495 | ret = 0; | |
083fc83a SG |
496 | } |
497 | ||
498 | return ret; | |
499 | } | |
500 | ||
af41e8db PM |
501 | UCLASS_DRIVER(regulator) = { |
502 | .id = UCLASS_REGULATOR, | |
503 | .name = "regulator", | |
504 | .post_bind = regulator_post_bind, | |
505 | .pre_probe = regulator_pre_probe, | |
506 | .per_device_platdata_auto_alloc_size = | |
507 | sizeof(struct dm_regulator_uclass_platdata), | |
508 | }; |