2 * Regulators driver for Marvell 88PM8607
4 * Copyright (C) 2009 Marvell International Ltd.
5 * Haojian Zhuang <haojian.zhuang@marvell.com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/err.h>
14 #include <linux/platform_device.h>
15 #include <linux/regulator/driver.h>
16 #include <linux/regulator/machine.h>
17 #include <linux/mfd/88pm8607.h>
19 struct pm8607_regulator_info
{
20 struct regulator_desc desc
;
21 struct pm8607_chip
*chip
;
22 struct regulator_dev
*regulator
;
37 static inline int check_range(struct pm8607_regulator_info
*info
,
38 int min_uV
, int max_uV
)
40 if (max_uV
< info
->min_uV
|| min_uV
> info
->max_uV
|| min_uV
> max_uV
)
46 static int pm8607_list_voltage(struct regulator_dev
*rdev
, unsigned index
)
48 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
49 uint8_t chip_id
= info
->chip
->chip_id
;
52 switch (info
->desc
.id
) {
54 ret
= (index
< 0x1d) ? (index
* 25000 + 800000) :
55 ((index
< 0x20) ? 1500000 :
56 ((index
< 0x40) ? ((index
- 0x20) * 25000) :
60 ret
= (index
< 0x3d) ? (index
* 25000) :
61 ((index
< 0x40) ? 1500000 : -EINVAL
);
64 if (info
->slope_double
)
68 ret
= (index
== 0) ? 1800000 :
69 ((index
== 1) ? 1200000 :
70 ((index
== 2) ? 2800000 : -EINVAL
));
73 ret
= (index
== 0) ? 2900000 :
74 ((index
== 1) ? 3000000 :
75 ((index
== 2) ? 3100000 : 3300000));
79 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
80 ((index
< 8) ? (index
* 50000 + 2550000) :
84 ret
= (index
< 2) ? (index
* 100000 + 1800000) :
85 ((index
< 7) ? (index
* 100000 + 2500000) :
86 ((index
== 7) ? 3300000 : 1200000));
94 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
95 ((index
< 8) ? (index
* 50000 + 2550000) :
99 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
100 ((index
< 7) ? (index
* 50000 + 2550000) :
109 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
110 ((index
< 8) ? (index
* 50000 + 2550000) :
114 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
115 ((index
< 6) ? (index
* 50000 + 2550000) :
116 ((index
== 6) ? 2900000 : 3300000));
124 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
125 ((index
< 8) ? (index
* 50000 + 2450000) :
129 ret
= (index
< 2) ? (index
* 50000 + 1800000) :
130 ((index
< 7) ? (index
* 50000 + 2500000) :
135 case PM8607_ID_LDO10
:
139 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
140 ((index
< 8) ? (index
* 50000 + 2550000) :
144 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
145 ((index
< 7) ? (index
* 50000 + 2550000) :
146 ((index
== 7) ? 3300000 : 1200000));
150 case PM8607_ID_LDO14
:
154 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
155 ((index
< 8) ? (index
* 50000 + 2550000) :
159 ret
= (index
< 2) ? (index
* 50000 + 1800000) :
160 ((index
< 7) ? (index
* 50000 + 2600000) :
169 static int choose_voltage(struct regulator_dev
*rdev
, int min_uV
, int max_uV
)
171 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
172 uint8_t chip_id
= info
->chip
->chip_id
;
176 switch (info
->desc
.id
) {
177 case PM8607_ID_BUCK1
:
178 if (min_uV
>= 800000) /* 800mV ~ 1500mV / 25mV */
179 val
= (min_uV
- 775001) / 25000;
180 else { /* 25mV ~ 775mV / 25mV */
181 val
= (min_uV
+ 249999) / 25000;
185 case PM8607_ID_BUCK3
:
186 if (info
->slope_double
)
187 min_uV
= min_uV
>> 1;
188 val
= (min_uV
+ 249999) / 25000; /* 0mV ~ 1500mV / 25mV */
192 if (min_uV
> 1800000)
194 else if (min_uV
> 1200000)
200 if (min_uV
> 3100000)
202 else /* 2900mV ~ 3100mV / 100mV */
203 val
= (min_uV
- 2800001) / 100000;
207 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
208 if (min_uV
<= 1800000)
209 val
= 0; /* 1800mv */
210 else if (min_uV
<= 1900000)
211 val
= (min_uV
- 1750001) / 50000;
213 val
= 3; /* 2700mV */
214 } else { /* 2700mV ~ 2900mV / 50mV */
215 if (min_uV
<= 2900000) {
216 val
= (min_uV
- 2650001) / 50000;
222 case PM8607_ID_LDO10
:
223 if (min_uV
> 2850000)
225 else if (min_uV
<= 1200000)
227 else if (min_uV
< 2700000) /* 1800mV ~ 1900mV / 50mV */
228 val
= (min_uV
- 1750001) / 50000;
229 else { /* 2700mV ~ 2850mV / 50mV */
230 val
= (min_uV
- 2650001) / 50000;
234 case PM8607_ID_LDO12
:
235 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 100mV */
236 if (min_uV
<= 1200000)
237 val
= 8; /* 1200mV */
238 else if (min_uV
<= 1800000)
239 val
= 0; /* 1800mV */
240 else if (min_uV
<= 1900000)
241 val
= (min_uV
- 1700001) / 100000;
243 val
= 2; /* 2700mV */
244 } else { /* 2700mV ~ 3100mV / 100mV */
245 if (min_uV
<= 3100000) {
246 val
= (min_uV
- 2600001) / 100000;
248 } else if (min_uV
<= 3300000)
260 if (min_uV
< 2700000) /* 1800mV ~ 1900mV / 50mV */
261 if (min_uV
<= 1800000)
263 else if (min_uV
<= 1900000)
264 val
= (min_uV
- 1750001) / 50000;
266 val
= 3; /* 2700mV */
267 else { /* 2700mV ~ 2900mV / 50mV */
268 if (min_uV
<= 2900000) {
269 val
= (min_uV
- 2650001) / 50000;
276 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
277 if (min_uV
<= 1800000)
279 else if (min_uV
<= 1900000)
280 val
= (min_uV
- 1750001) / 50000;
282 val
= 3; /* 2700mV */
283 } else { /* 2700mV ~ 2850mV / 50mV */
284 if (min_uV
<= 2850000) {
285 val
= (min_uV
- 2650001) / 50000;
287 } else if (min_uV
<= 3300000)
299 if (min_uV
< 2700000) /* 1800mV ~ 1900mV / 50mV */
300 if (min_uV
<= 1800000)
302 else if (min_uV
<= 1900000)
303 val
= (min_uV
- 1750001) / 50000;
305 val
= 3; /* 2700mV */
306 else { /* 2700mV ~ 2900mV / 50mV */
307 if (min_uV
<= 2900000) {
308 val
= (min_uV
- 2650001) / 50000;
315 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
316 if (min_uV
<= 1800000)
318 else if (min_uV
<= 1900000)
319 val
= (min_uV
- 1750001) / 50000;
321 val
= 3; /* 2700mV */
322 } else { /* 2700mV ~ 2800mV / 50mV */
323 if (min_uV
<= 2850000) {
324 val
= (min_uV
- 2650001) / 50000;
326 } else if (min_uV
<= 2900000)
328 else if (min_uV
<= 3300000)
340 if (min_uV
< 2600000) { /* 1800mV ~ 1900mV / 50mV */
341 if (min_uV
<= 1800000)
343 else if (min_uV
<= 1900000)
344 val
= (min_uV
- 1750001) / 50000;
346 val
= 3; /* 2600mV */
347 } else { /* 2600mV ~ 2800mV / 50mV */
348 if (min_uV
<= 2800000) {
349 val
= (min_uV
- 2550001) / 50000;
356 if (min_uV
< 2600000) { /* 1800mV ~ 1850mV / 50mV */
357 if (min_uV
<= 1800000)
359 else if (min_uV
<= 1850000)
360 val
= (min_uV
- 1750001) / 50000;
362 val
= 2; /* 2600mV */
363 } else { /* 2600mV ~ 2800mV / 50mV */
364 if (min_uV
<= 2800000) {
365 val
= (min_uV
- 2550001) / 50000;
367 } else if (min_uV
<= 3300000)
375 case PM8607_ID_LDO14
:
379 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
380 if (min_uV
<= 1800000)
382 else if (min_uV
<= 1900000)
383 val
= (min_uV
- 1750001) / 50000;
385 val
= 3; /* 2700mV */
386 } else { /* 2700mV ~ 2900mV / 50mV */
387 if (min_uV
<= 2900000) {
388 val
= (min_uV
- 2650001) / 50000;
395 if (min_uV
< 2700000) { /* 1800mV ~ 1850mV / 50mV */
396 if (min_uV
<= 1800000)
398 else if (min_uV
<= 1850000)
399 val
= (min_uV
- 1750001) / 50000;
401 val
= 2; /* 2700mV */
402 } else { /* 2700mV ~ 2900mV / 50mV */
403 if (min_uV
<= 2900000) {
404 val
= (min_uV
- 2650001) / 50000;
406 } else if (min_uV
<= 3300000)
416 ret
= pm8607_list_voltage(rdev
, val
);
418 pr_err("exceed voltage range (%d %d) uV",
423 pr_err("invalid voltage range (%d %d) uV", min_uV
, max_uV
);
427 static int pm8607_set_voltage(struct regulator_dev
*rdev
,
428 int min_uV
, int max_uV
)
430 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
431 struct pm8607_chip
*chip
= info
->chip
;
435 if (check_range(info
, min_uV
, max_uV
)) {
436 pr_err("invalid voltage range (%d, %d) uV\n", min_uV
, max_uV
);
440 ret
= choose_voltage(rdev
, min_uV
, max_uV
);
443 val
= (uint8_t)(ret
<< info
->vol_shift
);
444 mask
= ((1 << info
->vol_nbits
) - 1) << info
->vol_shift
;
446 ret
= pm8607_set_bits(chip
, info
->vol_reg
, mask
, val
);
449 switch (info
->desc
.id
) {
450 case PM8607_ID_BUCK1
:
451 case PM8607_ID_BUCK3
:
452 ret
= pm8607_set_bits(chip
, info
->update_reg
,
453 1 << info
->update_bit
,
454 1 << info
->update_bit
);
460 static int pm8607_get_voltage(struct regulator_dev
*rdev
)
462 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
463 struct pm8607_chip
*chip
= info
->chip
;
467 ret
= pm8607_reg_read(chip
, info
->vol_reg
);
471 mask
= ((1 << info
->vol_nbits
) - 1) << info
->vol_shift
;
472 val
= ((unsigned char)ret
& mask
) >> info
->vol_shift
;
474 return pm8607_list_voltage(rdev
, val
);
477 static int pm8607_enable(struct regulator_dev
*rdev
)
479 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
480 struct pm8607_chip
*chip
= info
->chip
;
482 return pm8607_set_bits(chip
, info
->enable_reg
,
483 1 << info
->enable_bit
,
484 1 << info
->enable_bit
);
487 static int pm8607_disable(struct regulator_dev
*rdev
)
489 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
490 struct pm8607_chip
*chip
= info
->chip
;
492 return pm8607_set_bits(chip
, info
->enable_reg
,
493 1 << info
->enable_bit
, 0);
496 static int pm8607_is_enabled(struct regulator_dev
*rdev
)
498 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
499 struct pm8607_chip
*chip
= info
->chip
;
502 ret
= pm8607_reg_read(chip
, info
->enable_reg
);
506 return !!((unsigned char)ret
& (1 << info
->enable_bit
));
509 static struct regulator_ops pm8607_regulator_ops
= {
510 .set_voltage
= pm8607_set_voltage
,
511 .get_voltage
= pm8607_get_voltage
,
512 .enable
= pm8607_enable
,
513 .disable
= pm8607_disable
,
514 .is_enabled
= pm8607_is_enabled
,
517 #define PM8607_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
520 .name = "BUCK" #_id, \
521 .ops = &pm8607_regulator_ops, \
522 .type = REGULATOR_VOLTAGE, \
523 .id = PM8607_ID_BUCK##_id, \
524 .owner = THIS_MODULE, \
526 .min_uV = (min) * 1000, \
527 .max_uV = (max) * 1000, \
528 .step_uV = (step) * 1000, \
529 .vol_reg = PM8607_##vreg, \
531 .vol_nbits = (nbits), \
532 .update_reg = PM8607_##ureg, \
533 .update_bit = (ubit), \
534 .enable_reg = PM8607_##ereg, \
535 .enable_bit = (ebit), \
536 .slope_double = (0), \
539 #define PM8607_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \
542 .name = "LDO" #_id, \
543 .ops = &pm8607_regulator_ops, \
544 .type = REGULATOR_VOLTAGE, \
545 .id = PM8607_ID_LDO##_id, \
546 .owner = THIS_MODULE, \
548 .min_uV = (min) * 1000, \
549 .max_uV = (max) * 1000, \
550 .step_uV = (step) * 1000, \
551 .vol_reg = PM8607_##vreg, \
552 .vol_shift = (shift), \
553 .vol_nbits = (nbits), \
554 .enable_reg = PM8607_##ereg, \
555 .enable_bit = (ebit), \
556 .slope_double = (0), \
559 static struct pm8607_regulator_info pm8607_regulator_info
[] = {
560 PM8607_DVC(1, 0, 1500, 25, BUCK1
, 6, GO
, 0, SUPPLIES_EN11
, 0),
561 PM8607_DVC(3, 0, 1500, 25, BUCK3
, 6, GO
, 2, SUPPLIES_EN11
, 2),
563 PM8607_LDO(1 , 1200, 2800, 0, LDO1
, 0, 2, SUPPLIES_EN11
, 3),
564 PM8607_LDO(2 , 1800, 3300, 0, LDO2
, 0, 3, SUPPLIES_EN11
, 4),
565 PM8607_LDO(3 , 1800, 3300, 0, LDO3
, 0, 3, SUPPLIES_EN11
, 5),
566 PM8607_LDO(4 , 1800, 3300, 0, LDO4
, 0, 3, SUPPLIES_EN11
, 6),
567 PM8607_LDO(5 , 2900, 3300, 0, LDO5
, 0, 2, SUPPLIES_EN11
, 7),
568 PM8607_LDO(6 , 1800, 3300, 0, LDO6
, 0, 3, SUPPLIES_EN12
, 0),
569 PM8607_LDO(7 , 1800, 2900, 0, LDO7
, 0, 3, SUPPLIES_EN12
, 1),
570 PM8607_LDO(8 , 1800, 2900, 0, LDO8
, 0, 3, SUPPLIES_EN12
, 2),
571 PM8607_LDO(9 , 1800, 3300, 0, LDO9
, 0, 3, SUPPLIES_EN12
, 3),
572 PM8607_LDO(10, 1200, 3300, 0, LDO10
, 0, 4, SUPPLIES_EN11
, 4),
573 PM8607_LDO(12, 1200, 3300, 0, LDO12
, 0, 4, SUPPLIES_EN11
, 5),
574 PM8607_LDO(14, 1800, 3300, 0, LDO14
, 0, 3, SUPPLIES_EN11
, 6),
577 static inline struct pm8607_regulator_info
*find_regulator_info(int id
)
579 struct pm8607_regulator_info
*info
;
582 for (i
= 0; i
< ARRAY_SIZE(pm8607_regulator_info
); i
++) {
583 info
= &pm8607_regulator_info
[i
];
584 if (info
->desc
.id
== id
)
590 static int __devinit
pm8607_regulator_probe(struct platform_device
*pdev
)
592 struct pm8607_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
593 struct pm8607_platform_data
*pdata
= chip
->dev
->platform_data
;
594 struct pm8607_regulator_info
*info
= NULL
;
596 info
= find_regulator_info(pdev
->id
);
598 dev_err(&pdev
->dev
, "invalid regulator ID specified\n");
604 info
->regulator
= regulator_register(&info
->desc
, &pdev
->dev
,
605 pdata
->regulator
[pdev
->id
], info
);
606 if (IS_ERR(info
->regulator
)) {
607 dev_err(&pdev
->dev
, "failed to register regulator %s\n",
609 return PTR_ERR(info
->regulator
);
612 /* check DVC ramp slope double */
613 if (info
->desc
.id
== PM8607_ID_BUCK3
)
614 if (info
->chip
->buck3_double
)
615 info
->slope_double
= 1;
617 platform_set_drvdata(pdev
, info
);
621 static int __devexit
pm8607_regulator_remove(struct platform_device
*pdev
)
623 struct pm8607_regulator_info
*info
= platform_get_drvdata(pdev
);
625 regulator_unregister(info
->regulator
);
629 #define PM8607_REGULATOR_DRIVER(_name) \
632 .name = "88pm8607-" #_name, \
633 .owner = THIS_MODULE, \
635 .probe = pm8607_regulator_probe, \
636 .remove = __devexit_p(pm8607_regulator_remove), \
639 static struct platform_driver pm8607_regulator_driver
[] = {
640 PM8607_REGULATOR_DRIVER(buck1
),
641 PM8607_REGULATOR_DRIVER(buck2
),
642 PM8607_REGULATOR_DRIVER(buck3
),
643 PM8607_REGULATOR_DRIVER(ldo1
),
644 PM8607_REGULATOR_DRIVER(ldo2
),
645 PM8607_REGULATOR_DRIVER(ldo3
),
646 PM8607_REGULATOR_DRIVER(ldo4
),
647 PM8607_REGULATOR_DRIVER(ldo5
),
648 PM8607_REGULATOR_DRIVER(ldo6
),
649 PM8607_REGULATOR_DRIVER(ldo7
),
650 PM8607_REGULATOR_DRIVER(ldo8
),
651 PM8607_REGULATOR_DRIVER(ldo9
),
652 PM8607_REGULATOR_DRIVER(ldo10
),
653 PM8607_REGULATOR_DRIVER(ldo12
),
654 PM8607_REGULATOR_DRIVER(ldo14
),
657 static int __init
pm8607_regulator_init(void)
661 count
= ARRAY_SIZE(pm8607_regulator_driver
);
662 for (i
= 0; i
< count
; i
++) {
663 ret
= platform_driver_register(&pm8607_regulator_driver
[i
]);
665 pr_err("Failed to register regulator driver: %d\n",
670 subsys_initcall(pm8607_regulator_init
);
672 static void __exit
pm8607_regulator_exit(void)
676 count
= ARRAY_SIZE(pm8607_regulator_driver
);
677 for (i
= 0; i
< count
; i
++)
678 platform_driver_unregister(&pm8607_regulator_driver
[i
]);
680 module_exit(pm8607_regulator_exit
);
682 MODULE_LICENSE("GPL");
683 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
684 MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
685 MODULE_ALIAS("platform:88pm8607-regulator");