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