aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd
diff options
context:
space:
mode:
authorAmit Kucheria <amit.kucheria@verdurent.com>2009-08-31 12:32:18 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2009-09-17 03:47:22 -0400
commitebf0bd366ed8161e6fbc919705d878ccbfd51624 (patch)
treee6425ad580bdc6a4bc34163dcde3f08b6deb7474 /drivers/mfd
parent12992dd89c84839167f97aae540f2ec889daf782 (diff)
mfd: Add support for TWL4030/5030 dynamic power switching
The TWL4030/5030 family of multifunction devices allows board-specific control of the the various regulators, clock and reset lines through 'scripts' that are loaded into its memory. This allows for Dynamic Power Switching (DPS). Implement board-independent core support for DPS that is then used by board-specific code to load custom DPS scripts. Signed-off-by: Amit Kucheria <amit.kucheria@verdurent.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig13
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/twl4030-core.c10
-rw-r--r--drivers/mfd/twl4030-power.c466
4 files changed, 490 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 4fd4f913c36b..570be139f9df 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -108,6 +108,19 @@ config TWL4030_CORE
108 high speed USB OTG transceiver, an audio codec (on most 108 high speed USB OTG transceiver, an audio codec (on most
109 versions) and many other features. 109 versions) and many other features.
110 110
111config TWL4030_POWER
112 bool "Support power resources on TWL4030 family chips"
113 depends on TWL4030_CORE && ARM
114 help
115 Say yes here if you want to use the power resources on the
116 TWL4030 family chips. Most of these resources are regulators,
117 which have a separate driver; some are control signals, such
118 as clock request handshaking.
119
120 This driver uses board-specific data to initialize the resources
121 and load scripts controling which resources are switched off/on
122 or reset when a sleep, wakeup or warm reset event occurs.
123
111config MFD_TMIO 124config MFD_TMIO
112 bool 125 bool
113 default n 126 default n
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 6a5d8cb545fc..f3b277b90d40 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_TPS65010) += tps65010.o
25obj-$(CONFIG_MENELAUS) += menelaus.o 25obj-$(CONFIG_MENELAUS) += menelaus.o
26 26
27obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o 27obj-$(CONFIG_TWL4030_CORE) += twl4030-core.o twl4030-irq.o
28obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o
28 29
29obj-$(CONFIG_MFD_MC13783) += mc13783-core.o 30obj-$(CONFIG_MFD_MC13783) += mc13783-core.o
30 31
diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c
index 1fd2620819d0..e424cf6d8e9e 100644
--- a/drivers/mfd/twl4030-core.c
+++ b/drivers/mfd/twl4030-core.c
@@ -89,6 +89,12 @@
89#define twl_has_madc() false 89#define twl_has_madc() false
90#endif 90#endif
91 91
92#ifdef CONFIG_TWL4030_POWER
93#define twl_has_power() true
94#else
95#define twl_has_power() false
96#endif
97
92#if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE) 98#if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
93#define twl_has_rtc() true 99#define twl_has_rtc() true
94#else 100#else
@@ -801,6 +807,10 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
801 /* setup clock framework */ 807 /* setup clock framework */
802 clocks_init(&client->dev); 808 clocks_init(&client->dev);
803 809
810 /* load power event scripts */
811 if (twl_has_power() && pdata->power)
812 twl4030_power_init(pdata->power);
813
804 /* Maybe init the T2 Interrupt subsystem */ 814 /* Maybe init the T2 Interrupt subsystem */
805 if (client->irq 815 if (client->irq
806 && pdata->irq_base 816 && pdata->irq_base
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
new file mode 100644
index 000000000000..e7688b041264
--- /dev/null
+++ b/drivers/mfd/twl4030-power.c
@@ -0,0 +1,466 @@
1/*
2 * linux/drivers/i2c/chips/twl4030-power.c
3 *
4 * Handle TWL4030 Power initialization
5 *
6 * Copyright (C) 2008 Nokia Corporation
7 * Copyright (C) 2006 Texas Instruments, Inc
8 *
9 * Written by Kalle Jokiniemi
10 * Peter De Schrijver <peter.de-schrijver@nokia.com>
11 * Several fixes by Amit Kucheria <amit.kucheria@verdurent.com>
12 *
13 * This file is subject to the terms and conditions of the GNU General
14 * Public License. See the file "COPYING" in the main directory of this
15 * archive for more details.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 */
26
27#include <linux/module.h>
28#include <linux/pm.h>
29#include <linux/i2c/twl4030.h>
30#include <linux/platform_device.h>
31
32#include <asm/mach-types.h>
33
34static u8 twl4030_start_script_address = 0x2b;
35
36#define PWR_P1_SW_EVENTS 0x10
37#define PWR_DEVOFF (1<<0)
38
39#define PHY_TO_OFF_PM_MASTER(p) (p - 0x36)
40#define PHY_TO_OFF_PM_RECEIVER(p) (p - 0x5b)
41
42/* resource - hfclk */
43#define R_HFCLKOUT_DEV_GRP PHY_TO_OFF_PM_RECEIVER(0xe6)
44
45/* PM events */
46#define R_P1_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x46)
47#define R_P2_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x47)
48#define R_P3_SW_EVENTS PHY_TO_OFF_PM_MASTER(0x48)
49#define R_CFG_P1_TRANSITION PHY_TO_OFF_PM_MASTER(0x36)
50#define R_CFG_P2_TRANSITION PHY_TO_OFF_PM_MASTER(0x37)
51#define R_CFG_P3_TRANSITION PHY_TO_OFF_PM_MASTER(0x38)
52
53#define LVL_WAKEUP 0x08
54
55#define ENABLE_WARMRESET (1<<4)
56
57#define END_OF_SCRIPT 0x3f
58
59#define R_SEQ_ADD_A2S PHY_TO_OFF_PM_MASTER(0x55)
60#define R_SEQ_ADD_S2A12 PHY_TO_OFF_PM_MASTER(0x56)
61#define R_SEQ_ADD_S2A3 PHY_TO_OFF_PM_MASTER(0x57)
62#define R_SEQ_ADD_WARM PHY_TO_OFF_PM_MASTER(0x58)
63#define R_MEMORY_ADDRESS PHY_TO_OFF_PM_MASTER(0x59)
64#define R_MEMORY_DATA PHY_TO_OFF_PM_MASTER(0x5a)
65
66#define R_PROTECT_KEY 0x0E
67#define KEY_1 0xC0
68#define KEY_2 0x0C
69
70/* resource configuration registers */
71
72#define DEVGROUP_OFFSET 0
73#define TYPE_OFFSET 1
74
75/* Bit positions */
76#define DEVGROUP_SHIFT 5
77#define DEVGROUP_MASK (7 << DEVGROUP_SHIFT)
78#define TYPE_SHIFT 0
79#define TYPE_MASK (7 << TYPE_SHIFT)
80#define TYPE2_SHIFT 3
81#define TYPE2_MASK (3 << TYPE2_SHIFT)
82
83static u8 res_config_addrs[] = {
84 [RES_VAUX1] = 0x17,
85 [RES_VAUX2] = 0x1b,
86 [RES_VAUX3] = 0x1f,
87 [RES_VAUX4] = 0x23,
88 [RES_VMMC1] = 0x27,
89 [RES_VMMC2] = 0x2b,
90 [RES_VPLL1] = 0x2f,
91 [RES_VPLL2] = 0x33,
92 [RES_VSIM] = 0x37,
93 [RES_VDAC] = 0x3b,
94 [RES_VINTANA1] = 0x3f,
95 [RES_VINTANA2] = 0x43,
96 [RES_VINTDIG] = 0x47,
97 [RES_VIO] = 0x4b,
98 [RES_VDD1] = 0x55,
99 [RES_VDD2] = 0x63,
100 [RES_VUSB_1V5] = 0x71,
101 [RES_VUSB_1V8] = 0x74,
102 [RES_VUSB_3V1] = 0x77,
103 [RES_VUSBCP] = 0x7a,
104 [RES_REGEN] = 0x7f,
105 [RES_NRES_PWRON] = 0x82,
106 [RES_CLKEN] = 0x85,
107 [RES_SYSEN] = 0x88,
108 [RES_HFCLKOUT] = 0x8b,
109 [RES_32KCLKOUT] = 0x8e,
110 [RES_RESET] = 0x91,
111 [RES_Main_Ref] = 0x94,
112};
113
114static int __init twl4030_write_script_byte(u8 address, u8 byte)
115{
116 int err;
117
118 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
119 R_MEMORY_ADDRESS);
120 if (err)
121 goto out;
122 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
123 R_MEMORY_DATA);
124out:
125 return err;
126}
127
128static int __init twl4030_write_script_ins(u8 address, u16 pmb_message,
129 u8 delay, u8 next)
130{
131 int err;
132
133 address *= 4;
134 err = twl4030_write_script_byte(address++, pmb_message >> 8);
135 if (err)
136 goto out;
137 err = twl4030_write_script_byte(address++, pmb_message & 0xff);
138 if (err)
139 goto out;
140 err = twl4030_write_script_byte(address++, delay);
141 if (err)
142 goto out;
143 err = twl4030_write_script_byte(address++, next);
144out:
145 return err;
146}
147
148static int __init twl4030_write_script(u8 address, struct twl4030_ins *script,
149 int len)
150{
151 int err;
152
153 for (; len; len--, address++, script++) {
154 if (len == 1) {
155 err = twl4030_write_script_ins(address,
156 script->pmb_message,
157 script->delay,
158 END_OF_SCRIPT);
159 if (err)
160 break;
161 } else {
162 err = twl4030_write_script_ins(address,
163 script->pmb_message,
164 script->delay,
165 address + 1);
166 if (err)
167 break;
168 }
169 }
170 return err;
171}
172
173static int __init twl4030_config_wakeup3_sequence(u8 address)
174{
175 int err;
176 u8 data;
177
178 /* Set SLEEP to ACTIVE SEQ address for P3 */
179 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
180 R_SEQ_ADD_S2A3);
181 if (err)
182 goto out;
183
184 /* P3 LVL_WAKEUP should be on LEVEL */
185 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
186 R_P3_SW_EVENTS);
187 if (err)
188 goto out;
189 data |= LVL_WAKEUP;
190 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
191 R_P3_SW_EVENTS);
192out:
193 if (err)
194 pr_err("TWL4030 wakeup sequence for P3 config error\n");
195 return err;
196}
197
198static int __init twl4030_config_wakeup12_sequence(u8 address)
199{
200 int err = 0;
201 u8 data;
202
203 /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
204 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
205 R_SEQ_ADD_S2A12);
206 if (err)
207 goto out;
208
209 /* P1/P2 LVL_WAKEUP should be on LEVEL */
210 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
211 R_P1_SW_EVENTS);
212 if (err)
213 goto out;
214
215 data |= LVL_WAKEUP;
216 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
217 R_P1_SW_EVENTS);
218 if (err)
219 goto out;
220
221 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
222 R_P2_SW_EVENTS);
223 if (err)
224 goto out;
225
226 data |= LVL_WAKEUP;
227 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data,
228 R_P2_SW_EVENTS);
229 if (err)
230 goto out;
231
232 if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
233 /* Disabling AC charger effect on sleep-active transitions */
234 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
235 R_CFG_P1_TRANSITION);
236 if (err)
237 goto out;
238 data &= ~(1<<1);
239 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
240 R_CFG_P1_TRANSITION);
241 if (err)
242 goto out;
243 }
244
245out:
246 if (err)
247 pr_err("TWL4030 wakeup sequence for P1 and P2" \
248 "config error\n");
249 return err;
250}
251
252static int __init twl4030_config_sleep_sequence(u8 address)
253{
254 int err;
255
256 /* Set ACTIVE to SLEEP SEQ address in T2 memory*/
257 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
258 R_SEQ_ADD_A2S);
259
260 if (err)
261 pr_err("TWL4030 sleep sequence config error\n");
262
263 return err;
264}
265
266static int __init twl4030_config_warmreset_sequence(u8 address)
267{
268 int err;
269 u8 rd_data;
270
271 /* Set WARM RESET SEQ address for P1 */
272 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
273 R_SEQ_ADD_WARM);
274 if (err)
275 goto out;
276
277 /* P1/P2/P3 enable WARMRESET */
278 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
279 R_P1_SW_EVENTS);
280 if (err)
281 goto out;
282
283 rd_data |= ENABLE_WARMRESET;
284 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
285 R_P1_SW_EVENTS);
286 if (err)
287 goto out;
288
289 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
290 R_P2_SW_EVENTS);
291 if (err)
292 goto out;
293
294 rd_data |= ENABLE_WARMRESET;
295 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
296 R_P2_SW_EVENTS);
297 if (err)
298 goto out;
299
300 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
301 R_P3_SW_EVENTS);
302 if (err)
303 goto out;
304
305 rd_data |= ENABLE_WARMRESET;
306 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
307 R_P3_SW_EVENTS);
308out:
309 if (err)
310 pr_err("TWL4030 warmreset seq config error\n");
311 return err;
312}
313
314static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig)
315{
316 int rconfig_addr;
317 int err;
318 u8 type;
319 u8 grp;
320
321 if (rconfig->resource > TOTAL_RESOURCES) {
322 pr_err("TWL4030 Resource %d does not exist\n",
323 rconfig->resource);
324 return -EINVAL;
325 }
326
327 rconfig_addr = res_config_addrs[rconfig->resource];
328
329 /* Set resource group */
330 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp,
331 rconfig_addr + DEVGROUP_OFFSET);
332 if (err) {
333 pr_err("TWL4030 Resource %d group could not be read\n",
334 rconfig->resource);
335 return err;
336 }
337
338 if (rconfig->devgroup >= 0) {
339 grp &= ~DEVGROUP_MASK;
340 grp |= rconfig->devgroup << DEVGROUP_SHIFT;
341 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
342 grp, rconfig_addr + DEVGROUP_OFFSET);
343 if (err < 0) {
344 pr_err("TWL4030 failed to program devgroup\n");
345 return err;
346 }
347 }
348
349 /* Set resource types */
350 err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type,
351 rconfig_addr + TYPE_OFFSET);
352 if (err < 0) {
353 pr_err("TWL4030 Resource %d type could not be read\n",
354 rconfig->resource);
355 return err;
356 }
357
358 if (rconfig->type >= 0) {
359 type &= ~TYPE_MASK;
360 type |= rconfig->type << TYPE_SHIFT;
361 }
362
363 if (rconfig->type2 >= 0) {
364 type &= ~TYPE2_MASK;
365 type |= rconfig->type2 << TYPE2_SHIFT;
366 }
367
368 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
369 type, rconfig_addr + TYPE_OFFSET);
370 if (err < 0) {
371 pr_err("TWL4030 failed to program resource type\n");
372 return err;
373 }
374
375 return 0;
376}
377
378static int __init load_twl4030_script(struct twl4030_script *tscript,
379 u8 address)
380{
381 int err;
382
383 /* Make sure the script isn't going beyond last valid address (0x3f) */
384 if ((address + tscript->size) > END_OF_SCRIPT) {
385 pr_err("TWL4030 scripts too big error\n");
386 return -EINVAL;
387 }
388
389 err = twl4030_write_script(address, tscript->script, tscript->size);
390 if (err)
391 goto out;
392
393 if (tscript->flags & TWL4030_WRST_SCRIPT) {
394 err = twl4030_config_warmreset_sequence(address);
395 if (err)
396 goto out;
397 }
398 if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) {
399 err = twl4030_config_wakeup12_sequence(address);
400 if (err)
401 goto out;
402 }
403 if (tscript->flags & TWL4030_WAKEUP3_SCRIPT) {
404 err = twl4030_config_wakeup3_sequence(address);
405 if (err)
406 goto out;
407 }
408 if (tscript->flags & TWL4030_SLEEP_SCRIPT)
409 err = twl4030_config_sleep_sequence(address);
410out:
411 return err;
412}
413
414void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts)
415{
416 int err = 0;
417 int i;
418 struct twl4030_resconfig *resconfig;
419 u8 address = twl4030_start_script_address;
420
421 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_1,
422 R_PROTECT_KEY);
423 if (err)
424 goto unlock;
425
426 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_2,
427 R_PROTECT_KEY);
428 if (err)
429 goto unlock;
430
431 for (i = 0; i < twl4030_scripts->num; i++) {
432 err = load_twl4030_script(twl4030_scripts->scripts[i], address);
433 if (err)
434 goto load;
435 address += twl4030_scripts->scripts[i]->size;
436 }
437
438 resconfig = twl4030_scripts->resource_config;
439 if (resconfig) {
440 while (resconfig->resource) {
441 err = twl4030_configure_resource(resconfig);
442 if (err)
443 goto resource;
444 resconfig++;
445
446 }
447 }
448
449 err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
450 if (err)
451 pr_err("TWL4030 Unable to relock registers\n");
452 return;
453
454unlock:
455 if (err)
456 pr_err("TWL4030 Unable to unlock registers\n");
457 return;
458load:
459 if (err)
460 pr_err("TWL4030 failed to load scripts\n");
461 return;
462resource:
463 if (err)
464 pr_err("TWL4030 failed to configure resource\n");
465 return;
466}