Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | /* * intc-simr.c * * Interrupt controller code for the ColdFire 5208, 5207 & 532x parts. * * (C) Copyright 2009-2011, Greg Ungerer <gerg@snapgear.com> * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of this archive * for more details. */ #include <linux/types.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/io.h> #include <asm/coldfire.h> #include <asm/mcfsim.h> #include <asm/traps.h> /* * The EDGE Port interrupts are the fixed 7 external interrupts. * They need some special treatment, for example they need to be acked. */ #ifdef CONFIG_M520x /* * The 520x parts only support a limited range of these external * interrupts, only 1, 4 and 7 (as interrupts 65, 66 and 67). */ #define EINT0 64 /* Is not actually used, but spot reserved for it */ #define EINT1 65 /* EDGE Port interrupt 1 */ #define EINT4 66 /* EDGE Port interrupt 4 */ #define EINT7 67 /* EDGE Port interrupt 7 */ static unsigned int irqebitmap[] = { 0, 1, 4, 7 }; static inline unsigned int irq2ebit(unsigned int irq) { return irqebitmap[irq - EINT0]; } #else /* * Most of the ColdFire parts with the EDGE Port module just have * a strait direct mapping of the 7 external interrupts. Although * there is a bit reserved for 0, it is not used. */ #define EINT0 64 /* Is not actually used, but spot reserved for it */ #define EINT1 65 /* EDGE Port interrupt 1 */ #define EINT7 71 /* EDGE Port interrupt 7 */ static inline unsigned int irq2ebit(unsigned int irq) { return irq - EINT0; } #endif /* * There maybe one, two or three interrupt control units, each has 64 * interrupts. If there is no second or third unit then MCFINTC1_* or * MCFINTC2_* defines will be 0 (and code for them optimized away). */ static void intc_irq_mask(struct irq_data *d) { unsigned int irq = d->irq - MCFINT_VECBASE; if (MCFINTC2_SIMR && (irq > 127)) __raw_writeb(irq - 128, MCFINTC2_SIMR); else if (MCFINTC1_SIMR && (irq > 63)) __raw_writeb(irq - 64, MCFINTC1_SIMR); else __raw_writeb(irq, MCFINTC0_SIMR); } static void intc_irq_unmask(struct irq_data *d) { unsigned int irq = d->irq - MCFINT_VECBASE; if (MCFINTC2_CIMR && (irq > 127)) __raw_writeb(irq - 128, MCFINTC2_CIMR); else if (MCFINTC1_CIMR && (irq > 63)) __raw_writeb(irq - 64, MCFINTC1_CIMR); else __raw_writeb(irq, MCFINTC0_CIMR); } static void intc_irq_ack(struct irq_data *d) { unsigned int ebit = irq2ebit(d->irq); __raw_writeb(0x1 << ebit, MCFEPORT_EPFR); } static unsigned int intc_irq_startup(struct irq_data *d) { unsigned int irq = d->irq; if ((irq >= EINT1) && (irq <= EINT7)) { unsigned int ebit = irq2ebit(irq); u8 v; #if defined(MCFEPORT_EPDDR) /* Set EPORT line as input */ v = __raw_readb(MCFEPORT_EPDDR); __raw_writeb(v & ~(0x1 << ebit), MCFEPORT_EPDDR); #endif /* Set EPORT line as interrupt source */ v = __raw_readb(MCFEPORT_EPIER); __raw_writeb(v | (0x1 << ebit), MCFEPORT_EPIER); } irq -= MCFINT_VECBASE; if (MCFINTC2_ICR0 && (irq > 127)) __raw_writeb(5, MCFINTC2_ICR0 + irq - 128); else if (MCFINTC1_ICR0 && (irq > 63)) __raw_writeb(5, MCFINTC1_ICR0 + irq - 64); else __raw_writeb(5, MCFINTC0_ICR0 + irq); intc_irq_unmask(d); return 0; } static int intc_irq_set_type(struct irq_data *d, unsigned int type) { unsigned int ebit, irq = d->irq; u16 pa, tb; switch (type) { case IRQ_TYPE_EDGE_RISING: tb = 0x1; break; case IRQ_TYPE_EDGE_FALLING: tb = 0x2; break; case IRQ_TYPE_EDGE_BOTH: tb = 0x3; break; default: /* Level triggered */ tb = 0; break; } if (tb) irq_set_handler(irq, handle_edge_irq); ebit = irq2ebit(irq) * 2; pa = __raw_readw(MCFEPORT_EPPAR); pa = (pa & ~(0x3 << ebit)) | (tb << ebit); __raw_writew(pa, MCFEPORT_EPPAR); return 0; } static struct irq_chip intc_irq_chip = { .name = "CF-INTC", .irq_startup = intc_irq_startup, .irq_mask = intc_irq_mask, .irq_unmask = intc_irq_unmask, }; static struct irq_chip intc_irq_chip_edge_port = { .name = "CF-INTC-EP", .irq_startup = intc_irq_startup, .irq_mask = intc_irq_mask, .irq_unmask = intc_irq_unmask, .irq_ack = intc_irq_ack, .irq_set_type = intc_irq_set_type, }; void __init init_IRQ(void) { int irq, eirq; /* Mask all interrupt sources */ __raw_writeb(0xff, MCFINTC0_SIMR); if (MCFINTC1_SIMR) __raw_writeb(0xff, MCFINTC1_SIMR); if (MCFINTC2_SIMR) __raw_writeb(0xff, MCFINTC2_SIMR); eirq = MCFINT_VECBASE + 64 + (MCFINTC1_ICR0 ? 64 : 0) + (MCFINTC2_ICR0 ? 64 : 0); for (irq = MCFINT_VECBASE; (irq < eirq); irq++) { if ((irq >= EINT1) && (irq <= EINT7)) irq_set_chip(irq, &intc_irq_chip_edge_port); else irq_set_chip(irq, &intc_irq_chip); irq_set_irq_type(irq, IRQ_TYPE_LEVEL_HIGH); irq_set_handler(irq, handle_level_irq); } } |