blob: 5fc4c220158c41226336a2122d4d2b4b25f2fc5b [file] [log] [blame]
Adrian Bunk88278ca2008-05-19 16:53:02 -07001/*
Sam Ravnborge54f8542011-01-28 22:08:21 +00002 * SS1000/SC2000 interrupt handling.
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
4 * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
5 * Heavily based on arch/sparc/kernel/irq.c.
6 */
7
Linus Torvalds1da177e2005-04-16 15:20:36 -07008#include <linux/kernel_stat.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include <linux/seq_file.h>
10
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include <asm/timer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include <asm/traps.h>
13#include <asm/irq.h>
14#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <asm/sbi.h>
16#include <asm/cacheflush.h>
17
Sam Ravnborg81265fd2008-12-08 01:08:24 -080018#include "kernel.h"
Al Viro32231a62007-07-21 19:18:57 -070019#include "irq.h"
20
Sam Ravnborge54f8542011-01-28 22:08:21 +000021/* Sun4d interrupts fall roughly into two categories. SBUS and
22 * cpu local. CPU local interrupts cover the timer interrupts
23 * and whatnot, and we encode those as normal PILs between
24 * 0 and 15.
25 *
26 * SBUS interrupts are encoded integers including the board number
27 * (plus one), the SBUS level, and the SBUS slot number. Sun4D
28 * IRQ dispatch is done by:
29 *
30 * 1) Reading the BW local interrupt table in order to get the bus
31 * interrupt mask.
32 *
33 * This table is indexed by SBUS interrupt level which can be
34 * derived from the PIL we got interrupted on.
35 *
36 * 2) For each bus showing interrupt pending from #1, read the
37 * SBI interrupt state register. This will indicate which slots
38 * have interrupts pending for that SBUS interrupt level.
39 */
40
41/*
42 * If you trust current SCSI layer to handle different
43 * SCSI IRQs, enable this.
44 * I don't trust it... -jj
45 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070046/* #define DISTRIBUTE_IRQS */
47
David S. Millerf5f10852008-09-13 22:04:55 -070048struct sun4d_timer_regs {
49 u32 l10_timer_limit;
50 u32 l10_cur_countx;
51 u32 l10_limit_noclear;
52 u32 ctrl;
53 u32 l10_cur_count;
54};
55
56static struct sun4d_timer_regs __iomem *sun4d_timers;
57
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#define TIMER_IRQ 10
59
60#define MAX_STATIC_ALLOC 4
Adrian Bunkc61c65c2008-06-05 11:40:58 -070061static unsigned char sbus_tid[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
Bob Breuera54123e2006-03-23 22:36:19 -080063static struct irqaction *irq_action[NR_IRQS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Adrian Bunkc61c65c2008-06-05 11:40:58 -070065static struct sbus_action {
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 struct irqaction *action;
67 /* For SMP this needs to be extended */
68} *sbus_actions;
69
70static int pil_to_sbus[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000071 0,
72 0,
73 1,
74 2,
75 0,
76 3,
77 0,
78 4,
79 0,
80 5,
81 0,
82 6,
83 0,
84 7,
85 0,
86 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -070087};
88
89static int sbus_to_pil[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000090 0,
91 2,
92 3,
93 5,
94 7,
95 9,
96 11,
97 13,
Linus Torvalds1da177e2005-04-16 15:20:36 -070098};
99
100static int nsbi;
David S. Millerf8376e92008-09-13 22:05:25 -0700101
102/* Exported for sun4d_smp.c */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103DEFINE_SPINLOCK(sun4d_imsk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104
105int show_sun4d_interrupts(struct seq_file *p, void *v)
106{
107 int i = *(loff_t *) v, j = 0, k = 0, sbusl;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000108 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109 unsigned long flags;
110#ifdef CONFIG_SMP
111 int x;
112#endif
113
114 spin_lock_irqsave(&irq_action_lock, flags);
115 if (i < NR_IRQS) {
116 sbusl = pil_to_sbus[i];
117 if (!sbusl) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000118 action = *(i + irq_action);
119 if (!action)
120 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 } else {
122 for (j = 0; j < nsbi; j++) {
123 for (k = 0; k < 4; k++)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000124 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
125 if (action)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 goto found_it;
127 }
128 goto out_unlock;
129 }
130found_it: seq_printf(p, "%3d: ", i);
131#ifndef CONFIG_SMP
132 seq_printf(p, "%10u ", kstat_irqs(i));
133#else
Andrew Morton394e3902006-03-23 03:01:05 -0800134 for_each_online_cpu(x)
135 seq_printf(p, "%10u ",
136 kstat_cpu(cpu_logical_map(x)).irqs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137#endif
138 seq_printf(p, "%c %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700139 (action->flags & IRQF_DISABLED) ? '+' : ' ',
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140 action->name);
141 action = action->next;
142 for (;;) {
143 for (; action; action = action->next) {
144 seq_printf(p, ",%s %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700145 (action->flags & IRQF_DISABLED) ? " +" : "",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 action->name);
147 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000148 if (!sbusl)
149 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 k++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000151 if (k < 4) {
152 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
153 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 j++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000155 if (j == nsbi)
156 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 k = 0;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000158 action = sbus_actions[(j << 5) + (sbusl << 2)].action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
160 }
161 seq_putc(p, '\n');
162 }
163out_unlock:
164 spin_unlock_irqrestore(&irq_action_lock, flags);
165 return 0;
166}
167
168void sun4d_free_irq(unsigned int irq, void *dev_id)
169{
170 struct irqaction *action, **actionp;
171 struct irqaction *tmp = NULL;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000172 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173
174 spin_lock_irqsave(&irq_action_lock, flags);
175 if (irq < 15)
176 actionp = irq + irq_action;
177 else
178 actionp = &(sbus_actions[irq - (1 << 5)].action);
179 action = *actionp;
180 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000181 printk(KERN_ERR "Trying to free free IRQ%d\n", irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 goto out_unlock;
183 }
184 if (dev_id) {
185 for (; action; action = action->next) {
186 if (action->dev_id == dev_id)
187 break;
188 tmp = action;
189 }
190 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000191 printk(KERN_ERR "Trying to free free shared IRQ%d\n",
192 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 goto out_unlock;
194 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700195 } else if (action->flags & IRQF_SHARED) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000196 printk(KERN_ERR "Trying to free shared IRQ%d with NULL device ID\n",
197 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 goto out_unlock;
199 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000200 if (action->flags & SA_STATIC_ALLOC) {
201 /*
202 * This interrupt is marked as specially allocated
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 * so it is a bad idea to free it.
204 */
Sam Ravnborge54f8542011-01-28 22:08:21 +0000205 printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 irq, action->name);
207 goto out_unlock;
208 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000209
Julia Lawall0d0659c2010-06-04 16:17:37 -0700210 if (tmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 tmp->next = action->next;
212 else
213 *actionp = action->next;
214
215 spin_unlock_irqrestore(&irq_action_lock, flags);
216
217 synchronize_irq(irq);
218
219 spin_lock_irqsave(&irq_action_lock, flags);
220
221 kfree(action);
222
223 if (!(*actionp))
Al Viro0f516812007-07-21 19:19:38 -0700224 __disable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
226out_unlock:
227 spin_unlock_irqrestore(&irq_action_lock, flags);
228}
229
Sam Ravnborge54f8542011-01-28 22:08:21 +0000230void sun4d_handler_irq(int pil, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231{
Al Viro0d844382006-10-08 14:30:44 +0100232 struct pt_regs *old_regs;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000233 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 int cpu = smp_processor_id();
235 /* SBUS IRQ level (1 - 7) */
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000236 int sbusl = pil_to_sbus[pil];
Sam Ravnborge54f8542011-01-28 22:08:21 +0000237
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 /* FIXME: Is this necessary?? */
239 cc_get_ipen();
Sam Ravnborge54f8542011-01-28 22:08:21 +0000240
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000241 cc_set_iclr(1 << pil);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000242
Al Viro0d844382006-10-08 14:30:44 +0100243 old_regs = set_irq_regs(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244 irq_enter();
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000245 kstat_cpu(cpu).irqs[pil]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 if (!sbusl) {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000247 action = *(pil + irq_action);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000249 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000251 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 action = action->next;
253 } while (action);
254 } else {
255 int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
256 int sbino;
257 struct sbus_action *actionp;
258 unsigned mask, slot;
259 int sbil = (sbusl << 2);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 bw_clear_intr_mask(sbusl, bus_mask);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 /* Loop for each pending SBI */
264 for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
265 if (bus_mask & 1) {
266 mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
267 mask &= (0xf << sbil);
268 actionp = sbus_actions + (sbino << 5) + (sbil);
269 /* Loop for each pending SBI slot */
270 for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
271 if (mask & slot) {
272 mask &= ~slot;
273 action = actionp->action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000276 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000278 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 action = action->next;
280 } while (action);
281 release_sbi(SBI2DEVID(sbino), slot);
282 }
283 }
284 }
285 irq_exit();
Al Viro0d844382006-10-08 14:30:44 +0100286 set_irq_regs(old_regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287}
288
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289int sun4d_request_irq(unsigned int irq,
David Howells40220c12006-10-09 12:19:47 +0100290 irq_handler_t handler,
Sam Ravnborge54f8542011-01-28 22:08:21 +0000291 unsigned long irqflags, const char *devname, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 struct irqaction *action, *tmp = NULL, **actionp;
294 unsigned long flags;
295 int ret;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000296
297 if (irq > 14 && irq < (1 << 5)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 ret = -EINVAL;
299 goto out;
300 }
301
302 if (!handler) {
303 ret = -EINVAL;
304 goto out;
305 }
306
307 spin_lock_irqsave(&irq_action_lock, flags);
308
309 if (irq >= (1 << 5))
310 actionp = &(sbus_actions[irq - (1 << 5)].action);
311 else
312 actionp = irq + irq_action;
313 action = *actionp;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000314
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 if (action) {
Thomas Gleixner67413202006-07-01 19:29:26 -0700316 if ((action->flags & IRQF_SHARED) && (irqflags & IRQF_SHARED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 for (tmp = action; tmp->next; tmp = tmp->next);
318 } else {
319 ret = -EBUSY;
320 goto out_unlock;
321 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700322 if ((action->flags & IRQF_DISABLED) ^ (irqflags & IRQF_DISABLED)) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000323 printk(KERN_ERR "Attempt to mix fast and slow interrupts on IRQ%d denied\n",
324 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 ret = -EBUSY;
326 goto out_unlock;
327 }
328 action = NULL; /* Or else! */
329 }
330
331 /* If this is flagged as statically allocated then we use our
332 * private struct which is never freed.
333 */
334 if (irqflags & SA_STATIC_ALLOC) {
335 if (static_irq_count < MAX_STATIC_ALLOC)
336 action = &static_irqaction[static_irq_count++];
337 else
Sam Ravnborge54f8542011-01-28 22:08:21 +0000338 printk(KERN_ERR "Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
339 irq, devname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000341
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 if (action == NULL)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000343 action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
344
345 if (!action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 ret = -ENOMEM;
347 goto out_unlock;
348 }
349
350 action->handler = handler;
351 action->flags = irqflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 action->name = devname;
353 action->next = NULL;
354 action->dev_id = dev_id;
355
356 if (tmp)
357 tmp->next = action;
358 else
359 *actionp = action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000360
Al Viro0f516812007-07-21 19:19:38 -0700361 __enable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
363 ret = 0;
364out_unlock:
365 spin_unlock_irqrestore(&irq_action_lock, flags);
366out:
367 return ret;
368}
369
370static void sun4d_disable_irq(unsigned int irq)
371{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 int tid = sbus_tid[(irq >> 5) - 1];
373 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000374
David S. Millerf8376e92008-09-13 22:05:25 -0700375 if (irq < NR_IRQS)
376 return;
377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 spin_lock_irqsave(&sun4d_imsk_lock, flags);
379 cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
380 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381}
382
383static void sun4d_enable_irq(unsigned int irq)
384{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 int tid = sbus_tid[(irq >> 5) - 1];
386 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000387
David S. Millerf8376e92008-09-13 22:05:25 -0700388 if (irq < NR_IRQS)
389 return;
390
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 spin_lock_irqsave(&sun4d_imsk_lock, flags);
392 cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
393 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394}
395
396#ifdef CONFIG_SMP
397static void sun4d_set_cpu_int(int cpu, int level)
398{
399 sun4d_send_ipi(cpu, level);
400}
401
402static void sun4d_clear_ipi(int cpu, int level)
403{
404}
405
406static void sun4d_set_udt(int cpu)
407{
408}
409
410/* Setup IRQ distribution scheme. */
411void __init sun4d_distribute_irqs(void)
412{
David S. Miller71d37212008-08-27 02:50:57 -0700413 struct device_node *dp;
414
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415#ifdef DISTRIBUTE_IRQS
David S. Miller71d37212008-08-27 02:50:57 -0700416 cpumask_t sbus_serving_map;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417
418 sbus_serving_map = cpu_present_map;
David S. Miller71d37212008-08-27 02:50:57 -0700419 for_each_node_by_name(dp, "sbi") {
420 int board = of_getintprop_default(dp, "board#", 0);
421
422 if ((board * 2) == boot_cpu_id && cpu_isset(board * 2 + 1, cpu_present_map))
423 sbus_tid[board] = (board * 2 + 1);
424 else if (cpu_isset(board * 2, cpu_present_map))
425 sbus_tid[board] = (board * 2);
426 else if (cpu_isset(board * 2 + 1, cpu_present_map))
427 sbus_tid[board] = (board * 2 + 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 else
David S. Miller71d37212008-08-27 02:50:57 -0700429 sbus_tid[board] = 0xff;
430 if (sbus_tid[board] != 0xff)
431 cpu_clear(sbus_tid[board], sbus_serving_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432 }
David S. Miller71d37212008-08-27 02:50:57 -0700433 for_each_node_by_name(dp, "sbi") {
434 int board = of_getintprop_default(dp, "board#", 0);
435 if (sbus_tid[board] == 0xff) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436 int i = 31;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000437
David S. Miller71d37212008-08-27 02:50:57 -0700438 if (cpus_empty(sbus_serving_map))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 sbus_serving_map = cpu_present_map;
David S. Miller71d37212008-08-27 02:50:57 -0700440 while (cpu_isset(i, sbus_serving_map))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 i--;
David S. Miller71d37212008-08-27 02:50:57 -0700442 sbus_tid[board] = i;
443 cpu_clear(i, sbus_serving_map);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444 }
David S. Miller71d37212008-08-27 02:50:57 -0700445 }
446 for_each_node_by_name(dp, "sbi") {
447 int devid = of_getintprop_default(dp, "device-id", 0);
448 int board = of_getintprop_default(dp, "board#", 0);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000449 printk(KERN_ERR "sbus%d IRQs directed to CPU%d\n",
450 board, sbus_tid[board]);
David S. Miller71d37212008-08-27 02:50:57 -0700451 set_sbi_tid(devid, sbus_tid[board] << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453#else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 int cpuid = cpu_logical_map(1);
455
456 if (cpuid == -1)
457 cpuid = cpu_logical_map(0);
David S. Miller71d37212008-08-27 02:50:57 -0700458 for_each_node_by_name(dp, "sbi") {
459 int devid = of_getintprop_default(dp, "device-id", 0);
460 int board = of_getintprop_default(dp, "board#", 0);
461 sbus_tid[board] = cpuid;
462 set_sbi_tid(devid, cpuid << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000464 printk(KERN_ERR "All sbus IRQs directed to CPU%d\n", cpuid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465#endif
466}
467#endif
Sam Ravnborge54f8542011-01-28 22:08:21 +0000468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469static void sun4d_clear_clock_irq(void)
470{
David S. Millerf5f10852008-09-13 22:04:55 -0700471 sbus_readl(&sun4d_timers->l10_timer_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472}
473
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474static void sun4d_load_profile_irq(int cpu, unsigned int limit)
475{
476 bw_set_prof_limit(cpu, limit);
477}
478
David S. Millerf5f10852008-09-13 22:04:55 -0700479static void __init sun4d_load_profile_irqs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480{
David S. Millerf5f10852008-09-13 22:04:55 -0700481 int cpu = 0, mid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 while (!cpu_find_by_instance(cpu, NULL, &mid)) {
484 sun4d_load_profile_irq(mid >> 3, 0);
485 cpu++;
486 }
David S. Millerf5f10852008-09-13 22:04:55 -0700487}
488
489static void __init sun4d_fixup_trap_table(void)
490{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491#ifdef CONFIG_SMP
David S. Millerf5f10852008-09-13 22:04:55 -0700492 unsigned long flags;
David S. Millerf5f10852008-09-13 22:04:55 -0700493 struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494
David S. Millerf5f10852008-09-13 22:04:55 -0700495 /* Adjust so that we jump directly to smp4d_ticker */
496 lvl14_save[2] += smp4d_ticker - real_irq_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
David S. Millerf5f10852008-09-13 22:04:55 -0700498 /* For SMP we use the level 14 ticker, however the bootup code
499 * has copied the firmware's level 14 vector into the boot cpu's
500 * trap table, we must fix this now or we get squashed.
501 */
502 local_irq_save(flags);
503 patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
504 trap_table->inst_one = lvl14_save[0];
505 trap_table->inst_two = lvl14_save[1];
506 trap_table->inst_three = lvl14_save[2];
507 trap_table->inst_four = lvl14_save[3];
508 local_flush_cache_all();
509 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510#endif
511}
512
David S. Millerf5f10852008-09-13 22:04:55 -0700513static void __init sun4d_init_timers(irq_handler_t counter_fn)
514{
515 struct device_node *dp;
516 struct resource res;
517 const u32 *reg;
518 int err;
519
520 dp = of_find_node_by_name(NULL, "cpu-unit");
521 if (!dp) {
522 prom_printf("sun4d_init_timers: Unable to find cpu-unit\n");
523 prom_halt();
524 }
525
526 /* Which cpu-unit we use is arbitrary, we can view the bootbus timer
527 * registers via any cpu's mapping. The first 'reg' property is the
528 * bootbus.
529 */
530 reg = of_get_property(dp, "reg", NULL);
Nicolas Palixc2e27c32008-12-03 21:10:57 -0800531 of_node_put(dp);
David S. Millerf5f10852008-09-13 22:04:55 -0700532 if (!reg) {
533 prom_printf("sun4d_init_timers: No reg property\n");
534 prom_halt();
535 }
536
537 res.start = reg[1];
538 res.end = reg[2] - 1;
539 res.flags = reg[0] & 0xff;
540 sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT,
541 sizeof(struct sun4d_timer_regs), "user timer");
542 if (!sun4d_timers) {
543 prom_printf("sun4d_init_timers: Can't map timer regs\n");
544 prom_halt();
545 }
546
547 sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit);
548
549 master_l10_counter = &sun4d_timers->l10_cur_count;
David S. Millerf5f10852008-09-13 22:04:55 -0700550
551 err = request_irq(TIMER_IRQ, counter_fn,
552 (IRQF_DISABLED | SA_STATIC_ALLOC),
553 "timer", NULL);
554 if (err) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000555 prom_printf("sun4d_init_timers: request_irq() failed with %d\n",
556 err);
David S. Millerf5f10852008-09-13 22:04:55 -0700557 prom_halt();
558 }
559 sun4d_load_profile_irqs();
560 sun4d_fixup_trap_table();
561}
562
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563void __init sun4d_init_sbi_irq(void)
564{
David S. Miller71d37212008-08-27 02:50:57 -0700565 struct device_node *dp;
David S. Millerf8376e92008-09-13 22:05:25 -0700566 int target_cpu = 0;
567
568#ifdef CONFIG_SMP
569 target_cpu = boot_cpu_id;
570#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571
572 nsbi = 0;
David S. Miller71d37212008-08-27 02:50:57 -0700573 for_each_node_by_name(dp, "sbi")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 nsbi++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000575 sbus_actions = kzalloc(nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC);
David S. Millerd4accd62006-11-30 17:11:26 -0800576 if (!sbus_actions) {
577 prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n");
578 prom_halt();
579 }
David S. Miller71d37212008-08-27 02:50:57 -0700580 for_each_node_by_name(dp, "sbi") {
581 int devid = of_getintprop_default(dp, "device-id", 0);
582 int board = of_getintprop_default(dp, "board#", 0);
583 unsigned int mask;
584
David S. Millerf8376e92008-09-13 22:05:25 -0700585 set_sbi_tid(devid, target_cpu << 3);
586 sbus_tid[board] = target_cpu;
587
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 /* Get rid of pending irqs from PROM */
David S. Miller71d37212008-08-27 02:50:57 -0700589 mask = acquire_sbi(devid, 0xffffffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (mask) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000591 printk(KERN_ERR "Clearing pending IRQs %08x on SBI %d\n",
592 mask, board);
David S. Miller71d37212008-08-27 02:50:57 -0700593 release_sbi(devid, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 }
595 }
596}
597
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598void __init sun4d_init_IRQ(void)
599{
600 local_irq_disable();
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
603 BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
604 BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 sparc_init_timers = sun4d_init_timers;
607#ifdef CONFIG_SMP
608 BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
609 BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
610 BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
611#endif
612 /* Cannot enable interrupts until OBP ticker is disabled. */
613}