]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0 |
60599ea6 JC |
2 | /* |
3 | * Copyright (C) 2018 Samsung Electronics | |
4 | * Jaehoon Chung <jh80.chung@samsung.com> | |
60599ea6 JC |
5 | */ |
6 | ||
d678a59d | 7 | #include <common.h> |
60599ea6 JC |
8 | #include <fdtdec.h> |
9 | #include <errno.h> | |
10 | #include <dm.h> | |
c05ed00a | 11 | #include <linux/delay.h> |
1e94b46f | 12 | #include <linux/printk.h> |
60599ea6 JC |
13 | #include <power/pmic.h> |
14 | #include <power/regulator.h> | |
15 | #include <power/s2mps11.h> | |
16 | ||
60599ea6 JC |
17 | #define MODE(_id, _val, _name) { \ |
18 | .id = _id, \ | |
19 | .register_value = _val, \ | |
20 | .name = _name, \ | |
21 | } | |
22 | ||
23 | /* BUCK : 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 */ | |
24 | static struct dm_regulator_mode s2mps11_buck_modes[] = { | |
25 | MODE(OP_OFF, S2MPS11_BUCK_MODE_OFF, "OFF"), | |
26 | MODE(OP_STANDBY, S2MPS11_BUCK_MODE_STANDBY, "ON/OFF"), | |
27 | MODE(OP_ON, S2MPS11_BUCK_MODE_STANDBY, "ON"), | |
28 | }; | |
29 | ||
30 | static struct dm_regulator_mode s2mps11_ldo_modes[] = { | |
31 | MODE(OP_OFF, S2MPS11_LDO_MODE_OFF, "OFF"), | |
32 | MODE(OP_STANDBY, S2MPS11_LDO_MODE_STANDBY, "ON/OFF"), | |
33 | MODE(OP_STANDBY_LPM, S2MPS11_LDO_MODE_STANDBY_LPM, "ON/LPM"), | |
34 | MODE(OP_ON, S2MPS11_LDO_MODE_ON, "ON"), | |
35 | }; | |
36 | ||
37 | static const char s2mps11_buck_ctrl[] = { | |
38 | 0xff, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x33, 0x35, 0x37, 0x39, 0x3b | |
39 | }; | |
40 | ||
41 | static const char s2mps11_buck_out[] = { | |
42 | 0xff, 0x26, 0x28, 0x2a, 0x2c, 0x2f, 0x34, 0x36, 0x38, 0x3a, 0x3c | |
43 | }; | |
44 | ||
45 | static int s2mps11_buck_hex2volt(int buck, int hex) | |
46 | { | |
47 | unsigned int uV = 0; | |
48 | ||
49 | if (hex < 0) | |
50 | goto bad; | |
51 | ||
52 | switch (buck) { | |
53 | case 7: | |
54 | case 8: | |
55 | case 10: | |
56 | if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX) | |
57 | goto bad; | |
58 | ||
59 | uV = hex * S2MPS11_BUCK_HSTEP + S2MPS11_BUCK_UV_HMIN; | |
60 | break; | |
61 | case 9: | |
62 | if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX) | |
63 | goto bad; | |
64 | uV = hex * S2MPS11_BUCK9_STEP * 2 + S2MPS11_BUCK9_UV_MIN; | |
65 | break; | |
66 | default: | |
67 | if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX) | |
68 | goto bad; | |
69 | else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX) | |
70 | goto bad; | |
71 | ||
72 | uV = hex * S2MPS11_BUCK_LSTEP + S2MPS11_BUCK_UV_MIN; | |
73 | break; | |
74 | } | |
75 | ||
76 | return uV; | |
77 | bad: | |
78 | pr_err("Value: %#x is wrong for BUCK%d", hex, buck); | |
79 | return -EINVAL; | |
80 | } | |
81 | ||
82 | static int s2mps11_buck_volt2hex(int buck, int uV) | |
83 | { | |
84 | int hex; | |
85 | ||
86 | switch (buck) { | |
87 | case 7: | |
88 | case 8: | |
89 | case 10: | |
90 | hex = (uV - S2MPS11_BUCK_UV_HMIN) / S2MPS11_BUCK_HSTEP; | |
91 | if (hex > S2MPS11_BUCK7_8_10_VOLT_MAX_HEX) | |
92 | goto bad; | |
93 | ||
94 | break; | |
95 | case 9: | |
96 | hex = (uV - S2MPS11_BUCK9_UV_MIN) / S2MPS11_BUCK9_STEP; | |
97 | if (hex > S2MPS11_BUCK9_VOLT_MAX_HEX) | |
98 | goto bad; | |
99 | break; | |
100 | default: | |
101 | hex = (uV - S2MPS11_BUCK_UV_MIN) / S2MPS11_BUCK_LSTEP; | |
102 | if (buck == 5 && hex > S2MPS11_BUCK5_VOLT_MAX_HEX) | |
103 | goto bad; | |
104 | else if (buck != 5 && hex > S2MPS11_BUCK_VOLT_MAX_HEX) | |
105 | goto bad; | |
106 | break; | |
107 | }; | |
108 | ||
109 | if (hex >= 0) | |
110 | return hex; | |
111 | ||
112 | bad: | |
113 | pr_err("Value: %d uV is wrong for BUCK%d", uV, buck); | |
114 | return -EINVAL; | |
115 | } | |
116 | ||
117 | static int s2mps11_buck_val(struct udevice *dev, int op, int *uV) | |
118 | { | |
119 | int hex, buck, ret; | |
120 | u32 mask, addr; | |
121 | u8 val; | |
122 | ||
123 | buck = dev->driver_data; | |
124 | if (buck < 1 || buck > S2MPS11_BUCK_NUM) { | |
125 | pr_err("Wrong buck number: %d\n", buck); | |
126 | return -EINVAL; | |
127 | } | |
128 | ||
129 | if (op == PMIC_OP_GET) | |
130 | *uV = 0; | |
131 | ||
132 | addr = s2mps11_buck_out[buck]; | |
133 | ||
134 | switch (buck) { | |
135 | case 9: | |
136 | mask = S2MPS11_BUCK9_VOLT_MASK; | |
137 | break; | |
138 | default: | |
139 | mask = S2MPS11_BUCK_VOLT_MASK; | |
140 | break; | |
141 | } | |
142 | ||
143 | ret = pmic_read(dev->parent, addr, &val, 1); | |
144 | if (ret) | |
145 | return ret; | |
146 | ||
147 | if (op == PMIC_OP_GET) { | |
148 | val &= mask; | |
149 | ret = s2mps11_buck_hex2volt(buck, val); | |
150 | if (ret < 0) | |
151 | return ret; | |
152 | *uV = ret; | |
153 | return 0; | |
154 | } | |
155 | ||
156 | hex = s2mps11_buck_volt2hex(buck, *uV); | |
157 | if (hex < 0) | |
158 | return hex; | |
159 | ||
160 | val &= ~mask; | |
161 | val |= hex; | |
162 | ret = pmic_write(dev->parent, addr, &val, 1); | |
163 | ||
164 | return ret; | |
165 | } | |
166 | ||
167 | static int s2mps11_buck_mode(struct udevice *dev, int op, int *opmode) | |
168 | { | |
169 | unsigned int addr, mode; | |
170 | unsigned char val; | |
171 | int buck, ret; | |
172 | ||
173 | buck = dev->driver_data; | |
174 | if (buck < 1 || buck > S2MPS11_BUCK_NUM) { | |
175 | pr_err("Wrong buck number: %d\n", buck); | |
176 | return -EINVAL; | |
177 | } | |
178 | ||
179 | addr = s2mps11_buck_ctrl[buck]; | |
180 | ||
181 | ret = pmic_read(dev->parent, addr, &val, 1); | |
182 | if (ret) | |
183 | return ret; | |
184 | ||
185 | if (op == PMIC_OP_GET) { | |
186 | val &= (S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT); | |
187 | switch (val) { | |
188 | case S2MPS11_BUCK_MODE_OFF: | |
189 | *opmode = OP_OFF; | |
190 | break; | |
191 | case S2MPS11_BUCK_MODE_STANDBY: | |
192 | *opmode = OP_STANDBY; | |
193 | break; | |
194 | case S2MPS11_BUCK_MODE_ON: | |
195 | *opmode = OP_ON; | |
196 | break; | |
197 | default: | |
198 | return -EINVAL; | |
199 | } | |
200 | return 0; | |
201 | } | |
202 | ||
203 | switch (*opmode) { | |
204 | case OP_OFF: | |
205 | mode = S2MPS11_BUCK_MODE_OFF; | |
206 | break; | |
207 | case OP_STANDBY: | |
208 | mode = S2MPS11_BUCK_MODE_STANDBY; | |
209 | break; | |
210 | case OP_ON: | |
211 | mode = S2MPS11_BUCK_MODE_ON; | |
212 | break; | |
213 | default: | |
214 | pr_err("Wrong mode: %d for buck: %d\n", *opmode, buck); | |
215 | return -EINVAL; | |
216 | } | |
217 | ||
218 | val &= ~(S2MPS11_BUCK_MODE_MASK << S2MPS11_BUCK_MODE_SHIFT); | |
219 | val |= mode; | |
220 | ret = pmic_write(dev->parent, addr, &val, 1); | |
221 | ||
222 | return ret; | |
223 | } | |
224 | ||
225 | static int s2mps11_buck_enable(struct udevice *dev, int op, bool *enable) | |
226 | { | |
227 | int ret, on_off; | |
228 | ||
229 | if (op == PMIC_OP_GET) { | |
230 | ret = s2mps11_buck_mode(dev, op, &on_off); | |
231 | if (ret) | |
232 | return ret; | |
233 | switch (on_off) { | |
234 | case OP_OFF: | |
235 | *enable = false; | |
236 | break; | |
237 | case OP_ON: | |
238 | *enable = true; | |
239 | break; | |
240 | default: | |
241 | return -EINVAL; | |
242 | } | |
243 | } else if (op == PMIC_OP_SET) { | |
244 | if (*enable) | |
245 | on_off = OP_ON; | |
246 | else | |
247 | on_off = OP_OFF; | |
248 | ||
249 | ret = s2mps11_buck_mode(dev, op, &on_off); | |
250 | if (ret) | |
251 | return ret; | |
252 | } | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | static int buck_get_value(struct udevice *dev) | |
258 | { | |
259 | int uV; | |
260 | int ret; | |
261 | ||
262 | ret = s2mps11_buck_val(dev, PMIC_OP_GET, &uV); | |
263 | if (ret) | |
264 | return ret; | |
265 | return uV; | |
266 | } | |
267 | ||
268 | static int buck_set_value(struct udevice *dev, int uV) | |
269 | { | |
270 | return s2mps11_buck_val(dev, PMIC_OP_SET, &uV); | |
271 | } | |
272 | ||
273 | static int buck_get_enable(struct udevice *dev) | |
274 | { | |
275 | bool enable = false; | |
276 | int ret; | |
277 | ||
278 | ret = s2mps11_buck_enable(dev, PMIC_OP_GET, &enable); | |
279 | if (ret) | |
280 | return ret; | |
281 | return enable; | |
282 | } | |
283 | ||
284 | static int buck_set_enable(struct udevice *dev, bool enable) | |
285 | { | |
286 | return s2mps11_buck_enable(dev, PMIC_OP_SET, &enable); | |
287 | } | |
288 | ||
289 | static int buck_get_mode(struct udevice *dev) | |
290 | { | |
291 | int mode; | |
292 | int ret; | |
293 | ||
294 | ret = s2mps11_buck_mode(dev, PMIC_OP_GET, &mode); | |
295 | if (ret) | |
296 | return ret; | |
297 | ||
298 | return mode; | |
299 | } | |
300 | ||
301 | static int buck_set_mode(struct udevice *dev, int mode) | |
302 | { | |
303 | return s2mps11_buck_mode(dev, PMIC_OP_SET, &mode); | |
304 | } | |
305 | ||
306 | static int s2mps11_buck_probe(struct udevice *dev) | |
307 | { | |
caa4daa2 | 308 | struct dm_regulator_uclass_plat *uc_pdata; |
60599ea6 | 309 | |
caa4daa2 | 310 | uc_pdata = dev_get_uclass_plat(dev); |
60599ea6 JC |
311 | |
312 | uc_pdata->type = REGULATOR_TYPE_BUCK; | |
313 | uc_pdata->mode = s2mps11_buck_modes; | |
314 | uc_pdata->mode_count = ARRAY_SIZE(s2mps11_buck_modes); | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static const struct dm_regulator_ops s2mps11_buck_ops = { | |
320 | .get_value = buck_get_value, | |
321 | .set_value = buck_set_value, | |
322 | .get_enable = buck_get_enable, | |
323 | .set_enable = buck_set_enable, | |
324 | .get_mode = buck_get_mode, | |
325 | .set_mode = buck_set_mode, | |
326 | }; | |
327 | ||
328 | U_BOOT_DRIVER(s2mps11_buck) = { | |
329 | .name = S2MPS11_BUCK_DRIVER, | |
330 | .id = UCLASS_REGULATOR, | |
331 | .ops = &s2mps11_buck_ops, | |
332 | .probe = s2mps11_buck_probe, | |
333 | }; | |
334 | ||
335 | static int s2mps11_ldo_hex2volt(int ldo, int hex) | |
336 | { | |
337 | unsigned int uV = 0; | |
338 | ||
339 | if (hex > S2MPS11_LDO_VOLT_MAX_HEX) { | |
340 | pr_err("Value: %#x is wrong for LDO%d", hex, ldo); | |
341 | return -EINVAL; | |
342 | } | |
343 | ||
344 | switch (ldo) { | |
345 | case 1: | |
346 | case 6: | |
347 | case 11: | |
348 | case 22: | |
349 | case 23: | |
311eaf74 KK |
350 | case 27: |
351 | case 35: | |
60599ea6 JC |
352 | uV = hex * S2MPS11_LDO_STEP + S2MPS11_LDO_UV_MIN; |
353 | break; | |
354 | default: | |
355 | uV = hex * S2MPS11_LDO_STEP * 2 + S2MPS11_LDO_UV_MIN; | |
356 | break; | |
357 | } | |
358 | ||
359 | return uV; | |
360 | } | |
361 | ||
362 | static int s2mps11_ldo_volt2hex(int ldo, int uV) | |
363 | { | |
364 | int hex = 0; | |
365 | ||
366 | switch (ldo) { | |
367 | case 1: | |
368 | case 6: | |
369 | case 11: | |
370 | case 22: | |
371 | case 23: | |
311eaf74 KK |
372 | case 27: |
373 | case 35: | |
60599ea6 JC |
374 | hex = (uV - S2MPS11_LDO_UV_MIN) / S2MPS11_LDO_STEP; |
375 | break; | |
376 | default: | |
377 | hex = (uV - S2MPS11_LDO_UV_MIN) / (S2MPS11_LDO_STEP * 2); | |
378 | break; | |
379 | } | |
380 | ||
381 | if (hex >= 0 && hex <= S2MPS11_LDO_VOLT_MAX_HEX) | |
382 | return hex; | |
383 | ||
384 | pr_err("Value: %d uV is wrong for LDO%d", uV, ldo); | |
385 | return -EINVAL; | |
386 | ||
387 | return 0; | |
388 | } | |
389 | ||
390 | static int s2mps11_ldo_val(struct udevice *dev, int op, int *uV) | |
391 | { | |
392 | unsigned int addr; | |
393 | unsigned char val; | |
394 | int hex, ldo, ret; | |
395 | ||
396 | ldo = dev->driver_data; | |
397 | if (ldo < 1 || ldo > S2MPS11_LDO_NUM) { | |
398 | pr_err("Wrong ldo number: %d\n", ldo); | |
399 | return -EINVAL; | |
400 | } | |
401 | ||
402 | addr = S2MPS11_REG_L1CTRL + ldo - 1; | |
403 | ||
404 | ret = pmic_read(dev->parent, addr, &val, 1); | |
405 | if (ret) | |
406 | return ret; | |
407 | ||
408 | if (op == PMIC_OP_GET) { | |
409 | *uV = 0; | |
410 | val &= S2MPS11_LDO_VOLT_MASK; | |
411 | ret = s2mps11_ldo_hex2volt(ldo, val); | |
412 | if (ret < 0) | |
413 | return ret; | |
414 | ||
415 | *uV = ret; | |
416 | return 0; | |
417 | } | |
418 | ||
419 | hex = s2mps11_ldo_volt2hex(ldo, *uV); | |
420 | if (hex < 0) | |
421 | return hex; | |
422 | ||
423 | val &= ~S2MPS11_LDO_VOLT_MASK; | |
424 | val |= hex; | |
425 | ret = pmic_write(dev->parent, addr, &val, 1); | |
426 | ||
427 | return ret; | |
428 | } | |
429 | ||
430 | static int s2mps11_ldo_mode(struct udevice *dev, int op, int *opmode) | |
431 | { | |
432 | unsigned int addr, mode; | |
433 | unsigned char val; | |
434 | int ldo, ret; | |
435 | ||
436 | ldo = dev->driver_data; | |
437 | if (ldo < 1 || ldo > S2MPS11_LDO_NUM) { | |
438 | pr_err("Wrong ldo number: %d\n", ldo); | |
439 | return -EINVAL; | |
440 | } | |
441 | addr = S2MPS11_REG_L1CTRL + ldo - 1; | |
442 | ||
443 | ret = pmic_read(dev->parent, addr, &val, 1); | |
444 | if (ret) | |
445 | return ret; | |
446 | ||
447 | if (op == PMIC_OP_GET) { | |
448 | val &= (S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT); | |
449 | switch (val) { | |
450 | case S2MPS11_LDO_MODE_OFF: | |
451 | *opmode = OP_OFF; | |
452 | break; | |
453 | case S2MPS11_LDO_MODE_STANDBY: | |
454 | *opmode = OP_STANDBY; | |
455 | break; | |
456 | case S2MPS11_LDO_MODE_STANDBY_LPM: | |
457 | *opmode = OP_STANDBY_LPM; | |
458 | break; | |
459 | case S2MPS11_LDO_MODE_ON: | |
460 | *opmode = OP_ON; | |
461 | break; | |
462 | default: | |
463 | return -EINVAL; | |
464 | } | |
465 | return 0; | |
466 | } | |
467 | ||
468 | switch (*opmode) { | |
469 | case OP_OFF: | |
470 | mode = S2MPS11_LDO_MODE_OFF; | |
471 | break; | |
472 | case OP_STANDBY: | |
473 | mode = S2MPS11_LDO_MODE_STANDBY; | |
474 | break; | |
475 | case OP_STANDBY_LPM: | |
476 | mode = S2MPS11_LDO_MODE_STANDBY_LPM; | |
477 | break; | |
478 | case OP_ON: | |
479 | mode = S2MPS11_LDO_MODE_ON; | |
480 | break; | |
481 | default: | |
482 | pr_err("Wrong mode: %d for ldo: %d\n", *opmode, ldo); | |
483 | return -EINVAL; | |
484 | } | |
485 | ||
486 | val &= ~(S2MPS11_LDO_MODE_MASK << S2MPS11_LDO_MODE_SHIFT); | |
487 | val |= mode; | |
488 | ret = pmic_write(dev->parent, addr, &val, 1); | |
489 | ||
490 | return ret; | |
491 | } | |
492 | ||
493 | static int s2mps11_ldo_enable(struct udevice *dev, int op, bool *enable) | |
494 | { | |
495 | int ret, on_off; | |
496 | ||
497 | if (op == PMIC_OP_GET) { | |
498 | ret = s2mps11_ldo_mode(dev, op, &on_off); | |
499 | if (ret) | |
500 | return ret; | |
501 | switch (on_off) { | |
502 | case OP_OFF: | |
503 | *enable = false; | |
504 | break; | |
505 | case OP_ON: | |
506 | *enable = true; | |
507 | break; | |
508 | default: | |
509 | return -EINVAL; | |
510 | } | |
511 | } else if (op == PMIC_OP_SET) { | |
512 | if (*enable) | |
513 | on_off = OP_ON; | |
514 | else | |
515 | on_off = OP_OFF; | |
516 | ||
517 | ret = s2mps11_ldo_mode(dev, op, &on_off); | |
518 | if (ret) | |
519 | return ret; | |
520 | } | |
521 | ||
522 | return 0; | |
523 | } | |
524 | ||
525 | static int ldo_get_value(struct udevice *dev) | |
526 | { | |
527 | int uV; | |
528 | int ret; | |
529 | ||
530 | ret = s2mps11_ldo_val(dev, PMIC_OP_GET, &uV); | |
531 | if (ret) | |
532 | return ret; | |
533 | ||
534 | return uV; | |
535 | } | |
536 | ||
537 | static int ldo_set_value(struct udevice *dev, int uV) | |
538 | { | |
539 | return s2mps11_ldo_val(dev, PMIC_OP_SET, &uV); | |
540 | } | |
541 | ||
542 | static int ldo_get_enable(struct udevice *dev) | |
543 | { | |
544 | bool enable = false; | |
545 | int ret; | |
546 | ||
547 | ret = s2mps11_ldo_enable(dev, PMIC_OP_GET, &enable); | |
548 | if (ret) | |
549 | return ret; | |
550 | return enable; | |
551 | } | |
552 | ||
553 | static int ldo_set_enable(struct udevice *dev, bool enable) | |
554 | { | |
000ee4b7 KK |
555 | int ret; |
556 | ||
557 | ret = s2mps11_ldo_enable(dev, PMIC_OP_SET, &enable); | |
558 | if (ret) | |
559 | return ret; | |
560 | ||
561 | /* Wait the "enable delay" for voltage to start to rise */ | |
562 | udelay(15); | |
563 | ||
564 | return 0; | |
60599ea6 JC |
565 | } |
566 | ||
567 | static int ldo_get_mode(struct udevice *dev) | |
568 | { | |
569 | int mode, ret; | |
570 | ||
571 | ret = s2mps11_ldo_mode(dev, PMIC_OP_GET, &mode); | |
572 | if (ret) | |
573 | return ret; | |
574 | return mode; | |
575 | } | |
576 | ||
577 | static int ldo_set_mode(struct udevice *dev, int mode) | |
578 | { | |
579 | return s2mps11_ldo_mode(dev, PMIC_OP_SET, &mode); | |
580 | } | |
581 | ||
582 | static int s2mps11_ldo_probe(struct udevice *dev) | |
583 | { | |
caa4daa2 | 584 | struct dm_regulator_uclass_plat *uc_pdata; |
60599ea6 | 585 | |
caa4daa2 | 586 | uc_pdata = dev_get_uclass_plat(dev); |
60599ea6 JC |
587 | uc_pdata->type = REGULATOR_TYPE_LDO; |
588 | uc_pdata->mode = s2mps11_ldo_modes; | |
589 | uc_pdata->mode_count = ARRAY_SIZE(s2mps11_ldo_modes); | |
590 | ||
591 | return 0; | |
592 | } | |
593 | ||
594 | static const struct dm_regulator_ops s2mps11_ldo_ops = { | |
595 | .get_value = ldo_get_value, | |
596 | .set_value = ldo_set_value, | |
597 | .get_enable = ldo_get_enable, | |
598 | .set_enable = ldo_set_enable, | |
599 | .get_mode = ldo_get_mode, | |
600 | .set_mode = ldo_set_mode, | |
601 | }; | |
602 | ||
603 | U_BOOT_DRIVER(s2mps11_ldo) = { | |
604 | .name = S2MPS11_LDO_DRIVER, | |
605 | .id = UCLASS_REGULATOR, | |
606 | .ops = &s2mps11_ldo_ops, | |
607 | .probe = s2mps11_ldo_probe, | |
608 | }; |