diff options
Diffstat (limited to 'arch/mn10300/kernel/irq.c')
-rw-r--r-- | arch/mn10300/kernel/irq.c | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/arch/mn10300/kernel/irq.c b/arch/mn10300/kernel/irq.c new file mode 100644 index 000000000000..761c434a2488 --- /dev/null +++ b/arch/mn10300/kernel/irq.c | |||
@@ -0,0 +1,235 @@ | |||
1 | /* MN10300 Arch-specific interrupt handling | ||
2 | * | ||
3 | * Copyright (C) 2007 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 Licence | ||
8 | * as published by the Free Software Foundation; either version | ||
9 | * 2 of the Licence, or (at your option) any later version. | ||
10 | */ | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/interrupt.h> | ||
13 | #include <linux/kernel_stat.h> | ||
14 | #include <linux/seq_file.h> | ||
15 | #include <asm/setup.h> | ||
16 | |||
17 | unsigned long __mn10300_irq_enabled_epsw = EPSW_IE | EPSW_IM_7; | ||
18 | EXPORT_SYMBOL(__mn10300_irq_enabled_epsw); | ||
19 | |||
20 | atomic_t irq_err_count; | ||
21 | |||
22 | /* | ||
23 | * MN10300 INTC controller operations | ||
24 | */ | ||
25 | static void mn10300_cpupic_disable(unsigned int irq) | ||
26 | { | ||
27 | u16 tmp = GxICR(irq); | ||
28 | GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; | ||
29 | tmp = GxICR(irq); | ||
30 | } | ||
31 | |||
32 | static void mn10300_cpupic_enable(unsigned int irq) | ||
33 | { | ||
34 | u16 tmp = GxICR(irq); | ||
35 | GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; | ||
36 | tmp = GxICR(irq); | ||
37 | } | ||
38 | |||
39 | static void mn10300_cpupic_ack(unsigned int irq) | ||
40 | { | ||
41 | u16 tmp; | ||
42 | *(volatile u8 *) &GxICR(irq) = GxICR_DETECT; | ||
43 | tmp = GxICR(irq); | ||
44 | } | ||
45 | |||
46 | static void mn10300_cpupic_mask(unsigned int irq) | ||
47 | { | ||
48 | u16 tmp = GxICR(irq); | ||
49 | GxICR(irq) = (tmp & GxICR_LEVEL); | ||
50 | tmp = GxICR(irq); | ||
51 | } | ||
52 | |||
53 | static void mn10300_cpupic_mask_ack(unsigned int irq) | ||
54 | { | ||
55 | u16 tmp = GxICR(irq); | ||
56 | GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_DETECT; | ||
57 | tmp = GxICR(irq); | ||
58 | } | ||
59 | |||
60 | static void mn10300_cpupic_unmask(unsigned int irq) | ||
61 | { | ||
62 | u16 tmp = GxICR(irq); | ||
63 | GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE | GxICR_DETECT; | ||
64 | tmp = GxICR(irq); | ||
65 | } | ||
66 | |||
67 | static void mn10300_cpupic_end(unsigned int irq) | ||
68 | { | ||
69 | u16 tmp = GxICR(irq); | ||
70 | GxICR(irq) = (tmp & GxICR_LEVEL) | GxICR_ENABLE; | ||
71 | tmp = GxICR(irq); | ||
72 | } | ||
73 | |||
74 | static struct irq_chip mn10300_cpu_pic = { | ||
75 | .name = "cpu", | ||
76 | .disable = mn10300_cpupic_disable, | ||
77 | .enable = mn10300_cpupic_enable, | ||
78 | .ack = mn10300_cpupic_ack, | ||
79 | .mask = mn10300_cpupic_mask, | ||
80 | .mask_ack = mn10300_cpupic_mask_ack, | ||
81 | .unmask = mn10300_cpupic_unmask, | ||
82 | .end = mn10300_cpupic_end, | ||
83 | }; | ||
84 | |||
85 | /* | ||
86 | * 'what should we do if we get a hw irq event on an illegal vector'. | ||
87 | * each architecture has to answer this themselves. | ||
88 | */ | ||
89 | void ack_bad_irq(int irq) | ||
90 | { | ||
91 | printk(KERN_WARNING "unexpected IRQ trap at vector %02x\n", irq); | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * change the level at which an IRQ executes | ||
96 | * - must not be called whilst interrupts are being processed! | ||
97 | */ | ||
98 | void set_intr_level(int irq, u16 level) | ||
99 | { | ||
100 | u16 tmp; | ||
101 | |||
102 | if (in_interrupt()) | ||
103 | BUG(); | ||
104 | |||
105 | tmp = GxICR(irq); | ||
106 | GxICR(irq) = (tmp & GxICR_ENABLE) | level; | ||
107 | tmp = GxICR(irq); | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | * mark an interrupt to be ACK'd after interrupt handlers have been run rather | ||
112 | * than before | ||
113 | * - see Documentation/mn10300/features.txt | ||
114 | */ | ||
115 | void set_intr_postackable(int irq) | ||
116 | { | ||
117 | set_irq_handler(irq, handle_level_irq); | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * initialise the interrupt system | ||
122 | */ | ||
123 | void __init init_IRQ(void) | ||
124 | { | ||
125 | int irq; | ||
126 | |||
127 | for (irq = 0; irq < NR_IRQS; irq++) | ||
128 | if (irq_desc[irq].chip == &no_irq_type) | ||
129 | set_irq_chip_and_handler(irq, &mn10300_cpu_pic, | ||
130 | handle_edge_irq); | ||
131 | unit_init_IRQ(); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * handle normal device IRQs | ||
136 | */ | ||
137 | asmlinkage void do_IRQ(void) | ||
138 | { | ||
139 | unsigned long sp, epsw, irq_disabled_epsw, old_irq_enabled_epsw; | ||
140 | int irq; | ||
141 | |||
142 | sp = current_stack_pointer(); | ||
143 | if (sp - (sp & ~(THREAD_SIZE - 1)) < STACK_WARN) | ||
144 | BUG(); | ||
145 | |||
146 | /* make sure local_irq_enable() doesn't muck up the interrupt priority | ||
147 | * setting in EPSW */ | ||
148 | old_irq_enabled_epsw = __mn10300_irq_enabled_epsw; | ||
149 | local_save_flags(epsw); | ||
150 | __mn10300_irq_enabled_epsw = EPSW_IE | (EPSW_IM & epsw); | ||
151 | irq_disabled_epsw = EPSW_IE | MN10300_CLI_LEVEL; | ||
152 | |||
153 | __IRQ_STAT(smp_processor_id(), __irq_count)++; | ||
154 | |||
155 | irq_enter(); | ||
156 | |||
157 | for (;;) { | ||
158 | /* ask the interrupt controller for the next IRQ to process | ||
159 | * - the result we get depends on EPSW.IM | ||
160 | */ | ||
161 | irq = IAGR & IAGR_GN; | ||
162 | if (!irq) | ||
163 | break; | ||
164 | |||
165 | local_irq_restore(irq_disabled_epsw); | ||
166 | |||
167 | generic_handle_irq(irq >> 2); | ||
168 | |||
169 | /* restore IRQ controls for IAGR access */ | ||
170 | local_irq_restore(epsw); | ||
171 | } | ||
172 | |||
173 | __mn10300_irq_enabled_epsw = old_irq_enabled_epsw; | ||
174 | |||
175 | irq_exit(); | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Display interrupt management information through /proc/interrupts | ||
180 | */ | ||
181 | int show_interrupts(struct seq_file *p, void *v) | ||
182 | { | ||
183 | int i = *(loff_t *) v, j, cpu; | ||
184 | struct irqaction *action; | ||
185 | unsigned long flags; | ||
186 | |||
187 | switch (i) { | ||
188 | /* display column title bar naming CPUs */ | ||
189 | case 0: | ||
190 | seq_printf(p, " "); | ||
191 | for (j = 0; j < NR_CPUS; j++) | ||
192 | if (cpu_online(j)) | ||
193 | seq_printf(p, "CPU%d ", j); | ||
194 | seq_putc(p, '\n'); | ||
195 | break; | ||
196 | |||
197 | /* display information rows, one per active CPU */ | ||
198 | case 1 ... NR_IRQS - 1: | ||
199 | spin_lock_irqsave(&irq_desc[i].lock, flags); | ||
200 | |||
201 | action = irq_desc[i].action; | ||
202 | if (action) { | ||
203 | seq_printf(p, "%3d: ", i); | ||
204 | for_each_present_cpu(cpu) | ||
205 | seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]); | ||
206 | seq_printf(p, " %14s.%u", irq_desc[i].chip->name, | ||
207 | (GxICR(i) & GxICR_LEVEL) >> | ||
208 | GxICR_LEVEL_SHIFT); | ||
209 | seq_printf(p, " %s", action->name); | ||
210 | |||
211 | for (action = action->next; | ||
212 | action; | ||
213 | action = action->next) | ||
214 | seq_printf(p, ", %s", action->name); | ||
215 | |||
216 | seq_putc(p, '\n'); | ||
217 | } | ||
218 | |||
219 | spin_unlock_irqrestore(&irq_desc[i].lock, flags); | ||
220 | break; | ||
221 | |||
222 | /* polish off with NMI and error counters */ | ||
223 | case NR_IRQS: | ||
224 | seq_printf(p, "NMI: "); | ||
225 | for (j = 0; j < NR_CPUS; j++) | ||
226 | if (cpu_online(j)) | ||
227 | seq_printf(p, "%10u ", nmi_count(j)); | ||
228 | seq_putc(p, '\n'); | ||
229 | |||
230 | seq_printf(p, "ERR: %10u\n", atomic_read(&irq_err_count)); | ||
231 | break; | ||
232 | } | ||
233 | |||
234 | return 0; | ||
235 | } | ||