Commit | Line | Data |
---|---|---|
139563ad PW |
1 | /* |
2 | * OMAP3xxx PRM module functions | |
3 | * | |
4 | * Copyright (C) 2010-2012 Texas Instruments, Inc. | |
5 | * Copyright (C) 2010 Nokia Corporation | |
6 | * BenoƮt Cousson | |
7 | * Paul Walmsley | |
49815399 | 8 | * Rajendra Nayak <rnayak@ti.com> |
139563ad PW |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/errno.h> | |
17 | #include <linux/err.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/irq.h> | |
20 | ||
e8d3d47a | 21 | #include "soc.h" |
139563ad | 22 | #include "common.h" |
139563ad | 23 | #include "vp.h" |
49815399 | 24 | #include "powerdomain.h" |
139563ad | 25 | #include "prm3xxx.h" |
49815399 | 26 | #include "prm2xxx_3xxx.h" |
139563ad PW |
27 | #include "cm2xxx_3xxx.h" |
28 | #include "prm-regbits-34xx.h" | |
29 | ||
30 | static const struct omap_prcm_irq omap3_prcm_irqs[] = { | |
31 | OMAP_PRCM_IRQ("wkup", 0, 0), | |
32 | OMAP_PRCM_IRQ("io", 9, 1), | |
33 | }; | |
34 | ||
35 | static struct omap_prcm_irq_setup omap3_prcm_irq_setup = { | |
36 | .ack = OMAP3_PRM_IRQSTATUS_MPU_OFFSET, | |
37 | .mask = OMAP3_PRM_IRQENABLE_MPU_OFFSET, | |
38 | .nr_regs = 1, | |
39 | .irqs = omap3_prcm_irqs, | |
40 | .nr_irqs = ARRAY_SIZE(omap3_prcm_irqs), | |
41 | .irq = 11 + OMAP_INTC_START, | |
42 | .read_pending_irqs = &omap3xxx_prm_read_pending_irqs, | |
43 | .ocp_barrier = &omap3xxx_prm_ocp_barrier, | |
44 | .save_and_clear_irqen = &omap3xxx_prm_save_and_clear_irqen, | |
45 | .restore_irqen = &omap3xxx_prm_restore_irqen, | |
81243651 | 46 | .reconfigure_io_chain = &omap3xxx_prm_reconfigure_io_chain, |
139563ad PW |
47 | }; |
48 | ||
2bb2a5d3 PW |
49 | /* |
50 | * omap3_prm_reset_src_map - map from bits in the PRM_RSTST hardware | |
51 | * register (which are specific to OMAP3xxx SoCs) to reset source ID | |
52 | * bit shifts (which is an OMAP SoC-independent enumeration) | |
53 | */ | |
54 | static struct prm_reset_src_map omap3xxx_prm_reset_src_map[] = { | |
55 | { OMAP3430_GLOBAL_COLD_RST_SHIFT, OMAP_GLOBAL_COLD_RST_SRC_ID_SHIFT }, | |
56 | { OMAP3430_GLOBAL_SW_RST_SHIFT, OMAP_GLOBAL_WARM_RST_SRC_ID_SHIFT }, | |
57 | { OMAP3430_SECURITY_VIOL_RST_SHIFT, OMAP_SECU_VIOL_RST_SRC_ID_SHIFT }, | |
58 | { OMAP3430_MPU_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, | |
59 | { OMAP3430_SECURE_WD_RST_SHIFT, OMAP_MPU_WD_RST_SRC_ID_SHIFT }, | |
60 | { OMAP3430_EXTERNAL_WARM_RST_SHIFT, OMAP_EXTWARM_RST_SRC_ID_SHIFT }, | |
61 | { OMAP3430_VDD1_VOLTAGE_MANAGER_RST_SHIFT, | |
62 | OMAP_VDD_MPU_VM_RST_SRC_ID_SHIFT }, | |
63 | { OMAP3430_VDD2_VOLTAGE_MANAGER_RST_SHIFT, | |
64 | OMAP_VDD_CORE_VM_RST_SRC_ID_SHIFT }, | |
65 | { OMAP3430_ICEPICK_RST_SHIFT, OMAP_ICEPICK_RST_SRC_ID_SHIFT }, | |
66 | { OMAP3430_ICECRUSHER_RST_SHIFT, OMAP_ICECRUSHER_RST_SRC_ID_SHIFT }, | |
67 | { -1, -1 }, | |
68 | }; | |
69 | ||
139563ad PW |
70 | /* PRM VP */ |
71 | ||
72 | /* | |
73 | * struct omap3_vp - OMAP3 VP register access description. | |
74 | * @tranxdone_status: VP_TRANXDONE_ST bitmask in PRM_IRQSTATUS_MPU reg | |
75 | */ | |
76 | struct omap3_vp { | |
77 | u32 tranxdone_status; | |
78 | }; | |
79 | ||
80 | static struct omap3_vp omap3_vp[] = { | |
81 | [OMAP3_VP_VDD_MPU_ID] = { | |
82 | .tranxdone_status = OMAP3430_VP1_TRANXDONE_ST_MASK, | |
83 | }, | |
84 | [OMAP3_VP_VDD_CORE_ID] = { | |
85 | .tranxdone_status = OMAP3430_VP2_TRANXDONE_ST_MASK, | |
86 | }, | |
87 | }; | |
88 | ||
89 | #define MAX_VP_ID ARRAY_SIZE(omap3_vp); | |
90 | ||
91 | u32 omap3_prm_vp_check_txdone(u8 vp_id) | |
92 | { | |
93 | struct omap3_vp *vp = &omap3_vp[vp_id]; | |
94 | u32 irqstatus; | |
95 | ||
96 | irqstatus = omap2_prm_read_mod_reg(OCP_MOD, | |
97 | OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | |
98 | return irqstatus & vp->tranxdone_status; | |
99 | } | |
100 | ||
101 | void omap3_prm_vp_clear_txdone(u8 vp_id) | |
102 | { | |
103 | struct omap3_vp *vp = &omap3_vp[vp_id]; | |
104 | ||
105 | omap2_prm_write_mod_reg(vp->tranxdone_status, | |
106 | OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | |
107 | } | |
108 | ||
109 | u32 omap3_prm_vcvp_read(u8 offset) | |
110 | { | |
111 | return omap2_prm_read_mod_reg(OMAP3430_GR_MOD, offset); | |
112 | } | |
113 | ||
114 | void omap3_prm_vcvp_write(u32 val, u8 offset) | |
115 | { | |
116 | omap2_prm_write_mod_reg(val, OMAP3430_GR_MOD, offset); | |
117 | } | |
118 | ||
119 | u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset) | |
120 | { | |
121 | return omap2_prm_rmw_mod_reg_bits(mask, bits, OMAP3430_GR_MOD, offset); | |
122 | } | |
123 | ||
d08cce6a PW |
124 | /** |
125 | * omap3xxx_prm_dpll3_reset - use DPLL3 reset to reboot the OMAP SoC | |
126 | * | |
127 | * Set the DPLL3 reset bit, which should reboot the SoC. This is the | |
128 | * recommended way to restart the SoC, considering Errata i520. No | |
129 | * return value. | |
130 | */ | |
131 | void omap3xxx_prm_dpll3_reset(void) | |
132 | { | |
133 | omap2_prm_set_mod_reg_bits(OMAP_RST_DPLL3_MASK, OMAP3430_GR_MOD, | |
134 | OMAP2_RM_RSTCTRL); | |
135 | /* OCP barrier */ | |
136 | omap2_prm_read_mod_reg(OMAP3430_GR_MOD, OMAP2_RM_RSTCTRL); | |
137 | } | |
138 | ||
139563ad PW |
139 | /** |
140 | * omap3xxx_prm_read_pending_irqs - read pending PRM MPU IRQs into @events | |
141 | * @events: ptr to a u32, preallocated by caller | |
142 | * | |
143 | * Read PRM_IRQSTATUS_MPU bits, AND'ed with the currently-enabled PRM | |
144 | * MPU IRQs, and store the result into the u32 pointed to by @events. | |
145 | * No return value. | |
146 | */ | |
147 | void omap3xxx_prm_read_pending_irqs(unsigned long *events) | |
148 | { | |
149 | u32 mask, st; | |
150 | ||
151 | /* XXX Can the mask read be avoided (e.g., can it come from RAM?) */ | |
152 | mask = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | |
153 | st = omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_IRQSTATUS_MPU_OFFSET); | |
154 | ||
155 | events[0] = mask & st; | |
156 | } | |
157 | ||
158 | /** | |
159 | * omap3xxx_prm_ocp_barrier - force buffered MPU writes to the PRM to complete | |
160 | * | |
161 | * Force any buffered writes to the PRM IP block to complete. Needed | |
162 | * by the PRM IRQ handler, which reads and writes directly to the IP | |
163 | * block, to avoid race conditions after acknowledging or clearing IRQ | |
164 | * bits. No return value. | |
165 | */ | |
166 | void omap3xxx_prm_ocp_barrier(void) | |
167 | { | |
168 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | |
169 | } | |
170 | ||
171 | /** | |
172 | * omap3xxx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU reg | |
173 | * @saved_mask: ptr to a u32 array to save IRQENABLE bits | |
174 | * | |
175 | * Save the PRM_IRQENABLE_MPU register to @saved_mask. @saved_mask | |
176 | * must be allocated by the caller. Intended to be used in the PRM | |
177 | * interrupt handler suspend callback. The OCP barrier is needed to | |
178 | * ensure the write to disable PRM interrupts reaches the PRM before | |
179 | * returning; otherwise, spurious interrupts might occur. No return | |
180 | * value. | |
181 | */ | |
182 | void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask) | |
183 | { | |
184 | saved_mask[0] = omap2_prm_read_mod_reg(OCP_MOD, | |
185 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | |
186 | omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | |
187 | ||
188 | /* OCP barrier */ | |
189 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | |
190 | } | |
191 | ||
192 | /** | |
193 | * omap3xxx_prm_restore_irqen - set PRM_IRQENABLE_MPU register from args | |
194 | * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously | |
195 | * | |
196 | * Restore the PRM_IRQENABLE_MPU register from @saved_mask. Intended | |
197 | * to be used in the PRM interrupt handler resume callback to restore | |
198 | * values saved by omap3xxx_prm_save_and_clear_irqen(). No OCP | |
199 | * barrier should be needed here; any pending PRM interrupts will fire | |
200 | * once the writes reach the PRM. No return value. | |
201 | */ | |
202 | void omap3xxx_prm_restore_irqen(u32 *saved_mask) | |
203 | { | |
204 | omap2_prm_write_mod_reg(saved_mask[0], OCP_MOD, | |
205 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | |
206 | } | |
207 | ||
208 | /** | |
209 | * omap3xxx_prm_reconfigure_io_chain - clear latches and reconfigure I/O chain | |
210 | * | |
211 | * Clear any previously-latched I/O wakeup events and ensure that the | |
212 | * I/O wakeup gates are aligned with the current mux settings. Works | |
213 | * by asserting WUCLKIN, waiting for WUCLKOUT to be asserted, and then | |
214 | * deasserting WUCLKIN and clearing the ST_IO_CHAIN WKST bit. No | |
215 | * return value. | |
216 | */ | |
217 | void omap3xxx_prm_reconfigure_io_chain(void) | |
218 | { | |
219 | int i = 0; | |
220 | ||
221 | omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD, | |
222 | PM_WKEN); | |
223 | ||
224 | omap_test_timeout(omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST) & | |
225 | OMAP3430_ST_IO_CHAIN_MASK, | |
226 | MAX_IOPAD_LATCH_TIME, i); | |
227 | if (i == MAX_IOPAD_LATCH_TIME) | |
228 | pr_warn("PRM: I/O chain clock line assertion timed out\n"); | |
229 | ||
230 | omap2_prm_clear_mod_reg_bits(OMAP3430_EN_IO_CHAIN_MASK, WKUP_MOD, | |
231 | PM_WKEN); | |
232 | ||
233 | omap2_prm_set_mod_reg_bits(OMAP3430_ST_IO_CHAIN_MASK, WKUP_MOD, | |
234 | PM_WKST); | |
235 | ||
236 | omap2_prm_read_mod_reg(WKUP_MOD, PM_WKST); | |
237 | } | |
238 | ||
239 | /** | |
240 | * omap3xxx_prm_enable_io_wakeup - enable wakeup events from I/O wakeup latches | |
241 | * | |
242 | * Activates the I/O wakeup event latches and allows events logged by | |
243 | * those latches to signal a wakeup event to the PRCM. For I/O | |
244 | * wakeups to occur, WAKEUPENABLE bits must be set in the pad mux | |
245 | * registers, and omap3xxx_prm_reconfigure_io_chain() must be called. | |
246 | * No return value. | |
247 | */ | |
248 | static void __init omap3xxx_prm_enable_io_wakeup(void) | |
249 | { | |
2541d15f | 250 | if (prm_features & PRM_HAS_IO_WAKEUP) |
139563ad PW |
251 | omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, |
252 | PM_WKEN); | |
253 | } | |
254 | ||
2bb2a5d3 PW |
255 | /** |
256 | * omap3xxx_prm_read_reset_sources - return the last SoC reset source | |
257 | * | |
258 | * Return a u32 representing the last reset sources of the SoC. The | |
259 | * returned reset source bits are standardized across OMAP SoCs. | |
260 | */ | |
261 | static u32 omap3xxx_prm_read_reset_sources(void) | |
262 | { | |
263 | struct prm_reset_src_map *p; | |
264 | u32 r = 0; | |
265 | u32 v; | |
266 | ||
267 | v = omap2_prm_read_mod_reg(WKUP_MOD, OMAP2_RM_RSTST); | |
268 | ||
269 | p = omap3xxx_prm_reset_src_map; | |
270 | while (p->reg_shift >= 0 && p->std_shift >= 0) { | |
271 | if (v & (1 << p->reg_shift)) | |
272 | r |= 1 << p->std_shift; | |
273 | p++; | |
274 | } | |
275 | ||
276 | return r; | |
277 | } | |
278 | ||
49815399 PW |
279 | /* Powerdomain low-level functions */ |
280 | ||
7e7fff82 PW |
281 | static int omap3_pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) |
282 | { | |
283 | omap2_prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, | |
284 | (pwrst << OMAP_POWERSTATE_SHIFT), | |
285 | pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); | |
286 | return 0; | |
287 | } | |
288 | ||
289 | static int omap3_pwrdm_read_next_pwrst(struct powerdomain *pwrdm) | |
290 | { | |
291 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
292 | OMAP2_PM_PWSTCTRL, | |
293 | OMAP_POWERSTATE_MASK); | |
294 | } | |
295 | ||
296 | static int omap3_pwrdm_read_pwrst(struct powerdomain *pwrdm) | |
297 | { | |
298 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
299 | OMAP2_PM_PWSTST, | |
300 | OMAP_POWERSTATEST_MASK); | |
301 | } | |
302 | ||
49815399 PW |
303 | /* Applicable only for OMAP3. Not supported on OMAP2 */ |
304 | static int omap3_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) | |
305 | { | |
306 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
307 | OMAP3430_PM_PREPWSTST, | |
308 | OMAP3430_LASTPOWERSTATEENTERED_MASK); | |
309 | } | |
310 | ||
311 | static int omap3_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) | |
312 | { | |
313 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
314 | OMAP2_PM_PWSTST, | |
315 | OMAP3430_LOGICSTATEST_MASK); | |
316 | } | |
317 | ||
318 | static int omap3_pwrdm_read_logic_retst(struct powerdomain *pwrdm) | |
319 | { | |
320 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
321 | OMAP2_PM_PWSTCTRL, | |
322 | OMAP3430_LOGICSTATEST_MASK); | |
323 | } | |
324 | ||
325 | static int omap3_pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) | |
326 | { | |
327 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
328 | OMAP3430_PM_PREPWSTST, | |
329 | OMAP3430_LASTLOGICSTATEENTERED_MASK); | |
330 | } | |
331 | ||
332 | static int omap3_get_mem_bank_lastmemst_mask(u8 bank) | |
333 | { | |
334 | switch (bank) { | |
335 | case 0: | |
336 | return OMAP3430_LASTMEM1STATEENTERED_MASK; | |
337 | case 1: | |
338 | return OMAP3430_LASTMEM2STATEENTERED_MASK; | |
339 | case 2: | |
340 | return OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; | |
341 | case 3: | |
342 | return OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; | |
343 | default: | |
344 | WARN_ON(1); /* should never happen */ | |
345 | return -EEXIST; | |
346 | } | |
347 | return 0; | |
348 | } | |
349 | ||
350 | static int omap3_pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | |
351 | { | |
352 | u32 m; | |
353 | ||
354 | m = omap3_get_mem_bank_lastmemst_mask(bank); | |
355 | ||
356 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | |
357 | OMAP3430_PM_PREPWSTST, m); | |
358 | } | |
359 | ||
360 | static int omap3_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) | |
361 | { | |
362 | omap2_prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); | |
363 | return 0; | |
364 | } | |
365 | ||
366 | static int omap3_pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) | |
367 | { | |
368 | return omap2_prm_rmw_mod_reg_bits(0, | |
369 | 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, | |
370 | pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); | |
371 | } | |
372 | ||
373 | static int omap3_pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) | |
374 | { | |
375 | return omap2_prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, | |
376 | 0, pwrdm->prcm_offs, | |
377 | OMAP2_PM_PWSTCTRL); | |
378 | } | |
379 | ||
380 | struct pwrdm_ops omap3_pwrdm_operations = { | |
7e7fff82 PW |
381 | .pwrdm_set_next_pwrst = omap3_pwrdm_set_next_pwrst, |
382 | .pwrdm_read_next_pwrst = omap3_pwrdm_read_next_pwrst, | |
383 | .pwrdm_read_pwrst = omap3_pwrdm_read_pwrst, | |
49815399 PW |
384 | .pwrdm_read_prev_pwrst = omap3_pwrdm_read_prev_pwrst, |
385 | .pwrdm_set_logic_retst = omap2_pwrdm_set_logic_retst, | |
386 | .pwrdm_read_logic_pwrst = omap3_pwrdm_read_logic_pwrst, | |
387 | .pwrdm_read_logic_retst = omap3_pwrdm_read_logic_retst, | |
388 | .pwrdm_read_prev_logic_pwrst = omap3_pwrdm_read_prev_logic_pwrst, | |
389 | .pwrdm_set_mem_onst = omap2_pwrdm_set_mem_onst, | |
390 | .pwrdm_set_mem_retst = omap2_pwrdm_set_mem_retst, | |
391 | .pwrdm_read_mem_pwrst = omap2_pwrdm_read_mem_pwrst, | |
392 | .pwrdm_read_mem_retst = omap2_pwrdm_read_mem_retst, | |
393 | .pwrdm_read_prev_mem_pwrst = omap3_pwrdm_read_prev_mem_pwrst, | |
394 | .pwrdm_clear_all_prev_pwrst = omap3_pwrdm_clear_all_prev_pwrst, | |
395 | .pwrdm_enable_hdwr_sar = omap3_pwrdm_enable_hdwr_sar, | |
396 | .pwrdm_disable_hdwr_sar = omap3_pwrdm_disable_hdwr_sar, | |
397 | .pwrdm_wait_transition = omap2_pwrdm_wait_transition, | |
398 | }; | |
399 | ||
400 | /* | |
401 | * | |
402 | */ | |
403 | ||
b550e47f TK |
404 | static int omap3xxx_prm_late_init(void); |
405 | ||
2bb2a5d3 PW |
406 | static struct prm_ll_data omap3xxx_prm_ll_data = { |
407 | .read_reset_sources = &omap3xxx_prm_read_reset_sources, | |
b550e47f | 408 | .late_init = &omap3xxx_prm_late_init, |
2bb2a5d3 PW |
409 | }; |
410 | ||
63a293e0 PW |
411 | int __init omap3xxx_prm_init(void) |
412 | { | |
2541d15f TK |
413 | if (omap3_has_io_wakeup()) |
414 | prm_features |= PRM_HAS_IO_WAKEUP; | |
63a293e0 PW |
415 | |
416 | return prm_register(&omap3xxx_prm_ll_data); | |
417 | } | |
418 | ||
ea351c16 | 419 | static int omap3xxx_prm_late_init(void) |
139563ad PW |
420 | { |
421 | int ret; | |
422 | ||
2541d15f | 423 | if (!(prm_features & PRM_HAS_IO_WAKEUP)) |
139563ad PW |
424 | return 0; |
425 | ||
426 | omap3xxx_prm_enable_io_wakeup(); | |
427 | ret = omap_prcm_register_chain_handler(&omap3_prcm_irq_setup); | |
428 | if (!ret) | |
429 | irq_set_status_flags(omap_prcm_event_to_irq("io"), | |
430 | IRQ_NOAUTOEN); | |
431 | ||
432 | return ret; | |
433 | } | |
2bb2a5d3 PW |
434 | |
435 | static void __exit omap3xxx_prm_exit(void) | |
436 | { | |
d8871cd2 | 437 | prm_unregister(&omap3xxx_prm_ll_data); |
2bb2a5d3 PW |
438 | } |
439 | __exitcall(omap3xxx_prm_exit); |