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