]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/i2c/chips/twl4030-power.c
Merge current mainline tree into linux-omap tree
[linux-2.6-omap-h63xx.git] / drivers / i2c / chips / twl4030-power.c
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  *
12  * This file is subject to the terms and conditions of the GNU General
13  * Public License. See the file "COPYING" in the main directory of this
14  * archive for more details.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 #include <linux/module.h>
27 #include <linux/pm.h>
28 #include <linux/i2c/twl4030.h>
29
30 #include <asm/mach-types.h>
31
32 #define PWR_P1_SW_EVENTS        0x10
33 #define PWR_DEVOFF      (1<<0)
34
35 #define PHY_TO_OFF_PM_MASTER(p)         (p - 0x36)
36 #define PHY_TO_OFF_PM_RECIEVER(p)       (p - 0x5b)
37
38 /* resource - hfclk */
39 #define R_HFCLKOUT_DEV_GRP      PHY_TO_OFF_PM_RECIEVER(0xe6)
40
41 /* PM events */
42 #define R_P1_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x46)
43 #define R_P2_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x47)
44 #define R_P3_SW_EVENTS          PHY_TO_OFF_PM_MASTER(0x48)
45 #define R_CFG_P1_TRANSITION     PHY_TO_OFF_PM_MASTER(0x36)
46 #define R_CFG_P2_TRANSITION     PHY_TO_OFF_PM_MASTER(0x37)
47 #define R_CFG_P3_TRANSITION     PHY_TO_OFF_PM_MASTER(0x38)
48
49 #define LVL_WAKEUP      0x08
50
51 #define ENABLE_WARMRESET (1<<4)
52
53 /* sequence script */
54
55 #define END_OF_SCRIPT           0x3f
56
57 #define R_SEQ_ADD_A2S           PHY_TO_OFF_PM_MASTER(0x55)
58 #define R_SEQ_ADD_SA12          PHY_TO_OFF_PM_MASTER(0x56)
59 #define R_SEQ_ADD_S2A3          PHY_TO_OFF_PM_MASTER(0x57)
60 #define R_SEQ_ADD_WARM          PHY_TO_OFF_PM_MASTER(0x58)
61 #define R_MEMORY_ADDRESS        PHY_TO_OFF_PM_MASTER(0x59)
62 #define R_MEMORY_DATA           PHY_TO_OFF_PM_MASTER(0x5a)
63
64 /* Power bus message definitions */
65
66 #define DEV_GRP_NULL            0x0
67 #define DEV_GRP_P1              0x1
68 #define DEV_GRP_P2              0x2
69 #define DEV_GRP_P3              0x4
70
71 #define RES_GRP_RES             0x0
72 #define RES_GRP_PP              0x1
73 #define RES_GRP_RC              0x2
74 #define RES_GRP_PP_RC           0x3
75 #define RES_GRP_PR              0x4
76 #define RES_GRP_PP_PR           0x5
77 #define RES_GRP_RC_PR           0x6
78 #define RES_GRP_ALL             0x7
79
80 #define RES_TYPE2_R0            0x0
81
82 #define RES_TYPE_ALL            0x7
83
84 #define RES_STATE_WRST          0xF
85 #define RES_STATE_ACTIVE        0xE
86 #define RES_STATE_SLEEP         0x8
87 #define RES_STATE_OFF           0x0
88
89 /*
90 *       Power Bus Message Format
91 *
92 *       Broadcast Message (16 Bits)
93 *       DEV_GRP[15:13] MT[12]  RES_GRP[11:9]  RES_TYPE2[8:7] RES_TYPE[6:4]
94 *       RES_STATE[3:0]
95 *
96 *       Singular Message (16 Bits)
97 *       DEV_GRP[15:13] MT[12]  RES_ID[11:4]  RES_STATE[3:0]
98 *
99 */
100
101 #define MSG_BROADCAST(devgrp, grp, type, type2, state) \
102         (devgrp << 13 | 1 << 12 | grp << 9 | type2 << 7 | type << 4 | state)
103
104 #define MSG_SINGULAR(devgrp, id, state) \
105         (devgrp << 13 | 0 << 12 | id << 4 | state)
106
107 #define R_PROTECT_KEY           0x0E
108 #define KEY_1                   0xC0
109 #define KEY_2                   0x0C
110
111 struct triton_ins {
112         u16 pmb_message;
113         u8 delay;
114 };
115
116
117 #define CONFIG_DISABLE_HFCLK    1
118
119 #if defined(CONFIG_MACH_OMAP_3430SDP) || defined(CONFIG_MACH_OMAP_3430LABRADOR)
120
121 struct triton_ins sleep_on_seq[] __initdata = {
122         {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_OFF), 4},
123         {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_OFF), 2},
124 #ifdef CONFIG_DISABLE_HFCLK
125         {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_OFF), 3},
126         {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_OFF), 3},
127 #endif /* #ifdef CONFIG_DISABLE_HFCLK */
128 };
129
130 struct triton_ins sleep_off_seq[] __initdata = {
131 #ifndef CONFIG_DISABLE_HFCLK
132         {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 4},
133         {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 2},
134 #else
135         {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_ACTIVE), 0x30},
136         {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 0x30},
137         {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 0x37},
138         {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 3},
139 #endif /* #ifndef CONFIG_DISABLE_HFCLK */
140 };
141
142 struct triton_ins t2_wrst_seq[] __initdata = {
143         {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_OFF), 2},
144         {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_WRST), 15},
145         {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_WRST), 15},
146         {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_WRST), 0x60},
147         {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 2},
148         {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_ACTIVE), 2},
149 };
150 #else
151 struct triton_ins sleep_on_seq[] __initdata = {
152         {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0,
153                         RES_STATE_SLEEP), 4},
154         {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_ALL, RES_TYPE2_R0,
155                         RES_STATE_SLEEP), 4},
156 };
157
158 struct triton_ins sleep_off_seq[] __initdata = {
159         {MSG_SINGULAR(DEV_GRP_NULL, 0x17, RES_STATE_ACTIVE), 0x30},
160         {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_PP_PR, RES_TYPE_ALL, RES_TYPE2_R0,
161                         RES_STATE_ACTIVE), 0x37},
162         {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_ALL, RES_TYPE2_R0,
163                         RES_STATE_ACTIVE), 0x2},
164 };
165
166 struct triton_ins t2_wrst_seq[] __initdata = { };
167
168 #endif
169
170 static int __init twl4030_write_script_byte(u8 address, u8 byte)
171 {
172         int err;
173
174         err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
175                                         R_MEMORY_ADDRESS);
176         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
177                                         R_MEMORY_DATA);
178
179         return err;
180 }
181
182 static int __init twl4030_write_script_ins(u8 address, u16 pmb_message,
183                                                 u8 delay, u8 next)
184 {
185         int err = 0;
186
187         address *= 4;
188         err |= twl4030_write_script_byte(address++, pmb_message >> 8);
189         err |= twl4030_write_script_byte(address++, pmb_message & 0xff);
190         err |= twl4030_write_script_byte(address++, delay);
191         err |= twl4030_write_script_byte(address++, next);
192
193         return err;
194 }
195
196 static int __init twl4030_write_script(u8 address, struct triton_ins *script,
197                                         int len)
198 {
199         int err = 0;
200
201         for (; len; len--, address++, script++) {
202                 if (len == 1)
203                         err |= twl4030_write_script_ins(address,
204                                                         script->pmb_message,
205                                                         script->delay,
206                                                         END_OF_SCRIPT);
207                 else
208                         err |= twl4030_write_script_ins(address,
209                                                         script->pmb_message,
210                                                         script->delay,
211                                                         address + 1);
212         }
213
214         return err;
215 }
216
217 static int __init config_sleep_wake_sequence(void)
218 {
219         int err = 0;
220
221         /*
222          * CLKREQ is pulled high on the 2430SDP, therefore, we need to take
223          * it out of the HFCLKOUT DEV_GRP for P1 else HFCLKOUT can't be stopped.
224          */
225
226         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
227                                   0x20, R_HFCLKOUT_DEV_GRP);
228
229         /* Set ACTIVE to SLEEP SEQ address in T2 memory*/
230         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x2B,
231                                   R_SEQ_ADD_A2S);
232
233         /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
234         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x2F,
235                                   R_SEQ_ADD_SA12);
236
237         /* Set SLEEP to ACTIVE SEQ address for P3 */
238         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x2F,
239                                   R_SEQ_ADD_S2A3);
240
241         /* Install Active->Sleep (A2S) sequence */
242         err |= twl4030_write_script(0x2B, sleep_on_seq,
243                                         ARRAY_SIZE(sleep_on_seq));
244
245         /* Install Sleep->Active (S2A) sequence */
246         err |= twl4030_write_script(0x2F, sleep_off_seq,
247                                         ARRAY_SIZE(sleep_off_seq));
248
249         if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
250                 u8 data;
251                 /* Disabling AC charger effect on sleep-active transitions */
252                 err |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
253                                                 R_CFG_P1_TRANSITION);
254                 data &= ~(1<<1);
255                 err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
256                                                 R_CFG_P1_TRANSITION);
257         }
258
259         /* P1/P2/P3 LVL_WAKEUP should be on LEVEL */
260         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
261                                         R_P1_SW_EVENTS);
262         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
263                                         R_P2_SW_EVENTS);
264         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
265                                 R_P3_SW_EVENTS);
266
267         if (err)
268                 printk(KERN_ERR "TWL4030 sleep-wake sequence config error\n");
269
270         return err;
271 }
272
273
274 /* Programming the WARMRESET Sequence on TRITON */
275 static int __init config_warmreset_sequence(void)
276 {
277
278         int e = 0;
279         u8 rd_data;
280
281         if (!ARRAY_SIZE(t2_wrst_seq))
282                 return 0;
283
284         /* Set WARM RESET SEQ address for P1 */
285         e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0x38,
286                                 R_SEQ_ADD_WARM);
287
288         /* Install Warm Reset sequence */
289         e |= twl4030_write_script(0x38, t2_wrst_seq,
290                                         ARRAY_SIZE(t2_wrst_seq));
291
292         /* P1/P2/P3 enable WARMRESET */
293         e |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
294                                 R_P1_SW_EVENTS);
295         rd_data |= ENABLE_WARMRESET;
296         e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
297                                 R_P1_SW_EVENTS);
298
299         e |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
300                                 R_P2_SW_EVENTS);
301         rd_data |= ENABLE_WARMRESET;
302         e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
303                                 R_P2_SW_EVENTS);
304
305         e |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
306                                 R_P3_SW_EVENTS);
307         rd_data |= ENABLE_WARMRESET;
308         e |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
309                                 R_P3_SW_EVENTS);
310
311         if (e)
312                 printk(KERN_ERR
313                         "TWL4030 Power Companion Warmreset seq config error\n");
314         return e;
315 }
316
317 static int __init twl4030_power_init(void)
318 {
319         int err = 0;
320
321         err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_1,
322                                 R_PROTECT_KEY);
323         err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_2,
324                                 R_PROTECT_KEY);
325
326         if (err)
327                 return err;
328
329         err = config_sleep_wake_sequence();
330         if (err)
331                 return err;
332
333         err = config_warmreset_sequence();
334         if (err)
335                 return err;
336
337         err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY);
338
339         return err;
340
341 }
342
343 module_init(twl4030_power_init);