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/i2c.h>
15 #include <linux/platform_device.h>
16 #include <linux/regulator/driver.h>
17 #include <linux/regulator/machine.h>
18 #include <linux/mfd/88pm860x.h>
20 struct pm8607_regulator_info
{
21 struct regulator_desc desc
;
22 struct pm860x_chip
*chip
;
23 struct regulator_dev
*regulator
;
24 struct i2c_client
*i2c
;
39 static inline int check_range(struct pm8607_regulator_info
*info
,
40 int min_uV
, int max_uV
)
42 if (max_uV
< info
->min_uV
|| min_uV
> info
->max_uV
|| min_uV
> max_uV
)
48 static int pm8607_list_voltage(struct regulator_dev
*rdev
, unsigned index
)
50 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
53 switch (info
->desc
.id
) {
55 ret
= (index
< 0x1d) ? (index
* 25000 + 800000) :
56 ((index
< 0x20) ? 1500000 :
57 ((index
< 0x40) ? ((index
- 0x20) * 25000) :
61 ret
= (index
< 0x3d) ? (index
* 25000) :
62 ((index
< 0x40) ? 1500000 : -EINVAL
);
65 if (info
->slope_double
)
69 ret
= (index
== 0) ? 1800000 :
70 ((index
== 1) ? 1200000 :
71 ((index
== 2) ? 2800000 : -EINVAL
));
74 ret
= (index
== 0) ? 2900000 :
75 ((index
== 1) ? 3000000 :
76 ((index
== 2) ? 3100000 : 3300000));
80 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
81 ((index
< 8) ? (index
* 50000 + 2550000) :
85 ret
= (index
< 2) ? (index
* 100000 + 1800000) :
86 ((index
< 7) ? (index
* 100000 + 2500000) :
87 ((index
== 7) ? 3300000 : 1200000));
92 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
93 ((index
< 7) ? (index
* 50000 + 2550000) :
97 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
98 ((index
< 6) ? (index
* 50000 + 2550000) :
99 ((index
== 6) ? 2900000 : 3300000));
102 ret
= (index
< 2) ? (index
* 50000 + 1800000) :
103 ((index
< 7) ? (index
* 50000 + 2500000) :
106 case PM8607_ID_LDO10
:
107 ret
= (index
< 3) ? (index
* 50000 + 1800000) :
108 ((index
< 7) ? (index
* 50000 + 2550000) :
109 ((index
== 7) ? 3300000 : 1200000));
111 case PM8607_ID_LDO14
:
112 ret
= (index
< 2) ? (index
* 50000 + 1800000) :
113 ((index
< 7) ? (index
* 50000 + 2600000) :
120 static int choose_voltage(struct regulator_dev
*rdev
, int min_uV
, int max_uV
)
122 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
126 switch (info
->desc
.id
) {
127 case PM8607_ID_BUCK1
:
128 if (min_uV
>= 800000) /* 800mV ~ 1500mV / 25mV */
129 val
= (min_uV
- 775001) / 25000;
130 else { /* 25mV ~ 775mV / 25mV */
131 val
= (min_uV
+ 249999) / 25000;
135 case PM8607_ID_BUCK3
:
136 if (info
->slope_double
)
137 min_uV
= min_uV
>> 1;
138 val
= (min_uV
+ 249999) / 25000; /* 0mV ~ 1500mV / 25mV */
142 if (min_uV
> 1800000)
144 else if (min_uV
> 1200000)
150 if (min_uV
> 3100000)
152 else /* 2900mV ~ 3100mV / 100mV */
153 val
= (min_uV
- 2800001) / 100000;
157 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
158 if (min_uV
<= 1800000)
159 val
= 0; /* 1800mv */
160 else if (min_uV
<= 1900000)
161 val
= (min_uV
- 1750001) / 50000;
163 val
= 3; /* 2700mV */
164 } else { /* 2700mV ~ 2900mV / 50mV */
165 if (min_uV
<= 2900000) {
166 val
= (min_uV
- 2650001) / 50000;
172 case PM8607_ID_LDO10
:
173 if (min_uV
> 2850000)
175 else if (min_uV
<= 1200000)
177 else if (min_uV
< 2700000) /* 1800mV ~ 1900mV / 50mV */
178 val
= (min_uV
- 1750001) / 50000;
179 else { /* 2700mV ~ 2850mV / 50mV */
180 val
= (min_uV
- 2650001) / 50000;
184 case PM8607_ID_LDO12
:
185 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 100mV */
186 if (min_uV
<= 1200000)
187 val
= 8; /* 1200mV */
188 else if (min_uV
<= 1800000)
189 val
= 0; /* 1800mV */
190 else if (min_uV
<= 1900000)
191 val
= (min_uV
- 1700001) / 100000;
193 val
= 2; /* 2700mV */
194 } else { /* 2700mV ~ 3100mV / 100mV */
195 if (min_uV
<= 3100000) {
196 val
= (min_uV
- 2600001) / 100000;
198 } else if (min_uV
<= 3300000)
207 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
208 if (min_uV
<= 1800000)
210 else if (min_uV
<= 1900000)
211 val
= (min_uV
- 1750001) / 50000;
213 val
= 3; /* 2700mV */
214 } else { /* 2700mV ~ 2850mV / 50mV */
215 if (min_uV
<= 2850000) {
216 val
= (min_uV
- 2650001) / 50000;
218 } else if (min_uV
<= 3300000)
225 if (min_uV
< 2700000) { /* 1800mV ~ 1900mV / 50mV */
226 if (min_uV
<= 1800000)
228 else if (min_uV
<= 1900000)
229 val
= (min_uV
- 1750001) / 50000;
231 val
= 3; /* 2700mV */
232 } else { /* 2700mV ~ 2800mV / 50mV */
233 if (min_uV
<= 2850000) {
234 val
= (min_uV
- 2650001) / 50000;
236 } else if (min_uV
<= 2900000)
238 else if (min_uV
<= 3300000)
245 if (min_uV
< 2600000) { /* 1800mV ~ 1850mV / 50mV */
246 if (min_uV
<= 1800000)
248 else if (min_uV
<= 1850000)
249 val
= (min_uV
- 1750001) / 50000;
251 val
= 2; /* 2600mV */
252 } else { /* 2600mV ~ 2800mV / 50mV */
253 if (min_uV
<= 2800000) {
254 val
= (min_uV
- 2550001) / 50000;
256 } else if (min_uV
<= 3300000)
262 case PM8607_ID_LDO14
:
263 if (min_uV
< 2700000) { /* 1800mV ~ 1850mV / 50mV */
264 if (min_uV
<= 1800000)
266 else if (min_uV
<= 1850000)
267 val
= (min_uV
- 1750001) / 50000;
269 val
= 2; /* 2700mV */
270 } else { /* 2700mV ~ 2900mV / 50mV */
271 if (min_uV
<= 2900000) {
272 val
= (min_uV
- 2650001) / 50000;
274 } else if (min_uV
<= 3300000)
282 ret
= pm8607_list_voltage(rdev
, val
);
284 pr_err("exceed voltage range (%d %d) uV",
289 pr_err("invalid voltage range (%d %d) uV", min_uV
, max_uV
);
293 static int pm8607_set_voltage(struct regulator_dev
*rdev
,
294 int min_uV
, int max_uV
)
296 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
300 if (check_range(info
, min_uV
, max_uV
)) {
301 pr_err("invalid voltage range (%d, %d) uV\n", min_uV
, max_uV
);
305 ret
= choose_voltage(rdev
, min_uV
, max_uV
);
308 val
= (uint8_t)(ret
<< info
->vol_shift
);
309 mask
= ((1 << info
->vol_nbits
) - 1) << info
->vol_shift
;
311 ret
= pm860x_set_bits(info
->i2c
, info
->vol_reg
, mask
, val
);
314 switch (info
->desc
.id
) {
315 case PM8607_ID_BUCK1
:
316 case PM8607_ID_BUCK3
:
317 ret
= pm860x_set_bits(info
->i2c
, info
->update_reg
,
318 1 << info
->update_bit
,
319 1 << info
->update_bit
);
325 static int pm8607_get_voltage(struct regulator_dev
*rdev
)
327 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
331 ret
= pm860x_reg_read(info
->i2c
, info
->vol_reg
);
335 mask
= ((1 << info
->vol_nbits
) - 1) << info
->vol_shift
;
336 val
= ((unsigned char)ret
& mask
) >> info
->vol_shift
;
338 return pm8607_list_voltage(rdev
, val
);
341 static int pm8607_enable(struct regulator_dev
*rdev
)
343 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
345 return pm860x_set_bits(info
->i2c
, info
->enable_reg
,
346 1 << info
->enable_bit
,
347 1 << info
->enable_bit
);
350 static int pm8607_disable(struct regulator_dev
*rdev
)
352 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
354 return pm860x_set_bits(info
->i2c
, info
->enable_reg
,
355 1 << info
->enable_bit
, 0);
358 static int pm8607_is_enabled(struct regulator_dev
*rdev
)
360 struct pm8607_regulator_info
*info
= rdev_get_drvdata(rdev
);
363 ret
= pm860x_reg_read(info
->i2c
, info
->enable_reg
);
367 return !!((unsigned char)ret
& (1 << info
->enable_bit
));
370 static struct regulator_ops pm8607_regulator_ops
= {
371 .set_voltage
= pm8607_set_voltage
,
372 .get_voltage
= pm8607_get_voltage
,
373 .enable
= pm8607_enable
,
374 .disable
= pm8607_disable
,
375 .is_enabled
= pm8607_is_enabled
,
378 #define PM8607_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
381 .name = "BUCK" #_id, \
382 .ops = &pm8607_regulator_ops, \
383 .type = REGULATOR_VOLTAGE, \
384 .id = PM8607_ID_BUCK##_id, \
385 .owner = THIS_MODULE, \
387 .min_uV = (min) * 1000, \
388 .max_uV = (max) * 1000, \
389 .step_uV = (step) * 1000, \
390 .vol_reg = PM8607_##vreg, \
392 .vol_nbits = (nbits), \
393 .update_reg = PM8607_##ureg, \
394 .update_bit = (ubit), \
395 .enable_reg = PM8607_##ereg, \
396 .enable_bit = (ebit), \
397 .slope_double = (0), \
400 #define PM8607_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \
403 .name = "LDO" #_id, \
404 .ops = &pm8607_regulator_ops, \
405 .type = REGULATOR_VOLTAGE, \
406 .id = PM8607_ID_LDO##_id, \
407 .owner = THIS_MODULE, \
409 .min_uV = (min) * 1000, \
410 .max_uV = (max) * 1000, \
411 .step_uV = (step) * 1000, \
412 .vol_reg = PM8607_##vreg, \
413 .vol_shift = (shift), \
414 .vol_nbits = (nbits), \
415 .enable_reg = PM8607_##ereg, \
416 .enable_bit = (ebit), \
417 .slope_double = (0), \
420 static struct pm8607_regulator_info pm8607_regulator_info
[] = {
421 PM8607_DVC(1, 0, 1500, 25, BUCK1
, 6, GO
, 0, SUPPLIES_EN11
, 0),
422 PM8607_DVC(3, 0, 1500, 25, BUCK3
, 6, GO
, 2, SUPPLIES_EN11
, 2),
424 PM8607_LDO(1 , 1200, 2800, 0, LDO1
, 0, 2, SUPPLIES_EN11
, 3),
425 PM8607_LDO(2 , 1800, 3300, 0, LDO2
, 0, 3, SUPPLIES_EN11
, 4),
426 PM8607_LDO(3 , 1800, 3300, 0, LDO3
, 0, 3, SUPPLIES_EN11
, 5),
427 PM8607_LDO(4 , 1800, 3300, 0, LDO4
, 0, 3, SUPPLIES_EN11
, 6),
428 PM8607_LDO(5 , 2900, 3300, 0, LDO5
, 0, 2, SUPPLIES_EN11
, 7),
429 PM8607_LDO(6 , 1800, 3300, 0, LDO6
, 0, 3, SUPPLIES_EN12
, 0),
430 PM8607_LDO(7 , 1800, 2900, 0, LDO7
, 0, 3, SUPPLIES_EN12
, 1),
431 PM8607_LDO(8 , 1800, 2900, 0, LDO8
, 0, 3, SUPPLIES_EN12
, 2),
432 PM8607_LDO(9 , 1800, 3300, 0, LDO9
, 0, 3, SUPPLIES_EN12
, 3),
433 PM8607_LDO(10, 1200, 3300, 0, LDO10
, 0, 4, SUPPLIES_EN11
, 4),
434 PM8607_LDO(12, 1200, 3300, 0, LDO12
, 0, 4, SUPPLIES_EN11
, 5),
435 PM8607_LDO(14, 1800, 3300, 0, LDO14
, 0, 3, SUPPLIES_EN11
, 6),
438 static inline struct pm8607_regulator_info
*find_regulator_info(int id
)
440 struct pm8607_regulator_info
*info
;
443 for (i
= 0; i
< ARRAY_SIZE(pm8607_regulator_info
); i
++) {
444 info
= &pm8607_regulator_info
[i
];
445 if (info
->desc
.id
== id
)
451 static int __devinit
pm8607_regulator_probe(struct platform_device
*pdev
)
453 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
454 struct pm860x_platform_data
*pdata
= chip
->dev
->platform_data
;
455 struct pm8607_regulator_info
*info
= NULL
;
457 info
= find_regulator_info(pdev
->id
);
459 dev_err(&pdev
->dev
, "invalid regulator ID specified\n");
463 info
->i2c
= (chip
->id
== CHIP_PM8607
) ? chip
->client
: chip
->companion
;
466 info
->regulator
= regulator_register(&info
->desc
, &pdev
->dev
,
467 pdata
->regulator
[pdev
->id
], info
);
468 if (IS_ERR(info
->regulator
)) {
469 dev_err(&pdev
->dev
, "failed to register regulator %s\n",
471 return PTR_ERR(info
->regulator
);
474 /* check DVC ramp slope double */
475 if (info
->desc
.id
== PM8607_ID_BUCK3
)
476 if (info
->chip
->buck3_double
)
477 info
->slope_double
= 1;
479 platform_set_drvdata(pdev
, info
);
483 static int __devexit
pm8607_regulator_remove(struct platform_device
*pdev
)
485 struct pm8607_regulator_info
*info
= platform_get_drvdata(pdev
);
487 regulator_unregister(info
->regulator
);
491 #define PM8607_REGULATOR_DRIVER(_name) \
494 .name = "88pm8607-" #_name, \
495 .owner = THIS_MODULE, \
497 .probe = pm8607_regulator_probe, \
498 .remove = __devexit_p(pm8607_regulator_remove), \
501 static struct platform_driver pm8607_regulator_driver
[] = {
502 PM8607_REGULATOR_DRIVER(buck1
),
503 PM8607_REGULATOR_DRIVER(buck2
),
504 PM8607_REGULATOR_DRIVER(buck3
),
505 PM8607_REGULATOR_DRIVER(ldo1
),
506 PM8607_REGULATOR_DRIVER(ldo2
),
507 PM8607_REGULATOR_DRIVER(ldo3
),
508 PM8607_REGULATOR_DRIVER(ldo4
),
509 PM8607_REGULATOR_DRIVER(ldo5
),
510 PM8607_REGULATOR_DRIVER(ldo6
),
511 PM8607_REGULATOR_DRIVER(ldo7
),
512 PM8607_REGULATOR_DRIVER(ldo8
),
513 PM8607_REGULATOR_DRIVER(ldo9
),
514 PM8607_REGULATOR_DRIVER(ldo10
),
515 PM8607_REGULATOR_DRIVER(ldo12
),
516 PM8607_REGULATOR_DRIVER(ldo14
),
519 static int __init
pm8607_regulator_init(void)
523 count
= ARRAY_SIZE(pm8607_regulator_driver
);
524 for (i
= 0; i
< count
; i
++) {
525 ret
= platform_driver_register(&pm8607_regulator_driver
[i
]);
527 pr_err("Failed to register regulator driver: %d\n",
532 subsys_initcall(pm8607_regulator_init
);
534 static void __exit
pm8607_regulator_exit(void)
538 count
= ARRAY_SIZE(pm8607_regulator_driver
);
539 for (i
= 0; i
< count
; i
++)
540 platform_driver_unregister(&pm8607_regulator_driver
[i
]);
542 module_exit(pm8607_regulator_exit
);
544 MODULE_LICENSE("GPL");
545 MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
546 MODULE_DESCRIPTION("Regulator Driver for Marvell 88PM8607 PMIC");
547 MODULE_ALIAS("platform:88pm8607-regulator");