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