summaryrefslogtreecommitdiffstats
path: root/arch/microblaze/kernel/intc.c
diff options
context:
space:
mode:
authorMichal Simek <michal.simek@xilinx.com>2012-11-05 11:51:13 +0100
committerMichal Simek <michal.simek@xilinx.com>2012-12-13 14:39:00 +0100
commit7958a689199fbc557fad7d79b16b2017a7e40a71 (patch)
treed8501caf1ac27f3d7bbb534aeaa1a088dc2d44dc /arch/microblaze/kernel/intc.c
parentb881e99eeadcc5a6194e225ba3986fde545469d3 (diff)
microblaze: Fix intc_enable_or_unmask function
Intc_enable_or_unmask() is called at the last stage of handle_level_irq(). This function enables the irq first (Write INTC.SIE) and clear ISR next (Write INTC.IAR). This would create problems that processor will get into a new interrupt as soon as SIE is written because the previous level interrupt has been captured by INTC. If the description bring some puzzles, here is the details of how interrupt is handled for MicroBlaze after Interrupt signal is detected: 1. disable INTC (INTC.CIE = 1) 2. Acknowledge INTC (INTC.IAR = 1) 3. gets into interrupt source's handler, for example, timer's handler 4. Timer is interrupt handler acknowledge Timer Interrupt Status (Timer.TCSR0[23] = 1), and return 5. Enable INTC (INTC.SIE = 1) 6. Acknowledge INTC (INTC.IAR = 1) INTC continue to capture source inputs even if INTC is disabled (INTC.IER == 1). So between the gap of step 2 and step 3, the level interrupt from source makes INTC captures a new interrupt and thus the INTC.ISR = 1 during step 3, 4, and 5. When INTC is enabled in step 5, INTC's interrupt output will go high immediately. In summary, the driver should issue step 6 before step 5. Signed-off-by: Michal Simek <michal.simek@xilinx.com>
Diffstat (limited to 'arch/microblaze/kernel/intc.c')
-rw-r--r--arch/microblaze/kernel/intc.c3
1 files changed, 2 insertions, 1 deletions
diff --git a/arch/microblaze/kernel/intc.c b/arch/microblaze/kernel/intc.c
index 6c54d4dcdec..7b4c6444bc1 100644
--- a/arch/microblaze/kernel/intc.c
+++ b/arch/microblaze/kernel/intc.c
@@ -44,7 +44,6 @@ static void intc_enable_or_unmask(struct irq_data *d)
unsigned long mask = 1 << d->hwirq;
pr_debug("enable_or_unmask: %ld\n", d->hwirq);
- out_be32(INTC_BASE + SIE, mask);
/* ack level irqs because they can't be acked during
* ack function since the handle_level_irq function
@@ -52,6 +51,8 @@ static void intc_enable_or_unmask(struct irq_data *d)
*/
if (irqd_is_level_type(d))
out_be32(INTC_BASE + IAR, mask);
+
+ out_be32(INTC_BASE + SIE, mask);
}
static void intc_disable_or_mask(struct irq_data *d)