aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-omap2/prcm-common.h16
-rw-r--r--arch/arm/mach-omap2/prm2xxx_3xxx.c37
-rw-r--r--arch/arm/mach-omap2/prm2xxx_3xxx.h2
-rw-r--r--arch/arm/mach-omap2/prm44xx.c48
-rw-r--r--arch/arm/mach-omap2/prm44xx.h2
-rw-r--r--arch/arm/mach-omap2/prm_common.c47
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 */
447struct omap_prcm_irq_setup { 452struct 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);
468extern int omap_prcm_register_chain_handler( 478extern int omap_prcm_register_chain_handler(
469 struct omap_prcm_irq_setup *irq_setup); 479 struct omap_prcm_irq_setup *irq_setup);
470extern int omap_prcm_event_to_irq(const char *event); 480extern int omap_prcm_event_to_irq(const char *event);
481extern void omap_prcm_irq_prepare(void);
482extern 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 */
259void 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 */
279void 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 */
319extern void omap3xxx_prm_read_pending_irqs(unsigned long *events); 319extern void omap3xxx_prm_read_pending_irqs(unsigned long *events);
320extern void omap3xxx_prm_ocp_barrier(void); 320extern void omap3xxx_prm_ocp_barrier(void);
321extern void omap3xxx_prm_save_and_clear_irqen(u32 *saved_mask);
322extern 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 */
178void 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 */
207void 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 */
767extern void omap44xx_prm_read_pending_irqs(unsigned long *events); 767extern void omap44xx_prm_read_pending_irqs(unsigned long *events);
768extern void omap44xx_prm_ocp_barrier(void); 768extern void omap44xx_prm_ocp_barrier(void);
769extern void omap44xx_prm_save_and_clear_irqen(u32 *saved_mask);
770extern 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
206void omap_prcm_irq_prepare(void)
207{
208 prcm_irq_setup->suspended = true;
209}
210
211void 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 }