]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/regulator/twl4030-regulator.c
regulator: twl4030 regulators
[linux-2.6-omap-h63xx.git] / drivers / regulator / twl4030-regulator.c
1 /*
2  * twl4030-regulator.c -- support regulators in twl4030 family chips
3  *
4  * Copyright (C) 2008 David Brownell
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/err.h>
15 #include <linux/platform_device.h>
16 #include <linux/regulator/driver.h>
17 #include <linux/regulator/machine.h>
18 #include <linux/i2c/twl4030.h>
19
20
21 /*
22  * The TWL4030/TW5030/TPS659x0 family chips include power management, a
23  * USB OTG transceiver, an RTC, ADC, PWM, and lots more.  Some versions
24  * include an audio codec, battery charger, and more voltage regulators.
25  * These chips are often used in OMAP-based systems.
26  *
27  * This driver implements software-based resource control for various
28  * voltage regulators.  This is usually augmented with state machine
29  * based control.
30  */
31
32 struct twlreg_info {
33         /* start of regulator's PM_RECEIVER control register bank */
34         u8                      base;
35
36         /* twl4030 resource ID, for resource control state machine */
37         u8                      id;
38
39         /* voltage in mV = table[VSEL]; table_len must be a power-of-two */
40         u8                      table_len;
41         const u16               *table;
42
43         /* chip constraints on regulator behavior */
44         u16                     min_mV;
45         u16                     max_mV;
46
47         /* used by regulator core */
48         struct regulator_desc   desc;
49 };
50
51
52 /* LDO control registers ... offset is from the base of its register bank.
53  * The first three registers of all power resource banks help hardware to
54  * manage the various resource groups.
55  */
56 #define VREG_GRP                0
57 #define VREG_TYPE               1
58 #define VREG_REMAP              2
59 #define VREG_DEDICATED          3       /* LDO control */
60
61
62 static inline int
63 twl4030reg_read(struct twlreg_info *info, unsigned offset)
64 {
65         u8 value;
66         int status;
67
68         status = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER,
69                         &value, info->base + offset);
70         return (status < 0) ? status : value;
71 }
72
73 static inline int
74 twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value)
75 {
76         return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
77                         value, info->base + offset);
78 }
79
80 /*----------------------------------------------------------------------*/
81
82 /* generic power resource operations, which work on all regulators */
83
84 static int twl4030reg_grp(struct regulator_dev *rdev)
85 {
86         return twl4030reg_read(rdev_get_drvdata(rdev), VREG_GRP);
87 }
88
89 /*
90  * Enable/disable regulators by joining/leaving the P1 (processor) group.
91  * We assume nobody else is updating the DEV_GRP registers.
92  */
93
94 #define P3_GRP          BIT(7)          /* "peripherals" */
95 #define P2_GRP          BIT(6)          /* secondary processor, modem, etc */
96 #define P1_GRP          BIT(5)          /* CPU/Linux */
97
98 static int twl4030reg_is_enabled(struct regulator_dev *rdev)
99 {
100         int     state = twl4030reg_grp(rdev);
101
102         if (state < 0)
103                 return state;
104
105         return (state & P1_GRP) != 0;
106 }
107
108 static int twl4030reg_enable(struct regulator_dev *rdev)
109 {
110         struct twlreg_info      *info = rdev_get_drvdata(rdev);
111         int                     grp;
112
113         grp = twl4030reg_read(info, VREG_GRP);
114         if (grp < 0)
115                 return grp;
116
117         grp |= P1_GRP;
118         return twl4030reg_write(info, VREG_GRP, grp);
119 }
120
121 static int twl4030reg_disable(struct regulator_dev *rdev)
122 {
123         struct twlreg_info      *info = rdev_get_drvdata(rdev);
124         int                     grp;
125
126         grp = twl4030reg_read(info, VREG_GRP);
127         if (grp < 0)
128                 return grp;
129
130         grp &= ~P1_GRP;
131         return twl4030reg_write(info, VREG_GRP, grp);
132 }
133
134 static int twl4030reg_get_status(struct regulator_dev *rdev)
135 {
136         int     state = twl4030reg_grp(rdev);
137
138         if (state < 0)
139                 return state;
140         state &= 0x0f;
141
142         /* assume state != WARM_RESET; we'd not be running...  */
143         if (!state)
144                 return REGULATOR_STATUS_OFF;
145         return (state & BIT(3))
146                 ? REGULATOR_STATUS_NORMAL
147                 : REGULATOR_STATUS_STANDBY;
148 }
149
150 static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode)
151 {
152         struct twlreg_info      *info = rdev_get_drvdata(rdev);
153         unsigned                message;
154         int                     status;
155
156         /* We can only set the mode through state machine commands... */
157         switch (mode) {
158         case REGULATOR_MODE_NORMAL:
159                 message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_ACTIVE);
160                 break;
161         case REGULATOR_MODE_STANDBY:
162                 message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_SLEEP);
163                 break;
164         default:
165                 return -EINVAL;
166         }
167
168         /* Ensure the resource is associated with some group */
169         status = twl4030reg_grp(rdev);
170         if (status < 0)
171                 return status;
172         if (!(status & (P3_GRP | P2_GRP | P1_GRP)))
173                 return -EACCES;
174
175         status = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
176                         message >> 8, 0x15 /* PB_WORD_MSB */ );
177         if (status >= 0)
178                 return status;
179
180         return twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER,
181                         message, 0x16 /* PB_WORD_LSB */ );
182 }
183
184 /*----------------------------------------------------------------------*/
185
186 /*
187  * Support for adjustable-voltage LDOs uses a four bit (or less) voltage
188  * select field in its control register.   We use tables indexed by VSEL
189  * to record voltages in milliVolts.  (Accuracy is about three percent.)
190  *
191  * Note that VSEL values for VAUX2 changed in twl5030 and newer silicon;
192  * currently handled by listing two slightly different VAUX2 regulators,
193  * only one of which will be configured.
194  *
195  * VSEL values documented as "TI cannot support these values" are flagged
196  * in these tables as UNSUP() values; we normally won't assign them.
197  */
198 #ifdef CONFIG_TWL4030_ALLOW_UNSUPPORTED
199 #define UNSUP_MASK      0x0000
200 #else
201 #define UNSUP_MASK      0x8000
202 #endif
203
204 #define UNSUP(x)        (UNSUP_MASK | (x))
205 #define IS_UNSUP(x)     (UNSUP_MASK & (x))
206 #define LDO_MV(x)       (~UNSUP_MASK & (x))
207
208
209 static const u16 VAUX1_VSEL_table[] = {
210         UNSUP(1500), UNSUP(1800), 2500, 2800,
211         3000, 3000, 3000, 3000,
212 };
213 static const u16 VAUX2_4030_VSEL_table[] = {
214         UNSUP(1000), UNSUP(1000), UNSUP(1200), 1300,
215         1500, 1800, UNSUP(1850), 2500,
216         UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000),
217         UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150),
218 };
219 static const u16 VAUX2_VSEL_table[] = {
220         1700, 1700, 1900, 1300,
221         1500, 1800, 2000, 2500,
222         2100, 2800, 2200, 2300,
223         2400, 2400, 2400, 2400,
224 };
225 static const u16 VAUX3_VSEL_table[] = {
226         1500, 1800, 2500, 2800,
227         UNSUP(3000), UNSUP(3000), UNSUP(3000), UNSUP(3000),
228 };
229 static const u16 VAUX4_VSEL_table[] = {
230         700, 1000, 1200, UNSUP(1300),
231         1500, 1800, UNSUP(1850), 2500,
232 };
233 static const u16 VMMC1_VSEL_table[] = {
234         1850, 2850, 3000, 3150,
235 };
236 static const u16 VMMC2_VSEL_table[] = {
237         UNSUP(1000), UNSUP(1000), UNSUP(1200), UNSUP(1300),
238         UNSUP(1500), UNSUP(1800), 1850, UNSUP(2500),
239         2600, 2800, 2850, 3000,
240         3150, 3150, 3150, 3150,
241 };
242 static const u16 VPLL1_VSEL_table[] = {
243         1000, 1200, 1300, 1800,
244         UNSUP(2800), UNSUP(3000), UNSUP(3000), UNSUP(3000),
245 };
246 static const u16 VPLL2_VSEL_table[] = {
247         700, 1000, 1200, 1300,
248         UNSUP(1500), 1800, UNSUP(1850), UNSUP(2500),
249         UNSUP(2600), UNSUP(2800), UNSUP(2850), UNSUP(3000),
250         UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150),
251 };
252 static const u16 VSIM_VSEL_table[] = {
253         UNSUP(1000), UNSUP(1200), UNSUP(1300), 1800,
254         2800, 3000, 3000, 3000,
255 };
256 static const u16 VDAC_VSEL_table[] = {
257         1200, 1300, 1800, 1800,
258 };
259
260
261 static int
262 twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV)
263 {
264         struct twlreg_info      *info = rdev_get_drvdata(rdev);
265         int                     vsel;
266
267         for (vsel = 0; vsel < info->table_len; vsel++) {
268                 int mV = info->table[vsel];
269                 int uV;
270
271                 if (IS_UNSUP(mV))
272                         continue;
273                 uV = LDO_MV(mV) * 1000;
274
275                 /* use the first in-range value */
276                 if (min_uV <= uV && uV <= max_uV)
277                         return twl4030reg_write(info, VREG_DEDICATED, vsel);
278         }
279
280         return -EDOM;
281 }
282
283 static int twl4030ldo_get_voltage(struct regulator_dev *rdev)
284 {
285         struct twlreg_info      *info = rdev_get_drvdata(rdev);
286         int                     vsel = twl4030reg_read(info, VREG_DEDICATED);
287
288         if (vsel < 0)
289                 return vsel;
290
291         vsel &= info->table_len - 1;
292         return LDO_MV(info->table[vsel]) * 1000;
293 }
294
295 static struct regulator_ops twl4030ldo_ops = {
296         .set_voltage    = twl4030ldo_set_voltage,
297         .get_voltage    = twl4030ldo_get_voltage,
298
299         .enable         = twl4030reg_enable,
300         .disable        = twl4030reg_disable,
301         .is_enabled     = twl4030reg_is_enabled,
302
303         .set_mode       = twl4030reg_set_mode,
304
305         .get_status     = twl4030reg_get_status,
306 };
307
308 /*----------------------------------------------------------------------*/
309
310 /*
311  * Fixed voltage LDOs don't have a VSEL field to update.
312  */
313 static int twl4030fixed_get_voltage(struct regulator_dev *rdev)
314 {
315         struct twlreg_info      *info = rdev_get_drvdata(rdev);
316
317         return info->min_mV * 1000;
318 }
319
320 static struct regulator_ops twl4030fixed_ops = {
321         .get_voltage    = twl4030fixed_get_voltage,
322
323         .enable         = twl4030reg_enable,
324         .disable        = twl4030reg_disable,
325         .is_enabled     = twl4030reg_is_enabled,
326
327         .set_mode       = twl4030reg_set_mode,
328
329         .get_status     = twl4030reg_get_status,
330 };
331
332 /*----------------------------------------------------------------------*/
333
334 #define TWL_ADJUSTABLE_LDO(label, offset, num) { \
335         .base = offset, \
336         .id = num, \
337         .table_len = ARRAY_SIZE(label##_VSEL_table), \
338         .table = label##_VSEL_table, \
339         .desc = { \
340                 .name = #label, \
341                 .id = TWL4030_REG_##label, \
342                 .ops = &twl4030ldo_ops, \
343                 .type = REGULATOR_VOLTAGE, \
344                 .owner = THIS_MODULE, \
345                 }, \
346         }
347
348 #define TWL_FIXED_LDO(label, offset, mVolts, num) { \
349         .base = offset, \
350         .id = num, \
351         .min_mV = mVolts, \
352         .max_mV = mVolts, \
353         .desc = { \
354                 .name = #label, \
355                 .id = TWL4030_REG_##label, \
356                 .ops = &twl4030fixed_ops, \
357                 .type = REGULATOR_VOLTAGE, \
358                 .owner = THIS_MODULE, \
359                 }, \
360         }
361
362 /*
363  * We list regulators here if systems need some level of
364  * software control over them after boot.
365  */
366 static struct twlreg_info twl4030_regs[] = {
367         TWL_ADJUSTABLE_LDO(VAUX1, 0x17, 1),
368         TWL_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2),
369         TWL_ADJUSTABLE_LDO(VAUX2, 0x1b, 2),
370         TWL_ADJUSTABLE_LDO(VAUX3, 0x1f, 3),
371         TWL_ADJUSTABLE_LDO(VAUX4, 0x23, 4),
372         TWL_ADJUSTABLE_LDO(VMMC1, 0x27, 5),
373         TWL_ADJUSTABLE_LDO(VMMC2, 0x2b, 6),
374         /*
375         TWL_ADJUSTABLE_LDO(VPLL1, 0x2f, 7),
376         TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8),
377         */
378         TWL_ADJUSTABLE_LDO(VSIM, 0x37, 9),
379         TWL_ADJUSTABLE_LDO(VDAC, 0x3b, 10),
380         /*
381         TWL_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11),
382         TWL_ADJUSTABLE_LDO(VINTANA2, 0x43, 12),
383         TWL_ADJUSTABLE_LDO(VINTDIG, 0x47, 13),
384         TWL_SMPS(VIO, 0x4b, 14),
385         TWL_SMPS(VDD1, 0x55, 15),
386         TWL_SMPS(VDD2, 0x63, 16),
387          */
388         TWL_FIXED_LDO(VUSB1V5, 0x71, 1500, 17),
389         TWL_FIXED_LDO(VUSB1V8, 0x74, 1800, 18),
390         TWL_FIXED_LDO(VUSB3V1, 0x77, 3100, 19),
391         /* VUSBCP is managed *only* by the USB subchip */
392 };
393
394 static int twl4030reg_probe(struct platform_device *pdev)
395 {
396         int                             i;
397         struct twlreg_info              *info;
398         struct regulator_init_data      *initdata;
399         struct regulation_constraints   *c;
400         struct regulator_dev            *rdev;
401         int                             min_uV, max_uV;
402
403         for (i = 0, info = NULL; i < ARRAY_SIZE(twl4030_regs); i++) {
404                 if (twl4030_regs[i].desc.id != pdev->id)
405                         continue;
406                 info = twl4030_regs + i;
407                 min_uV = info->min_mV * 1000;
408                 max_uV = info->max_mV * 1000;
409                 break;
410         }
411         if (!info)
412                 return -ENODEV;
413
414         initdata = pdev->dev.platform_data;
415         if (!initdata)
416                 return -EINVAL;
417
418         /* Constrain board-specific capabilities according to what
419          * this driver and the chip itself can actually do.
420          */
421         c = &initdata->constraints;
422         if (!c->min_uV || c->min_uV < min_uV)
423                 c->min_uV = min_uV;
424         if (!c->max_uV || c->max_uV > max_uV)
425                 c->max_uV = max_uV;
426         c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY;
427         c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE
428                                 | REGULATOR_CHANGE_MODE
429                                 | REGULATOR_CHANGE_STATUS;
430
431         rdev = regulator_register(&info->desc, &pdev->dev, initdata, info);
432         if (IS_ERR(rdev)) {
433                 dev_err(&pdev->dev, "can't register %s, %ld\n",
434                                 info->desc.name, PTR_ERR(rdev));
435                 return PTR_ERR(rdev);
436         }
437         platform_set_drvdata(pdev, rdev);
438
439         /* NOTE:  many regulators support short-circuit IRQs (presentable
440          * as REGULATOR_OVER_CURRENT notifications?) configured via:
441          *  - SC_CONFIG
442          *  - SC_DETECT1 (vintana2, vmmc1/2, vaux1/2/3/4)
443          *  - SC_DETECT2 (vusb, vdac, vio, vdd1/2, vpll2)
444          *  - IT_CONFIG
445          */
446
447         return 0;
448 }
449
450 static int __devexit twl4030reg_remove(struct platform_device *pdev)
451 {
452         regulator_unregister(platform_get_drvdata(pdev));
453         return 0;
454 }
455
456 MODULE_ALIAS("platform:twl4030_reg");
457
458 static struct platform_driver twl4030reg_driver = {
459         .probe          = twl4030reg_probe,
460         .remove         = __devexit_p(twl4030reg_remove),
461         /* NOTE: short name, to work around driver model truncation of
462          * "twl4030_regulator.12" (and friends) to "twl4030_regulator.1".
463          */
464         .driver.name    = "twl4030_reg",
465         .driver.owner   = THIS_MODULE,
466 };
467
468 static int __init twl4030reg_init(void)
469 {
470         unsigned i, j;
471
472         /* determine min/max voltage constraints, taking into account
473          * whether set_voltage() will use the "unsupported" settings
474          */
475         for (i = 0; i < ARRAY_SIZE(twl4030_regs); i++) {
476                 struct twlreg_info      *info = twl4030_regs + i;
477                 const u16               *table;
478
479                 /* fixed-voltage regulators */
480                 if (!info->table_len)
481                         continue;
482
483                 /* LDO regulators: */
484                 for (j = 0, table = info->table;
485                                 j < info->table_len;
486                                 j++, table++) {
487                         u16             mV = *table;
488
489                         if (IS_UNSUP(mV))
490                                 continue;
491                         mV = LDO_MV(mV);
492
493                         if (info->min_mV == 0 || info->min_mV > mV)
494                                 info->min_mV = mV;
495                         if (info->max_mV < mV)
496                                 info->max_mV = mV;
497                 }
498         }
499
500         return platform_driver_register(&twl4030reg_driver);
501 }
502 subsys_initcall(twl4030reg_init);
503
504 static void __exit twl4030reg_exit(void)
505 {
506         platform_driver_unregister(&twl4030reg_driver);
507 }
508 module_exit(twl4030reg_exit)
509
510 MODULE_DESCRIPTION("TWL4030 regulator driver");
511 MODULE_LICENSE("GPL");