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 | ||
