Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6.git] / drivers / regulator / 88pm8607.c
1 /*
2  * Regulators driver for Marvell 88PM8607
3  *
4  * Copyright (C) 2009 Marvell International Ltd.
5  *      Haojian Zhuang <haojian.zhuang@marvell.com>
6  *
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.
10  */
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>
19
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;
25
26         int     min_uV;
27         int     max_uV;
28         int     step_uV;
29         int     vol_reg;
30         int     vol_shift;
31         int     vol_nbits;
32         int     update_reg;
33         int     update_bit;
34         int     enable_reg;
35         int     enable_bit;
36         int     slope_double;
37 };
38
39 static inline int check_range(struct pm8607_regulator_info *info,
40                                 int min_uV, int max_uV)
41 {
42         if (max_uV < info->min_uV || min_uV > info->max_uV || min_uV > max_uV)
43                 return -EINVAL;
44
45         return 0;
46 }
47
48 static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index)
49 {
50         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
51         int ret = -EINVAL;
52
53         switch (info->desc.id) {
54         case PM8607_ID_BUCK1:
55                 ret = (index < 0x1d) ? (index * 25000 + 800000) :
56                         ((index < 0x20) ? 1500000 :
57                         ((index < 0x40) ? ((index - 0x20) * 25000) :
58                         -EINVAL));
59                 break;
60         case PM8607_ID_BUCK3:
61                 ret = (index < 0x3d) ? (index * 25000) :
62                         ((index < 0x40) ? 1500000 : -EINVAL);
63                 if (ret < 0)
64                         break;
65                 if (info->slope_double)
66                         ret <<= 1;
67                 break;
68         case PM8607_ID_LDO1:
69                 ret = (index == 0) ? 1800000 :
70                         ((index == 1) ? 1200000 :
71                         ((index == 2) ? 2800000 : -EINVAL));
72                 break;
73         case PM8607_ID_LDO5:
74                 ret = (index == 0) ? 2900000 :
75                         ((index == 1) ? 3000000 :
76                         ((index == 2) ? 3100000 : 3300000));
77                 break;
78         case PM8607_ID_LDO7:
79         case PM8607_ID_LDO8:
80                 ret = (index < 3) ? (index * 50000 + 1800000) :
81                         ((index < 8) ? (index * 50000 + 2550000) :
82                          -EINVAL);
83                 break;
84         case PM8607_ID_LDO12:
85                 ret = (index < 2) ? (index * 100000 + 1800000) :
86                         ((index < 7) ? (index * 100000 + 2500000) :
87                         ((index == 7) ? 3300000 : 1200000));
88                 break;
89         case PM8607_ID_LDO2:
90         case PM8607_ID_LDO3:
91         case PM8607_ID_LDO9:
92                 ret = (index < 3) ? (index * 50000 + 1800000) :
93                         ((index < 7) ? (index * 50000 + 2550000) :
94                         3300000);
95                 break;
96         case PM8607_ID_LDO4:
97                 ret = (index < 3) ? (index * 50000 + 1800000) :
98                         ((index < 6) ? (index * 50000 + 2550000) :
99                         ((index == 6) ? 2900000 : 3300000));
100                 break;
101         case PM8607_ID_LDO6:
102                 ret = (index < 2) ? (index * 50000 + 1800000) :
103                         ((index < 7) ? (index * 50000 + 2500000) :
104                         3300000);
105                 break;
106         case PM8607_ID_LDO10:
107                 ret = (index < 3) ? (index * 50000 + 1800000) :
108                         ((index < 7) ? (index * 50000 + 2550000) :
109                         ((index == 7) ? 3300000 : 1200000));
110                 break;
111         case PM8607_ID_LDO14:
112                 ret = (index < 2) ? (index * 50000 + 1800000) :
113                         ((index < 7) ? (index * 50000 + 2600000) :
114                         3300000);
115                 break;
116         }
117         return ret;
118 }
119
120 static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
121 {
122         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
123         int val = -ENOENT;
124         int ret;
125
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;
132                         val += 32;
133                 }
134                 break;
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 */
139
140                 break;
141         case PM8607_ID_LDO1:
142                 if (min_uV > 1800000)
143                         val = 2;
144                 else if (min_uV > 1200000)
145                         val = 0;
146                 else
147                         val = 1;
148                 break;
149         case PM8607_ID_LDO5:
150                 if (min_uV > 3100000)
151                         val = 3;
152                 else                            /* 2900mV ~ 3100mV / 100mV */
153                         val = (min_uV - 2800001) / 100000;
154                 break;
155         case PM8607_ID_LDO7:
156         case PM8607_ID_LDO8:
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;
162                         else
163                                 val = 3;        /* 2700mV */
164                 } else {                 /* 2700mV ~ 2900mV / 50mV */
165                         if (min_uV <= 2900000) {
166                                 val = (min_uV - 2650001) / 50000;
167                                 val += 3;
168                         } else
169                                 val = -EINVAL;
170                 }
171                 break;
172         case PM8607_ID_LDO10:
173                 if (min_uV > 2850000)
174                         val = 7;
175                 else if (min_uV <= 1200000)
176                         val = 8;
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;
181                         val += 3;
182                 }
183                 break;
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;
192                         else
193                                 val = 2;        /* 2700mV */
194                 } else {                        /* 2700mV ~ 3100mV / 100mV */
195                         if (min_uV <= 3100000) {
196                                 val = (min_uV - 2600001) / 100000;
197                                 val += 2;
198                         } else if (min_uV <= 3300000)
199                                 val = 7;
200                         else
201                                 val = -EINVAL;
202                 }
203                 break;
204         case PM8607_ID_LDO2:
205         case PM8607_ID_LDO3:
206         case PM8607_ID_LDO9:
207                 if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
208                         if (min_uV <= 1800000)
209                                 val = 0;
210                         else if (min_uV <= 1900000)
211                                 val = (min_uV - 1750001) / 50000;
212                         else
213                                 val = 3;        /* 2700mV */
214                 } else {                 /* 2700mV ~ 2850mV / 50mV */
215                         if (min_uV <= 2850000) {
216                                 val = (min_uV - 2650001) / 50000;
217                                 val += 3;
218                         } else if (min_uV <= 3300000)
219                                 val = 7;
220                         else
221                                 val = -EINVAL;
222                 }
223                 break;
224         case PM8607_ID_LDO4:
225                 if (min_uV < 2700000) { /* 1800mV ~ 1900mV / 50mV */
226                         if (min_uV <= 1800000)
227                                 val = 0;
228                         else if (min_uV <= 1900000)
229                                 val = (min_uV - 1750001) / 50000;
230                         else
231                                 val = 3;        /* 2700mV */
232                 } else {                 /* 2700mV ~ 2800mV / 50mV */
233                         if (min_uV <= 2850000) {
234                                 val = (min_uV - 2650001) / 50000;
235                                 val += 3;
236                         } else if (min_uV <= 2900000)
237                                 val = 6;
238                         else if (min_uV <= 3300000)
239                                 val = 7;
240                         else
241                                 val = -EINVAL;
242                 }
243                 break;
244         case PM8607_ID_LDO6:
245                 if (min_uV < 2600000) { /* 1800mV ~ 1850mV / 50mV */
246                         if (min_uV <= 1800000)
247                                 val = 0;
248                         else if (min_uV <= 1850000)
249                                 val = (min_uV - 1750001) / 50000;
250                         else
251                                 val = 2;        /* 2600mV */
252                 } else {                /* 2600mV ~ 2800mV / 50mV */
253                         if (min_uV <= 2800000) {
254                                 val = (min_uV - 2550001) / 50000;
255                                 val += 2;
256                         } else if (min_uV <= 3300000)
257                                 val = 7;
258                         else
259                                 val = -EINVAL;
260                 }
261                 break;
262         case PM8607_ID_LDO14:
263                 if (min_uV < 2700000) { /* 1800mV ~ 1850mV / 50mV */
264                         if (min_uV <= 1800000)
265                                 val = 0;
266                         else if (min_uV <= 1850000)
267                                 val = (min_uV - 1750001) / 50000;
268                         else
269                                 val = 2;        /* 2700mV */
270                 } else {                 /* 2700mV ~ 2900mV / 50mV */
271                         if (min_uV <= 2900000) {
272                                 val = (min_uV - 2650001) / 50000;
273                                 val += 2;
274                         } else if (min_uV <= 3300000)
275                                 val = 7;
276                         else
277                                 val = -EINVAL;
278                 }
279                 break;
280         }
281         if (val >= 0) {
282                 ret = pm8607_list_voltage(rdev, val);
283                 if (ret > max_uV) {
284                         pr_err("exceed voltage range (%d %d) uV",
285                                 min_uV, max_uV);
286                         return -EINVAL;
287                 }
288         } else
289                 pr_err("invalid voltage range (%d %d) uV", min_uV, max_uV);
290         return val;
291 }
292
293 static int pm8607_set_voltage(struct regulator_dev *rdev,
294                               int min_uV, int max_uV)
295 {
296         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
297         uint8_t val, mask;
298         int ret;
299
300         if (check_range(info, min_uV, max_uV)) {
301                 pr_err("invalid voltage range (%d, %d) uV\n", min_uV, max_uV);
302                 return -EINVAL;
303         }
304
305         ret = choose_voltage(rdev, min_uV, max_uV);
306         if (ret < 0)
307                 return -EINVAL;
308         val = (uint8_t)(ret << info->vol_shift);
309         mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
310
311         ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val);
312         if (ret)
313                 return ret;
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);
320                 break;
321         }
322         return ret;
323 }
324
325 static int pm8607_get_voltage(struct regulator_dev *rdev)
326 {
327         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
328         uint8_t val, mask;
329         int ret;
330
331         ret = pm860x_reg_read(info->i2c, info->vol_reg);
332         if (ret < 0)
333                 return ret;
334
335         mask = ((1 << info->vol_nbits) - 1)  << info->vol_shift;
336         val = ((unsigned char)ret & mask) >> info->vol_shift;
337
338         return pm8607_list_voltage(rdev, val);
339 }
340
341 static int pm8607_enable(struct regulator_dev *rdev)
342 {
343         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
344
345         return pm860x_set_bits(info->i2c, info->enable_reg,
346                                1 << info->enable_bit,
347                                1 << info->enable_bit);
348 }
349
350 static int pm8607_disable(struct regulator_dev *rdev)
351 {
352         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
353
354         return pm860x_set_bits(info->i2c, info->enable_reg,
355                                1 << info->enable_bit, 0);
356 }
357
358 static int pm8607_is_enabled(struct regulator_dev *rdev)
359 {
360         struct pm8607_regulator_info *info = rdev_get_drvdata(rdev);
361         int ret;
362
363         ret = pm860x_reg_read(info->i2c, info->enable_reg);
364         if (ret < 0)
365                 return ret;
366
367         return !!((unsigned char)ret & (1 << info->enable_bit));
368 }
369
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,
376 };
377
378 #define PM8607_DVC(_id, min, max, step, vreg, nbits, ureg, ubit, ereg, ebit) \
379 {                                                                       \
380         .desc   = {                                                     \
381                 .name   = "BUCK" #_id,                                  \
382                 .ops    = &pm8607_regulator_ops,                        \
383                 .type   = REGULATOR_VOLTAGE,                            \
384                 .id     = PM8607_ID_BUCK##_id,                          \
385                 .owner  = THIS_MODULE,                                  \
386         },                                                              \
387         .min_uV         = (min) * 1000,                                 \
388         .max_uV         = (max) * 1000,                                 \
389         .step_uV        = (step) * 1000,                                \
390         .vol_reg        = PM8607_##vreg,                                \
391         .vol_shift      = (0),                                          \
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),                                          \
398 }
399
400 #define PM8607_LDO(_id, min, max, step, vreg, shift, nbits, ereg, ebit) \
401 {                                                                       \
402         .desc   = {                                                     \
403                 .name   = "LDO" #_id,                                   \
404                 .ops    = &pm8607_regulator_ops,                        \
405                 .type   = REGULATOR_VOLTAGE,                            \
406                 .id     = PM8607_ID_LDO##_id,                           \
407                 .owner  = THIS_MODULE,                                  \
408         },                                                              \
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),                                          \
418 }
419
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),
423
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),
436 };
437
438 static inline struct pm8607_regulator_info *find_regulator_info(int id)
439 {
440         struct pm8607_regulator_info *info;
441         int i;
442
443         for (i = 0; i < ARRAY_SIZE(pm8607_regulator_info); i++) {
444                 info = &pm8607_regulator_info[i];
445                 if (info->desc.id == id)
446                         return info;
447         }
448         return NULL;
449 }
450
451 static int __devinit pm8607_regulator_probe(struct platform_device *pdev)
452 {
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;
456
457         info = find_regulator_info(pdev->id);
458         if (info == NULL) {
459                 dev_err(&pdev->dev, "invalid regulator ID specified\n");
460                 return -EINVAL;
461         }
462
463         info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
464         info->chip = chip;
465
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",
470                         info->desc.name);
471                 return PTR_ERR(info->regulator);
472         }
473
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;
478
479         platform_set_drvdata(pdev, info);
480         return 0;
481 }
482
483 static int __devexit pm8607_regulator_remove(struct platform_device *pdev)
484 {
485         struct pm8607_regulator_info *info = platform_get_drvdata(pdev);
486
487         regulator_unregister(info->regulator);
488         return 0;
489 }
490
491 #define PM8607_REGULATOR_DRIVER(_name)                          \
492 {                                                               \
493         .driver         = {                                     \
494                 .name   = "88pm8607-" #_name,                   \
495                 .owner  = THIS_MODULE,                          \
496         },                                                      \
497         .probe          = pm8607_regulator_probe,               \
498         .remove         = __devexit_p(pm8607_regulator_remove), \
499 }
500
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),
517 };
518
519 static int __init pm8607_regulator_init(void)
520 {
521         int i, count, ret;
522
523         count = ARRAY_SIZE(pm8607_regulator_driver);
524         for (i = 0; i < count; i++) {
525                 ret = platform_driver_register(&pm8607_regulator_driver[i]);
526                 if (ret != 0)
527                         pr_err("Failed to register regulator driver: %d\n",
528                                 ret);
529         }
530         return 0;
531 }
532 subsys_initcall(pm8607_regulator_init);
533
534 static void __exit pm8607_regulator_exit(void)
535 {
536         int i, count;
537
538         count = ARRAY_SIZE(pm8607_regulator_driver);
539         for (i = 0; i < count; i++)
540                 platform_driver_unregister(&pm8607_regulator_driver[i]);
541 }
542 module_exit(pm8607_regulator_exit);
543
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");