blob: 77b4a89927100fe51be857b6a47d4fead6a8e135 [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
David S. Millerf5f10852008-09-13 22:04:55 -070041struct sun4d_timer_regs {
42 u32 l10_timer_limit;
43 u32 l10_cur_countx;
44 u32 l10_limit_noclear;
45 u32 ctrl;
46 u32 l10_cur_count;
47};
48
49static struct sun4d_timer_regs __iomem *sun4d_timers;
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define TIMER_IRQ 10
52
53#define MAX_STATIC_ALLOC 4
Adrian Bunkc61c65c2008-06-05 11:40:58 -070054static unsigned char sbus_tid[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -070055
Bob Breuera54123e2006-03-23 22:36:19 -080056static struct irqaction *irq_action[NR_IRQS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
Adrian Bunkc61c65c2008-06-05 11:40:58 -070058static struct sbus_action {
Linus Torvalds1da177e2005-04-16 15:20:36 -070059 struct irqaction *action;
60 /* For SMP this needs to be extended */
61} *sbus_actions;
62
63static int pil_to_sbus[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000064 0,
65 0,
66 1,
67 2,
68 0,
69 3,
70 0,
71 4,
72 0,
73 5,
74 0,
75 6,
76 0,
77 7,
78 0,
79 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -070080};
81
82static int sbus_to_pil[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000083 0,
84 2,
85 3,
86 5,
87 7,
88 9,
89 11,
90 13,
Linus Torvalds1da177e2005-04-16 15:20:36 -070091};
92
93static int nsbi;
David S. Millerf8376e92008-09-13 22:05:25 -070094
95/* Exported for sun4d_smp.c */
Linus Torvalds1da177e2005-04-16 15:20:36 -070096DEFINE_SPINLOCK(sun4d_imsk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097
98int show_sun4d_interrupts(struct seq_file *p, void *v)
99{
100 int i = *(loff_t *) v, j = 0, k = 0, sbusl;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000101 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 unsigned long flags;
103#ifdef CONFIG_SMP
104 int x;
105#endif
106
107 spin_lock_irqsave(&irq_action_lock, flags);
108 if (i < NR_IRQS) {
109 sbusl = pil_to_sbus[i];
110 if (!sbusl) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000111 action = *(i + irq_action);
112 if (!action)
113 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 } else {
115 for (j = 0; j < nsbi; j++) {
116 for (k = 0; k < 4; k++)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000117 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
118 if (action)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 goto found_it;
120 }
121 goto out_unlock;
122 }
123found_it: seq_printf(p, "%3d: ", i);
124#ifndef CONFIG_SMP
125 seq_printf(p, "%10u ", kstat_irqs(i));
126#else
Andrew Morton394e3902006-03-23 03:01:05 -0800127 for_each_online_cpu(x)
128 seq_printf(p, "%10u ",
129 kstat_cpu(cpu_logical_map(x)).irqs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130#endif
131 seq_printf(p, "%c %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700132 (action->flags & IRQF_DISABLED) ? '+' : ' ',
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 action->name);
134 action = action->next;
135 for (;;) {
136 for (; action; action = action->next) {
137 seq_printf(p, ",%s %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700138 (action->flags & IRQF_DISABLED) ? " +" : "",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 action->name);
140 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000141 if (!sbusl)
142 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 k++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000144 if (k < 4) {
145 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
146 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 j++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000148 if (j == nsbi)
149 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 k = 0;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000151 action = sbus_actions[(j << 5) + (sbusl << 2)].action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 }
153 }
154 seq_putc(p, '\n');
155 }
156out_unlock:
157 spin_unlock_irqrestore(&irq_action_lock, flags);
158 return 0;
159}
160
161void sun4d_free_irq(unsigned int irq, void *dev_id)
162{
163 struct irqaction *action, **actionp;
164 struct irqaction *tmp = NULL;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000165 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166
167 spin_lock_irqsave(&irq_action_lock, flags);
168 if (irq < 15)
169 actionp = irq + irq_action;
170 else
171 actionp = &(sbus_actions[irq - (1 << 5)].action);
172 action = *actionp;
173 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000174 printk(KERN_ERR "Trying to free free IRQ%d\n", irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 goto out_unlock;
176 }
177 if (dev_id) {
178 for (; action; action = action->next) {
179 if (action->dev_id == dev_id)
180 break;
181 tmp = action;
182 }
183 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000184 printk(KERN_ERR "Trying to free free shared IRQ%d\n",
185 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 goto out_unlock;
187 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700188 } else if (action->flags & IRQF_SHARED) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000189 printk(KERN_ERR "Trying to free shared IRQ%d with NULL device ID\n",
190 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 goto out_unlock;
192 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000193 if (action->flags & SA_STATIC_ALLOC) {
194 /*
195 * This interrupt is marked as specially allocated
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 * so it is a bad idea to free it.
197 */
Sam Ravnborge54f8542011-01-28 22:08:21 +0000198 printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 irq, action->name);
200 goto out_unlock;
201 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000202
Julia Lawall0d0659c2010-06-04 16:17:37 -0700203 if (tmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 tmp->next = action->next;
205 else
206 *actionp = action->next;
207
208 spin_unlock_irqrestore(&irq_action_lock, flags);
209
210 synchronize_irq(irq);
211
212 spin_lock_irqsave(&irq_action_lock, flags);
213
214 kfree(action);
215
216 if (!(*actionp))
Al Viro0f516812007-07-21 19:19:38 -0700217 __disable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
219out_unlock:
220 spin_unlock_irqrestore(&irq_action_lock, flags);
221}
222
Sam Ravnborge54f8542011-01-28 22:08:21 +0000223void sun4d_handler_irq(int pil, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224{
Al Viro0d844382006-10-08 14:30:44 +0100225 struct pt_regs *old_regs;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000226 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 int cpu = smp_processor_id();
228 /* SBUS IRQ level (1 - 7) */
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000229 int sbusl = pil_to_sbus[pil];
Sam Ravnborge54f8542011-01-28 22:08:21 +0000230
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 /* FIXME: Is this necessary?? */
232 cc_get_ipen();
Sam Ravnborge54f8542011-01-28 22:08:21 +0000233
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000234 cc_set_iclr(1 << pil);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000235
Al Viro0d844382006-10-08 14:30:44 +0100236 old_regs = set_irq_regs(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 irq_enter();
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000238 kstat_cpu(cpu).irqs[pil]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 if (!sbusl) {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000240 action = *(pil + irq_action);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000242 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000244 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 action = action->next;
246 } while (action);
247 } else {
248 int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
249 int sbino;
250 struct sbus_action *actionp;
251 unsigned mask, slot;
252 int sbil = (sbusl << 2);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 bw_clear_intr_mask(sbusl, bus_mask);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 /* Loop for each pending SBI */
257 for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
258 if (bus_mask & 1) {
259 mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
260 mask &= (0xf << sbil);
261 actionp = sbus_actions + (sbino << 5) + (sbil);
262 /* Loop for each pending SBI slot */
263 for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
264 if (mask & slot) {
265 mask &= ~slot;
266 action = actionp->action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000267
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000269 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000271 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 action = action->next;
273 } while (action);
274 release_sbi(SBI2DEVID(sbino), slot);
275 }
276 }
277 }
278 irq_exit();
Al Viro0d844382006-10-08 14:30:44 +0100279 set_irq_regs(old_regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280}
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282int sun4d_request_irq(unsigned int irq,
David Howells40220c12006-10-09 12:19:47 +0100283 irq_handler_t handler,
Sam Ravnborge54f8542011-01-28 22:08:21 +0000284 unsigned long irqflags, const char *devname, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 struct irqaction *action, *tmp = NULL, **actionp;
287 unsigned long flags;
288 int ret;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000289
290 if (irq > 14 && irq < (1 << 5)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 ret = -EINVAL;
292 goto out;
293 }
294
295 if (!handler) {
296 ret = -EINVAL;
297 goto out;
298 }
299
300 spin_lock_irqsave(&irq_action_lock, flags);
301
302 if (irq >= (1 << 5))
303 actionp = &(sbus_actions[irq - (1 << 5)].action);
304 else
305 actionp = irq + irq_action;
306 action = *actionp;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 if (action) {
Thomas Gleixner67413202006-07-01 19:29:26 -0700309 if ((action->flags & IRQF_SHARED) && (irqflags & IRQF_SHARED)) {
Sam Ravnborg70044df2011-01-28 22:08:23 +0000310 for (tmp = action; tmp->next; tmp = tmp->next)
311 /* find last entry - tmp used below */;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 } else {
313 ret = -EBUSY;
314 goto out_unlock;
315 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700316 if ((action->flags & IRQF_DISABLED) ^ (irqflags & IRQF_DISABLED)) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000317 printk(KERN_ERR "Attempt to mix fast and slow interrupts on IRQ%d denied\n",
318 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 ret = -EBUSY;
320 goto out_unlock;
321 }
322 action = NULL; /* Or else! */
323 }
324
325 /* If this is flagged as statically allocated then we use our
326 * private struct which is never freed.
327 */
328 if (irqflags & SA_STATIC_ALLOC) {
329 if (static_irq_count < MAX_STATIC_ALLOC)
330 action = &static_irqaction[static_irq_count++];
331 else
Sam Ravnborge54f8542011-01-28 22:08:21 +0000332 printk(KERN_ERR "Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
333 irq, devname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000335
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 if (action == NULL)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000337 action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
338
339 if (!action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 ret = -ENOMEM;
341 goto out_unlock;
342 }
343
344 action->handler = handler;
345 action->flags = irqflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 action->name = devname;
347 action->next = NULL;
348 action->dev_id = dev_id;
349
350 if (tmp)
351 tmp->next = action;
352 else
353 *actionp = action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000354
Al Viro0f516812007-07-21 19:19:38 -0700355 __enable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
357 ret = 0;
358out_unlock:
359 spin_unlock_irqrestore(&irq_action_lock, flags);
360out:
361 return ret;
362}
363
364static void sun4d_disable_irq(unsigned int irq)
365{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 int tid = sbus_tid[(irq >> 5) - 1];
367 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000368
David S. Millerf8376e92008-09-13 22:05:25 -0700369 if (irq < NR_IRQS)
370 return;
371
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 spin_lock_irqsave(&sun4d_imsk_lock, flags);
373 cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
374 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375}
376
377static void sun4d_enable_irq(unsigned int irq)
378{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379 int tid = sbus_tid[(irq >> 5) - 1];
380 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000381
David S. Millerf8376e92008-09-13 22:05:25 -0700382 if (irq < NR_IRQS)
383 return;
384
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 spin_lock_irqsave(&sun4d_imsk_lock, flags);
386 cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
387 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388}
389
390#ifdef CONFIG_SMP
391static void sun4d_set_cpu_int(int cpu, int level)
392{
393 sun4d_send_ipi(cpu, level);
394}
395
396static void sun4d_clear_ipi(int cpu, int level)
397{
398}
399
400static void sun4d_set_udt(int cpu)
401{
402}
403
404/* Setup IRQ distribution scheme. */
405void __init sun4d_distribute_irqs(void)
406{
David S. Miller71d37212008-08-27 02:50:57 -0700407 struct device_node *dp;
408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 int cpuid = cpu_logical_map(1);
410
411 if (cpuid == -1)
412 cpuid = cpu_logical_map(0);
David S. Miller71d37212008-08-27 02:50:57 -0700413 for_each_node_by_name(dp, "sbi") {
414 int devid = of_getintprop_default(dp, "device-id", 0);
415 int board = of_getintprop_default(dp, "board#", 0);
416 sbus_tid[board] = cpuid;
417 set_sbi_tid(devid, cpuid << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000419 printk(KERN_ERR "All sbus IRQs directed to CPU%d\n", cpuid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420}
421#endif
Sam Ravnborge54f8542011-01-28 22:08:21 +0000422
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423static void sun4d_clear_clock_irq(void)
424{
David S. Millerf5f10852008-09-13 22:04:55 -0700425 sbus_readl(&sun4d_timers->l10_timer_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700426}
427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428static void sun4d_load_profile_irq(int cpu, unsigned int limit)
429{
430 bw_set_prof_limit(cpu, limit);
431}
432
David S. Millerf5f10852008-09-13 22:04:55 -0700433static void __init sun4d_load_profile_irqs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
David S. Millerf5f10852008-09-13 22:04:55 -0700435 int cpu = 0, mid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 while (!cpu_find_by_instance(cpu, NULL, &mid)) {
438 sun4d_load_profile_irq(mid >> 3, 0);
439 cpu++;
440 }
David S. Millerf5f10852008-09-13 22:04:55 -0700441}
442
Sam Ravnborg1d059952011-02-25 23:01:19 -0800443unsigned int sun4d_build_device_irq(struct platform_device *op,
444 unsigned int real_irq)
445{
446 static int pil_to_sbus[] = {
447 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
448 };
449 struct device_node *dp = op->dev.of_node;
450 struct device_node *io_unit, *sbi = dp->parent;
451 const struct linux_prom_registers *regs;
452 int board, slot;
453 int sbusl;
454
455 while (sbi) {
456 if (!strcmp(sbi->name, "sbi"))
457 break;
458
459 sbi = sbi->parent;
460 }
461 if (!sbi)
462 goto err_out;
463
464 regs = of_get_property(dp, "reg", NULL);
465 if (!regs)
466 goto err_out;
467
468 slot = regs->which_io;
469
470 /*
471 * If SBI's parent is not io-unit or the io-unit lacks
472 * a "board#" property, something is very wrong.
473 */
474 if (!sbi->parent || strcmp(sbi->parent->name, "io-unit")) {
475 printk("%s: Error, parent is not io-unit.\n", sbi->full_name);
476 goto err_out;
477 }
478 io_unit = sbi->parent;
479 board = of_getintprop_default(io_unit, "board#", -1);
480 if (board == -1) {
481 printk("%s: Error, lacks board# property.\n", io_unit->full_name);
482 goto err_out;
483 }
484
485 sbusl = pil_to_sbus[real_irq];
486 if (sbusl)
487 return (((board + 1) << 5) + (sbusl << 2) + slot);
488
489err_out:
490 return real_irq;
491}
492
David S. Millerf5f10852008-09-13 22:04:55 -0700493static void __init sun4d_fixup_trap_table(void)
494{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495#ifdef CONFIG_SMP
David S. Millerf5f10852008-09-13 22:04:55 -0700496 unsigned long flags;
David S. Millerf5f10852008-09-13 22:04:55 -0700497 struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498
David S. Millerf5f10852008-09-13 22:04:55 -0700499 /* Adjust so that we jump directly to smp4d_ticker */
500 lvl14_save[2] += smp4d_ticker - real_irq_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
David S. Millerf5f10852008-09-13 22:04:55 -0700502 /* For SMP we use the level 14 ticker, however the bootup code
503 * has copied the firmware's level 14 vector into the boot cpu's
504 * trap table, we must fix this now or we get squashed.
505 */
506 local_irq_save(flags);
507 patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
508 trap_table->inst_one = lvl14_save[0];
509 trap_table->inst_two = lvl14_save[1];
510 trap_table->inst_three = lvl14_save[2];
511 trap_table->inst_four = lvl14_save[3];
512 local_flush_cache_all();
513 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514#endif
515}
516
David S. Millerf5f10852008-09-13 22:04:55 -0700517static void __init sun4d_init_timers(irq_handler_t counter_fn)
518{
519 struct device_node *dp;
520 struct resource res;
521 const u32 *reg;
522 int err;
523
524 dp = of_find_node_by_name(NULL, "cpu-unit");
525 if (!dp) {
526 prom_printf("sun4d_init_timers: Unable to find cpu-unit\n");
527 prom_halt();
528 }
529
530 /* Which cpu-unit we use is arbitrary, we can view the bootbus timer
531 * registers via any cpu's mapping. The first 'reg' property is the
532 * bootbus.
533 */
534 reg = of_get_property(dp, "reg", NULL);
Nicolas Palixc2e27c32008-12-03 21:10:57 -0800535 of_node_put(dp);
David S. Millerf5f10852008-09-13 22:04:55 -0700536 if (!reg) {
537 prom_printf("sun4d_init_timers: No reg property\n");
538 prom_halt();
539 }
540
541 res.start = reg[1];
542 res.end = reg[2] - 1;
543 res.flags = reg[0] & 0xff;
544 sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT,
545 sizeof(struct sun4d_timer_regs), "user timer");
546 if (!sun4d_timers) {
547 prom_printf("sun4d_init_timers: Can't map timer regs\n");
548 prom_halt();
549 }
550
551 sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit);
552
553 master_l10_counter = &sun4d_timers->l10_cur_count;
David S. Millerf5f10852008-09-13 22:04:55 -0700554
555 err = request_irq(TIMER_IRQ, counter_fn,
556 (IRQF_DISABLED | SA_STATIC_ALLOC),
557 "timer", NULL);
558 if (err) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000559 prom_printf("sun4d_init_timers: request_irq() failed with %d\n",
560 err);
David S. Millerf5f10852008-09-13 22:04:55 -0700561 prom_halt();
562 }
563 sun4d_load_profile_irqs();
564 sun4d_fixup_trap_table();
565}
566
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567void __init sun4d_init_sbi_irq(void)
568{
David S. Miller71d37212008-08-27 02:50:57 -0700569 struct device_node *dp;
David S. Millerf8376e92008-09-13 22:05:25 -0700570 int target_cpu = 0;
571
572#ifdef CONFIG_SMP
573 target_cpu = boot_cpu_id;
574#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576 nsbi = 0;
David S. Miller71d37212008-08-27 02:50:57 -0700577 for_each_node_by_name(dp, "sbi")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 nsbi++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000579 sbus_actions = kzalloc(nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC);
David S. Millerd4accd62006-11-30 17:11:26 -0800580 if (!sbus_actions) {
581 prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n");
582 prom_halt();
583 }
David S. Miller71d37212008-08-27 02:50:57 -0700584 for_each_node_by_name(dp, "sbi") {
585 int devid = of_getintprop_default(dp, "device-id", 0);
586 int board = of_getintprop_default(dp, "board#", 0);
587 unsigned int mask;
588
David S. Millerf8376e92008-09-13 22:05:25 -0700589 set_sbi_tid(devid, target_cpu << 3);
590 sbus_tid[board] = target_cpu;
591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 /* Get rid of pending irqs from PROM */
David S. Miller71d37212008-08-27 02:50:57 -0700593 mask = acquire_sbi(devid, 0xffffffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 if (mask) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000595 printk(KERN_ERR "Clearing pending IRQs %08x on SBI %d\n",
596 mask, board);
David S. Miller71d37212008-08-27 02:50:57 -0700597 release_sbi(devid, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 }
599 }
600}
601
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602void __init sun4d_init_IRQ(void)
603{
604 local_irq_disable();
605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
607 BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
608 BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
Sam Ravnborgbbdc2662011-02-25 23:00:19 -0800610
611 sparc_irq_config.init_timers = sun4d_init_timers;
Sam Ravnborg1d059952011-02-25 23:01:19 -0800612 sparc_irq_config.build_device_irq = sun4d_build_device_irq;
Sam Ravnborgbbdc2662011-02-25 23:00:19 -0800613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614#ifdef CONFIG_SMP
615 BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
616 BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
617 BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
618#endif
619 /* Cannot enable interrupts until OBP ticker is disabled. */
620}