aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/m68knommu/platform/5272/intc.c56
1 files changed, 51 insertions, 5 deletions
diff --git a/arch/m68knommu/platform/5272/intc.c b/arch/m68knommu/platform/5272/intc.c
index a61c9c288f40..3cf681c177aa 100644
--- a/arch/m68knommu/platform/5272/intc.c
+++ b/arch/m68knommu/platform/5272/intc.c
@@ -12,6 +12,7 @@
12#include <linux/init.h> 12#include <linux/init.h>
13#include <linux/kernel.h> 13#include <linux/kernel.h>
14#include <linux/interrupt.h> 14#include <linux/interrupt.h>
15#include <linux/kernel_stat.h>
15#include <linux/irq.h> 16#include <linux/irq.h>
16#include <linux/io.h> 17#include <linux/io.h>
17#include <asm/coldfire.h> 18#include <asm/coldfire.h>
@@ -29,6 +30,10 @@
29 * via a set of 4 "Interrupt Controller Registers" (ICR). There is a 30 * via a set of 4 "Interrupt Controller Registers" (ICR). There is a
30 * loose mapping of vector number to register and internal bits, but 31 * loose mapping of vector number to register and internal bits, but
31 * a table is the easiest and quickest way to map them. 32 * a table is the easiest and quickest way to map them.
33 *
34 * Note that the external interrupts are edge triggered (unlike the
35 * internal interrupt sources which are level triggered). Which means
36 * they also need acknowledgeing via acknowledge bits.
32 */ 37 */
33struct irqmap { 38struct irqmap {
34 unsigned char icr; 39 unsigned char icr;
@@ -68,6 +73,11 @@ static struct irqmap intc_irqmap[MCFINT_VECMAX - MCFINT_VECBASE] = {
68 /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, }, 73 /*MCF_IRQ_SWTO*/ { .icr = MCFSIM_ICR4, .index = 16, .ack = 0, },
69}; 74};
70 75
76/*
77 * The act of masking the interrupt also has a side effect of 'ack'ing
78 * an interrupt on this irq (for the external irqs). So this mask function
79 * is also an ack_mask function.
80 */
71static void intc_irq_mask(unsigned int irq) 81static void intc_irq_mask(unsigned int irq)
72{ 82{
73 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) { 83 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
@@ -95,7 +105,9 @@ static void intc_irq_ack(unsigned int irq)
95 irq -= MCFINT_VECBASE; 105 irq -= MCFINT_VECBASE;
96 if (intc_irqmap[irq].ack) { 106 if (intc_irqmap[irq].ack) {
97 u32 v; 107 u32 v;
98 v = 0xd << intc_irqmap[irq].index; 108 v = readl(MCF_MBAR + intc_irqmap[irq].icr);
109 v &= (0x7 << intc_irqmap[irq].index);
110 v |= (0x8 << intc_irqmap[irq].index);
99 writel(v, MCF_MBAR + intc_irqmap[irq].icr); 111 writel(v, MCF_MBAR + intc_irqmap[irq].icr);
100 } 112 }
101 } 113 }
@@ -103,21 +115,47 @@ static void intc_irq_ack(unsigned int irq)
103 115
104static int intc_irq_set_type(unsigned int irq, unsigned int type) 116static int intc_irq_set_type(unsigned int irq, unsigned int type)
105{ 117{
106 /* We can set the edge type here for external interrupts */ 118 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX)) {
119 irq -= MCFINT_VECBASE;
120 if (intc_irqmap[irq].ack) {
121 u32 v;
122 v = readl(MCF_MBAR + MCFSIM_PITR);
123 if (type == IRQ_TYPE_EDGE_FALLING)
124 v &= ~(0x1 << (32 - irq));
125 else
126 v |= (0x1 << (32 - irq));
127 writel(v, MCF_MBAR + MCFSIM_PITR);
128 }
129 }
107 return 0; 130 return 0;
108} 131}
109 132
133/*
134 * Simple flow handler to deal with the external edge triggered interrupts.
135 * We need to be careful with the masking/acking due to the side effects
136 * of masking an interrupt.
137 */
138static void intc_external_irq(unsigned int irq, struct irq_desc *desc)
139{
140 kstat_incr_irqs_this_cpu(irq, desc);
141 desc->status |= IRQ_INPROGRESS;
142 desc->chip->ack(irq);
143 handle_IRQ_event(irq, desc->action);
144 desc->status &= ~IRQ_INPROGRESS;
145}
146
110static struct irq_chip intc_irq_chip = { 147static struct irq_chip intc_irq_chip = {
111 .name = "CF-INTC", 148 .name = "CF-INTC",
112 .mask = intc_irq_mask, 149 .mask = intc_irq_mask,
113 .unmask = intc_irq_unmask, 150 .unmask = intc_irq_unmask,
151 .mask_ack = intc_irq_mask,
114 .ack = intc_irq_ack, 152 .ack = intc_irq_ack,
115 .set_type = intc_irq_set_type, 153 .set_type = intc_irq_set_type,
116}; 154};
117 155
118void __init init_IRQ(void) 156void __init init_IRQ(void)
119{ 157{
120 int irq; 158 int irq, edge;
121 159
122 init_vectors(); 160 init_vectors();
123 161
@@ -129,8 +167,16 @@ void __init init_IRQ(void)
129 167
130 for (irq = 0; (irq < NR_IRQS); irq++) { 168 for (irq = 0; (irq < NR_IRQS); irq++) {
131 set_irq_chip(irq, &intc_irq_chip); 169 set_irq_chip(irq, &intc_irq_chip);
132 set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); 170 edge = 0;
133 set_irq_handler(irq, handle_level_irq); 171 if ((irq >= MCFINT_VECBASE) && (irq <= MCFINT_VECMAX))
172 edge = intc_irqmap[irq - MCFINT_VECBASE].ack;
173 if (edge) {
174 set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
175 set_irq_handler(irq, intc_external_irq);
176 } else {
177 set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH);
178 set_irq_handler(irq, handle_level_irq);
179 }
134 } 180 }
135} 181}
136 182