Commit | Line | Data |
---|---|---|
b920de1b DH |
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 | } |