diff options
-rw-r--r-- | arch/m68knommu/platform/5272/intc.c | 56 |
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 | */ |
33 | struct irqmap { | 38 | struct 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 | */ | ||
71 | static void intc_irq_mask(unsigned int irq) | 81 | static 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 | ||
104 | static int intc_irq_set_type(unsigned int irq, unsigned int type) | 116 | static 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 | */ | ||
138 | static 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 | |||
110 | static struct irq_chip intc_irq_chip = { | 147 | static 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 | ||
118 | void __init init_IRQ(void) | 156 | void __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 | ||