diff options
Diffstat (limited to 'arch/sparc/kernel/sun4d_irq.c')
-rw-r--r-- | arch/sparc/kernel/sun4d_irq.c | 664 |
1 files changed, 299 insertions, 365 deletions
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index e11b4612dabb..1d13c5bda0b1 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c | |||
@@ -1,50 +1,41 @@ | |||
1 | /* | 1 | /* |
2 | * arch/sparc/kernel/sun4d_irq.c: | 2 | * SS1000/SC2000 interrupt handling. |
3 | * SS1000/SC2000 interrupt handling. | ||
4 | * | 3 | * |
5 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) | 4 | * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) |
6 | * Heavily based on arch/sparc/kernel/irq.c. | 5 | * Heavily based on arch/sparc/kernel/irq.c. |
7 | */ | 6 | */ |
8 | 7 | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/linkage.h> | ||
11 | #include <linux/kernel_stat.h> | 8 | #include <linux/kernel_stat.h> |
12 | #include <linux/signal.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/random.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/smp.h> | ||
20 | #include <linux/spinlock.h> | ||
21 | #include <linux/seq_file.h> | 9 | #include <linux/seq_file.h> |
22 | #include <linux/of.h> | 10 | |
23 | #include <linux/of_device.h> | ||
24 | |||
25 | #include <asm/ptrace.h> | ||
26 | #include <asm/processor.h> | ||
27 | #include <asm/system.h> | ||
28 | #include <asm/psr.h> | ||
29 | #include <asm/smp.h> | ||
30 | #include <asm/vaddrs.h> | ||
31 | #include <asm/timer.h> | 11 | #include <asm/timer.h> |
32 | #include <asm/openprom.h> | ||
33 | #include <asm/oplib.h> | ||
34 | #include <asm/traps.h> | 12 | #include <asm/traps.h> |
35 | #include <asm/irq.h> | 13 | #include <asm/irq.h> |
36 | #include <asm/io.h> | 14 | #include <asm/io.h> |
37 | #include <asm/pgalloc.h> | ||
38 | #include <asm/pgtable.h> | ||
39 | #include <asm/sbi.h> | 15 | #include <asm/sbi.h> |
40 | #include <asm/cacheflush.h> | 16 | #include <asm/cacheflush.h> |
41 | #include <asm/irq_regs.h> | 17 | #include <asm/setup.h> |
42 | 18 | ||
43 | #include "kernel.h" | 19 | #include "kernel.h" |
44 | #include "irq.h" | 20 | #include "irq.h" |
45 | 21 | ||
46 | /* If you trust current SCSI layer to handle different SCSI IRQs, enable this. I don't trust it... -jj */ | 22 | /* Sun4d interrupts fall roughly into two categories. SBUS and |
47 | /* #define DISTRIBUTE_IRQS */ | 23 | * cpu local. CPU local interrupts cover the timer interrupts |
24 | * and whatnot, and we encode those as normal PILs between | ||
25 | * 0 and 15. | ||
26 | * SBUS interrupts are encodes as a combination of board, level and slot. | ||
27 | */ | ||
28 | |||
29 | struct sun4d_handler_data { | ||
30 | unsigned int cpuid; /* target cpu */ | ||
31 | unsigned int real_irq; /* interrupt level */ | ||
32 | }; | ||
33 | |||
34 | |||
35 | static unsigned int sun4d_encode_irq(int board, int lvl, int slot) | ||
36 | { | ||
37 | return (board + 1) << 5 | (lvl << 2) | slot; | ||
38 | } | ||
48 | 39 | ||
49 | struct sun4d_timer_regs { | 40 | struct sun4d_timer_regs { |
50 | u32 l10_timer_limit; | 41 | u32 l10_timer_limit; |
@@ -56,320 +47,201 @@ struct sun4d_timer_regs { | |||
56 | 47 | ||
57 | static struct sun4d_timer_regs __iomem *sun4d_timers; | 48 | static struct sun4d_timer_regs __iomem *sun4d_timers; |
58 | 49 | ||
59 | #define TIMER_IRQ 10 | 50 | #define SUN4D_TIMER_IRQ 10 |
60 | |||
61 | #define MAX_STATIC_ALLOC 4 | ||
62 | extern int static_irq_count; | ||
63 | static unsigned char sbus_tid[32]; | ||
64 | |||
65 | static struct irqaction *irq_action[NR_IRQS]; | ||
66 | extern spinlock_t irq_action_lock; | ||
67 | 51 | ||
68 | static struct sbus_action { | 52 | /* Specify which cpu handle interrupts from which board. |
69 | struct irqaction *action; | 53 | * Index is board - value is cpu. |
70 | /* For SMP this needs to be extended */ | 54 | */ |
71 | } *sbus_actions; | 55 | static unsigned char board_to_cpu[32]; |
72 | 56 | ||
73 | static int pil_to_sbus[] = { | 57 | static int pil_to_sbus[] = { |
74 | 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0, | 58 | 0, |
75 | }; | 59 | 0, |
76 | 60 | 1, | |
77 | static int sbus_to_pil[] = { | 61 | 2, |
78 | 0, 2, 3, 5, 7, 9, 11, 13, | 62 | 0, |
63 | 3, | ||
64 | 0, | ||
65 | 4, | ||
66 | 0, | ||
67 | 5, | ||
68 | 0, | ||
69 | 6, | ||
70 | 0, | ||
71 | 7, | ||
72 | 0, | ||
73 | 0, | ||
79 | }; | 74 | }; |
80 | 75 | ||
81 | static int nsbi; | ||
82 | |||
83 | /* Exported for sun4d_smp.c */ | 76 | /* Exported for sun4d_smp.c */ |
84 | DEFINE_SPINLOCK(sun4d_imsk_lock); | 77 | DEFINE_SPINLOCK(sun4d_imsk_lock); |
85 | 78 | ||
86 | int show_sun4d_interrupts(struct seq_file *p, void *v) | 79 | /* SBUS interrupts are encoded integers including the board number |
87 | { | 80 | * (plus one), the SBUS level, and the SBUS slot number. Sun4D |
88 | int i = *(loff_t *) v, j = 0, k = 0, sbusl; | 81 | * IRQ dispatch is done by: |
89 | struct irqaction * action; | 82 | * |
90 | unsigned long flags; | 83 | * 1) Reading the BW local interrupt table in order to get the bus |
91 | #ifdef CONFIG_SMP | 84 | * interrupt mask. |
92 | int x; | 85 | * |
93 | #endif | 86 | * This table is indexed by SBUS interrupt level which can be |
94 | 87 | * derived from the PIL we got interrupted on. | |
95 | spin_lock_irqsave(&irq_action_lock, flags); | 88 | * |
96 | if (i < NR_IRQS) { | 89 | * 2) For each bus showing interrupt pending from #1, read the |
97 | sbusl = pil_to_sbus[i]; | 90 | * SBI interrupt state register. This will indicate which slots |
98 | if (!sbusl) { | 91 | * have interrupts pending for that SBUS interrupt level. |
99 | action = *(i + irq_action); | 92 | * |
100 | if (!action) | 93 | * 3) Call the genreric IRQ support. |
101 | goto out_unlock; | 94 | */ |
102 | } else { | 95 | static void sun4d_sbus_handler_irq(int sbusl) |
103 | for (j = 0; j < nsbi; j++) { | ||
104 | for (k = 0; k < 4; k++) | ||
105 | if ((action = sbus_actions [(j << 5) + (sbusl << 2) + k].action)) | ||
106 | goto found_it; | ||
107 | } | ||
108 | goto out_unlock; | ||
109 | } | ||
110 | found_it: seq_printf(p, "%3d: ", i); | ||
111 | #ifndef CONFIG_SMP | ||
112 | seq_printf(p, "%10u ", kstat_irqs(i)); | ||
113 | #else | ||
114 | for_each_online_cpu(x) | ||
115 | seq_printf(p, "%10u ", | ||
116 | kstat_cpu(cpu_logical_map(x)).irqs[i]); | ||
117 | #endif | ||
118 | seq_printf(p, "%c %s", | ||
119 | (action->flags & IRQF_DISABLED) ? '+' : ' ', | ||
120 | action->name); | ||
121 | action = action->next; | ||
122 | for (;;) { | ||
123 | for (; action; action = action->next) { | ||
124 | seq_printf(p, ",%s %s", | ||
125 | (action->flags & IRQF_DISABLED) ? " +" : "", | ||
126 | action->name); | ||
127 | } | ||
128 | if (!sbusl) break; | ||
129 | k++; | ||
130 | if (k < 4) | ||
131 | action = sbus_actions [(j << 5) + (sbusl << 2) + k].action; | ||
132 | else { | ||
133 | j++; | ||
134 | if (j == nsbi) break; | ||
135 | k = 0; | ||
136 | action = sbus_actions [(j << 5) + (sbusl << 2)].action; | ||
137 | } | ||
138 | } | ||
139 | seq_putc(p, '\n'); | ||
140 | } | ||
141 | out_unlock: | ||
142 | spin_unlock_irqrestore(&irq_action_lock, flags); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | void sun4d_free_irq(unsigned int irq, void *dev_id) | ||
147 | { | 96 | { |
148 | struct irqaction *action, **actionp; | 97 | unsigned int bus_mask; |
149 | struct irqaction *tmp = NULL; | 98 | unsigned int sbino, slot; |
150 | unsigned long flags; | 99 | unsigned int sbil; |
151 | 100 | ||
152 | spin_lock_irqsave(&irq_action_lock, flags); | 101 | bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; |
153 | if (irq < 15) | 102 | bw_clear_intr_mask(sbusl, bus_mask); |
154 | actionp = irq + irq_action; | 103 | |
155 | else | 104 | sbil = (sbusl << 2); |
156 | actionp = &(sbus_actions[irq - (1 << 5)].action); | 105 | /* Loop for each pending SBI */ |
157 | action = *actionp; | 106 | for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1) { |
158 | if (!action) { | 107 | unsigned int idx, mask; |
159 | printk("Trying to free free IRQ%d\n",irq); | 108 | |
160 | goto out_unlock; | 109 | if (!(bus_mask & 1)) |
161 | } | 110 | continue; |
162 | if (dev_id) { | 111 | /* XXX This seems to ACK the irq twice. acquire_sbi() |
163 | for (; action; action = action->next) { | 112 | * XXX uses swap, therefore this writes 0xf << sbil, |
164 | if (action->dev_id == dev_id) | 113 | * XXX then later release_sbi() will write the individual |
165 | break; | 114 | * XXX bits which were set again. |
166 | tmp = action; | ||
167 | } | ||
168 | if (!action) { | ||
169 | printk("Trying to free free shared IRQ%d\n",irq); | ||
170 | goto out_unlock; | ||
171 | } | ||
172 | } else if (action->flags & IRQF_SHARED) { | ||
173 | printk("Trying to free shared IRQ%d with NULL device ID\n", irq); | ||
174 | goto out_unlock; | ||
175 | } | ||
176 | if (action->flags & SA_STATIC_ALLOC) | ||
177 | { | ||
178 | /* This interrupt is marked as specially allocated | ||
179 | * so it is a bad idea to free it. | ||
180 | */ | 115 | */ |
181 | printk("Attempt to free statically allocated IRQ%d (%s)\n", | 116 | mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil); |
182 | irq, action->name); | 117 | mask &= (0xf << sbil); |
183 | goto out_unlock; | ||
184 | } | ||
185 | |||
186 | if (tmp) | ||
187 | tmp->next = action->next; | ||
188 | else | ||
189 | *actionp = action->next; | ||
190 | |||
191 | spin_unlock_irqrestore(&irq_action_lock, flags); | ||
192 | 118 | ||
193 | synchronize_irq(irq); | 119 | /* Loop for each pending SBI slot */ |
120 | slot = (1 << sbil); | ||
121 | for (idx = 0; mask != 0; idx++, slot <<= 1) { | ||
122 | unsigned int pil; | ||
123 | struct irq_bucket *p; | ||
194 | 124 | ||
195 | spin_lock_irqsave(&irq_action_lock, flags); | 125 | if (!(mask & slot)) |
126 | continue; | ||
196 | 127 | ||
197 | kfree(action); | 128 | mask &= ~slot; |
129 | pil = sun4d_encode_irq(sbino, sbusl, idx); | ||
198 | 130 | ||
199 | if (!(*actionp)) | 131 | p = irq_map[pil]; |
200 | __disable_irq(irq); | 132 | while (p) { |
133 | struct irq_bucket *next; | ||
201 | 134 | ||
202 | out_unlock: | 135 | next = p->next; |
203 | spin_unlock_irqrestore(&irq_action_lock, flags); | 136 | generic_handle_irq(p->irq); |
137 | p = next; | ||
138 | } | ||
139 | release_sbi(SBI2DEVID(sbino), slot); | ||
140 | } | ||
141 | } | ||
204 | } | 142 | } |
205 | 143 | ||
206 | extern void unexpected_irq(int, void *, struct pt_regs *); | 144 | void sun4d_handler_irq(int pil, struct pt_regs *regs) |
207 | |||
208 | void sun4d_handler_irq(int irq, struct pt_regs * regs) | ||
209 | { | 145 | { |
210 | struct pt_regs *old_regs; | 146 | struct pt_regs *old_regs; |
211 | struct irqaction * action; | ||
212 | int cpu = smp_processor_id(); | ||
213 | /* SBUS IRQ level (1 - 7) */ | 147 | /* SBUS IRQ level (1 - 7) */ |
214 | int sbusl = pil_to_sbus[irq]; | 148 | int sbusl = pil_to_sbus[pil]; |
215 | 149 | ||
216 | /* FIXME: Is this necessary?? */ | 150 | /* FIXME: Is this necessary?? */ |
217 | cc_get_ipen(); | 151 | cc_get_ipen(); |
218 | 152 | ||
219 | cc_set_iclr(1 << irq); | 153 | cc_set_iclr(1 << pil); |
220 | 154 | ||
155 | #ifdef CONFIG_SMP | ||
156 | /* | ||
157 | * Check IPI data structures after IRQ has been cleared. Hard and Soft | ||
158 | * IRQ can happen at the same time, so both cases are always handled. | ||
159 | */ | ||
160 | if (pil == SUN4D_IPI_IRQ) | ||
161 | sun4d_ipi_interrupt(); | ||
162 | #endif | ||
163 | |||
221 | old_regs = set_irq_regs(regs); | 164 | old_regs = set_irq_regs(regs); |
222 | irq_enter(); | 165 | irq_enter(); |
223 | kstat_cpu(cpu).irqs[irq]++; | 166 | if (sbusl == 0) { |
224 | if (!sbusl) { | 167 | /* cpu interrupt */ |
225 | action = *(irq + irq_action); | 168 | struct irq_bucket *p; |
226 | if (!action) | 169 | |
227 | unexpected_irq(irq, NULL, regs); | 170 | p = irq_map[pil]; |
228 | do { | 171 | while (p) { |
229 | action->handler(irq, action->dev_id); | 172 | struct irq_bucket *next; |
230 | action = action->next; | 173 | |
231 | } while (action); | 174 | next = p->next; |
175 | generic_handle_irq(p->irq); | ||
176 | p = next; | ||
177 | } | ||
232 | } else { | 178 | } else { |
233 | int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff; | 179 | /* SBUS interrupt */ |
234 | int sbino; | 180 | sun4d_sbus_handler_irq(sbusl); |
235 | struct sbus_action *actionp; | ||
236 | unsigned mask, slot; | ||
237 | int sbil = (sbusl << 2); | ||
238 | |||
239 | bw_clear_intr_mask(sbusl, bus_mask); | ||
240 | |||
241 | /* Loop for each pending SBI */ | ||
242 | for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1) | ||
243 | if (bus_mask & 1) { | ||
244 | mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil); | ||
245 | mask &= (0xf << sbil); | ||
246 | actionp = sbus_actions + (sbino << 5) + (sbil); | ||
247 | /* Loop for each pending SBI slot */ | ||
248 | for (slot = (1 << sbil); mask; slot <<= 1, actionp++) | ||
249 | if (mask & slot) { | ||
250 | mask &= ~slot; | ||
251 | action = actionp->action; | ||
252 | |||
253 | if (!action) | ||
254 | unexpected_irq(irq, NULL, regs); | ||
255 | do { | ||
256 | action->handler(irq, action->dev_id); | ||
257 | action = action->next; | ||
258 | } while (action); | ||
259 | release_sbi(SBI2DEVID(sbino), slot); | ||
260 | } | ||
261 | } | ||
262 | } | 181 | } |
263 | irq_exit(); | 182 | irq_exit(); |
264 | set_irq_regs(old_regs); | 183 | set_irq_regs(old_regs); |
265 | } | 184 | } |
266 | 185 | ||
267 | int sun4d_request_irq(unsigned int irq, | 186 | |
268 | irq_handler_t handler, | 187 | static void sun4d_mask_irq(struct irq_data *data) |
269 | unsigned long irqflags, const char * devname, void *dev_id) | ||
270 | { | 188 | { |
271 | struct irqaction *action, *tmp = NULL, **actionp; | 189 | struct sun4d_handler_data *handler_data = data->handler_data; |
190 | unsigned int real_irq; | ||
191 | #ifdef CONFIG_SMP | ||
192 | int cpuid = handler_data->cpuid; | ||
272 | unsigned long flags; | 193 | unsigned long flags; |
273 | int ret; | 194 | #endif |
274 | 195 | real_irq = handler_data->real_irq; | |
275 | if(irq > 14 && irq < (1 << 5)) { | 196 | #ifdef CONFIG_SMP |
276 | ret = -EINVAL; | 197 | spin_lock_irqsave(&sun4d_imsk_lock, flags); |
277 | goto out; | 198 | cc_set_imsk_other(cpuid, cc_get_imsk_other(cpuid) | (1 << real_irq)); |
278 | } | 199 | spin_unlock_irqrestore(&sun4d_imsk_lock, flags); |
279 | 200 | #else | |
280 | if (!handler) { | 201 | cc_set_imsk(cc_get_imsk() | (1 << real_irq)); |
281 | ret = -EINVAL; | 202 | #endif |
282 | goto out; | ||
283 | } | ||
284 | |||
285 | spin_lock_irqsave(&irq_action_lock, flags); | ||
286 | |||
287 | if (irq >= (1 << 5)) | ||
288 | actionp = &(sbus_actions[irq - (1 << 5)].action); | ||
289 | else | ||
290 | actionp = irq + irq_action; | ||
291 | action = *actionp; | ||
292 | |||
293 | if (action) { | ||
294 | if ((action->flags & IRQF_SHARED) && (irqflags & IRQF_SHARED)) { | ||
295 | for (tmp = action; tmp->next; tmp = tmp->next); | ||
296 | } else { | ||
297 | ret = -EBUSY; | ||
298 | goto out_unlock; | ||
299 | } | ||
300 | if ((action->flags & IRQF_DISABLED) ^ (irqflags & IRQF_DISABLED)) { | ||
301 | printk("Attempt to mix fast and slow interrupts on IRQ%d denied\n", irq); | ||
302 | ret = -EBUSY; | ||
303 | goto out_unlock; | ||
304 | } | ||
305 | action = NULL; /* Or else! */ | ||
306 | } | ||
307 | |||
308 | /* If this is flagged as statically allocated then we use our | ||
309 | * private struct which is never freed. | ||
310 | */ | ||
311 | if (irqflags & SA_STATIC_ALLOC) { | ||
312 | if (static_irq_count < MAX_STATIC_ALLOC) | ||
313 | action = &static_irqaction[static_irq_count++]; | ||
314 | else | ||
315 | printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n", irq, devname); | ||
316 | } | ||
317 | |||
318 | if (action == NULL) | ||
319 | action = kmalloc(sizeof(struct irqaction), | ||
320 | GFP_ATOMIC); | ||
321 | |||
322 | if (!action) { | ||
323 | ret = -ENOMEM; | ||
324 | goto out_unlock; | ||
325 | } | ||
326 | |||
327 | action->handler = handler; | ||
328 | action->flags = irqflags; | ||
329 | action->name = devname; | ||
330 | action->next = NULL; | ||
331 | action->dev_id = dev_id; | ||
332 | |||
333 | if (tmp) | ||
334 | tmp->next = action; | ||
335 | else | ||
336 | *actionp = action; | ||
337 | |||
338 | __enable_irq(irq); | ||
339 | |||
340 | ret = 0; | ||
341 | out_unlock: | ||
342 | spin_unlock_irqrestore(&irq_action_lock, flags); | ||
343 | out: | ||
344 | return ret; | ||
345 | } | 203 | } |
346 | 204 | ||
347 | static void sun4d_disable_irq(unsigned int irq) | 205 | static void sun4d_unmask_irq(struct irq_data *data) |
348 | { | 206 | { |
349 | int tid = sbus_tid[(irq >> 5) - 1]; | 207 | struct sun4d_handler_data *handler_data = data->handler_data; |
208 | unsigned int real_irq; | ||
209 | #ifdef CONFIG_SMP | ||
210 | int cpuid = handler_data->cpuid; | ||
350 | unsigned long flags; | 211 | unsigned long flags; |
351 | 212 | #endif | |
352 | if (irq < NR_IRQS) | 213 | real_irq = handler_data->real_irq; |
353 | return; | ||
354 | 214 | ||
215 | #ifdef CONFIG_SMP | ||
355 | spin_lock_irqsave(&sun4d_imsk_lock, flags); | 216 | spin_lock_irqsave(&sun4d_imsk_lock, flags); |
356 | cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7])); | 217 | cc_set_imsk_other(cpuid, cc_get_imsk_other(cpuid) & ~(1 << real_irq)); |
357 | spin_unlock_irqrestore(&sun4d_imsk_lock, flags); | 218 | spin_unlock_irqrestore(&sun4d_imsk_lock, flags); |
219 | #else | ||
220 | cc_set_imsk(cc_get_imsk() & ~(1 << real_irq)); | ||
221 | #endif | ||
358 | } | 222 | } |
359 | 223 | ||
360 | static void sun4d_enable_irq(unsigned int irq) | 224 | static unsigned int sun4d_startup_irq(struct irq_data *data) |
361 | { | 225 | { |
362 | int tid = sbus_tid[(irq >> 5) - 1]; | 226 | irq_link(data->irq); |
363 | unsigned long flags; | 227 | sun4d_unmask_irq(data); |
364 | 228 | return 0; | |
365 | if (irq < NR_IRQS) | 229 | } |
366 | return; | ||
367 | 230 | ||
368 | spin_lock_irqsave(&sun4d_imsk_lock, flags); | 231 | static void sun4d_shutdown_irq(struct irq_data *data) |
369 | cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7])); | 232 | { |
370 | spin_unlock_irqrestore(&sun4d_imsk_lock, flags); | 233 | sun4d_mask_irq(data); |
234 | irq_unlink(data->irq); | ||
371 | } | 235 | } |
372 | 236 | ||
237 | struct irq_chip sun4d_irq = { | ||
238 | .name = "sun4d", | ||
239 | .irq_startup = sun4d_startup_irq, | ||
240 | .irq_shutdown = sun4d_shutdown_irq, | ||
241 | .irq_unmask = sun4d_unmask_irq, | ||
242 | .irq_mask = sun4d_mask_irq, | ||
243 | }; | ||
244 | |||
373 | #ifdef CONFIG_SMP | 245 | #ifdef CONFIG_SMP |
374 | static void sun4d_set_cpu_int(int cpu, int level) | 246 | static void sun4d_set_cpu_int(int cpu, int level) |
375 | { | 247 | { |
@@ -389,44 +261,6 @@ void __init sun4d_distribute_irqs(void) | |||
389 | { | 261 | { |
390 | struct device_node *dp; | 262 | struct device_node *dp; |
391 | 263 | ||
392 | #ifdef DISTRIBUTE_IRQS | ||
393 | cpumask_t sbus_serving_map; | ||
394 | |||
395 | sbus_serving_map = cpu_present_map; | ||
396 | for_each_node_by_name(dp, "sbi") { | ||
397 | int board = of_getintprop_default(dp, "board#", 0); | ||
398 | |||
399 | if ((board * 2) == boot_cpu_id && cpu_isset(board * 2 + 1, cpu_present_map)) | ||
400 | sbus_tid[board] = (board * 2 + 1); | ||
401 | else if (cpu_isset(board * 2, cpu_present_map)) | ||
402 | sbus_tid[board] = (board * 2); | ||
403 | else if (cpu_isset(board * 2 + 1, cpu_present_map)) | ||
404 | sbus_tid[board] = (board * 2 + 1); | ||
405 | else | ||
406 | sbus_tid[board] = 0xff; | ||
407 | if (sbus_tid[board] != 0xff) | ||
408 | cpu_clear(sbus_tid[board], sbus_serving_map); | ||
409 | } | ||
410 | for_each_node_by_name(dp, "sbi") { | ||
411 | int board = of_getintprop_default(dp, "board#", 0); | ||
412 | if (sbus_tid[board] == 0xff) { | ||
413 | int i = 31; | ||
414 | |||
415 | if (cpus_empty(sbus_serving_map)) | ||
416 | sbus_serving_map = cpu_present_map; | ||
417 | while (cpu_isset(i, sbus_serving_map)) | ||
418 | i--; | ||
419 | sbus_tid[board] = i; | ||
420 | cpu_clear(i, sbus_serving_map); | ||
421 | } | ||
422 | } | ||
423 | for_each_node_by_name(dp, "sbi") { | ||
424 | int devid = of_getintprop_default(dp, "device-id", 0); | ||
425 | int board = of_getintprop_default(dp, "board#", 0); | ||
426 | printk("sbus%d IRQs directed to CPU%d\n", board, sbus_tid[board]); | ||
427 | set_sbi_tid(devid, sbus_tid[board] << 3); | ||
428 | } | ||
429 | #else | ||
430 | int cpuid = cpu_logical_map(1); | 264 | int cpuid = cpu_logical_map(1); |
431 | 265 | ||
432 | if (cpuid == -1) | 266 | if (cpuid == -1) |
@@ -434,14 +268,13 @@ void __init sun4d_distribute_irqs(void) | |||
434 | for_each_node_by_name(dp, "sbi") { | 268 | for_each_node_by_name(dp, "sbi") { |
435 | int devid = of_getintprop_default(dp, "device-id", 0); | 269 | int devid = of_getintprop_default(dp, "device-id", 0); |
436 | int board = of_getintprop_default(dp, "board#", 0); | 270 | int board = of_getintprop_default(dp, "board#", 0); |
437 | sbus_tid[board] = cpuid; | 271 | board_to_cpu[board] = cpuid; |
438 | set_sbi_tid(devid, cpuid << 3); | 272 | set_sbi_tid(devid, cpuid << 3); |
439 | } | 273 | } |
440 | printk("All sbus IRQs directed to CPU%d\n", cpuid); | 274 | printk(KERN_ERR "All sbus IRQs directed to CPU%d\n", cpuid); |
441 | #endif | ||
442 | } | 275 | } |
443 | #endif | 276 | #endif |
444 | 277 | ||
445 | static void sun4d_clear_clock_irq(void) | 278 | static void sun4d_clear_clock_irq(void) |
446 | { | 279 | { |
447 | sbus_readl(&sun4d_timers->l10_timer_limit); | 280 | sbus_readl(&sun4d_timers->l10_timer_limit); |
@@ -462,14 +295,115 @@ static void __init sun4d_load_profile_irqs(void) | |||
462 | } | 295 | } |
463 | } | 296 | } |
464 | 297 | ||
298 | unsigned int _sun4d_build_device_irq(unsigned int real_irq, | ||
299 | unsigned int pil, | ||
300 | unsigned int board) | ||
301 | { | ||
302 | struct sun4d_handler_data *handler_data; | ||
303 | unsigned int irq; | ||
304 | |||
305 | irq = irq_alloc(real_irq, pil); | ||
306 | if (irq == 0) { | ||
307 | prom_printf("IRQ: allocate for %d %d %d failed\n", | ||
308 | real_irq, pil, board); | ||
309 | goto err_out; | ||
310 | } | ||
311 | |||
312 | handler_data = irq_get_handler_data(irq); | ||
313 | if (unlikely(handler_data)) | ||
314 | goto err_out; | ||
315 | |||
316 | handler_data = kzalloc(sizeof(struct sun4d_handler_data), GFP_ATOMIC); | ||
317 | if (unlikely(!handler_data)) { | ||
318 | prom_printf("IRQ: kzalloc(sun4d_handler_data) failed.\n"); | ||
319 | prom_halt(); | ||
320 | } | ||
321 | handler_data->cpuid = board_to_cpu[board]; | ||
322 | handler_data->real_irq = real_irq; | ||
323 | irq_set_chip_and_handler_name(irq, &sun4d_irq, | ||
324 | handle_level_irq, "level"); | ||
325 | irq_set_handler_data(irq, handler_data); | ||
326 | |||
327 | err_out: | ||
328 | return irq; | ||
329 | } | ||
330 | |||
331 | |||
332 | |||
333 | unsigned int sun4d_build_device_irq(struct platform_device *op, | ||
334 | unsigned int real_irq) | ||
335 | { | ||
336 | struct device_node *dp = op->dev.of_node; | ||
337 | struct device_node *board_parent, *bus = dp->parent; | ||
338 | char *bus_connection; | ||
339 | const struct linux_prom_registers *regs; | ||
340 | unsigned int pil; | ||
341 | unsigned int irq; | ||
342 | int board, slot; | ||
343 | int sbusl; | ||
344 | |||
345 | irq = real_irq; | ||
346 | while (bus) { | ||
347 | if (!strcmp(bus->name, "sbi")) { | ||
348 | bus_connection = "io-unit"; | ||
349 | break; | ||
350 | } | ||
351 | |||
352 | if (!strcmp(bus->name, "bootbus")) { | ||
353 | bus_connection = "cpu-unit"; | ||
354 | break; | ||
355 | } | ||
356 | |||
357 | bus = bus->parent; | ||
358 | } | ||
359 | if (!bus) | ||
360 | goto err_out; | ||
361 | |||
362 | regs = of_get_property(dp, "reg", NULL); | ||
363 | if (!regs) | ||
364 | goto err_out; | ||
365 | |||
366 | slot = regs->which_io; | ||
367 | |||
368 | /* | ||
369 | * If Bus nodes parent is not io-unit/cpu-unit or the io-unit/cpu-unit | ||
370 | * lacks a "board#" property, something is very wrong. | ||
371 | */ | ||
372 | if (!bus->parent || strcmp(bus->parent->name, bus_connection)) { | ||
373 | printk(KERN_ERR "%s: Error, parent is not %s.\n", | ||
374 | bus->full_name, bus_connection); | ||
375 | goto err_out; | ||
376 | } | ||
377 | board_parent = bus->parent; | ||
378 | board = of_getintprop_default(board_parent, "board#", -1); | ||
379 | if (board == -1) { | ||
380 | printk(KERN_ERR "%s: Error, lacks board# property.\n", | ||
381 | board_parent->full_name); | ||
382 | goto err_out; | ||
383 | } | ||
384 | |||
385 | sbusl = pil_to_sbus[real_irq]; | ||
386 | if (sbusl) | ||
387 | pil = sun4d_encode_irq(board, sbusl, slot); | ||
388 | else | ||
389 | pil = real_irq; | ||
390 | |||
391 | irq = _sun4d_build_device_irq(real_irq, pil, board); | ||
392 | err_out: | ||
393 | return irq; | ||
394 | } | ||
395 | |||
396 | unsigned int sun4d_build_timer_irq(unsigned int board, unsigned int real_irq) | ||
397 | { | ||
398 | return _sun4d_build_device_irq(real_irq, real_irq, board); | ||
399 | } | ||
400 | |||
401 | |||
465 | static void __init sun4d_fixup_trap_table(void) | 402 | static void __init sun4d_fixup_trap_table(void) |
466 | { | 403 | { |
467 | #ifdef CONFIG_SMP | 404 | #ifdef CONFIG_SMP |
468 | unsigned long flags; | 405 | unsigned long flags; |
469 | extern unsigned long lvl14_save[4]; | ||
470 | struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; | 406 | struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)]; |
471 | extern unsigned int real_irq_entry[], smp4d_ticker[]; | ||
472 | extern unsigned int patchme_maybe_smp_msg[]; | ||
473 | 407 | ||
474 | /* Adjust so that we jump directly to smp4d_ticker */ | 408 | /* Adjust so that we jump directly to smp4d_ticker */ |
475 | lvl14_save[2] += smp4d_ticker - real_irq_entry; | 409 | lvl14_save[2] += smp4d_ticker - real_irq_entry; |
@@ -493,8 +427,10 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) | |||
493 | { | 427 | { |
494 | struct device_node *dp; | 428 | struct device_node *dp; |
495 | struct resource res; | 429 | struct resource res; |
430 | unsigned int irq; | ||
496 | const u32 *reg; | 431 | const u32 *reg; |
497 | int err; | 432 | int err; |
433 | int board; | ||
498 | 434 | ||
499 | dp = of_find_node_by_name(NULL, "cpu-unit"); | 435 | dp = of_find_node_by_name(NULL, "cpu-unit"); |
500 | if (!dp) { | 436 | if (!dp) { |
@@ -507,12 +443,19 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) | |||
507 | * bootbus. | 443 | * bootbus. |
508 | */ | 444 | */ |
509 | reg = of_get_property(dp, "reg", NULL); | 445 | reg = of_get_property(dp, "reg", NULL); |
510 | of_node_put(dp); | ||
511 | if (!reg) { | 446 | if (!reg) { |
512 | prom_printf("sun4d_init_timers: No reg property\n"); | 447 | prom_printf("sun4d_init_timers: No reg property\n"); |
513 | prom_halt(); | 448 | prom_halt(); |
514 | } | 449 | } |
515 | 450 | ||
451 | board = of_getintprop_default(dp, "board#", -1); | ||
452 | if (board == -1) { | ||
453 | prom_printf("sun4d_init_timers: No board# property on cpu-unit\n"); | ||
454 | prom_halt(); | ||
455 | } | ||
456 | |||
457 | of_node_put(dp); | ||
458 | |||
516 | res.start = reg[1]; | 459 | res.start = reg[1]; |
517 | res.end = reg[2] - 1; | 460 | res.end = reg[2] - 1; |
518 | res.flags = reg[0] & 0xff; | 461 | res.flags = reg[0] & 0xff; |
@@ -527,11 +470,11 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) | |||
527 | 470 | ||
528 | master_l10_counter = &sun4d_timers->l10_cur_count; | 471 | master_l10_counter = &sun4d_timers->l10_cur_count; |
529 | 472 | ||
530 | err = request_irq(TIMER_IRQ, counter_fn, | 473 | irq = sun4d_build_timer_irq(board, SUN4D_TIMER_IRQ); |
531 | (IRQF_DISABLED | SA_STATIC_ALLOC), | 474 | err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); |
532 | "timer", NULL); | ||
533 | if (err) { | 475 | if (err) { |
534 | prom_printf("sun4d_init_timers: request_irq() failed with %d\n", err); | 476 | prom_printf("sun4d_init_timers: request_irq() failed with %d\n", |
477 | err); | ||
535 | prom_halt(); | 478 | prom_halt(); |
536 | } | 479 | } |
537 | sun4d_load_profile_irqs(); | 480 | sun4d_load_profile_irqs(); |
@@ -541,32 +484,22 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) | |||
541 | void __init sun4d_init_sbi_irq(void) | 484 | void __init sun4d_init_sbi_irq(void) |
542 | { | 485 | { |
543 | struct device_node *dp; | 486 | struct device_node *dp; |
544 | int target_cpu = 0; | 487 | int target_cpu; |
545 | 488 | ||
546 | #ifdef CONFIG_SMP | ||
547 | target_cpu = boot_cpu_id; | 489 | target_cpu = boot_cpu_id; |
548 | #endif | ||
549 | |||
550 | nsbi = 0; | ||
551 | for_each_node_by_name(dp, "sbi") | ||
552 | nsbi++; | ||
553 | sbus_actions = kzalloc (nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC); | ||
554 | if (!sbus_actions) { | ||
555 | prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n"); | ||
556 | prom_halt(); | ||
557 | } | ||
558 | for_each_node_by_name(dp, "sbi") { | 490 | for_each_node_by_name(dp, "sbi") { |
559 | int devid = of_getintprop_default(dp, "device-id", 0); | 491 | int devid = of_getintprop_default(dp, "device-id", 0); |
560 | int board = of_getintprop_default(dp, "board#", 0); | 492 | int board = of_getintprop_default(dp, "board#", 0); |
561 | unsigned int mask; | 493 | unsigned int mask; |
562 | 494 | ||
563 | set_sbi_tid(devid, target_cpu << 3); | 495 | set_sbi_tid(devid, target_cpu << 3); |
564 | sbus_tid[board] = target_cpu; | 496 | board_to_cpu[board] = target_cpu; |
565 | 497 | ||
566 | /* Get rid of pending irqs from PROM */ | 498 | /* Get rid of pending irqs from PROM */ |
567 | mask = acquire_sbi(devid, 0xffffffff); | 499 | mask = acquire_sbi(devid, 0xffffffff); |
568 | if (mask) { | 500 | if (mask) { |
569 | printk ("Clearing pending IRQs %08x on SBI %d\n", mask, board); | 501 | printk(KERN_ERR "Clearing pending IRQs %08x on SBI %d\n", |
502 | mask, board); | ||
570 | release_sbi(devid, mask); | 503 | release_sbi(devid, mask); |
571 | } | 504 | } |
572 | } | 505 | } |
@@ -576,11 +509,12 @@ void __init sun4d_init_IRQ(void) | |||
576 | { | 509 | { |
577 | local_irq_disable(); | 510 | local_irq_disable(); |
578 | 511 | ||
579 | BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM); | ||
580 | BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM); | ||
581 | BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM); | 512 | BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM); |
582 | BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); | 513 | BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM); |
583 | sparc_init_timers = sun4d_init_timers; | 514 | |
515 | sparc_irq_config.init_timers = sun4d_init_timers; | ||
516 | sparc_irq_config.build_device_irq = sun4d_build_device_irq; | ||
517 | |||
584 | #ifdef CONFIG_SMP | 518 | #ifdef CONFIG_SMP |
585 | BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM); | 519 | BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM); |
586 | BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP); | 520 | BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP); |