Commit | Line | Data |
---|---|---|
fa16a5c1 | 1 | /* |
c4aa6f31 | 2 | * twl-regulator.c -- support regulators in twl4030/twl6030 family chips |
fa16a5c1 DB |
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> | |
53b8a9d9 | 15 | #include <linux/delay.h> |
fa16a5c1 DB |
16 | #include <linux/platform_device.h> |
17 | #include <linux/regulator/driver.h> | |
18 | #include <linux/regulator/machine.h> | |
b07682b6 | 19 | #include <linux/i2c/twl.h> |
fa16a5c1 DB |
20 | |
21 | ||
22 | /* | |
c4aa6f31 | 23 | * The TWL4030/TW5030/TPS659x0/TWL6030 family chips include power management, a |
fa16a5c1 DB |
24 | * USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions |
25 | * include an audio codec, battery charger, and more voltage regulators. | |
26 | * These chips are often used in OMAP-based systems. | |
27 | * | |
28 | * This driver implements software-based resource control for various | |
29 | * voltage regulators. This is usually augmented with state machine | |
30 | * based control. | |
31 | */ | |
32 | ||
33 | struct twlreg_info { | |
34 | /* start of regulator's PM_RECEIVER control register bank */ | |
35 | u8 base; | |
36 | ||
c4aa6f31 | 37 | /* twl resource ID, for resource control state machine */ |
fa16a5c1 DB |
38 | u8 id; |
39 | ||
40 | /* voltage in mV = table[VSEL]; table_len must be a power-of-two */ | |
41 | u8 table_len; | |
42 | const u16 *table; | |
43 | ||
045f972f JKS |
44 | /* regulator specific turn-on delay */ |
45 | u16 delay; | |
46 | ||
47 | /* State REMAP default configuration */ | |
48 | u8 remap; | |
49 | ||
fa16a5c1 DB |
50 | /* chip constraints on regulator behavior */ |
51 | u16 min_mV; | |
3e3d3be7 | 52 | u16 max_mV; |
fa16a5c1 | 53 | |
4d94aee5 GG |
54 | u8 flags; |
55 | ||
fa16a5c1 DB |
56 | /* used by regulator core */ |
57 | struct regulator_desc desc; | |
4d94aee5 GG |
58 | |
59 | /* chip specific features */ | |
60 | unsigned long features; | |
fa16a5c1 DB |
61 | }; |
62 | ||
63 | ||
64 | /* LDO control registers ... offset is from the base of its register bank. | |
65 | * The first three registers of all power resource banks help hardware to | |
66 | * manage the various resource groups. | |
67 | */ | |
441a4505 | 68 | /* Common offset in TWL4030/6030 */ |
fa16a5c1 | 69 | #define VREG_GRP 0 |
441a4505 | 70 | /* TWL4030 register offsets */ |
fa16a5c1 DB |
71 | #define VREG_TYPE 1 |
72 | #define VREG_REMAP 2 | |
73 | #define VREG_DEDICATED 3 /* LDO control */ | |
ba305e31 | 74 | #define VREG_VOLTAGE_SMPS_4030 9 |
441a4505 RN |
75 | /* TWL6030 register offsets */ |
76 | #define VREG_TRANS 1 | |
77 | #define VREG_STATE 2 | |
78 | #define VREG_VOLTAGE 3 | |
4d94aee5 | 79 | #define VREG_VOLTAGE_SMPS 4 |
441a4505 RN |
80 | /* TWL6030 Misc register offsets */ |
81 | #define VREG_BC_ALL 1 | |
82 | #define VREG_BC_REF 2 | |
83 | #define VREG_BC_PROC 3 | |
84 | #define VREG_BC_CLK_RST 4 | |
fa16a5c1 | 85 | |
21657ebf SH |
86 | /* TWL6030 LDO register values for CFG_STATE */ |
87 | #define TWL6030_CFG_STATE_OFF 0x00 | |
88 | #define TWL6030_CFG_STATE_ON 0x01 | |
9a0244ad SH |
89 | #define TWL6030_CFG_STATE_OFF2 0x02 |
90 | #define TWL6030_CFG_STATE_SLEEP 0x03 | |
21657ebf | 91 | #define TWL6030_CFG_STATE_GRP_SHIFT 5 |
b2456779 SH |
92 | #define TWL6030_CFG_STATE_APP_SHIFT 2 |
93 | #define TWL6030_CFG_STATE_APP_MASK (0x03 << TWL6030_CFG_STATE_APP_SHIFT) | |
94 | #define TWL6030_CFG_STATE_APP(v) (((v) & TWL6030_CFG_STATE_APP_MASK) >>\ | |
95 | TWL6030_CFG_STATE_APP_SHIFT) | |
21657ebf | 96 | |
4d94aee5 GG |
97 | /* Flags for SMPS Voltage reading */ |
98 | #define SMPS_OFFSET_EN BIT(0) | |
99 | #define SMPS_EXTENDED_EN BIT(1) | |
100 | ||
101 | /* twl6025 SMPS EPROM values */ | |
102 | #define TWL6030_SMPS_OFFSET 0xB0 | |
103 | #define TWL6030_SMPS_MULT 0xB3 | |
104 | #define SMPS_MULTOFFSET_SMPS4 BIT(0) | |
105 | #define SMPS_MULTOFFSET_VIO BIT(1) | |
106 | #define SMPS_MULTOFFSET_SMPS3 BIT(6) | |
107 | ||
fa16a5c1 | 108 | static inline int |
441a4505 | 109 | twlreg_read(struct twlreg_info *info, unsigned slave_subgp, unsigned offset) |
fa16a5c1 DB |
110 | { |
111 | u8 value; | |
112 | int status; | |
113 | ||
441a4505 | 114 | status = twl_i2c_read_u8(slave_subgp, |
fa16a5c1 DB |
115 | &value, info->base + offset); |
116 | return (status < 0) ? status : value; | |
117 | } | |
118 | ||
119 | static inline int | |
441a4505 RN |
120 | twlreg_write(struct twlreg_info *info, unsigned slave_subgp, unsigned offset, |
121 | u8 value) | |
fa16a5c1 | 122 | { |
441a4505 | 123 | return twl_i2c_write_u8(slave_subgp, |
fa16a5c1 DB |
124 | value, info->base + offset); |
125 | } | |
126 | ||
127 | /*----------------------------------------------------------------------*/ | |
128 | ||
129 | /* generic power resource operations, which work on all regulators */ | |
130 | ||
c4aa6f31 | 131 | static int twlreg_grp(struct regulator_dev *rdev) |
fa16a5c1 | 132 | { |
441a4505 RN |
133 | return twlreg_read(rdev_get_drvdata(rdev), TWL_MODULE_PM_RECEIVER, |
134 | VREG_GRP); | |
fa16a5c1 DB |
135 | } |
136 | ||
137 | /* | |
138 | * Enable/disable regulators by joining/leaving the P1 (processor) group. | |
139 | * We assume nobody else is updating the DEV_GRP registers. | |
140 | */ | |
441a4505 RN |
141 | /* definition for 4030 family */ |
142 | #define P3_GRP_4030 BIT(7) /* "peripherals" */ | |
143 | #define P2_GRP_4030 BIT(6) /* secondary processor, modem, etc */ | |
144 | #define P1_GRP_4030 BIT(5) /* CPU/Linux */ | |
145 | /* definition for 6030 family */ | |
146 | #define P3_GRP_6030 BIT(2) /* secondary processor, modem, etc */ | |
147 | #define P2_GRP_6030 BIT(1) /* "peripherals" */ | |
148 | #define P1_GRP_6030 BIT(0) /* CPU/Linux */ | |
fa16a5c1 | 149 | |
b2456779 | 150 | static int twl4030reg_is_enabled(struct regulator_dev *rdev) |
fa16a5c1 | 151 | { |
c4aa6f31 | 152 | int state = twlreg_grp(rdev); |
fa16a5c1 DB |
153 | |
154 | if (state < 0) | |
155 | return state; | |
156 | ||
b2456779 SH |
157 | return state & P1_GRP_4030; |
158 | } | |
159 | ||
160 | static int twl6030reg_is_enabled(struct regulator_dev *rdev) | |
161 | { | |
162 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
4d94aee5 | 163 | int grp = 0, val; |
b2456779 | 164 | |
4d94aee5 GG |
165 | if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) |
166 | grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); | |
b2456779 SH |
167 | if (grp < 0) |
168 | return grp; | |
169 | ||
4d94aee5 GG |
170 | if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) |
171 | grp &= P1_GRP_6030; | |
172 | else | |
173 | grp = 1; | |
b2456779 SH |
174 | |
175 | val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); | |
176 | val = TWL6030_CFG_STATE_APP(val); | |
177 | ||
178 | return grp && (val == TWL6030_CFG_STATE_ON); | |
fa16a5c1 DB |
179 | } |
180 | ||
f8c2940b | 181 | static int twl4030reg_enable(struct regulator_dev *rdev) |
fa16a5c1 DB |
182 | { |
183 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
184 | int grp; | |
53b8a9d9 | 185 | int ret; |
fa16a5c1 | 186 | |
441a4505 | 187 | grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); |
fa16a5c1 DB |
188 | if (grp < 0) |
189 | return grp; | |
190 | ||
f8c2940b | 191 | grp |= P1_GRP_4030; |
441a4505 | 192 | |
53b8a9d9 JKS |
193 | ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); |
194 | ||
f8c2940b B |
195 | udelay(info->delay); |
196 | ||
197 | return ret; | |
198 | } | |
199 | ||
200 | static int twl6030reg_enable(struct regulator_dev *rdev) | |
201 | { | |
202 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
4d94aee5 | 203 | int grp = 0; |
f8c2940b B |
204 | int ret; |
205 | ||
4d94aee5 GG |
206 | if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) |
207 | grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); | |
f8c2940b B |
208 | if (grp < 0) |
209 | return grp; | |
210 | ||
211 | ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, | |
212 | grp << TWL6030_CFG_STATE_GRP_SHIFT | | |
213 | TWL6030_CFG_STATE_ON); | |
21657ebf | 214 | |
53b8a9d9 JKS |
215 | udelay(info->delay); |
216 | ||
217 | return ret; | |
fa16a5c1 DB |
218 | } |
219 | ||
0ff3897d | 220 | static int twl4030reg_disable(struct regulator_dev *rdev) |
fa16a5c1 DB |
221 | { |
222 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
223 | int grp; | |
21657ebf | 224 | int ret; |
fa16a5c1 | 225 | |
441a4505 | 226 | grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); |
fa16a5c1 DB |
227 | if (grp < 0) |
228 | return grp; | |
229 | ||
0ff3897d | 230 | grp &= ~(P1_GRP_4030 | P2_GRP_4030 | P3_GRP_4030); |
441a4505 | 231 | |
21657ebf SH |
232 | ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_GRP, grp); |
233 | ||
0ff3897d B |
234 | return ret; |
235 | } | |
236 | ||
237 | static int twl6030reg_disable(struct regulator_dev *rdev) | |
238 | { | |
239 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
240 | int grp = 0; | |
241 | int ret; | |
242 | ||
4d94aee5 GG |
243 | if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) |
244 | grp = P1_GRP_6030 | P2_GRP_6030 | P3_GRP_6030; | |
0ff3897d B |
245 | |
246 | /* For 6030, set the off state for all grps enabled */ | |
247 | ret = twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, | |
248 | (grp) << TWL6030_CFG_STATE_GRP_SHIFT | | |
249 | TWL6030_CFG_STATE_OFF); | |
21657ebf SH |
250 | |
251 | return ret; | |
fa16a5c1 DB |
252 | } |
253 | ||
9a0244ad | 254 | static int twl4030reg_get_status(struct regulator_dev *rdev) |
fa16a5c1 | 255 | { |
c4aa6f31 | 256 | int state = twlreg_grp(rdev); |
fa16a5c1 DB |
257 | |
258 | if (state < 0) | |
259 | return state; | |
260 | state &= 0x0f; | |
261 | ||
262 | /* assume state != WARM_RESET; we'd not be running... */ | |
263 | if (!state) | |
264 | return REGULATOR_STATUS_OFF; | |
265 | return (state & BIT(3)) | |
266 | ? REGULATOR_STATUS_NORMAL | |
267 | : REGULATOR_STATUS_STANDBY; | |
268 | } | |
269 | ||
9a0244ad SH |
270 | static int twl6030reg_get_status(struct regulator_dev *rdev) |
271 | { | |
272 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
273 | int val; | |
274 | ||
275 | val = twlreg_grp(rdev); | |
276 | if (val < 0) | |
277 | return val; | |
278 | ||
279 | val = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_STATE); | |
280 | ||
281 | switch (TWL6030_CFG_STATE_APP(val)) { | |
282 | case TWL6030_CFG_STATE_ON: | |
283 | return REGULATOR_STATUS_NORMAL; | |
284 | ||
285 | case TWL6030_CFG_STATE_SLEEP: | |
286 | return REGULATOR_STATUS_STANDBY; | |
287 | ||
288 | case TWL6030_CFG_STATE_OFF: | |
289 | case TWL6030_CFG_STATE_OFF2: | |
290 | default: | |
291 | break; | |
292 | } | |
293 | ||
294 | return REGULATOR_STATUS_OFF; | |
295 | } | |
296 | ||
1a39962f | 297 | static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) |
fa16a5c1 DB |
298 | { |
299 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
300 | unsigned message; | |
301 | int status; | |
302 | ||
303 | /* We can only set the mode through state machine commands... */ | |
304 | switch (mode) { | |
305 | case REGULATOR_MODE_NORMAL: | |
306 | message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_ACTIVE); | |
307 | break; | |
308 | case REGULATOR_MODE_STANDBY: | |
309 | message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_SLEEP); | |
310 | break; | |
311 | default: | |
312 | return -EINVAL; | |
313 | } | |
314 | ||
315 | /* Ensure the resource is associated with some group */ | |
c4aa6f31 | 316 | status = twlreg_grp(rdev); |
fa16a5c1 DB |
317 | if (status < 0) |
318 | return status; | |
441a4505 | 319 | if (!(status & (P3_GRP_4030 | P2_GRP_4030 | P1_GRP_4030))) |
fa16a5c1 DB |
320 | return -EACCES; |
321 | ||
c4aa6f31 | 322 | status = twl_i2c_write_u8(TWL_MODULE_PM_MASTER, |
b9e26bc8 AL |
323 | message >> 8, TWL4030_PM_MASTER_PB_WORD_MSB); |
324 | if (status < 0) | |
fa16a5c1 DB |
325 | return status; |
326 | ||
c4aa6f31 | 327 | return twl_i2c_write_u8(TWL_MODULE_PM_MASTER, |
b9e26bc8 | 328 | message & 0xff, TWL4030_PM_MASTER_PB_WORD_LSB); |
fa16a5c1 DB |
329 | } |
330 | ||
1a39962f SH |
331 | static int twl6030reg_set_mode(struct regulator_dev *rdev, unsigned mode) |
332 | { | |
333 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
4d94aee5 | 334 | int grp = 0; |
1a39962f SH |
335 | int val; |
336 | ||
4d94aee5 GG |
337 | if (!(twl_class_is_6030() && (info->features & TWL6025_SUBCLASS))) |
338 | grp = twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_GRP); | |
1a39962f SH |
339 | |
340 | if (grp < 0) | |
341 | return grp; | |
342 | ||
343 | /* Compose the state register settings */ | |
344 | val = grp << TWL6030_CFG_STATE_GRP_SHIFT; | |
345 | /* We can only set the mode through state machine commands... */ | |
346 | switch (mode) { | |
347 | case REGULATOR_MODE_NORMAL: | |
348 | val |= TWL6030_CFG_STATE_ON; | |
349 | break; | |
350 | case REGULATOR_MODE_STANDBY: | |
351 | val |= TWL6030_CFG_STATE_SLEEP; | |
352 | break; | |
353 | ||
354 | default: | |
355 | return -EINVAL; | |
356 | } | |
357 | ||
358 | return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_STATE, val); | |
359 | } | |
360 | ||
fa16a5c1 DB |
361 | /*----------------------------------------------------------------------*/ |
362 | ||
363 | /* | |
364 | * Support for adjustable-voltage LDOs uses a four bit (or less) voltage | |
365 | * select field in its control register. We use tables indexed by VSEL | |
366 | * to record voltages in milliVolts. (Accuracy is about three percent.) | |
367 | * | |
368 | * Note that VSEL values for VAUX2 changed in twl5030 and newer silicon; | |
369 | * currently handled by listing two slightly different VAUX2 regulators, | |
370 | * only one of which will be configured. | |
371 | * | |
372 | * VSEL values documented as "TI cannot support these values" are flagged | |
373 | * in these tables as UNSUP() values; we normally won't assign them. | |
d6bb69cf AH |
374 | * |
375 | * VAUX3 at 3V is incorrectly listed in some TI manuals as unsupported. | |
376 | * TI are revising the twl5030/tps659x0 specs to support that 3.0V setting. | |
fa16a5c1 DB |
377 | */ |
378 | #ifdef CONFIG_TWL4030_ALLOW_UNSUPPORTED | |
379 | #define UNSUP_MASK 0x0000 | |
380 | #else | |
381 | #define UNSUP_MASK 0x8000 | |
382 | #endif | |
383 | ||
384 | #define UNSUP(x) (UNSUP_MASK | (x)) | |
385 | #define IS_UNSUP(x) (UNSUP_MASK & (x)) | |
386 | #define LDO_MV(x) (~UNSUP_MASK & (x)) | |
387 | ||
388 | ||
389 | static const u16 VAUX1_VSEL_table[] = { | |
390 | UNSUP(1500), UNSUP(1800), 2500, 2800, | |
391 | 3000, 3000, 3000, 3000, | |
392 | }; | |
393 | static const u16 VAUX2_4030_VSEL_table[] = { | |
394 | UNSUP(1000), UNSUP(1000), UNSUP(1200), 1300, | |
395 | 1500, 1800, UNSUP(1850), 2500, | |
396 | UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), | |
397 | UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), | |
398 | }; | |
399 | static const u16 VAUX2_VSEL_table[] = { | |
400 | 1700, 1700, 1900, 1300, | |
401 | 1500, 1800, 2000, 2500, | |
402 | 2100, 2800, 2200, 2300, | |
403 | 2400, 2400, 2400, 2400, | |
404 | }; | |
405 | static const u16 VAUX3_VSEL_table[] = { | |
406 | 1500, 1800, 2500, 2800, | |
d6bb69cf | 407 | 3000, 3000, 3000, 3000, |
fa16a5c1 DB |
408 | }; |
409 | static const u16 VAUX4_VSEL_table[] = { | |
410 | 700, 1000, 1200, UNSUP(1300), | |
411 | 1500, 1800, UNSUP(1850), 2500, | |
1897e742 DB |
412 | UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), |
413 | UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), | |
fa16a5c1 DB |
414 | }; |
415 | static const u16 VMMC1_VSEL_table[] = { | |
416 | 1850, 2850, 3000, 3150, | |
417 | }; | |
418 | static const u16 VMMC2_VSEL_table[] = { | |
419 | UNSUP(1000), UNSUP(1000), UNSUP(1200), UNSUP(1300), | |
420 | UNSUP(1500), UNSUP(1800), 1850, UNSUP(2500), | |
421 | 2600, 2800, 2850, 3000, | |
422 | 3150, 3150, 3150, 3150, | |
423 | }; | |
424 | static const u16 VPLL1_VSEL_table[] = { | |
425 | 1000, 1200, 1300, 1800, | |
426 | UNSUP(2800), UNSUP(3000), UNSUP(3000), UNSUP(3000), | |
427 | }; | |
428 | static const u16 VPLL2_VSEL_table[] = { | |
429 | 700, 1000, 1200, 1300, | |
430 | UNSUP(1500), 1800, UNSUP(1850), UNSUP(2500), | |
431 | UNSUP(2600), UNSUP(2800), UNSUP(2850), UNSUP(3000), | |
432 | UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), | |
433 | }; | |
434 | static const u16 VSIM_VSEL_table[] = { | |
435 | UNSUP(1000), UNSUP(1200), UNSUP(1300), 1800, | |
436 | 2800, 3000, 3000, 3000, | |
437 | }; | |
438 | static const u16 VDAC_VSEL_table[] = { | |
439 | 1200, 1300, 1800, 1800, | |
440 | }; | |
07fc493f JKS |
441 | static const u16 VDD1_VSEL_table[] = { |
442 | 800, 1450, | |
443 | }; | |
444 | static const u16 VDD2_VSEL_table[] = { | |
445 | 800, 1450, 1500, | |
446 | }; | |
447 | static const u16 VIO_VSEL_table[] = { | |
448 | 1800, 1850, | |
449 | }; | |
450 | static const u16 VINTANA2_VSEL_table[] = { | |
451 | 2500, 2750, | |
452 | }; | |
fa16a5c1 | 453 | |
3e3d3be7 | 454 | static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) |
66b659e6 DB |
455 | { |
456 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
457 | int mV = info->table[index]; | |
458 | ||
459 | return IS_UNSUP(mV) ? 0 : (LDO_MV(mV) * 1000); | |
460 | } | |
461 | ||
fa16a5c1 | 462 | static int |
3a93f2a9 MB |
463 | twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, |
464 | unsigned *selector) | |
fa16a5c1 DB |
465 | { |
466 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
467 | int vsel; | |
468 | ||
469 | for (vsel = 0; vsel < info->table_len; vsel++) { | |
470 | int mV = info->table[vsel]; | |
471 | int uV; | |
472 | ||
473 | if (IS_UNSUP(mV)) | |
474 | continue; | |
475 | uV = LDO_MV(mV) * 1000; | |
476 | ||
66b659e6 DB |
477 | /* REVISIT for VAUX2, first match may not be best/lowest */ |
478 | ||
fa16a5c1 | 479 | /* use the first in-range value */ |
3a93f2a9 MB |
480 | if (min_uV <= uV && uV <= max_uV) { |
481 | *selector = vsel; | |
441a4505 RN |
482 | return twlreg_write(info, TWL_MODULE_PM_RECEIVER, |
483 | VREG_VOLTAGE, vsel); | |
3a93f2a9 | 484 | } |
fa16a5c1 DB |
485 | } |
486 | ||
487 | return -EDOM; | |
488 | } | |
489 | ||
3e3d3be7 | 490 | static int twl4030ldo_get_voltage(struct regulator_dev *rdev) |
fa16a5c1 DB |
491 | { |
492 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
441a4505 RN |
493 | int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, |
494 | VREG_VOLTAGE); | |
fa16a5c1 DB |
495 | |
496 | if (vsel < 0) | |
497 | return vsel; | |
498 | ||
499 | vsel &= info->table_len - 1; | |
500 | return LDO_MV(info->table[vsel]) * 1000; | |
501 | } | |
502 | ||
3e3d3be7 RN |
503 | static struct regulator_ops twl4030ldo_ops = { |
504 | .list_voltage = twl4030ldo_list_voltage, | |
66b659e6 | 505 | |
3e3d3be7 RN |
506 | .set_voltage = twl4030ldo_set_voltage, |
507 | .get_voltage = twl4030ldo_get_voltage, | |
508 | ||
f8c2940b | 509 | .enable = twl4030reg_enable, |
0ff3897d | 510 | .disable = twl4030reg_disable, |
b2456779 | 511 | .is_enabled = twl4030reg_is_enabled, |
3e3d3be7 | 512 | |
1a39962f | 513 | .set_mode = twl4030reg_set_mode, |
3e3d3be7 | 514 | |
9a0244ad | 515 | .get_status = twl4030reg_get_status, |
3e3d3be7 RN |
516 | }; |
517 | ||
ba305e31 TK |
518 | static int |
519 | twl4030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, | |
520 | unsigned *selector) | |
521 | { | |
522 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
523 | int vsel = DIV_ROUND_UP(min_uV - 600000, 12500); | |
524 | ||
525 | twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS_4030, | |
526 | vsel); | |
527 | return 0; | |
528 | } | |
529 | ||
530 | static int twl4030smps_get_voltage(struct regulator_dev *rdev) | |
531 | { | |
532 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
533 | int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, | |
534 | VREG_VOLTAGE_SMPS_4030); | |
535 | ||
536 | return vsel * 12500 + 600000; | |
537 | } | |
538 | ||
539 | static struct regulator_ops twl4030smps_ops = { | |
540 | .set_voltage = twl4030smps_set_voltage, | |
541 | .get_voltage = twl4030smps_get_voltage, | |
542 | }; | |
543 | ||
3e3d3be7 RN |
544 | static int twl6030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) |
545 | { | |
546 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
547 | ||
548 | return ((info->min_mV + (index * 100)) * 1000); | |
549 | } | |
550 | ||
551 | static int | |
3a93f2a9 MB |
552 | twl6030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, |
553 | unsigned *selector) | |
3e3d3be7 RN |
554 | { |
555 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
556 | int vsel; | |
557 | ||
558 | if ((min_uV/1000 < info->min_mV) || (max_uV/1000 > info->max_mV)) | |
559 | return -EDOM; | |
560 | ||
561 | /* | |
562 | * Use the below formula to calculate vsel | |
563 | * mV = 1000mv + 100mv * (vsel - 1) | |
564 | */ | |
565 | vsel = (min_uV/1000 - 1000)/100 + 1; | |
3a93f2a9 | 566 | *selector = vsel; |
3e3d3be7 RN |
567 | return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE, vsel); |
568 | ||
569 | } | |
570 | ||
571 | static int twl6030ldo_get_voltage(struct regulator_dev *rdev) | |
572 | { | |
573 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
574 | int vsel = twlreg_read(info, TWL_MODULE_PM_RECEIVER, | |
575 | VREG_VOLTAGE); | |
576 | ||
577 | if (vsel < 0) | |
578 | return vsel; | |
579 | ||
580 | /* | |
581 | * Use the below formula to calculate vsel | |
582 | * mV = 1000mv + 100mv * (vsel - 1) | |
583 | */ | |
584 | return (1000 + (100 * (vsel - 1))) * 1000; | |
585 | } | |
586 | ||
587 | static struct regulator_ops twl6030ldo_ops = { | |
588 | .list_voltage = twl6030ldo_list_voltage, | |
589 | ||
590 | .set_voltage = twl6030ldo_set_voltage, | |
591 | .get_voltage = twl6030ldo_get_voltage, | |
fa16a5c1 | 592 | |
f8c2940b | 593 | .enable = twl6030reg_enable, |
0ff3897d | 594 | .disable = twl6030reg_disable, |
b2456779 | 595 | .is_enabled = twl6030reg_is_enabled, |
fa16a5c1 | 596 | |
1a39962f | 597 | .set_mode = twl6030reg_set_mode, |
fa16a5c1 | 598 | |
9a0244ad | 599 | .get_status = twl6030reg_get_status, |
fa16a5c1 DB |
600 | }; |
601 | ||
602 | /*----------------------------------------------------------------------*/ | |
603 | ||
604 | /* | |
605 | * Fixed voltage LDOs don't have a VSEL field to update. | |
606 | */ | |
c4aa6f31 | 607 | static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index) |
66b659e6 DB |
608 | { |
609 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
610 | ||
611 | return info->min_mV * 1000; | |
612 | } | |
613 | ||
c4aa6f31 | 614 | static int twlfixed_get_voltage(struct regulator_dev *rdev) |
fa16a5c1 DB |
615 | { |
616 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
617 | ||
618 | return info->min_mV * 1000; | |
619 | } | |
620 | ||
b2456779 SH |
621 | static struct regulator_ops twl4030fixed_ops = { |
622 | .list_voltage = twlfixed_list_voltage, | |
623 | ||
624 | .get_voltage = twlfixed_get_voltage, | |
625 | ||
f8c2940b | 626 | .enable = twl4030reg_enable, |
0ff3897d | 627 | .disable = twl4030reg_disable, |
b2456779 SH |
628 | .is_enabled = twl4030reg_is_enabled, |
629 | ||
1a39962f | 630 | .set_mode = twl4030reg_set_mode, |
b2456779 | 631 | |
9a0244ad | 632 | .get_status = twl4030reg_get_status, |
b2456779 SH |
633 | }; |
634 | ||
635 | static struct regulator_ops twl6030fixed_ops = { | |
c4aa6f31 | 636 | .list_voltage = twlfixed_list_voltage, |
66b659e6 | 637 | |
c4aa6f31 | 638 | .get_voltage = twlfixed_get_voltage, |
fa16a5c1 | 639 | |
f8c2940b | 640 | .enable = twl6030reg_enable, |
0ff3897d | 641 | .disable = twl6030reg_disable, |
b2456779 | 642 | .is_enabled = twl6030reg_is_enabled, |
fa16a5c1 | 643 | |
1a39962f | 644 | .set_mode = twl6030reg_set_mode, |
fa16a5c1 | 645 | |
9a0244ad | 646 | .get_status = twl6030reg_get_status, |
fa16a5c1 DB |
647 | }; |
648 | ||
8e6de4a3 | 649 | static struct regulator_ops twl6030_fixed_resource = { |
f8c2940b | 650 | .enable = twl6030reg_enable, |
0ff3897d | 651 | .disable = twl6030reg_disable, |
b2456779 | 652 | .is_enabled = twl6030reg_is_enabled, |
9a0244ad | 653 | .get_status = twl6030reg_get_status, |
8e6de4a3 B |
654 | }; |
655 | ||
4d94aee5 GG |
656 | /* |
657 | * SMPS status and control | |
658 | */ | |
659 | ||
660 | static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index) | |
661 | { | |
662 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
663 | ||
664 | int voltage = 0; | |
665 | ||
666 | switch (info->flags) { | |
667 | case SMPS_OFFSET_EN: | |
668 | voltage = 100000; | |
669 | /* fall through */ | |
670 | case 0: | |
671 | switch (index) { | |
672 | case 0: | |
673 | voltage = 0; | |
674 | break; | |
675 | case 58: | |
676 | voltage = 1350 * 1000; | |
677 | break; | |
678 | case 59: | |
679 | voltage = 1500 * 1000; | |
680 | break; | |
681 | case 60: | |
682 | voltage = 1800 * 1000; | |
683 | break; | |
684 | case 61: | |
685 | voltage = 1900 * 1000; | |
686 | break; | |
687 | case 62: | |
688 | voltage = 2100 * 1000; | |
689 | break; | |
690 | default: | |
691 | voltage += (600000 + (12500 * (index - 1))); | |
692 | } | |
693 | break; | |
694 | case SMPS_EXTENDED_EN: | |
695 | switch (index) { | |
696 | case 0: | |
697 | voltage = 0; | |
698 | break; | |
699 | case 58: | |
700 | voltage = 2084 * 1000; | |
701 | break; | |
702 | case 59: | |
703 | voltage = 2315 * 1000; | |
704 | break; | |
705 | case 60: | |
706 | voltage = 2778 * 1000; | |
707 | break; | |
708 | case 61: | |
709 | voltage = 2932 * 1000; | |
710 | break; | |
711 | case 62: | |
712 | voltage = 3241 * 1000; | |
713 | break; | |
714 | default: | |
715 | voltage = (1852000 + (38600 * (index - 1))); | |
716 | } | |
717 | break; | |
718 | case SMPS_OFFSET_EN | SMPS_EXTENDED_EN: | |
719 | switch (index) { | |
720 | case 0: | |
721 | voltage = 0; | |
722 | break; | |
723 | case 58: | |
724 | voltage = 4167 * 1000; | |
725 | break; | |
726 | case 59: | |
727 | voltage = 2315 * 1000; | |
728 | break; | |
729 | case 60: | |
730 | voltage = 2778 * 1000; | |
731 | break; | |
732 | case 61: | |
733 | voltage = 2932 * 1000; | |
734 | break; | |
735 | case 62: | |
736 | voltage = 3241 * 1000; | |
737 | break; | |
738 | default: | |
739 | voltage = (2161000 + (38600 * (index - 1))); | |
740 | } | |
741 | break; | |
742 | } | |
743 | ||
744 | return voltage; | |
745 | } | |
746 | ||
747 | static int | |
748 | twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, | |
749 | unsigned int *selector) | |
750 | { | |
751 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
752 | int vsel = 0; | |
753 | ||
754 | switch (info->flags) { | |
755 | case 0: | |
756 | if (min_uV == 0) | |
757 | vsel = 0; | |
758 | else if ((min_uV >= 600000) && (max_uV <= 1300000)) { | |
759 | vsel = (min_uV - 600000) / 125; | |
760 | if (vsel % 100) | |
761 | vsel += 100; | |
762 | vsel /= 100; | |
763 | vsel++; | |
764 | } | |
765 | /* Values 1..57 for vsel are linear and can be calculated | |
766 | * values 58..62 are non linear. | |
767 | */ | |
768 | else if ((min_uV > 1900000) && (max_uV >= 2100000)) | |
769 | vsel = 62; | |
770 | else if ((min_uV > 1800000) && (max_uV >= 1900000)) | |
771 | vsel = 61; | |
772 | else if ((min_uV > 1500000) && (max_uV >= 1800000)) | |
773 | vsel = 60; | |
774 | else if ((min_uV > 1350000) && (max_uV >= 1500000)) | |
775 | vsel = 59; | |
776 | else if ((min_uV > 1300000) && (max_uV >= 1350000)) | |
777 | vsel = 58; | |
778 | else | |
779 | return -EINVAL; | |
780 | break; | |
781 | case SMPS_OFFSET_EN: | |
782 | if (min_uV == 0) | |
783 | vsel = 0; | |
784 | else if ((min_uV >= 700000) && (max_uV <= 1420000)) { | |
785 | vsel = (min_uV - 700000) / 125; | |
786 | if (vsel % 100) | |
787 | vsel += 100; | |
788 | vsel /= 100; | |
789 | vsel++; | |
790 | } | |
791 | /* Values 1..57 for vsel are linear and can be calculated | |
792 | * values 58..62 are non linear. | |
793 | */ | |
794 | else if ((min_uV > 1900000) && (max_uV >= 2100000)) | |
795 | vsel = 62; | |
796 | else if ((min_uV > 1800000) && (max_uV >= 1900000)) | |
797 | vsel = 61; | |
798 | else if ((min_uV > 1350000) && (max_uV >= 1800000)) | |
799 | vsel = 60; | |
800 | else if ((min_uV > 1350000) && (max_uV >= 1500000)) | |
801 | vsel = 59; | |
802 | else if ((min_uV > 1300000) && (max_uV >= 1350000)) | |
803 | vsel = 58; | |
804 | else | |
805 | return -EINVAL; | |
806 | break; | |
807 | case SMPS_EXTENDED_EN: | |
808 | if (min_uV == 0) | |
809 | vsel = 0; | |
810 | else if ((min_uV >= 1852000) && (max_uV <= 4013600)) { | |
811 | vsel = (min_uV - 1852000) / 386; | |
812 | if (vsel % 100) | |
813 | vsel += 100; | |
814 | vsel /= 100; | |
815 | vsel++; | |
816 | } | |
817 | break; | |
818 | case SMPS_OFFSET_EN|SMPS_EXTENDED_EN: | |
819 | if (min_uV == 0) | |
820 | vsel = 0; | |
821 | else if ((min_uV >= 2161000) && (max_uV <= 4321000)) { | |
822 | vsel = (min_uV - 1852000) / 386; | |
823 | if (vsel % 100) | |
824 | vsel += 100; | |
825 | vsel /= 100; | |
826 | vsel++; | |
827 | } | |
828 | break; | |
829 | } | |
830 | ||
831 | *selector = vsel; | |
832 | ||
833 | return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS, | |
834 | vsel); | |
835 | } | |
836 | ||
837 | static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev) | |
838 | { | |
839 | struct twlreg_info *info = rdev_get_drvdata(rdev); | |
840 | ||
841 | return twlreg_read(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS); | |
842 | } | |
843 | ||
844 | static struct regulator_ops twlsmps_ops = { | |
845 | .list_voltage = twl6030smps_list_voltage, | |
846 | ||
847 | .set_voltage = twl6030smps_set_voltage, | |
848 | .get_voltage_sel = twl6030smps_get_voltage_sel, | |
849 | ||
850 | .enable = twl6030reg_enable, | |
851 | .disable = twl6030reg_disable, | |
852 | .is_enabled = twl6030reg_is_enabled, | |
853 | ||
854 | .set_mode = twl6030reg_set_mode, | |
855 | ||
856 | .get_status = twl6030reg_get_status, | |
857 | }; | |
858 | ||
fa16a5c1 DB |
859 | /*----------------------------------------------------------------------*/ |
860 | ||
045f972f JKS |
861 | #define TWL4030_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ |
862 | remap_conf) \ | |
863 | TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ | |
b2456779 | 864 | remap_conf, TWL4030, twl4030fixed_ops) |
af8b244f A |
865 | #define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ |
866 | TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \ | |
b2456779 | 867 | 0x0, TWL6030, twl6030fixed_ops) |
045f972f | 868 | |
3e3d3be7 | 869 | #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \ |
fa16a5c1 DB |
870 | .base = offset, \ |
871 | .id = num, \ | |
872 | .table_len = ARRAY_SIZE(label##_VSEL_table), \ | |
873 | .table = label##_VSEL_table, \ | |
045f972f JKS |
874 | .delay = turnon_delay, \ |
875 | .remap = remap_conf, \ | |
fa16a5c1 DB |
876 | .desc = { \ |
877 | .name = #label, \ | |
3e3d3be7 | 878 | .id = TWL4030_REG_##label, \ |
66b659e6 | 879 | .n_voltages = ARRAY_SIZE(label##_VSEL_table), \ |
3e3d3be7 RN |
880 | .ops = &twl4030ldo_ops, \ |
881 | .type = REGULATOR_VOLTAGE, \ | |
882 | .owner = THIS_MODULE, \ | |
883 | }, \ | |
884 | } | |
885 | ||
ba305e31 TK |
886 | #define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \ |
887 | { \ | |
888 | .base = offset, \ | |
889 | .id = num, \ | |
890 | .delay = turnon_delay, \ | |
891 | .remap = remap_conf, \ | |
892 | .desc = { \ | |
893 | .name = #label, \ | |
894 | .id = TWL4030_REG_##label, \ | |
895 | .ops = &twl4030smps_ops, \ | |
896 | .type = REGULATOR_VOLTAGE, \ | |
897 | .owner = THIS_MODULE, \ | |
898 | }, \ | |
899 | } | |
900 | ||
af8b244f | 901 | #define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ |
3e3d3be7 | 902 | .base = offset, \ |
3e3d3be7 RN |
903 | .min_mV = min_mVolts, \ |
904 | .max_mV = max_mVolts, \ | |
3e3d3be7 RN |
905 | .desc = { \ |
906 | .name = #label, \ | |
907 | .id = TWL6030_REG_##label, \ | |
7736f11d | 908 | .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \ |
3e3d3be7 | 909 | .ops = &twl6030ldo_ops, \ |
fa16a5c1 DB |
910 | .type = REGULATOR_VOLTAGE, \ |
911 | .owner = THIS_MODULE, \ | |
912 | }, \ | |
913 | } | |
914 | ||
af8b244f | 915 | #define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ |
4d94aee5 | 916 | .base = offset, \ |
4d94aee5 GG |
917 | .min_mV = min_mVolts, \ |
918 | .max_mV = max_mVolts, \ | |
919 | .desc = { \ | |
920 | .name = #label, \ | |
921 | .id = TWL6025_REG_##label, \ | |
922 | .n_voltages = ((max_mVolts - min_mVolts)/100) + 1, \ | |
923 | .ops = &twl6030ldo_ops, \ | |
924 | .type = REGULATOR_VOLTAGE, \ | |
925 | .owner = THIS_MODULE, \ | |
926 | }, \ | |
927 | } | |
3e3d3be7 | 928 | |
045f972f | 929 | #define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \ |
b2456779 | 930 | family, operations) { \ |
fa16a5c1 DB |
931 | .base = offset, \ |
932 | .id = num, \ | |
933 | .min_mV = mVolts, \ | |
045f972f JKS |
934 | .delay = turnon_delay, \ |
935 | .remap = remap_conf, \ | |
fa16a5c1 DB |
936 | .desc = { \ |
937 | .name = #label, \ | |
c4aa6f31 | 938 | .id = family##_REG_##label, \ |
66b659e6 | 939 | .n_voltages = 1, \ |
b2456779 | 940 | .ops = &operations, \ |
fa16a5c1 DB |
941 | .type = REGULATOR_VOLTAGE, \ |
942 | .owner = THIS_MODULE, \ | |
943 | }, \ | |
944 | } | |
945 | ||
af8b244f | 946 | #define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \ |
8e6de4a3 | 947 | .base = offset, \ |
8e6de4a3 | 948 | .delay = turnon_delay, \ |
8e6de4a3 B |
949 | .desc = { \ |
950 | .name = #label, \ | |
951 | .id = TWL6030_REG_##label, \ | |
952 | .ops = &twl6030_fixed_resource, \ | |
953 | .type = REGULATOR_VOLTAGE, \ | |
954 | .owner = THIS_MODULE, \ | |
955 | }, \ | |
956 | } | |
957 | ||
af8b244f | 958 | #define TWL6025_ADJUSTABLE_SMPS(label, offset) { \ |
4d94aee5 | 959 | .base = offset, \ |
4d94aee5 GG |
960 | .min_mV = 600, \ |
961 | .max_mV = 2100, \ | |
962 | .desc = { \ | |
963 | .name = #label, \ | |
964 | .id = TWL6025_REG_##label, \ | |
965 | .n_voltages = 63, \ | |
966 | .ops = &twlsmps_ops, \ | |
967 | .type = REGULATOR_VOLTAGE, \ | |
968 | .owner = THIS_MODULE, \ | |
969 | }, \ | |
970 | } | |
971 | ||
fa16a5c1 DB |
972 | /* |
973 | * We list regulators here if systems need some level of | |
974 | * software control over them after boot. | |
975 | */ | |
c4aa6f31 | 976 | static struct twlreg_info twl_regs[] = { |
045f972f JKS |
977 | TWL4030_ADJUSTABLE_LDO(VAUX1, 0x17, 1, 100, 0x08), |
978 | TWL4030_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2, 100, 0x08), | |
979 | TWL4030_ADJUSTABLE_LDO(VAUX2, 0x1b, 2, 100, 0x08), | |
980 | TWL4030_ADJUSTABLE_LDO(VAUX3, 0x1f, 3, 100, 0x08), | |
981 | TWL4030_ADJUSTABLE_LDO(VAUX4, 0x23, 4, 100, 0x08), | |
982 | TWL4030_ADJUSTABLE_LDO(VMMC1, 0x27, 5, 100, 0x08), | |
983 | TWL4030_ADJUSTABLE_LDO(VMMC2, 0x2b, 6, 100, 0x08), | |
984 | TWL4030_ADJUSTABLE_LDO(VPLL1, 0x2f, 7, 100, 0x00), | |
985 | TWL4030_ADJUSTABLE_LDO(VPLL2, 0x33, 8, 100, 0x08), | |
986 | TWL4030_ADJUSTABLE_LDO(VSIM, 0x37, 9, 100, 0x00), | |
987 | TWL4030_ADJUSTABLE_LDO(VDAC, 0x3b, 10, 100, 0x08), | |
988 | TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08), | |
989 | TWL4030_ADJUSTABLE_LDO(VINTANA2, 0x43, 12, 100, 0x08), | |
990 | TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08), | |
991 | TWL4030_ADJUSTABLE_LDO(VIO, 0x4b, 14, 1000, 0x08), | |
ba305e31 TK |
992 | TWL4030_ADJUSTABLE_SMPS(VDD1, 0x55, 15, 1000, 0x08), |
993 | TWL4030_ADJUSTABLE_SMPS(VDD2, 0x63, 16, 1000, 0x08), | |
045f972f JKS |
994 | TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08), |
995 | TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08), | |
996 | TWL4030_FIXED_LDO(VUSB3V1, 0x77, 3100, 19, 150, 0x08), | |
fa16a5c1 | 997 | /* VUSBCP is managed *only* by the USB subchip */ |
441a4505 RN |
998 | |
999 | /* 6030 REG with base as PMC Slave Misc : 0x0030 */ | |
045f972f JKS |
1000 | /* Turnon-delay and remap configuration values for 6030 are not |
1001 | verified since the specification is not public */ | |
af8b244f A |
1002 | TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300), |
1003 | TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300), | |
1004 | TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300), | |
1005 | TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300), | |
1006 | TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300), | |
1007 | TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300), | |
1008 | TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0), | |
1009 | TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0), | |
1010 | TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0), | |
1011 | TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0), | |
1012 | TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0), | |
4d94aee5 GG |
1013 | |
1014 | /* 6025 are renamed compared to 6030 versions */ | |
af8b244f A |
1015 | TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300), |
1016 | TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300), | |
1017 | TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300), | |
1018 | TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300), | |
1019 | TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300), | |
1020 | TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300), | |
1021 | TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300), | |
1022 | TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300), | |
1023 | TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300), | |
1024 | ||
1025 | TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34), | |
1026 | TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10), | |
1027 | TWL6025_ADJUSTABLE_SMPS(VIO, 0x16), | |
fa16a5c1 DB |
1028 | }; |
1029 | ||
4d94aee5 GG |
1030 | static u8 twl_get_smps_offset(void) |
1031 | { | |
1032 | u8 value; | |
1033 | ||
1034 | twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, | |
1035 | TWL6030_SMPS_OFFSET); | |
1036 | return value; | |
1037 | } | |
1038 | ||
1039 | static u8 twl_get_smps_mult(void) | |
1040 | { | |
1041 | u8 value; | |
1042 | ||
1043 | twl_i2c_read_u8(TWL_MODULE_PM_RECEIVER, &value, | |
1044 | TWL6030_SMPS_MULT); | |
1045 | return value; | |
1046 | } | |
1047 | ||
24c29020 | 1048 | static int __devinit twlreg_probe(struct platform_device *pdev) |
fa16a5c1 DB |
1049 | { |
1050 | int i; | |
1051 | struct twlreg_info *info; | |
1052 | struct regulator_init_data *initdata; | |
1053 | struct regulation_constraints *c; | |
1054 | struct regulator_dev *rdev; | |
fa16a5c1 | 1055 | |
c4aa6f31 RN |
1056 | for (i = 0, info = NULL; i < ARRAY_SIZE(twl_regs); i++) { |
1057 | if (twl_regs[i].desc.id != pdev->id) | |
fa16a5c1 | 1058 | continue; |
c4aa6f31 | 1059 | info = twl_regs + i; |
fa16a5c1 DB |
1060 | break; |
1061 | } | |
1062 | if (!info) | |
1063 | return -ENODEV; | |
1064 | ||
1065 | initdata = pdev->dev.platform_data; | |
1066 | if (!initdata) | |
1067 | return -EINVAL; | |
1068 | ||
4d94aee5 GG |
1069 | /* copy the features into regulator data */ |
1070 | info->features = (unsigned long)initdata->driver_data; | |
1071 | ||
fa16a5c1 DB |
1072 | /* Constrain board-specific capabilities according to what |
1073 | * this driver and the chip itself can actually do. | |
1074 | */ | |
1075 | c = &initdata->constraints; | |
fa16a5c1 DB |
1076 | c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; |
1077 | c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE | |
1078 | | REGULATOR_CHANGE_MODE | |
1079 | | REGULATOR_CHANGE_STATUS; | |
205e5cd3 JKS |
1080 | switch (pdev->id) { |
1081 | case TWL4030_REG_VIO: | |
1082 | case TWL4030_REG_VDD1: | |
1083 | case TWL4030_REG_VDD2: | |
1084 | case TWL4030_REG_VPLL1: | |
1085 | case TWL4030_REG_VINTANA1: | |
1086 | case TWL4030_REG_VINTANA2: | |
1087 | case TWL4030_REG_VINTDIG: | |
1088 | c->always_on = true; | |
1089 | break; | |
1090 | default: | |
1091 | break; | |
1092 | } | |
fa16a5c1 | 1093 | |
4d94aee5 GG |
1094 | switch (pdev->id) { |
1095 | case TWL6025_REG_SMPS3: | |
1096 | if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS3) | |
1097 | info->flags |= SMPS_EXTENDED_EN; | |
1098 | if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS3) | |
1099 | info->flags |= SMPS_OFFSET_EN; | |
1100 | break; | |
1101 | case TWL6025_REG_SMPS4: | |
1102 | if (twl_get_smps_mult() & SMPS_MULTOFFSET_SMPS4) | |
1103 | info->flags |= SMPS_EXTENDED_EN; | |
1104 | if (twl_get_smps_offset() & SMPS_MULTOFFSET_SMPS4) | |
1105 | info->flags |= SMPS_OFFSET_EN; | |
1106 | break; | |
1107 | case TWL6025_REG_VIO: | |
1108 | if (twl_get_smps_mult() & SMPS_MULTOFFSET_VIO) | |
1109 | info->flags |= SMPS_EXTENDED_EN; | |
1110 | if (twl_get_smps_offset() & SMPS_MULTOFFSET_VIO) | |
1111 | info->flags |= SMPS_OFFSET_EN; | |
1112 | break; | |
1113 | } | |
1114 | ||
2c043bcb | 1115 | rdev = regulator_register(&info->desc, &pdev->dev, initdata, info, NULL); |
fa16a5c1 DB |
1116 | if (IS_ERR(rdev)) { |
1117 | dev_err(&pdev->dev, "can't register %s, %ld\n", | |
1118 | info->desc.name, PTR_ERR(rdev)); | |
1119 | return PTR_ERR(rdev); | |
1120 | } | |
1121 | platform_set_drvdata(pdev, rdev); | |
1122 | ||
776dc923 SH |
1123 | if (twl_class_is_4030()) |
1124 | twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_REMAP, | |
30010fa5 JKS |
1125 | info->remap); |
1126 | ||
fa16a5c1 DB |
1127 | /* NOTE: many regulators support short-circuit IRQs (presentable |
1128 | * as REGULATOR_OVER_CURRENT notifications?) configured via: | |
1129 | * - SC_CONFIG | |
1130 | * - SC_DETECT1 (vintana2, vmmc1/2, vaux1/2/3/4) | |
1131 | * - SC_DETECT2 (vusb, vdac, vio, vdd1/2, vpll2) | |
1132 | * - IT_CONFIG | |
1133 | */ | |
1134 | ||
1135 | return 0; | |
1136 | } | |
1137 | ||
c4aa6f31 | 1138 | static int __devexit twlreg_remove(struct platform_device *pdev) |
fa16a5c1 DB |
1139 | { |
1140 | regulator_unregister(platform_get_drvdata(pdev)); | |
1141 | return 0; | |
1142 | } | |
1143 | ||
c4aa6f31 | 1144 | MODULE_ALIAS("platform:twl_reg"); |
fa16a5c1 | 1145 | |
c4aa6f31 RN |
1146 | static struct platform_driver twlreg_driver = { |
1147 | .probe = twlreg_probe, | |
1148 | .remove = __devexit_p(twlreg_remove), | |
fa16a5c1 | 1149 | /* NOTE: short name, to work around driver model truncation of |
c4aa6f31 | 1150 | * "twl_regulator.12" (and friends) to "twl_regulator.1". |
fa16a5c1 | 1151 | */ |
c4aa6f31 | 1152 | .driver.name = "twl_reg", |
fa16a5c1 DB |
1153 | .driver.owner = THIS_MODULE, |
1154 | }; | |
1155 | ||
c4aa6f31 | 1156 | static int __init twlreg_init(void) |
fa16a5c1 | 1157 | { |
c4aa6f31 | 1158 | return platform_driver_register(&twlreg_driver); |
fa16a5c1 | 1159 | } |
c4aa6f31 | 1160 | subsys_initcall(twlreg_init); |
fa16a5c1 | 1161 | |
c4aa6f31 | 1162 | static void __exit twlreg_exit(void) |
fa16a5c1 | 1163 | { |
c4aa6f31 | 1164 | platform_driver_unregister(&twlreg_driver); |
fa16a5c1 | 1165 | } |
c4aa6f31 | 1166 | module_exit(twlreg_exit) |
fa16a5c1 | 1167 | |
c4aa6f31 | 1168 | MODULE_DESCRIPTION("TWL regulator driver"); |
fa16a5c1 | 1169 | MODULE_LICENSE("GPL"); |