aboutsummaryrefslogtreecommitdiffstats
path: root/arch/frv/kernel/irq-routing.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/frv/kernel/irq-routing.c')
-rw-r--r--arch/frv/kernel/irq-routing.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/arch/frv/kernel/irq-routing.c b/arch/frv/kernel/irq-routing.c
new file mode 100644
index 000000000000..d4776d1f4e82
--- /dev/null
+++ b/arch/frv/kernel/irq-routing.c
@@ -0,0 +1,291 @@
1/* irq-routing.c: IRQ routing
2 *
3 * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved.
4 * Written by David Howells (dhowells@redhat.com)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 */
11
12#include <linux/sched.h>
13#include <linux/random.h>
14#include <linux/init.h>
15#include <linux/serial_reg.h>
16#include <asm/io.h>
17#include <asm/irq-routing.h>
18#include <asm/irc-regs.h>
19#include <asm/serial-regs.h>
20#include <asm/dma.h>
21
22struct irq_level frv_irq_levels[16] = {
23 [0 ... 15] = {
24 .lock = SPIN_LOCK_UNLOCKED,
25 }
26};
27
28struct irq_group *irq_groups[NR_IRQ_GROUPS];
29
30extern struct irq_group frv_cpu_irqs;
31
32void __init frv_irq_route(struct irq_source *source, int irqlevel)
33{
34 source->level = &frv_irq_levels[irqlevel];
35 source->next = frv_irq_levels[irqlevel].sources;
36 frv_irq_levels[irqlevel].sources = source;
37}
38
39void __init frv_irq_route_external(struct irq_source *source, int irq)
40{
41 int irqlevel = 0;
42
43 switch (irq) {
44 case IRQ_CPU_EXTERNAL0: irqlevel = IRQ_XIRQ0_LEVEL; break;
45 case IRQ_CPU_EXTERNAL1: irqlevel = IRQ_XIRQ1_LEVEL; break;
46 case IRQ_CPU_EXTERNAL2: irqlevel = IRQ_XIRQ2_LEVEL; break;
47 case IRQ_CPU_EXTERNAL3: irqlevel = IRQ_XIRQ3_LEVEL; break;
48 case IRQ_CPU_EXTERNAL4: irqlevel = IRQ_XIRQ4_LEVEL; break;
49 case IRQ_CPU_EXTERNAL5: irqlevel = IRQ_XIRQ5_LEVEL; break;
50 case IRQ_CPU_EXTERNAL6: irqlevel = IRQ_XIRQ6_LEVEL; break;
51 case IRQ_CPU_EXTERNAL7: irqlevel = IRQ_XIRQ7_LEVEL; break;
52 default: BUG();
53 }
54
55 source->level = &frv_irq_levels[irqlevel];
56 source->next = frv_irq_levels[irqlevel].sources;
57 frv_irq_levels[irqlevel].sources = source;
58}
59
60void __init frv_irq_set_group(struct irq_group *group)
61{
62 irq_groups[group->first_irq >> NR_IRQ_LOG2_ACTIONS_PER_GROUP] = group;
63}
64
65void distribute_irqs(struct irq_group *group, unsigned long irqmask)
66{
67 struct irqaction *action;
68 int irq;
69
70 while (irqmask) {
71 asm("scan %1,gr0,%0" : "=r"(irq) : "r"(irqmask));
72 if (irq < 0 || irq > 31)
73 asm volatile("break");
74 irq = 31 - irq;
75
76 irqmask &= ~(1 << irq);
77 action = group->actions[irq];
78
79 irq += group->first_irq;
80
81 if (action) {
82 int status = 0;
83
84// if (!(action->flags & SA_INTERRUPT))
85// local_irq_enable();
86
87 do {
88 status |= action->flags;
89 action->handler(irq, action->dev_id, __frame);
90 action = action->next;
91 } while (action);
92
93 if (status & SA_SAMPLE_RANDOM)
94 add_interrupt_randomness(irq);
95 local_irq_disable();
96 }
97 }
98}
99
100/*****************************************************************************/
101/*
102 * CPU UART interrupts
103 */
104static void frv_cpuuart_doirq(struct irq_source *source)
105{
106// uint8_t iir = readb(source->muxdata + UART_IIR * 8);
107// if ((iir & 0x0f) != UART_IIR_NO_INT)
108 distribute_irqs(&frv_cpu_irqs, source->irqmask);
109}
110
111struct irq_source frv_cpuuart[2] = {
112#define __CPUUART(X, A) \
113 [X] = { \
114 .muxname = "uart", \
115 .muxdata = (volatile void __iomem *) A, \
116 .irqmask = 1 << IRQ_CPU_UART##X, \
117 .doirq = frv_cpuuart_doirq, \
118 }
119
120 __CPUUART(0, UART0_BASE),
121 __CPUUART(1, UART1_BASE),
122};
123
124/*****************************************************************************/
125/*
126 * CPU DMA interrupts
127 */
128static void frv_cpudma_doirq(struct irq_source *source)
129{
130 uint32_t cstr = readl(source->muxdata + DMAC_CSTRx);
131 if (cstr & DMAC_CSTRx_INT)
132 distribute_irqs(&frv_cpu_irqs, source->irqmask);
133}
134
135struct irq_source frv_cpudma[8] = {
136#define __CPUDMA(X, A) \
137 [X] = { \
138 .muxname = "dma", \
139 .muxdata = (volatile void __iomem *) A, \
140 .irqmask = 1 << IRQ_CPU_DMA##X, \
141 .doirq = frv_cpudma_doirq, \
142 }
143
144 __CPUDMA(0, 0xfe000900),
145 __CPUDMA(1, 0xfe000980),
146 __CPUDMA(2, 0xfe000a00),
147 __CPUDMA(3, 0xfe000a80),
148 __CPUDMA(4, 0xfe001000),
149 __CPUDMA(5, 0xfe001080),
150 __CPUDMA(6, 0xfe001100),
151 __CPUDMA(7, 0xfe001180),
152};
153
154/*****************************************************************************/
155/*
156 * CPU timer interrupts - can't tell whether they've generated an interrupt or not
157 */
158static void frv_cputimer_doirq(struct irq_source *source)
159{
160 distribute_irqs(&frv_cpu_irqs, source->irqmask);
161}
162
163struct irq_source frv_cputimer[3] = {
164#define __CPUTIMER(X) \
165 [X] = { \
166 .muxname = "timer", \
167 .muxdata = 0, \
168 .irqmask = 1 << IRQ_CPU_TIMER##X, \
169 .doirq = frv_cputimer_doirq, \
170 }
171
172 __CPUTIMER(0),
173 __CPUTIMER(1),
174 __CPUTIMER(2),
175};
176
177/*****************************************************************************/
178/*
179 * external CPU interrupts - can't tell directly whether they've generated an interrupt or not
180 */
181static void frv_cpuexternal_doirq(struct irq_source *source)
182{
183 distribute_irqs(&frv_cpu_irqs, source->irqmask);
184}
185
186struct irq_source frv_cpuexternal[8] = {
187#define __CPUEXTERNAL(X) \
188 [X] = { \
189 .muxname = "ext", \
190 .muxdata = 0, \
191 .irqmask = 1 << IRQ_CPU_EXTERNAL##X, \
192 .doirq = frv_cpuexternal_doirq, \
193 }
194
195 __CPUEXTERNAL(0),
196 __CPUEXTERNAL(1),
197 __CPUEXTERNAL(2),
198 __CPUEXTERNAL(3),
199 __CPUEXTERNAL(4),
200 __CPUEXTERNAL(5),
201 __CPUEXTERNAL(6),
202 __CPUEXTERNAL(7),
203};
204
205#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16))
206
207struct irq_group frv_cpu_irqs = {
208 .sources = {
209 [IRQ_CPU_UART0] = &frv_cpuuart[0],
210 [IRQ_CPU_UART1] = &frv_cpuuart[1],
211 [IRQ_CPU_TIMER0] = &frv_cputimer[0],
212 [IRQ_CPU_TIMER1] = &frv_cputimer[1],
213 [IRQ_CPU_TIMER2] = &frv_cputimer[2],
214 [IRQ_CPU_DMA0] = &frv_cpudma[0],
215 [IRQ_CPU_DMA1] = &frv_cpudma[1],
216 [IRQ_CPU_DMA2] = &frv_cpudma[2],
217 [IRQ_CPU_DMA3] = &frv_cpudma[3],
218 [IRQ_CPU_DMA4] = &frv_cpudma[4],
219 [IRQ_CPU_DMA5] = &frv_cpudma[5],
220 [IRQ_CPU_DMA6] = &frv_cpudma[6],
221 [IRQ_CPU_DMA7] = &frv_cpudma[7],
222 [IRQ_CPU_EXTERNAL0] = &frv_cpuexternal[0],
223 [IRQ_CPU_EXTERNAL1] = &frv_cpuexternal[1],
224 [IRQ_CPU_EXTERNAL2] = &frv_cpuexternal[2],
225 [IRQ_CPU_EXTERNAL3] = &frv_cpuexternal[3],
226 [IRQ_CPU_EXTERNAL4] = &frv_cpuexternal[4],
227 [IRQ_CPU_EXTERNAL5] = &frv_cpuexternal[5],
228 [IRQ_CPU_EXTERNAL6] = &frv_cpuexternal[6],
229 [IRQ_CPU_EXTERNAL7] = &frv_cpuexternal[7],
230 },
231};
232
233/*****************************************************************************/
234/*
235 * route the CPU's interrupt sources
236 */
237void __init route_cpu_irqs(void)
238{
239 frv_irq_set_group(&frv_cpu_irqs);
240
241 __set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 IRQ detect levels */
242 __set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 IRQ detect levels */
243
244 /* route UART and error interrupts */
245 frv_irq_route(&frv_cpuuart[0], IRQ_UART0_LEVEL);
246 frv_irq_route(&frv_cpuuart[1], IRQ_UART1_LEVEL);
247
248 set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, IRQ_UART1_LEVEL, IRQ_UART0_LEVEL);
249
250 /* route DMA channel interrupts */
251 frv_irq_route(&frv_cpudma[0], IRQ_DMA0_LEVEL);
252 frv_irq_route(&frv_cpudma[1], IRQ_DMA1_LEVEL);
253 frv_irq_route(&frv_cpudma[2], IRQ_DMA2_LEVEL);
254 frv_irq_route(&frv_cpudma[3], IRQ_DMA3_LEVEL);
255 frv_irq_route(&frv_cpudma[4], IRQ_DMA4_LEVEL);
256 frv_irq_route(&frv_cpudma[5], IRQ_DMA5_LEVEL);
257 frv_irq_route(&frv_cpudma[6], IRQ_DMA6_LEVEL);
258 frv_irq_route(&frv_cpudma[7], IRQ_DMA7_LEVEL);
259
260 set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, IRQ_DMA0_LEVEL);
261 set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, IRQ_DMA4_LEVEL);
262
263 /* route timer interrupts */
264 frv_irq_route(&frv_cputimer[0], IRQ_TIMER0_LEVEL);
265 frv_irq_route(&frv_cputimer[1], IRQ_TIMER1_LEVEL);
266 frv_irq_route(&frv_cputimer[2], IRQ_TIMER2_LEVEL);
267
268 set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL);
269
270 /* route external interrupts */
271 frv_irq_route(&frv_cpuexternal[0], IRQ_XIRQ0_LEVEL);
272 frv_irq_route(&frv_cpuexternal[1], IRQ_XIRQ1_LEVEL);
273 frv_irq_route(&frv_cpuexternal[2], IRQ_XIRQ2_LEVEL);
274 frv_irq_route(&frv_cpuexternal[3], IRQ_XIRQ3_LEVEL);
275 frv_irq_route(&frv_cpuexternal[4], IRQ_XIRQ4_LEVEL);
276 frv_irq_route(&frv_cpuexternal[5], IRQ_XIRQ5_LEVEL);
277 frv_irq_route(&frv_cpuexternal[6], IRQ_XIRQ6_LEVEL);
278 frv_irq_route(&frv_cpuexternal[7], IRQ_XIRQ7_LEVEL);
279
280 set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, IRQ_XIRQ4_LEVEL);
281 set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, IRQ_XIRQ0_LEVEL);
282
283#if defined(CONFIG_MB93091_VDK)
284 __set_TM1(0x55550000); /* XIRQ7-0 all active low */
285#elif defined(CONFIG_MB93093_PDK)
286 __set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */
287#else
288#error dont know external IRQ trigger levels for this setup
289#endif
290
291} /* end route_cpu_irqs() */