Commit | Line | Data |
---|---|---|
ebf0bd36 AK |
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> | |
b07682b6 | 29 | #include <linux/i2c/twl.h> |
ebf0bd36 AK |
30 | #include <linux/platform_device.h> |
31 | ||
32 | #include <asm/mach-types.h> | |
33 | ||
34 | static 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 | ||
890463f0 AK |
66 | /* resource configuration registers |
67 | <RESOURCE>_DEV_GRP at address 'n+0' | |
68 | <RESOURCE>_TYPE at address 'n+1' | |
69 | <RESOURCE>_REMAP at address 'n+2' | |
70 | <RESOURCE>_DEDICATED at address 'n+3' | |
71 | */ | |
e97d1546 | 72 | #define DEV_GRP_OFFSET 0 |
ebf0bd36 | 73 | #define TYPE_OFFSET 1 |
b4ead61e AK |
74 | #define REMAP_OFFSET 2 |
75 | #define DEDICATED_OFFSET 3 | |
ebf0bd36 | 76 | |
e97d1546 | 77 | /* Bit positions in the registers */ |
890463f0 AK |
78 | |
79 | /* <RESOURCE>_DEV_GRP */ | |
e97d1546 AK |
80 | #define DEV_GRP_SHIFT 5 |
81 | #define DEV_GRP_MASK (7 << DEV_GRP_SHIFT) | |
890463f0 AK |
82 | |
83 | /* <RESOURCE>_TYPE */ | |
ebf0bd36 AK |
84 | #define TYPE_SHIFT 0 |
85 | #define TYPE_MASK (7 << TYPE_SHIFT) | |
86 | #define TYPE2_SHIFT 3 | |
87 | #define TYPE2_MASK (3 << TYPE2_SHIFT) | |
88 | ||
b4ead61e AK |
89 | /* <RESOURCE>_REMAP */ |
90 | #define SLEEP_STATE_SHIFT 0 | |
91 | #define SLEEP_STATE_MASK (0xf << SLEEP_STATE_SHIFT) | |
92 | #define OFF_STATE_SHIFT 4 | |
93 | #define OFF_STATE_MASK (0xf << OFF_STATE_SHIFT) | |
94 | ||
ebf0bd36 AK |
95 | static u8 res_config_addrs[] = { |
96 | [RES_VAUX1] = 0x17, | |
97 | [RES_VAUX2] = 0x1b, | |
98 | [RES_VAUX3] = 0x1f, | |
99 | [RES_VAUX4] = 0x23, | |
100 | [RES_VMMC1] = 0x27, | |
101 | [RES_VMMC2] = 0x2b, | |
102 | [RES_VPLL1] = 0x2f, | |
103 | [RES_VPLL2] = 0x33, | |
104 | [RES_VSIM] = 0x37, | |
105 | [RES_VDAC] = 0x3b, | |
106 | [RES_VINTANA1] = 0x3f, | |
107 | [RES_VINTANA2] = 0x43, | |
108 | [RES_VINTDIG] = 0x47, | |
109 | [RES_VIO] = 0x4b, | |
110 | [RES_VDD1] = 0x55, | |
111 | [RES_VDD2] = 0x63, | |
112 | [RES_VUSB_1V5] = 0x71, | |
113 | [RES_VUSB_1V8] = 0x74, | |
114 | [RES_VUSB_3V1] = 0x77, | |
115 | [RES_VUSBCP] = 0x7a, | |
116 | [RES_REGEN] = 0x7f, | |
117 | [RES_NRES_PWRON] = 0x82, | |
118 | [RES_CLKEN] = 0x85, | |
119 | [RES_SYSEN] = 0x88, | |
120 | [RES_HFCLKOUT] = 0x8b, | |
121 | [RES_32KCLKOUT] = 0x8e, | |
122 | [RES_RESET] = 0x91, | |
d7ac829f | 123 | [RES_MAIN_REF] = 0x94, |
ebf0bd36 AK |
124 | }; |
125 | ||
126 | static int __init twl4030_write_script_byte(u8 address, u8 byte) | |
127 | { | |
128 | int err; | |
129 | ||
fc7b92fc | 130 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, |
ebf0bd36 AK |
131 | R_MEMORY_ADDRESS); |
132 | if (err) | |
133 | goto out; | |
fc7b92fc | 134 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte, |
ebf0bd36 AK |
135 | R_MEMORY_DATA); |
136 | out: | |
137 | return err; | |
138 | } | |
139 | ||
140 | static int __init twl4030_write_script_ins(u8 address, u16 pmb_message, | |
141 | u8 delay, u8 next) | |
142 | { | |
143 | int err; | |
144 | ||
145 | address *= 4; | |
146 | err = twl4030_write_script_byte(address++, pmb_message >> 8); | |
147 | if (err) | |
148 | goto out; | |
149 | err = twl4030_write_script_byte(address++, pmb_message & 0xff); | |
150 | if (err) | |
151 | goto out; | |
152 | err = twl4030_write_script_byte(address++, delay); | |
153 | if (err) | |
154 | goto out; | |
155 | err = twl4030_write_script_byte(address++, next); | |
156 | out: | |
157 | return err; | |
158 | } | |
159 | ||
160 | static int __init twl4030_write_script(u8 address, struct twl4030_ins *script, | |
161 | int len) | |
162 | { | |
163 | int err; | |
164 | ||
165 | for (; len; len--, address++, script++) { | |
166 | if (len == 1) { | |
167 | err = twl4030_write_script_ins(address, | |
168 | script->pmb_message, | |
169 | script->delay, | |
170 | END_OF_SCRIPT); | |
171 | if (err) | |
172 | break; | |
173 | } else { | |
174 | err = twl4030_write_script_ins(address, | |
175 | script->pmb_message, | |
176 | script->delay, | |
177 | address + 1); | |
178 | if (err) | |
179 | break; | |
180 | } | |
181 | } | |
182 | return err; | |
183 | } | |
184 | ||
185 | static int __init twl4030_config_wakeup3_sequence(u8 address) | |
186 | { | |
187 | int err; | |
188 | u8 data; | |
189 | ||
190 | /* Set SLEEP to ACTIVE SEQ address for P3 */ | |
fc7b92fc | 191 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, |
ebf0bd36 AK |
192 | R_SEQ_ADD_S2A3); |
193 | if (err) | |
194 | goto out; | |
195 | ||
196 | /* P3 LVL_WAKEUP should be on LEVEL */ | |
fc7b92fc | 197 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, |
ebf0bd36 AK |
198 | R_P3_SW_EVENTS); |
199 | if (err) | |
200 | goto out; | |
201 | data |= LVL_WAKEUP; | |
fc7b92fc | 202 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, |
ebf0bd36 AK |
203 | R_P3_SW_EVENTS); |
204 | out: | |
205 | if (err) | |
206 | pr_err("TWL4030 wakeup sequence for P3 config error\n"); | |
207 | return err; | |
208 | } | |
209 | ||
210 | static int __init twl4030_config_wakeup12_sequence(u8 address) | |
211 | { | |
212 | int err = 0; | |
213 | u8 data; | |
214 | ||
215 | /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */ | |
fc7b92fc | 216 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, |
ebf0bd36 AK |
217 | R_SEQ_ADD_S2A12); |
218 | if (err) | |
219 | goto out; | |
220 | ||
221 | /* P1/P2 LVL_WAKEUP should be on LEVEL */ | |
fc7b92fc | 222 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, |
ebf0bd36 AK |
223 | R_P1_SW_EVENTS); |
224 | if (err) | |
225 | goto out; | |
226 | ||
227 | data |= LVL_WAKEUP; | |
fc7b92fc | 228 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, |
ebf0bd36 AK |
229 | R_P1_SW_EVENTS); |
230 | if (err) | |
231 | goto out; | |
232 | ||
fc7b92fc | 233 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, |
ebf0bd36 AK |
234 | R_P2_SW_EVENTS); |
235 | if (err) | |
236 | goto out; | |
237 | ||
238 | data |= LVL_WAKEUP; | |
fc7b92fc | 239 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data, |
ebf0bd36 AK |
240 | R_P2_SW_EVENTS); |
241 | if (err) | |
242 | goto out; | |
243 | ||
244 | if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) { | |
245 | /* Disabling AC charger effect on sleep-active transitions */ | |
fc7b92fc | 246 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data, |
ebf0bd36 AK |
247 | R_CFG_P1_TRANSITION); |
248 | if (err) | |
249 | goto out; | |
250 | data &= ~(1<<1); | |
fc7b92fc | 251 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data , |
ebf0bd36 AK |
252 | R_CFG_P1_TRANSITION); |
253 | if (err) | |
254 | goto out; | |
255 | } | |
256 | ||
257 | out: | |
258 | if (err) | |
259 | pr_err("TWL4030 wakeup sequence for P1 and P2" \ | |
260 | "config error\n"); | |
261 | return err; | |
262 | } | |
263 | ||
264 | static int __init twl4030_config_sleep_sequence(u8 address) | |
265 | { | |
266 | int err; | |
267 | ||
268 | /* Set ACTIVE to SLEEP SEQ address in T2 memory*/ | |
fc7b92fc | 269 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, |
ebf0bd36 AK |
270 | R_SEQ_ADD_A2S); |
271 | ||
272 | if (err) | |
273 | pr_err("TWL4030 sleep sequence config error\n"); | |
274 | ||
275 | return err; | |
276 | } | |
277 | ||
278 | static int __init twl4030_config_warmreset_sequence(u8 address) | |
279 | { | |
280 | int err; | |
281 | u8 rd_data; | |
282 | ||
283 | /* Set WARM RESET SEQ address for P1 */ | |
fc7b92fc | 284 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address, |
ebf0bd36 AK |
285 | R_SEQ_ADD_WARM); |
286 | if (err) | |
287 | goto out; | |
288 | ||
289 | /* P1/P2/P3 enable WARMRESET */ | |
fc7b92fc | 290 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, |
ebf0bd36 AK |
291 | R_P1_SW_EVENTS); |
292 | if (err) | |
293 | goto out; | |
294 | ||
295 | rd_data |= ENABLE_WARMRESET; | |
fc7b92fc | 296 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, |
ebf0bd36 AK |
297 | R_P1_SW_EVENTS); |
298 | if (err) | |
299 | goto out; | |
300 | ||
fc7b92fc | 301 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, |
ebf0bd36 AK |
302 | R_P2_SW_EVENTS); |
303 | if (err) | |
304 | goto out; | |
305 | ||
306 | rd_data |= ENABLE_WARMRESET; | |
fc7b92fc | 307 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, |
ebf0bd36 AK |
308 | R_P2_SW_EVENTS); |
309 | if (err) | |
310 | goto out; | |
311 | ||
fc7b92fc | 312 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data, |
ebf0bd36 AK |
313 | R_P3_SW_EVENTS); |
314 | if (err) | |
315 | goto out; | |
316 | ||
317 | rd_data |= ENABLE_WARMRESET; | |
fc7b92fc | 318 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data, |
ebf0bd36 AK |
319 | R_P3_SW_EVENTS); |
320 | out: | |
321 | if (err) | |
322 | pr_err("TWL4030 warmreset seq config error\n"); | |
323 | return err; | |
324 | } | |
325 | ||
326 | static int __init twl4030_configure_resource(struct twl4030_resconfig *rconfig) | |
327 | { | |
328 | int rconfig_addr; | |
329 | int err; | |
330 | u8 type; | |
331 | u8 grp; | |
b4ead61e | 332 | u8 remap; |
ebf0bd36 AK |
333 | |
334 | if (rconfig->resource > TOTAL_RESOURCES) { | |
335 | pr_err("TWL4030 Resource %d does not exist\n", | |
336 | rconfig->resource); | |
337 | return -EINVAL; | |
338 | } | |
339 | ||
340 | rconfig_addr = res_config_addrs[rconfig->resource]; | |
341 | ||
342 | /* Set resource group */ | |
fc7b92fc B |
343 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &grp, |
344 | rconfig_addr + DEV_GRP_OFFSET); | |
ebf0bd36 AK |
345 | if (err) { |
346 | pr_err("TWL4030 Resource %d group could not be read\n", | |
347 | rconfig->resource); | |
348 | return err; | |
349 | } | |
350 | ||
56baa667 | 351 | if (rconfig->devgroup != TWL4030_RESCONFIG_UNDEF) { |
e97d1546 AK |
352 | grp &= ~DEV_GRP_MASK; |
353 | grp |= rconfig->devgroup << DEV_GRP_SHIFT; | |
fc7b92fc B |
354 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, |
355 | grp, rconfig_addr + DEV_GRP_OFFSET); | |
ebf0bd36 AK |
356 | if (err < 0) { |
357 | pr_err("TWL4030 failed to program devgroup\n"); | |
358 | return err; | |
359 | } | |
360 | } | |
361 | ||
362 | /* Set resource types */ | |
fc7b92fc | 363 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &type, |
ebf0bd36 AK |
364 | rconfig_addr + TYPE_OFFSET); |
365 | if (err < 0) { | |
366 | pr_err("TWL4030 Resource %d type could not be read\n", | |
367 | rconfig->resource); | |
368 | return err; | |
369 | } | |
370 | ||
56baa667 | 371 | if (rconfig->type != TWL4030_RESCONFIG_UNDEF) { |
ebf0bd36 AK |
372 | type &= ~TYPE_MASK; |
373 | type |= rconfig->type << TYPE_SHIFT; | |
374 | } | |
375 | ||
56baa667 | 376 | if (rconfig->type2 != TWL4030_RESCONFIG_UNDEF) { |
ebf0bd36 AK |
377 | type &= ~TYPE2_MASK; |
378 | type |= rconfig->type2 << TYPE2_SHIFT; | |
379 | } | |
380 | ||
fc7b92fc | 381 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, |
ebf0bd36 AK |
382 | type, rconfig_addr + TYPE_OFFSET); |
383 | if (err < 0) { | |
384 | pr_err("TWL4030 failed to program resource type\n"); | |
385 | return err; | |
386 | } | |
387 | ||
b4ead61e | 388 | /* Set remap states */ |
fc7b92fc B |
389 | err = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &remap, |
390 | rconfig_addr + REMAP_OFFSET); | |
b4ead61e AK |
391 | if (err < 0) { |
392 | pr_err("TWL4030 Resource %d remap could not be read\n", | |
393 | rconfig->resource); | |
394 | return err; | |
395 | } | |
396 | ||
53cf9a60 | 397 | if (rconfig->remap_off != TWL4030_RESCONFIG_UNDEF) { |
b4ead61e AK |
398 | remap &= ~OFF_STATE_MASK; |
399 | remap |= rconfig->remap_off << OFF_STATE_SHIFT; | |
400 | } | |
401 | ||
53cf9a60 | 402 | if (rconfig->remap_sleep != TWL4030_RESCONFIG_UNDEF) { |
b4ead61e | 403 | remap &= ~SLEEP_STATE_MASK; |
1ea933f4 | 404 | remap |= rconfig->remap_sleep << SLEEP_STATE_SHIFT; |
b4ead61e AK |
405 | } |
406 | ||
fc7b92fc B |
407 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, |
408 | remap, | |
409 | rconfig_addr + REMAP_OFFSET); | |
b4ead61e AK |
410 | if (err < 0) { |
411 | pr_err("TWL4030 failed to program remap\n"); | |
412 | return err; | |
413 | } | |
414 | ||
ebf0bd36 AK |
415 | return 0; |
416 | } | |
417 | ||
418 | static int __init load_twl4030_script(struct twl4030_script *tscript, | |
419 | u8 address) | |
420 | { | |
421 | int err; | |
75a74565 | 422 | static int order; |
ebf0bd36 AK |
423 | |
424 | /* Make sure the script isn't going beyond last valid address (0x3f) */ | |
425 | if ((address + tscript->size) > END_OF_SCRIPT) { | |
426 | pr_err("TWL4030 scripts too big error\n"); | |
427 | return -EINVAL; | |
428 | } | |
429 | ||
430 | err = twl4030_write_script(address, tscript->script, tscript->size); | |
431 | if (err) | |
432 | goto out; | |
433 | ||
434 | if (tscript->flags & TWL4030_WRST_SCRIPT) { | |
435 | err = twl4030_config_warmreset_sequence(address); | |
436 | if (err) | |
437 | goto out; | |
438 | } | |
439 | if (tscript->flags & TWL4030_WAKEUP12_SCRIPT) { | |
440 | err = twl4030_config_wakeup12_sequence(address); | |
441 | if (err) | |
442 | goto out; | |
75a74565 | 443 | order = 1; |
ebf0bd36 AK |
444 | } |
445 | if (tscript->flags & TWL4030_WAKEUP3_SCRIPT) { | |
446 | err = twl4030_config_wakeup3_sequence(address); | |
447 | if (err) | |
448 | goto out; | |
449 | } | |
c62dd365 | 450 | if (tscript->flags & TWL4030_SLEEP_SCRIPT) { |
1f968ff6 | 451 | if (!order) |
75a74565 AK |
452 | pr_warning("TWL4030: Bad order of scripts (sleep "\ |
453 | "script before wakeup) Leads to boot"\ | |
454 | "failure on some boards\n"); | |
ebf0bd36 | 455 | err = twl4030_config_sleep_sequence(address); |
c62dd365 | 456 | } |
ebf0bd36 AK |
457 | out: |
458 | return err; | |
459 | } | |
460 | ||
11a441ce MT |
461 | int twl4030_remove_script(u8 flags) |
462 | { | |
463 | int err = 0; | |
464 | ||
71084406 FB |
465 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, |
466 | TWL4030_PM_MASTER_KEY_CFG1, | |
467 | TWL4030_PM_MASTER_PROTECT_KEY); | |
11a441ce MT |
468 | if (err) { |
469 | pr_err("twl4030: unable to unlock PROTECT_KEY\n"); | |
470 | return err; | |
471 | } | |
472 | ||
71084406 FB |
473 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, |
474 | TWL4030_PM_MASTER_KEY_CFG2, | |
475 | TWL4030_PM_MASTER_PROTECT_KEY); | |
11a441ce MT |
476 | if (err) { |
477 | pr_err("twl4030: unable to unlock PROTECT_KEY\n"); | |
478 | return err; | |
479 | } | |
480 | ||
481 | if (flags & TWL4030_WRST_SCRIPT) { | |
482 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, | |
483 | R_SEQ_ADD_WARM); | |
484 | if (err) | |
485 | return err; | |
486 | } | |
487 | if (flags & TWL4030_WAKEUP12_SCRIPT) { | |
488 | if (err) | |
489 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, | |
490 | R_SEQ_ADD_S2A12); | |
491 | return err; | |
492 | } | |
493 | if (flags & TWL4030_WAKEUP3_SCRIPT) { | |
494 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, | |
495 | R_SEQ_ADD_S2A3); | |
496 | if (err) | |
497 | return err; | |
498 | } | |
499 | if (flags & TWL4030_SLEEP_SCRIPT) { | |
500 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, END_OF_SCRIPT, | |
501 | R_SEQ_ADD_A2S); | |
502 | if (err) | |
503 | return err; | |
504 | } | |
505 | ||
71084406 FB |
506 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, |
507 | TWL4030_PM_MASTER_PROTECT_KEY); | |
11a441ce MT |
508 | if (err) |
509 | pr_err("TWL4030 Unable to relock registers\n"); | |
510 | ||
511 | return err; | |
512 | } | |
513 | ||
ebf0bd36 AK |
514 | void __init twl4030_power_init(struct twl4030_power_data *twl4030_scripts) |
515 | { | |
516 | int err = 0; | |
517 | int i; | |
518 | struct twl4030_resconfig *resconfig; | |
519 | u8 address = twl4030_start_script_address; | |
520 | ||
71084406 FB |
521 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, |
522 | TWL4030_PM_MASTER_KEY_CFG1, | |
523 | TWL4030_PM_MASTER_PROTECT_KEY); | |
ebf0bd36 AK |
524 | if (err) |
525 | goto unlock; | |
526 | ||
71084406 FB |
527 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, |
528 | TWL4030_PM_MASTER_KEY_CFG2, | |
529 | TWL4030_PM_MASTER_PROTECT_KEY); | |
ebf0bd36 AK |
530 | if (err) |
531 | goto unlock; | |
532 | ||
533 | for (i = 0; i < twl4030_scripts->num; i++) { | |
534 | err = load_twl4030_script(twl4030_scripts->scripts[i], address); | |
535 | if (err) | |
536 | goto load; | |
537 | address += twl4030_scripts->scripts[i]->size; | |
538 | } | |
539 | ||
540 | resconfig = twl4030_scripts->resource_config; | |
541 | if (resconfig) { | |
542 | while (resconfig->resource) { | |
543 | err = twl4030_configure_resource(resconfig); | |
544 | if (err) | |
545 | goto resource; | |
546 | resconfig++; | |
547 | ||
548 | } | |
549 | } | |
550 | ||
71084406 FB |
551 | err = twl_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, |
552 | TWL4030_PM_MASTER_PROTECT_KEY); | |
ebf0bd36 AK |
553 | if (err) |
554 | pr_err("TWL4030 Unable to relock registers\n"); | |
555 | return; | |
556 | ||
557 | unlock: | |
558 | if (err) | |
559 | pr_err("TWL4030 Unable to unlock registers\n"); | |
560 | return; | |
561 | load: | |
562 | if (err) | |
563 | pr_err("TWL4030 failed to load scripts\n"); | |
564 | return; | |
565 | resource: | |
566 | if (err) | |
567 | pr_err("TWL4030 failed to configure resource\n"); | |
568 | return; | |
569 | } |