diff options
-rw-r--r-- | arch/arm/mach-omap2/prcm-common.h | 16 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm2xxx_3xxx.c | 37 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm2xxx_3xxx.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.c | 48 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm44xx.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-omap2/prm_common.c | 47 |
6 files changed, 148 insertions, 4 deletions
diff --git a/arch/arm/mach-omap2/prcm-common.h b/arch/arm/mach-omap2/prcm-common.h index 76db3795e68f..0f69d8fc5628 100644 --- a/arch/arm/mach-omap2/prcm-common.h +++ b/arch/arm/mach-omap2/prcm-common.h | |||
@@ -437,11 +437,16 @@ struct omap_prcm_irq { | |||
437 | * @irq: MPU IRQ asserted when a PRCM interrupt arrives | 437 | * @irq: MPU IRQ asserted when a PRCM interrupt arrives |
438 | * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending | 438 | * @read_pending_irqs: fn ptr to determine if any PRCM IRQs are pending |
439 | * @ocp_barrier: fn ptr to force buffered PRM writes to complete | 439 | * @ocp_barrier: fn ptr to force buffered PRM writes to complete |
440 | * @save_and_clear_irqen: fn ptr to save and clear IRQENABLE regs | ||
441 | * @restore_irqen: fn ptr to save and clear IRQENABLE regs | ||
442 | * @saved_mask: IRQENABLE regs are saved here during suspend | ||
440 | * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true | 443 | * @priority_mask: 1 bit per IRQ, set to 1 if omap_prcm_irq.priority = true |
441 | * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init | 444 | * @base_irq: base dynamic IRQ number, returned from irq_alloc_descs() in init |
445 | * @suspended: set to true after Linux suspend code has called our ->prepare() | ||
446 | * @suspend_save_flag: set to true after IRQ masks have been saved and disabled | ||
442 | * | 447 | * |
443 | * @priority_mask and @base_irq are populated dynamically during | 448 | * @saved_mask, @priority_mask, @base_irq, @suspended, and |
444 | * omap_prcm_register_chain_handler() - these fields are not to be | 449 | * @suspend_save_flag are populated dynamically, and are not to be |
445 | * specified in static initializers. | 450 | * specified in static initializers. |
446 | */ | 451 | */ |
447 | struct omap_prcm_irq_setup { | 452 | struct omap_prcm_irq_setup { |
@@ -453,8 +458,13 @@ struct omap_prcm_irq_setup { | |||
453 | int irq; | 458 | int irq; |
454 | void (*read_pending_irqs)(unsigned long *events); | 459 | void (*read_pending_irqs)(unsigned long *events); |
455 | void (*ocp_barrier)(void); | 460 | void (*ocp_barrier)(void); |
461 | void (*save_and_clear_irqen)(u32 *saved_mask); | ||
462 | void (*restore_irqen)(u32 *saved_mask); | ||
463 | u32 *saved_mask; | ||
456 | u32 *priority_mask; | 464 | u32 *priority_mask; |
457 | int base_irq; | 465 | int base_irq; |
466 | bool suspended; | ||
467 | bool suspend_save_flag; | ||
458 | }; | 468 | }; |
459 | 469 | ||
460 | /* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ | 470 | /* OMAP_PRCM_IRQ: convenience macro for creating struct omap_prcm_irq records */ |
@@ -468,6 +478,8 @@ extern void omap_prcm_irq_cleanup(void); | |||
468 | extern int omap_prcm_register_chain_handler( | 478 | extern int omap_prcm_register_chain_handler( |
469 | struct omap_prcm_irq_setup *irq_setup); | 479 | struct omap_prcm_irq_setup *irq_setup); |
470 | extern int omap_prcm_event_to_irq(const char *event); | 480 | extern int omap_prcm_event_to_irq(const char *event); |
481 | extern void omap_prcm_irq_prepare(void); | ||
482 | extern void omap_prcm_irq_complete(void); | ||
471 | 483 | ||
472 | # endif | 484 | # endif |
473 | 485 | ||
diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.c b/arch/arm/mach-omap2/prm2xxx_3xxx.c index 177c3ddba788..58d9ce70e792 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.c +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.c | |||
@@ -244,3 +244,40 @@ void omap3xxx_prm_ocp_barrier(void) | |||
244 | { | 244 | { |
245 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | 245 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); |
246 | } | 246 | } |
247 | |||
248 | /** | ||
249 | * omap3xxx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU reg | ||
250 | * @saved_mask: ptr to a u32 array to save IRQENABLE bits | ||
251 | * | ||
252 | * Save the PRM_IRQENABLE_MPU register to @saved_mask. @saved_mask | ||
253 | * must be allocated by the caller. Intended to be used in the PRM | ||
254 | * interrupt handler suspend callback. The OCP barrier is needed to | ||
255 | * ensure the write to disable PRM interrupts reaches the PRM before | ||
256 | * returning; otherwise, spurious interrupts might occur. No return | ||
257 | * value. | ||
258 | */ | ||
259 | void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask) | ||
260 | { | ||
261 | saved_mask[0] = omap2_prm_read_mod_reg(OCP_MOD, | ||
262 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
263 | omap2_prm_write_mod_reg(0, OCP_MOD, OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
264 | |||
265 | /* OCP barrier */ | ||
266 | omap2_prm_read_mod_reg(OCP_MOD, OMAP3_PRM_REVISION_OFFSET); | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * omap3xxx_prm_restore_irqen - set PRM_IRQENABLE_MPU register from args | ||
271 | * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously | ||
272 | * | ||
273 | * Restore the PRM_IRQENABLE_MPU register from @saved_mask. Intended | ||
274 | * to be used in the PRM interrupt handler resume callback to restore | ||
275 | * values saved by omap3xxx_prm_save_and_clear_irqen(). No OCP | ||
276 | * barrier should be needed here; any pending PRM interrupts will fire | ||
277 | * once the writes reach the PRM. No return value. | ||
278 | */ | ||
279 | void omap3xxx_prm_restore_irqen(u32 *saved_mask) | ||
280 | { | ||
281 | omap2_prm_write_mod_reg(saved_mask[0], OCP_MOD, | ||
282 | OMAP3_PRM_IRQENABLE_MPU_OFFSET); | ||
283 | } | ||
diff --git a/arch/arm/mach-omap2/prm2xxx_3xxx.h b/arch/arm/mach-omap2/prm2xxx_3xxx.h index 3ef0e77ff936..70ac2a19dc5f 100644 --- a/arch/arm/mach-omap2/prm2xxx_3xxx.h +++ b/arch/arm/mach-omap2/prm2xxx_3xxx.h | |||
@@ -318,6 +318,8 @@ extern u32 omap3_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); | |||
318 | /* PRM interrupt-related functions */ | 318 | /* PRM interrupt-related functions */ |
319 | extern void omap3xxx_prm_read_pending_irqs(unsigned long *events); | 319 | extern void omap3xxx_prm_read_pending_irqs(unsigned long *events); |
320 | extern void omap3xxx_prm_ocp_barrier(void); | 320 | extern void omap3xxx_prm_ocp_barrier(void); |
321 | extern void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask); | ||
322 | extern void omap3xxx_prm_restore_irqen(u32 *saved_mask); | ||
321 | 323 | ||
322 | #endif /* CONFIG_ARCH_OMAP4 */ | 324 | #endif /* CONFIG_ARCH_OMAP4 */ |
323 | 325 | ||
diff --git a/arch/arm/mach-omap2/prm44xx.c b/arch/arm/mach-omap2/prm44xx.c index 9b21154f0162..c4be5d94a019 100644 --- a/arch/arm/mach-omap2/prm44xx.c +++ b/arch/arm/mach-omap2/prm44xx.c | |||
@@ -163,3 +163,51 @@ void omap44xx_prm_ocp_barrier(void) | |||
163 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | 163 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, |
164 | OMAP4_REVISION_PRM_OFFSET); | 164 | OMAP4_REVISION_PRM_OFFSET); |
165 | } | 165 | } |
166 | |||
167 | /** | ||
168 | * omap44xx_prm_save_and_clear_irqen - save/clear PRM_IRQENABLE_MPU* regs | ||
169 | * @saved_mask: ptr to a u32 array to save IRQENABLE bits | ||
170 | * | ||
171 | * Save the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers to | ||
172 | * @saved_mask. @saved_mask must be allocated by the caller. | ||
173 | * Intended to be used in the PRM interrupt handler suspend callback. | ||
174 | * The OCP barrier is needed to ensure the write to disable PRM | ||
175 | * interrupts reaches the PRM before returning; otherwise, spurious | ||
176 | * interrupts might occur. No return value. | ||
177 | */ | ||
178 | void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask) | ||
179 | { | ||
180 | saved_mask[0] = | ||
181 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
182 | OMAP4_PRM_IRQSTATUS_MPU_OFFSET); | ||
183 | saved_mask[1] = | ||
184 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
185 | OMAP4_PRM_IRQSTATUS_MPU_2_OFFSET); | ||
186 | |||
187 | omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, | ||
188 | OMAP4_PRM_IRQENABLE_MPU_OFFSET); | ||
189 | omap4_prm_write_inst_reg(0, OMAP4430_PRM_DEVICE_INST, | ||
190 | OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); | ||
191 | |||
192 | /* OCP barrier */ | ||
193 | omap4_prm_read_inst_reg(OMAP4430_PRM_DEVICE_INST, | ||
194 | OMAP4_REVISION_PRM_OFFSET); | ||
195 | } | ||
196 | |||
197 | /** | ||
198 | * omap44xx_prm_restore_irqen - set PRM_IRQENABLE_MPU* registers from args | ||
199 | * @saved_mask: ptr to a u32 array of IRQENABLE bits saved previously | ||
200 | * | ||
201 | * Restore the PRM_IRQENABLE_MPU and PRM_IRQENABLE_MPU_2 registers from | ||
202 | * @saved_mask. Intended to be used in the PRM interrupt handler resume | ||
203 | * callback to restore values saved by omap44xx_prm_save_and_clear_irqen(). | ||
204 | * No OCP barrier should be needed here; any pending PRM interrupts will fire | ||
205 | * once the writes reach the PRM. No return value. | ||
206 | */ | ||
207 | void omap44xx_prm_restore_irqen(u32 *saved_mask) | ||
208 | { | ||
209 | omap4_prm_write_inst_reg(saved_mask[0], OMAP4430_PRM_DEVICE_INST, | ||
210 | OMAP4_PRM_IRQENABLE_MPU_OFFSET); | ||
211 | omap4_prm_write_inst_reg(saved_mask[1], OMAP4430_PRM_DEVICE_INST, | ||
212 | OMAP4_PRM_IRQENABLE_MPU_2_OFFSET); | ||
213 | } | ||
diff --git a/arch/arm/mach-omap2/prm44xx.h b/arch/arm/mach-omap2/prm44xx.h index bd7f2486e8b6..7978092946db 100644 --- a/arch/arm/mach-omap2/prm44xx.h +++ b/arch/arm/mach-omap2/prm44xx.h | |||
@@ -766,6 +766,8 @@ extern u32 omap4_prm_vcvp_rmw(u32 mask, u32 bits, u8 offset); | |||
766 | /* PRM interrupt-related functions */ | 766 | /* PRM interrupt-related functions */ |
767 | extern void omap44xx_prm_read_pending_irqs(unsigned long *events); | 767 | extern void omap44xx_prm_read_pending_irqs(unsigned long *events); |
768 | extern void omap44xx_prm_ocp_barrier(void); | 768 | extern void omap44xx_prm_ocp_barrier(void); |
769 | extern void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask); | ||
770 | extern void omap44xx_prm_restore_irqen(u32 *saved_mask); | ||
769 | 771 | ||
770 | # endif | 772 | # endif |
771 | 773 | ||
diff --git a/arch/arm/mach-omap2/prm_common.c b/arch/arm/mach-omap2/prm_common.c index 5694be56a947..860118ab43e2 100644 --- a/arch/arm/mach-omap2/prm_common.c +++ b/arch/arm/mach-omap2/prm_common.c | |||
@@ -89,10 +89,25 @@ static void omap_prcm_irq_handler(unsigned int irq, struct irq_desc *desc) | |||
89 | int nr_irqs = prcm_irq_setup->nr_regs * 32; | 89 | int nr_irqs = prcm_irq_setup->nr_regs * 32; |
90 | 90 | ||
91 | /* | 91 | /* |
92 | * If we are suspended, mask all interrupts from PRCM level, | ||
93 | * this does not ack them, and they will be pending until we | ||
94 | * re-enable the interrupts, at which point the | ||
95 | * omap_prcm_irq_handler will be executed again. The | ||
96 | * _save_and_clear_irqen() function must ensure that the PRM | ||
97 | * write to disable all IRQs has reached the PRM before | ||
98 | * returning, or spurious PRCM interrupts may occur during | ||
99 | * suspend. | ||
100 | */ | ||
101 | if (prcm_irq_setup->suspended) { | ||
102 | prcm_irq_setup->save_and_clear_irqen(prcm_irq_setup->saved_mask); | ||
103 | prcm_irq_setup->suspend_save_flag = true; | ||
104 | } | ||
105 | |||
106 | /* | ||
92 | * Loop until all pending irqs are handled, since | 107 | * Loop until all pending irqs are handled, since |
93 | * generic_handle_irq() can cause new irqs to come | 108 | * generic_handle_irq() can cause new irqs to come |
94 | */ | 109 | */ |
95 | while (1) { | 110 | while (!prcm_irq_setup->suspended) { |
96 | prcm_irq_setup->read_pending_irqs(pending); | 111 | prcm_irq_setup->read_pending_irqs(pending); |
97 | 112 | ||
98 | /* No bit set, then all IRQs are handled */ | 113 | /* No bit set, then all IRQs are handled */ |
@@ -174,6 +189,9 @@ void omap_prcm_irq_cleanup(void) | |||
174 | prcm_irq_chips = NULL; | 189 | prcm_irq_chips = NULL; |
175 | } | 190 | } |
176 | 191 | ||
192 | kfree(prcm_irq_setup->saved_mask); | ||
193 | prcm_irq_setup->saved_mask = NULL; | ||
194 | |||
177 | kfree(prcm_irq_setup->priority_mask); | 195 | kfree(prcm_irq_setup->priority_mask); |
178 | prcm_irq_setup->priority_mask = NULL; | 196 | prcm_irq_setup->priority_mask = NULL; |
179 | 197 | ||
@@ -185,6 +203,29 @@ void omap_prcm_irq_cleanup(void) | |||
185 | prcm_irq_setup->base_irq = 0; | 203 | prcm_irq_setup->base_irq = 0; |
186 | } | 204 | } |
187 | 205 | ||
206 | void omap_prcm_irq_prepare(void) | ||
207 | { | ||
208 | prcm_irq_setup->suspended = true; | ||
209 | } | ||
210 | |||
211 | void omap_prcm_irq_complete(void) | ||
212 | { | ||
213 | prcm_irq_setup->suspended = false; | ||
214 | |||
215 | /* If we have not saved the masks, do not attempt to restore */ | ||
216 | if (!prcm_irq_setup->suspend_save_flag) | ||
217 | return; | ||
218 | |||
219 | prcm_irq_setup->suspend_save_flag = false; | ||
220 | |||
221 | /* | ||
222 | * Re-enable all masked PRCM irq sources, this causes the PRCM | ||
223 | * interrupt to fire immediately if the events were masked | ||
224 | * previously in the chain handler | ||
225 | */ | ||
226 | prcm_irq_setup->restore_irqen(prcm_irq_setup->saved_mask); | ||
227 | } | ||
228 | |||
188 | /** | 229 | /** |
189 | * omap_prcm_register_chain_handler - initializes the prcm chained interrupt | 230 | * omap_prcm_register_chain_handler - initializes the prcm chained interrupt |
190 | * handler based on provided parameters | 231 | * handler based on provided parameters |
@@ -219,10 +260,12 @@ int omap_prcm_register_chain_handler(struct omap_prcm_irq_setup *irq_setup) | |||
219 | prcm_irq_setup = irq_setup; | 260 | prcm_irq_setup = irq_setup; |
220 | 261 | ||
221 | prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL); | 262 | prcm_irq_chips = kzalloc(sizeof(void *) * nr_regs, GFP_KERNEL); |
263 | prcm_irq_setup->saved_mask = kzalloc(sizeof(u32) * nr_regs, GFP_KERNEL); | ||
222 | prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs, | 264 | prcm_irq_setup->priority_mask = kzalloc(sizeof(u32) * nr_regs, |
223 | GFP_KERNEL); | 265 | GFP_KERNEL); |
224 | 266 | ||
225 | if (!prcm_irq_chips || !prcm_irq_setup->priority_mask) { | 267 | if (!prcm_irq_chips || !prcm_irq_setup->saved_mask || |
268 | !prcm_irq_setup->priority_mask) { | ||
226 | pr_err("PRCM: kzalloc failed\n"); | 269 | pr_err("PRCM: kzalloc failed\n"); |
227 | goto err; | 270 | goto err; |
228 | } | 271 | } |