aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2009-02-08 13:37:06 -0500
committerLiam Girdwood <lrg@slimlogic.co.uk>2009-03-31 04:56:25 -0400
commitfa16a5c13a2fc1433cfff38a083b4f8c5138d022 (patch)
tree81f2e5ce5a1c1b7bd4de59e695b5e423126f3ec2 /drivers
parent3b2a6061afe6fcc44437cd5ec641b0aeb2825ee3 (diff)
regulator: twl4030 regulators
Support most of the LDO regulators in the twl4030 family chips. In the case of LDOs supporting MMC/SD, the voltage controls are used; but in most other cases, the regulator framework is only used to enable/disable a supplies, conserving power when a given voltage rail is not needed. The drivers/mfd/twl4030-core.c code already sets up the various regulators according to board-specific configuration, and knows that some chips don't provide the full set of voltage rails. The omitted regulators are intended to be under hardware control, such as during the hardware-mediated system powerup, powerdown, and suspend states. Unless/until software hooks are known to be safe, they won't be exported here. These regulators implement the new get_status() operation, but can't realistically implement get_mode(); the status output is effectively the result of a vote, with the relevant hardware inputs not exposed. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/twl4030-regulator.c511
3 files changed, 519 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 85a1f407e755..e58c0ce65aa6 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -56,6 +56,13 @@ config REGULATOR_BQ24022
56 charging select between 100 mA and 500 mA charging current 56 charging select between 100 mA and 500 mA charging current
57 limit. 57 limit.
58 58
59config REGULATOR_TWL4030
60 bool "TI TWL4030/TWL5030/TPS695x0 PMIC"
61 depends on TWL4030_CORE
62 help
63 This driver supports the voltage regulators provided by
64 this family of companion chips.
65
59config REGULATOR_WM8350 66config REGULATOR_WM8350
60 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" 67 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC"
61 depends on MFD_WM8350 68 depends on MFD_WM8350
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 61b30c6ddecc..bac133afc061 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o
8obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o 8obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o
9 9
10obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o 10obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o
11obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o
11obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o 12obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
12obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o 13obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
13obj-$(CONFIG_REGULATOR_DA903X) += da903x.o 14obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c
new file mode 100644
index 000000000000..23f282670db5
--- /dev/null
+++ b/drivers/regulator/twl4030-regulator.c
@@ -0,0 +1,511 @@
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
32struct 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
62static inline int
63twl4030reg_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
73static inline int
74twl4030reg_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
84static 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
98static 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
108static 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
121static 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
134static 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
150static 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
209static const u16 VAUX1_VSEL_table[] = {
210 UNSUP(1500), UNSUP(1800), 2500, 2800,
211 3000, 3000, 3000, 3000,
212};
213static 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};
219static 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};
225static const u16 VAUX3_VSEL_table[] = {
226 1500, 1800, 2500, 2800,
227 UNSUP(3000), UNSUP(3000), UNSUP(3000), UNSUP(3000),
228};
229static const u16 VAUX4_VSEL_table[] = {
230 700, 1000, 1200, UNSUP(1300),
231 1500, 1800, UNSUP(1850), 2500,
232};
233static const u16 VMMC1_VSEL_table[] = {
234 1850, 2850, 3000, 3150,
235};
236static 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};
242static const u16 VPLL1_VSEL_table[] = {
243 1000, 1200, 1300, 1800,
244 UNSUP(2800), UNSUP(3000), UNSUP(3000), UNSUP(3000),
245};
246static 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};
252static const u16 VSIM_VSEL_table[] = {
253 UNSUP(1000), UNSUP(1200), UNSUP(1300), 1800,
254 2800, 3000, 3000, 3000,
255};
256static const u16 VDAC_VSEL_table[] = {
257 1200, 1300, 1800, 1800,
258};
259
260
261static int
262twl4030ldo_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
283static 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
295static 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 */
313static 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
320static 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 */
366static 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
394static 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
450static int __devexit twl4030reg_remove(struct platform_device *pdev)
451{
452 regulator_unregister(platform_get_drvdata(pdev));
453 return 0;
454}
455
456MODULE_ALIAS("platform:twl4030_reg");
457
458static 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
468static 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}
502subsys_initcall(twl4030reg_init);
503
504static void __exit twl4030reg_exit(void)
505{
506 platform_driver_unregister(&twl4030reg_driver);
507}
508module_exit(twl4030reg_exit)
509
510MODULE_DESCRIPTION("TWL4030 regulator driver");
511MODULE_LICENSE("GPL");