aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorTero Kristo <t-kristo@ti.com>2011-12-16 16:36:58 -0500
committerPaul Walmsley <paul@pwsan.com>2011-12-16 16:36:58 -0500
commit91285b6fa296657d92dc2225100fb94aee869bf2 (patch)
treead06e6b86a2da5e09eb9222e73ed82286ac06542 /arch/arm/mach-omap2
parent0a84a91c37ada296ffe7147e73af99b5654628ec (diff)
ARM: OMAP: PRCM: add suspend prepare / finish support
PRCM chain handler needs to disable forwarding of interrupts during suspend, because runtime PM is disabled and most of the drivers are potentially not able to handle interrupts coming at this time. This patch masks all the PRCM interrupt events if a PRCM interrupt occurs during suspend, but does not ack them. Once suspend finish is called, all the masked events will be re-enabled, which causes immediate PRCM interrupt and handles the postponed event. The suspend prepare and complete callbacks will be called from pm34xx.c / pm44xx.c files in the following patches. The functions defined in this patch should eventually be moved to suspend->prepare and suspend->finish driver hooks, once the PRCM chain handler will be made as its own driver. Signed-off-by: Tero Kristo <t-kristo@ti.com> Cc: Kevin Hilman <khilman@ti.com> Cc: Paul Walmsley <paul@pwsan.com> Tested-by: Kevin Hilman <khilman@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> [paul@pwsan.com: add kerneldoc, add omap_prcm_irq_setup.saved_mask, add fn ptrs for save_and_clear_irqen() and restore_irqen()] Signed-off-by: Paul Walmsley <paul@pwsan.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-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 }