aboutsummaryrefslogtreecommitdiffstats
path: root/arch/m68knommu
diff options
context:
space:
mode:
authorGreg Ungerer <gerg@uclinux.org>2010-10-12 23:42:22 -0400
committerGreg Ungerer <gerg@uclinux.org>2010-10-20 20:17:31 -0400
commita405f833f4cd6490caaf381efe58a5628b545733 (patch)
tree261b5bafe00fbda4aec2acccec5395b14c87f21e /arch/m68knommu
parent730251f27df1ed0177609d1e49817f0c3ada0b1a (diff)
m68knommu: support the external GPIO based interrupts of the 5272
The external GPIO interrupts of the ColdFire 5272 SoC are edge triggered, unlike the internal interrupt sources (which are level triggered). Add proper support for these interrupts. Signed-off-by: Greg Ungerer <gerg@uclinux.org>
Diffstat (limited to 'arch/m68knommu')
-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