blob: ee35c45ffb893ee14d5f5d9d93de20e0d9c5a6cc [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
Sam Ravnborgdb1cdd12011-04-18 11:25:42 +000054
55/* Specify which cpu handle interrupts from which board.
56 * Index is board - value is cpu.
57 */
58static unsigned char board_to_cpu[32];
Linus Torvalds1da177e2005-04-16 15:20:36 -070059
Bob Breuera54123e2006-03-23 22:36:19 -080060static struct irqaction *irq_action[NR_IRQS];
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
Adrian Bunkc61c65c2008-06-05 11:40:58 -070062static struct sbus_action {
Linus Torvalds1da177e2005-04-16 15:20:36 -070063 struct irqaction *action;
64 /* For SMP this needs to be extended */
65} *sbus_actions;
66
67static int pil_to_sbus[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000068 0,
69 0,
70 1,
71 2,
72 0,
73 3,
74 0,
75 4,
76 0,
77 5,
78 0,
79 6,
80 0,
81 7,
82 0,
83 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -070084};
85
86static int sbus_to_pil[] = {
Sam Ravnborge54f8542011-01-28 22:08:21 +000087 0,
88 2,
89 3,
90 5,
91 7,
92 9,
93 11,
94 13,
Linus Torvalds1da177e2005-04-16 15:20:36 -070095};
96
97static int nsbi;
David S. Millerf8376e92008-09-13 22:05:25 -070098
99/* Exported for sun4d_smp.c */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100DEFINE_SPINLOCK(sun4d_imsk_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
102int show_sun4d_interrupts(struct seq_file *p, void *v)
103{
104 int i = *(loff_t *) v, j = 0, k = 0, sbusl;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000105 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106 unsigned long flags;
107#ifdef CONFIG_SMP
108 int x;
109#endif
110
111 spin_lock_irqsave(&irq_action_lock, flags);
112 if (i < NR_IRQS) {
113 sbusl = pil_to_sbus[i];
114 if (!sbusl) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000115 action = *(i + irq_action);
116 if (!action)
117 goto out_unlock;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 } else {
119 for (j = 0; j < nsbi; j++) {
120 for (k = 0; k < 4; k++)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000121 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
122 if (action)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 goto found_it;
124 }
125 goto out_unlock;
126 }
127found_it: seq_printf(p, "%3d: ", i);
128#ifndef CONFIG_SMP
129 seq_printf(p, "%10u ", kstat_irqs(i));
130#else
Andrew Morton394e3902006-03-23 03:01:05 -0800131 for_each_online_cpu(x)
132 seq_printf(p, "%10u ",
133 kstat_cpu(cpu_logical_map(x)).irqs[i]);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134#endif
135 seq_printf(p, "%c %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700136 (action->flags & IRQF_DISABLED) ? '+' : ' ',
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 action->name);
138 action = action->next;
139 for (;;) {
140 for (; action; action = action->next) {
141 seq_printf(p, ",%s %s",
Thomas Gleixner67413202006-07-01 19:29:26 -0700142 (action->flags & IRQF_DISABLED) ? " +" : "",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 action->name);
144 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000145 if (!sbusl)
146 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 k++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000148 if (k < 4) {
149 action = sbus_actions[(j << 5) + (sbusl << 2) + k].action;
150 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 j++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000152 if (j == nsbi)
153 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 k = 0;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000155 action = sbus_actions[(j << 5) + (sbusl << 2)].action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 }
157 }
158 seq_putc(p, '\n');
159 }
160out_unlock:
161 spin_unlock_irqrestore(&irq_action_lock, flags);
162 return 0;
163}
164
165void sun4d_free_irq(unsigned int irq, void *dev_id)
166{
167 struct irqaction *action, **actionp;
168 struct irqaction *tmp = NULL;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000169 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
171 spin_lock_irqsave(&irq_action_lock, flags);
172 if (irq < 15)
173 actionp = irq + irq_action;
174 else
175 actionp = &(sbus_actions[irq - (1 << 5)].action);
176 action = *actionp;
177 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000178 printk(KERN_ERR "Trying to free free IRQ%d\n", irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 goto out_unlock;
180 }
181 if (dev_id) {
182 for (; action; action = action->next) {
183 if (action->dev_id == dev_id)
184 break;
185 tmp = action;
186 }
187 if (!action) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000188 printk(KERN_ERR "Trying to free free shared IRQ%d\n",
189 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 goto out_unlock;
191 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700192 } else if (action->flags & IRQF_SHARED) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000193 printk(KERN_ERR "Trying to free shared IRQ%d with NULL device ID\n",
194 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 goto out_unlock;
196 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000197 if (action->flags & SA_STATIC_ALLOC) {
198 /*
199 * This interrupt is marked as specially allocated
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 * so it is a bad idea to free it.
201 */
Sam Ravnborge54f8542011-01-28 22:08:21 +0000202 printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 irq, action->name);
204 goto out_unlock;
205 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000206
Julia Lawall0d0659c2010-06-04 16:17:37 -0700207 if (tmp)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 tmp->next = action->next;
209 else
210 *actionp = action->next;
211
212 spin_unlock_irqrestore(&irq_action_lock, flags);
213
214 synchronize_irq(irq);
215
216 spin_lock_irqsave(&irq_action_lock, flags);
217
218 kfree(action);
219
220 if (!(*actionp))
Al Viro0f516812007-07-21 19:19:38 -0700221 __disable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222
223out_unlock:
224 spin_unlock_irqrestore(&irq_action_lock, flags);
225}
226
Sam Ravnborge54f8542011-01-28 22:08:21 +0000227void sun4d_handler_irq(int pil, struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
Al Viro0d844382006-10-08 14:30:44 +0100229 struct pt_regs *old_regs;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000230 struct irqaction *action;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 int cpu = smp_processor_id();
232 /* SBUS IRQ level (1 - 7) */
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000233 int sbusl = pil_to_sbus[pil];
Sam Ravnborge54f8542011-01-28 22:08:21 +0000234
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 /* FIXME: Is this necessary?? */
236 cc_get_ipen();
Sam Ravnborge54f8542011-01-28 22:08:21 +0000237
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000238 cc_set_iclr(1 << pil);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000239
Al Viro0d844382006-10-08 14:30:44 +0100240 old_regs = set_irq_regs(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 irq_enter();
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000242 kstat_cpu(cpu).irqs[pil]++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 if (!sbusl) {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000244 action = *(pil + irq_action);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000246 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000248 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 action = action->next;
250 } while (action);
251 } else {
252 int bus_mask = bw_get_intr_mask(sbusl) & 0x3ffff;
253 int sbino;
254 struct sbus_action *actionp;
255 unsigned mask, slot;
256 int sbil = (sbusl << 2);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000257
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 bw_clear_intr_mask(sbusl, bus_mask);
Sam Ravnborge54f8542011-01-28 22:08:21 +0000259
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 /* Loop for each pending SBI */
261 for (sbino = 0; bus_mask; sbino++, bus_mask >>= 1)
262 if (bus_mask & 1) {
263 mask = acquire_sbi(SBI2DEVID(sbino), 0xf << sbil);
264 mask &= (0xf << sbil);
265 actionp = sbus_actions + (sbino << 5) + (sbil);
266 /* Loop for each pending SBI slot */
267 for (slot = (1 << sbil); mask; slot <<= 1, actionp++)
268 if (mask & slot) {
269 mask &= ~slot;
270 action = actionp->action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 if (!action)
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000273 unexpected_irq(pil, NULL, regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 do {
Sam Ravnborgd4d1ec42011-01-22 11:32:15 +0000275 action->handler(pil, action->dev_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 action = action->next;
277 } while (action);
278 release_sbi(SBI2DEVID(sbino), slot);
279 }
280 }
281 }
282 irq_exit();
Al Viro0d844382006-10-08 14:30:44 +0100283 set_irq_regs(old_regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284}
285
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286int sun4d_request_irq(unsigned int irq,
David Howells40220c12006-10-09 12:19:47 +0100287 irq_handler_t handler,
Sam Ravnborge54f8542011-01-28 22:08:21 +0000288 unsigned long irqflags, const char *devname, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289{
290 struct irqaction *action, *tmp = NULL, **actionp;
291 unsigned long flags;
292 int ret;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000293
294 if (irq > 14 && irq < (1 << 5)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 ret = -EINVAL;
296 goto out;
297 }
298
299 if (!handler) {
300 ret = -EINVAL;
301 goto out;
302 }
303
304 spin_lock_irqsave(&irq_action_lock, flags);
305
306 if (irq >= (1 << 5))
307 actionp = &(sbus_actions[irq - (1 << 5)].action);
308 else
309 actionp = irq + irq_action;
310 action = *actionp;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000311
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312 if (action) {
Thomas Gleixner67413202006-07-01 19:29:26 -0700313 if ((action->flags & IRQF_SHARED) && (irqflags & IRQF_SHARED)) {
Sam Ravnborg70044df2011-01-28 22:08:23 +0000314 for (tmp = action; tmp->next; tmp = tmp->next)
315 /* find last entry - tmp used below */;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 } else {
317 ret = -EBUSY;
318 goto out_unlock;
319 }
Thomas Gleixner67413202006-07-01 19:29:26 -0700320 if ((action->flags & IRQF_DISABLED) ^ (irqflags & IRQF_DISABLED)) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000321 printk(KERN_ERR "Attempt to mix fast and slow interrupts on IRQ%d denied\n",
322 irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 ret = -EBUSY;
324 goto out_unlock;
325 }
326 action = NULL; /* Or else! */
327 }
328
329 /* If this is flagged as statically allocated then we use our
330 * private struct which is never freed.
331 */
332 if (irqflags & SA_STATIC_ALLOC) {
333 if (static_irq_count < MAX_STATIC_ALLOC)
334 action = &static_irqaction[static_irq_count++];
335 else
Sam Ravnborge54f8542011-01-28 22:08:21 +0000336 printk(KERN_ERR "Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
337 irq, devname);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000339
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 if (action == NULL)
Sam Ravnborge54f8542011-01-28 22:08:21 +0000341 action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
342
343 if (!action) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 ret = -ENOMEM;
345 goto out_unlock;
346 }
347
348 action->handler = handler;
349 action->flags = irqflags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 action->name = devname;
351 action->next = NULL;
352 action->dev_id = dev_id;
353
354 if (tmp)
355 tmp->next = action;
356 else
357 *actionp = action;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000358
Al Viro0f516812007-07-21 19:19:38 -0700359 __enable_irq(irq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360
361 ret = 0;
362out_unlock:
363 spin_unlock_irqrestore(&irq_action_lock, flags);
364out:
365 return ret;
366}
367
368static void sun4d_disable_irq(unsigned int irq)
369{
Sam Ravnborgdb1cdd12011-04-18 11:25:42 +0000370 int tid = board_to_cpu[(irq >> 5) - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000372
David S. Millerf8376e92008-09-13 22:05:25 -0700373 if (irq < NR_IRQS)
374 return;
375
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 spin_lock_irqsave(&sun4d_imsk_lock, flags);
377 cc_set_imsk_other(tid, cc_get_imsk_other(tid) | (1 << sbus_to_pil[(irq >> 2) & 7]));
378 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379}
380
381static void sun4d_enable_irq(unsigned int irq)
382{
Sam Ravnborgdb1cdd12011-04-18 11:25:42 +0000383 int tid = board_to_cpu[(irq >> 5) - 1];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 unsigned long flags;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000385
David S. Millerf8376e92008-09-13 22:05:25 -0700386 if (irq < NR_IRQS)
387 return;
388
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 spin_lock_irqsave(&sun4d_imsk_lock, flags);
390 cc_set_imsk_other(tid, cc_get_imsk_other(tid) & ~(1 << sbus_to_pil[(irq >> 2) & 7]));
391 spin_unlock_irqrestore(&sun4d_imsk_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392}
393
394#ifdef CONFIG_SMP
395static void sun4d_set_cpu_int(int cpu, int level)
396{
397 sun4d_send_ipi(cpu, level);
398}
399
400static void sun4d_clear_ipi(int cpu, int level)
401{
402}
403
404static void sun4d_set_udt(int cpu)
405{
406}
407
408/* Setup IRQ distribution scheme. */
409void __init sun4d_distribute_irqs(void)
410{
David S. Miller71d37212008-08-27 02:50:57 -0700411 struct device_node *dp;
412
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 int cpuid = cpu_logical_map(1);
414
415 if (cpuid == -1)
416 cpuid = cpu_logical_map(0);
David S. Miller71d37212008-08-27 02:50:57 -0700417 for_each_node_by_name(dp, "sbi") {
418 int devid = of_getintprop_default(dp, "device-id", 0);
419 int board = of_getintprop_default(dp, "board#", 0);
Sam Ravnborgdb1cdd12011-04-18 11:25:42 +0000420 board_to_cpu[board] = cpuid;
David S. Miller71d37212008-08-27 02:50:57 -0700421 set_sbi_tid(devid, cpuid << 3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 }
Sam Ravnborge54f8542011-01-28 22:08:21 +0000423 printk(KERN_ERR "All sbus IRQs directed to CPU%d\n", cpuid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424}
425#endif
Sam Ravnborge54f8542011-01-28 22:08:21 +0000426
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427static void sun4d_clear_clock_irq(void)
428{
David S. Millerf5f10852008-09-13 22:04:55 -0700429 sbus_readl(&sun4d_timers->l10_timer_limit);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430}
431
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432static void sun4d_load_profile_irq(int cpu, unsigned int limit)
433{
434 bw_set_prof_limit(cpu, limit);
435}
436
David S. Millerf5f10852008-09-13 22:04:55 -0700437static void __init sun4d_load_profile_irqs(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438{
David S. Millerf5f10852008-09-13 22:04:55 -0700439 int cpu = 0, mid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700440
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441 while (!cpu_find_by_instance(cpu, NULL, &mid)) {
442 sun4d_load_profile_irq(mid >> 3, 0);
443 cpu++;
444 }
David S. Millerf5f10852008-09-13 22:04:55 -0700445}
446
Sam Ravnborg1d059952011-02-25 23:01:19 -0800447unsigned int sun4d_build_device_irq(struct platform_device *op,
448 unsigned int real_irq)
449{
450 static int pil_to_sbus[] = {
451 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0,
452 };
453 struct device_node *dp = op->dev.of_node;
454 struct device_node *io_unit, *sbi = dp->parent;
455 const struct linux_prom_registers *regs;
456 int board, slot;
457 int sbusl;
458
459 while (sbi) {
460 if (!strcmp(sbi->name, "sbi"))
461 break;
462
463 sbi = sbi->parent;
464 }
465 if (!sbi)
466 goto err_out;
467
468 regs = of_get_property(dp, "reg", NULL);
469 if (!regs)
470 goto err_out;
471
472 slot = regs->which_io;
473
474 /*
475 * If SBI's parent is not io-unit or the io-unit lacks
476 * a "board#" property, something is very wrong.
477 */
478 if (!sbi->parent || strcmp(sbi->parent->name, "io-unit")) {
479 printk("%s: Error, parent is not io-unit.\n", sbi->full_name);
480 goto err_out;
481 }
482 io_unit = sbi->parent;
483 board = of_getintprop_default(io_unit, "board#", -1);
484 if (board == -1) {
485 printk("%s: Error, lacks board# property.\n", io_unit->full_name);
486 goto err_out;
487 }
488
489 sbusl = pil_to_sbus[real_irq];
490 if (sbusl)
491 return (((board + 1) << 5) + (sbusl << 2) + slot);
492
493err_out:
494 return real_irq;
495}
496
David S. Millerf5f10852008-09-13 22:04:55 -0700497static void __init sun4d_fixup_trap_table(void)
498{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499#ifdef CONFIG_SMP
David S. Millerf5f10852008-09-13 22:04:55 -0700500 unsigned long flags;
David S. Millerf5f10852008-09-13 22:04:55 -0700501 struct tt_entry *trap_table = &sparc_ttable[SP_TRAP_IRQ1 + (14 - 1)];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
David S. Millerf5f10852008-09-13 22:04:55 -0700503 /* Adjust so that we jump directly to smp4d_ticker */
504 lvl14_save[2] += smp4d_ticker - real_irq_entry;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
David S. Millerf5f10852008-09-13 22:04:55 -0700506 /* For SMP we use the level 14 ticker, however the bootup code
507 * has copied the firmware's level 14 vector into the boot cpu's
508 * trap table, we must fix this now or we get squashed.
509 */
510 local_irq_save(flags);
511 patchme_maybe_smp_msg[0] = 0x01000000; /* NOP out the branch */
512 trap_table->inst_one = lvl14_save[0];
513 trap_table->inst_two = lvl14_save[1];
514 trap_table->inst_three = lvl14_save[2];
515 trap_table->inst_four = lvl14_save[3];
516 local_flush_cache_all();
517 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518#endif
519}
520
David S. Millerf5f10852008-09-13 22:04:55 -0700521static void __init sun4d_init_timers(irq_handler_t counter_fn)
522{
523 struct device_node *dp;
524 struct resource res;
525 const u32 *reg;
526 int err;
527
528 dp = of_find_node_by_name(NULL, "cpu-unit");
529 if (!dp) {
530 prom_printf("sun4d_init_timers: Unable to find cpu-unit\n");
531 prom_halt();
532 }
533
534 /* Which cpu-unit we use is arbitrary, we can view the bootbus timer
535 * registers via any cpu's mapping. The first 'reg' property is the
536 * bootbus.
537 */
538 reg = of_get_property(dp, "reg", NULL);
Nicolas Palixc2e27c32008-12-03 21:10:57 -0800539 of_node_put(dp);
David S. Millerf5f10852008-09-13 22:04:55 -0700540 if (!reg) {
541 prom_printf("sun4d_init_timers: No reg property\n");
542 prom_halt();
543 }
544
545 res.start = reg[1];
546 res.end = reg[2] - 1;
547 res.flags = reg[0] & 0xff;
548 sun4d_timers = of_ioremap(&res, BW_TIMER_LIMIT,
549 sizeof(struct sun4d_timer_regs), "user timer");
550 if (!sun4d_timers) {
551 prom_printf("sun4d_init_timers: Can't map timer regs\n");
552 prom_halt();
553 }
554
555 sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit);
556
557 master_l10_counter = &sun4d_timers->l10_cur_count;
David S. Millerf5f10852008-09-13 22:04:55 -0700558
559 err = request_irq(TIMER_IRQ, counter_fn,
560 (IRQF_DISABLED | SA_STATIC_ALLOC),
561 "timer", NULL);
562 if (err) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000563 prom_printf("sun4d_init_timers: request_irq() failed with %d\n",
564 err);
David S. Millerf5f10852008-09-13 22:04:55 -0700565 prom_halt();
566 }
567 sun4d_load_profile_irqs();
568 sun4d_fixup_trap_table();
569}
570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571void __init sun4d_init_sbi_irq(void)
572{
David S. Miller71d37212008-08-27 02:50:57 -0700573 struct device_node *dp;
David S. Millerf8376e92008-09-13 22:05:25 -0700574 int target_cpu = 0;
575
576#ifdef CONFIG_SMP
577 target_cpu = boot_cpu_id;
578#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579
580 nsbi = 0;
David S. Miller71d37212008-08-27 02:50:57 -0700581 for_each_node_by_name(dp, "sbi")
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 nsbi++;
Sam Ravnborge54f8542011-01-28 22:08:21 +0000583 sbus_actions = kzalloc(nsbi * 8 * 4 * sizeof(struct sbus_action), GFP_ATOMIC);
David S. Millerd4accd62006-11-30 17:11:26 -0800584 if (!sbus_actions) {
585 prom_printf("SUN4D: Cannot allocate sbus_actions, halting.\n");
586 prom_halt();
587 }
David S. Miller71d37212008-08-27 02:50:57 -0700588 for_each_node_by_name(dp, "sbi") {
589 int devid = of_getintprop_default(dp, "device-id", 0);
590 int board = of_getintprop_default(dp, "board#", 0);
591 unsigned int mask;
592
David S. Millerf8376e92008-09-13 22:05:25 -0700593 set_sbi_tid(devid, target_cpu << 3);
Sam Ravnborgdb1cdd12011-04-18 11:25:42 +0000594 board_to_cpu[board] = target_cpu;
David S. Millerf8376e92008-09-13 22:05:25 -0700595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 /* Get rid of pending irqs from PROM */
David S. Miller71d37212008-08-27 02:50:57 -0700597 mask = acquire_sbi(devid, 0xffffffff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 if (mask) {
Sam Ravnborge54f8542011-01-28 22:08:21 +0000599 printk(KERN_ERR "Clearing pending IRQs %08x on SBI %d\n",
600 mask, board);
David S. Miller71d37212008-08-27 02:50:57 -0700601 release_sbi(devid, mask);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 }
603 }
604}
605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606void __init sun4d_init_IRQ(void)
607{
608 local_irq_disable();
609
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 BTFIXUPSET_CALL(enable_irq, sun4d_enable_irq, BTFIXUPCALL_NORM);
611 BTFIXUPSET_CALL(disable_irq, sun4d_disable_irq, BTFIXUPCALL_NORM);
612 BTFIXUPSET_CALL(clear_clock_irq, sun4d_clear_clock_irq, BTFIXUPCALL_NORM);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
Sam Ravnborgbbdc2662011-02-25 23:00:19 -0800614
615 sparc_irq_config.init_timers = sun4d_init_timers;
Sam Ravnborg1d059952011-02-25 23:01:19 -0800616 sparc_irq_config.build_device_irq = sun4d_build_device_irq;
Sam Ravnborgbbdc2662011-02-25 23:00:19 -0800617
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618#ifdef CONFIG_SMP
619 BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
620 BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
621 BTFIXUPSET_CALL(set_irq_udt, sun4d_set_udt, BTFIXUPCALL_NOP);
622#endif
623 /* Cannot enable interrupts until OBP ticker is disabled. */
624}