diff options
Diffstat (limited to 'arch/arm/mach-omap2/prm3xxx.c')
-rw-r--r-- | arch/arm/mach-omap2/prm3xxx.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/prm3xxx.c b/arch/arm/mach-omap2/prm3xxx.c new file mode 100644 index 000000000000..db198d058584 --- /dev/null +++ b/arch/arm/mach-omap2/prm3xxx.c | |||
@@ -0,0 +1,420 @@ | |||
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 | ||
8 | * Rajendra Nayak <rnayak@ti.com> | ||
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 | |||
21 | #include "common.h" | ||
22 | #include <plat/cpu.h> | ||
23 | |||
24 | #include "vp.h" | ||
25 | #include "powerdomain.h" | ||
26 | #include "prm3xxx.h" | ||
27 | #include "prm2xxx_3xxx.h" | ||
28 | #include "cm2xxx_3xxx.h" | ||
29 | #include "prm-regbits-34xx.h" | ||
30 | |||
31 | static const struct omap_prcm_irq omap3_prcm_irqs[] = { | ||
32 | OMAP_PRCM_IRQ("wkup", 0, 0), | ||
33 | OMAP_PRCM_IRQ("io", 9, 1), | ||
34 | }; | ||
35 | |||
36 | static struct omap_prcm_irq_setup omap3_prcm_irq_setup = { | ||
37 | .ack = OMAP3_PRM_IRQSTATUS_MPU_OFFSET, | ||
38 | .mask = OMAP3_PRM_IRQENABLE_MPU_OFFSET, | ||
39 | .nr_regs = 1, | ||
40 | .irqs = omap3_prcm_irqs, | ||
41 | .nr_irqs = ARRAY_SIZE(omap3_prcm_irqs), | ||
42 | .irq = 11 + OMAP_INTC_START, | ||
43 | .read_pending_irqs = &omap3xxx_prm_read_pending_irqs, | ||
44 | .ocp_barrier = &omap3xxx_prm_ocp_barrier, | ||
45 | .save_and_clear_irqen = &omap3xxx_prm_save_and_clear_irqen, | ||
46 | .restore_irqen = &omap3xxx_prm_restore_irqen, | ||
47 | }; | ||
48 | |||
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 | |||
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 | |||
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 | |||
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 | { | ||
250 | if (omap3_has_io_wakeup()) | ||
251 | omap2_prm_set_mod_reg_bits(OMAP3430_EN_IO_MASK, WKUP_MOD, | ||
252 | PM_WKEN); | ||
253 | } | ||
254 | |||
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 | |||
279 | /* Powerdomain low-level functions */ | ||
280 | |||
281 | /* Applicable only for OMAP3. Not supported on OMAP2 */ | ||
282 | static int omap3_pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) | ||
283 | { | ||
284 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
285 | OMAP3430_PM_PREPWSTST, | ||
286 | OMAP3430_LASTPOWERSTATEENTERED_MASK); | ||
287 | } | ||
288 | |||
289 | static int omap3_pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) | ||
290 | { | ||
291 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
292 | OMAP2_PM_PWSTST, | ||
293 | OMAP3430_LOGICSTATEST_MASK); | ||
294 | } | ||
295 | |||
296 | static int omap3_pwrdm_read_logic_retst(struct powerdomain *pwrdm) | ||
297 | { | ||
298 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
299 | OMAP2_PM_PWSTCTRL, | ||
300 | OMAP3430_LOGICSTATEST_MASK); | ||
301 | } | ||
302 | |||
303 | static int omap3_pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) | ||
304 | { | ||
305 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
306 | OMAP3430_PM_PREPWSTST, | ||
307 | OMAP3430_LASTLOGICSTATEENTERED_MASK); | ||
308 | } | ||
309 | |||
310 | static int omap3_get_mem_bank_lastmemst_mask(u8 bank) | ||
311 | { | ||
312 | switch (bank) { | ||
313 | case 0: | ||
314 | return OMAP3430_LASTMEM1STATEENTERED_MASK; | ||
315 | case 1: | ||
316 | return OMAP3430_LASTMEM2STATEENTERED_MASK; | ||
317 | case 2: | ||
318 | return OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; | ||
319 | case 3: | ||
320 | return OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; | ||
321 | default: | ||
322 | WARN_ON(1); /* should never happen */ | ||
323 | return -EEXIST; | ||
324 | } | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | static int omap3_pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) | ||
329 | { | ||
330 | u32 m; | ||
331 | |||
332 | m = omap3_get_mem_bank_lastmemst_mask(bank); | ||
333 | |||
334 | return omap2_prm_read_mod_bits_shift(pwrdm->prcm_offs, | ||
335 | OMAP3430_PM_PREPWSTST, m); | ||
336 | } | ||
337 | |||
338 | static int omap3_pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) | ||
339 | { | ||
340 | omap2_prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); | ||
341 | return 0; | ||
342 | } | ||
343 | |||
344 | static int omap3_pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) | ||
345 | { | ||
346 | return omap2_prm_rmw_mod_reg_bits(0, | ||
347 | 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, | ||
348 | pwrdm->prcm_offs, OMAP2_PM_PWSTCTRL); | ||
349 | } | ||
350 | |||
351 | static int omap3_pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) | ||
352 | { | ||
353 | return omap2_prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, | ||
354 | 0, pwrdm->prcm_offs, | ||
355 | OMAP2_PM_PWSTCTRL); | ||
356 | } | ||
357 | |||
358 | struct pwrdm_ops omap3_pwrdm_operations = { | ||
359 | .pwrdm_set_next_pwrst = omap2_pwrdm_set_next_pwrst, | ||
360 | .pwrdm_read_next_pwrst = omap2_pwrdm_read_next_pwrst, | ||
361 | .pwrdm_read_pwrst = omap2_pwrdm_read_pwrst, | ||
362 | .pwrdm_read_prev_pwrst = omap3_pwrdm_read_prev_pwrst, | ||
363 | .pwrdm_set_logic_retst = omap2_pwrdm_set_logic_retst, | ||
364 | .pwrdm_read_logic_pwrst = omap3_pwrdm_read_logic_pwrst, | ||
365 | .pwrdm_read_logic_retst = omap3_pwrdm_read_logic_retst, | ||
366 | .pwrdm_read_prev_logic_pwrst = omap3_pwrdm_read_prev_logic_pwrst, | ||
367 | .pwrdm_set_mem_onst = omap2_pwrdm_set_mem_onst, | ||
368 | .pwrdm_set_mem_retst = omap2_pwrdm_set_mem_retst, | ||
369 | .pwrdm_read_mem_pwrst = omap2_pwrdm_read_mem_pwrst, | ||
370 | .pwrdm_read_mem_retst = omap2_pwrdm_read_mem_retst, | ||
371 | .pwrdm_read_prev_mem_pwrst = omap3_pwrdm_read_prev_mem_pwrst, | ||
372 | .pwrdm_clear_all_prev_pwrst = omap3_pwrdm_clear_all_prev_pwrst, | ||
373 | .pwrdm_enable_hdwr_sar = omap3_pwrdm_enable_hdwr_sar, | ||
374 | .pwrdm_disable_hdwr_sar = omap3_pwrdm_disable_hdwr_sar, | ||
375 | .pwrdm_wait_transition = omap2_pwrdm_wait_transition, | ||
376 | }; | ||
377 | |||
378 | /* | ||
379 | * | ||
380 | */ | ||
381 | |||
382 | static struct prm_ll_data omap3xxx_prm_ll_data = { | ||
383 | .read_reset_sources = &omap3xxx_prm_read_reset_sources, | ||
384 | }; | ||
385 | |||
386 | int __init omap3xxx_prm_init(void) | ||
387 | { | ||
388 | if (!cpu_is_omap34xx()) | ||
389 | return 0; | ||
390 | |||
391 | return prm_register(&omap3xxx_prm_ll_data); | ||
392 | } | ||
393 | |||
394 | static int __init omap3xxx_prm_late_init(void) | ||
395 | { | ||
396 | int ret; | ||
397 | |||
398 | if (!cpu_is_omap34xx()) | ||
399 | return 0; | ||
400 | |||
401 | omap3xxx_prm_enable_io_wakeup(); | ||
402 | ret = omap_prcm_register_chain_handler(&omap3_prcm_irq_setup); | ||
403 | if (!ret) | ||
404 | irq_set_status_flags(omap_prcm_event_to_irq("io"), | ||
405 | IRQ_NOAUTOEN); | ||
406 | |||
407 | return ret; | ||
408 | } | ||
409 | subsys_initcall(omap3xxx_prm_late_init); | ||
410 | |||
411 | static void __exit omap3xxx_prm_exit(void) | ||
412 | { | ||
413 | if (!cpu_is_omap34xx()) | ||
414 | return; | ||
415 | |||
416 | /* Should never happen */ | ||
417 | WARN(prm_unregister(&omap3xxx_prm_ll_data), | ||
418 | "%s: prm_ll_data function pointer mismatch\n", __func__); | ||
419 | } | ||
420 | __exitcall(omap3xxx_prm_exit); | ||