1
2
3
4
5
6
7
8#include <linux/config.h>
9#include <linux/init.h>
10#include <linux/irq.h>
11#include <linux/errno.h>
12#include <linux/signal.h>
13#include <linux/sched.h>
14#include <linux/types.h>
15#include <linux/interrupt.h>
16#include <linux/ioport.h>
17#include <linux/timex.h>
18#include <linux/slab.h>
19#include <linux/random.h>
20#include <linux/smp_lock.h>
21#include <linux/kernel_stat.h>
22#include <linux/delay.h>
23
24#include <asm/bitops.h>
25#include <asm/bootinfo.h>
26#include <asm/io.h>
27#include <asm/mipsregs.h>
28#include <asm/system.h>
29
30#include <asm/ptrace.h>
31#include <asm/processor.h>
32#include <asm/pci/bridge.h>
33#include <asm/sn/sn0/hub.h>
34#include <asm/sn/sn0/ip27.h>
35#include <asm/sn/addrs.h>
36#include <asm/sn/agent.h>
37#include <asm/sn/arch.h>
38#include <asm/sn/intr.h>
39#include <asm/sn/intr_public.h>
40
41
42#undef DEBUG_IRQ
43#ifdef DEBUG_IRQ
44#define DBG(x...) printk(x)
45#else
46#define DBG(x...)
47#endif
48
49
50unsigned char bus_to_wid[256];
51unsigned char bus_to_nid[256];
52unsigned char num_bridges;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70extern asmlinkage void ip27_irq(void);
71
72extern int irq_to_bus[], irq_to_slot[], bus_to_cpu[];
73int intr_connect_level(int cpu, int bit);
74int intr_disconnect_level(int cpu, int bit);
75
76
77
78
79
80int node_level_to_irq[MAX_COMPACT_NODES][PERNODE_LEVELS];
81
82
83
84
85
86#define IRQ_TO_BUS(i) irq_to_bus[(i)]
87#define IRQ_TO_CPU(i) bus_to_cpu[IRQ_TO_BUS(i)]
88#define NASID_FROM_PCI_IRQ(i) bus_to_nid[IRQ_TO_BUS(i)]
89#define WID_FROM_PCI_IRQ(i) bus_to_wid[IRQ_TO_BUS(i)]
90#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
91
92static inline int alloc_level(cpuid_t cpunum, int irq)
93{
94 cnodeid_t nodenum = CPUID_TO_COMPACT_NODEID(cpunum);
95 int j = LEAST_LEVEL + 3;
96
97 while (++j < PERNODE_LEVELS) {
98 if (node_level_to_irq[nodenum][j] == -1) {
99 node_level_to_irq[nodenum][j] = irq;
100 return j;
101 }
102 }
103 printk("Cpu %ld flooded with devices\n", cpunum);
104 while(1);
105 return -1;
106}
107
108static inline int find_level(cpuid_t *cpunum, int irq)
109{
110 int j;
111 cnodeid_t nodenum = INVALID_CNODEID;
112
113 while (++nodenum < MAX_COMPACT_NODES) {
114 j = LEAST_LEVEL + 3;
115 while (++j < PERNODE_LEVELS)
116 if (node_level_to_irq[nodenum][j] == irq) {
117 *cpunum = 0;
118 return(j);
119 }
120 }
121 printk("Could not identify cpu/level for irq %d\n", irq);
122 while(1);
123 return(-1);
124}
125
126
127
128
129static int ms1bit(unsigned long x)
130{
131 int b = 0, s;
132
133 s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
134 s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s;
135 s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s;
136 s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s;
137 s = 1; if (x >> 1 == 0) s = 0; b += s;
138
139 return b;
140}
141
142
143
144
145
146
147
148
149
150
151
152
153void ip27_do_irq(struct pt_regs *regs)
154{
155 int irq, swlevel;
156 hubreg_t pend0, mask0;
157 cpuid_t thiscpu = smp_processor_id();
158 int pi_int_mask0 = ((cputoslice(thiscpu) == 0) ?
159 PI_INT_MASK0_A : PI_INT_MASK0_B);
160
161
162 while (((pend0 = LOCAL_HUB_L(PI_INT_PEND0)) &
163 (mask0 = LOCAL_HUB_L(pi_int_mask0))) != 0) {
164 pend0 &= mask0;
165 if (pend0) {
166
167 LOCAL_HUB_S(pi_int_mask0, mask0 & ~(pend0));
168 do {
169 swlevel = ms1bit(pend0);
170 LOCAL_HUB_CLR_INTR(swlevel);
171
172 irq = LEVEL_TO_IRQ(thiscpu, swlevel);
173 do_IRQ(irq, regs);
174
175 pend0 ^= 1ULL << swlevel;
176 } while(pend0);
177
178 LOCAL_HUB_S(pi_int_mask0, mask0);
179 LOCAL_HUB_L(PI_INT_PEND0);
180 }
181 }
182}
183
184
185
186static unsigned int startup_bridge_irq(unsigned int irq)
187{
188 bridgereg_t device;
189 bridge_t *bridge;
190 int pin, swlevel;
191 cpuid_t cpu;
192 nasid_t master = NASID_FROM_PCI_IRQ(irq);
193
194 if (irq < BASE_PCI_IRQ)
195 return 0;
196
197 bridge = (bridge_t *) NODE_SWIN_BASE(master, WID_FROM_PCI_IRQ(irq));
198 pin = SLOT_FROM_PCI_IRQ(irq);
199 cpu = IRQ_TO_CPU(irq);
200
201 DBG("bridge_startup(): irq= 0x%x pin=%d\n", irq, pin);
202
203
204
205
206 swlevel = alloc_level(cpu, irq);
207 intr_connect_level(cpu, swlevel);
208
209 bridge->b_int_addr[pin].addr = (0x20000 | swlevel | (master << 8));
210 bridge->b_int_enable |= (1 << pin);
211
212 bridge->b_int_enable |= 0x7ffffe00;
213
214
215
216
217
218
219 device = bridge->b_int_device;
220 device |= (pin << (pin*3));
221 bridge->b_int_device = device;
222
223 bridge->b_widget.w_tflush;
224
225 return 0;
226}
227
228
229static unsigned int shutdown_bridge_irq(unsigned int irq)
230{
231 bridge_t *bridge;
232 int pin, swlevel;
233 cpuid_t cpu;
234
235 if (irq < BASE_PCI_IRQ)
236 return 0;
237
238 bridge = (bridge_t *) NODE_SWIN_BASE(NASID_FROM_PCI_IRQ(irq),
239 WID_FROM_PCI_IRQ(irq));
240 DBG("bridge_shutdown: irq 0x%x\n", irq);
241 pin = SLOT_FROM_PCI_IRQ(irq);
242
243
244
245
246
247 swlevel = find_level(&cpu, irq);
248 intr_disconnect_level(cpu, swlevel);
249 LEVEL_TO_IRQ(cpu, swlevel) = -1;
250
251 bridge->b_int_enable &= ~(1 << pin);
252 bridge->b_widget.w_tflush;
253
254 return 0;
255}
256
257static inline void enable_bridge_irq(unsigned int irq)
258{
259
260}
261
262static void disable_bridge_irq(unsigned int irq)
263{
264
265}
266
267static void mask_and_ack_bridge_irq(unsigned int irq)
268{
269
270}
271
272static void end_bridge_irq (unsigned int irq)
273{
274 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
275 enable_bridge_irq(irq);
276}
277
278static struct hw_interrupt_type bridge_irq_type = {
279 "bridge",
280 startup_bridge_irq,
281 shutdown_bridge_irq,
282 enable_bridge_irq,
283 disable_bridge_irq,
284 mask_and_ack_bridge_irq,
285 end_bridge_irq
286};
287
288void irq_debug(void)
289{
290 bridge_t *bridge = (bridge_t *) 0x9200000008000000;
291
292 printk("bridge->b_int_status = 0x%x\n", bridge->b_int_status);
293 printk("bridge->b_int_enable = 0x%x\n", bridge->b_int_enable);
294 printk("PI_INT_PEND0 = 0x%lx\n", LOCAL_HUB_L(PI_INT_PEND0));
295 printk("PI_INT_MASK0_A = 0x%lx\n", LOCAL_HUB_L(PI_INT_MASK0_A));
296}
297
298void __init init_IRQ(void)
299{
300 int i;
301
302 set_except_vector(0, ip27_irq);
303
304
305
306
307 for (i = 0; i <= NR_IRQS; i++) {
308 irq_desc[i].status = IRQ_DISABLED;
309 irq_desc[i].action = 0;
310 irq_desc[i].depth = 1;
311 irq_desc[i].handler = &bridge_irq_type;
312 }
313}
314
315
316
317
318static hub_intmasks_t *intr_get_ptrs(cpuid_t cpu, int bit, int *new_bit,
319 hubreg_t **intpend_masks, int *ip)
320{
321 hub_intmasks_t *hub_intmasks;
322
323 hub_intmasks = &cpu_data[cpu].p_intmasks;
324 if (bit < N_INTPEND_BITS) {
325 *intpend_masks = hub_intmasks->intpend0_masks;
326 *ip = 0;
327 *new_bit = bit;
328 } else {
329 *intpend_masks = hub_intmasks->intpend1_masks;
330 *ip = 1;
331 *new_bit = bit - N_INTPEND_BITS;
332 }
333 return hub_intmasks;
334}
335
336int intr_connect_level(int cpu, int bit)
337{
338 int ip;
339 int slice = cputoslice(cpu);
340 volatile hubreg_t *mask_reg;
341 hubreg_t *intpend_masks;
342 nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
343
344 (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
345
346
347 REMOTE_HUB_CLR_INTR(nasid, bit + ip * N_INTPEND_BITS);
348
349 intpend_masks[0] |= (1ULL << (u64)bit);
350
351 if (ip == 0) {
352 mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
353 PI_INT_MASK_OFFSET * slice);
354 } else {
355 mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
356 PI_INT_MASK_OFFSET * slice);
357 }
358 HUB_S(mask_reg, intpend_masks[0]);
359 return(0);
360}
361
362int intr_disconnect_level(int cpu, int bit)
363{
364 int ip;
365 int slice = cputoslice(cpu);
366 volatile hubreg_t *mask_reg;
367 hubreg_t *intpend_masks;
368 nasid_t nasid = COMPACT_TO_NASID_NODEID(cputocnode(cpu));
369
370 (void)intr_get_ptrs(cpu, bit, &bit, &intpend_masks, &ip);
371 intpend_masks[0] &= ~(1ULL << (u64)bit);
372 if (ip == 0) {
373 mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK0_A +
374 PI_INT_MASK_OFFSET * slice);
375 } else {
376 mask_reg = REMOTE_HUB_ADDR(nasid, PI_INT_MASK1_A +
377 PI_INT_MASK_OFFSET * slice);
378 }
379 HUB_S(mask_reg, intpend_masks[0]);
380 return(0);
381}
382
383
384void handle_resched_intr(int irq, void *dev_id, struct pt_regs *regs)
385{
386
387}
388
389#ifdef CONFIG_SMP
390
391void core_send_ipi(int destid, unsigned int action)
392{
393 int irq;
394
395#if (CPUS_PER_NODE == 2)
396 switch (action) {
397 case SMP_RESCHEDULE_YOURSELF:
398 irq = CPU_RESCHED_A_IRQ;
399 break;
400 case SMP_CALL_FUNCTION:
401 irq = CPU_CALL_A_IRQ;
402 break;
403 default:
404 panic("sendintr");
405 }
406 irq += cputoslice(destid);
407
408
409
410
411
412
413 REMOTE_HUB_SEND_INTR(COMPACT_TO_NASID_NODEID(cputocnode(destid)),
414 FAST_IRQ_TO_LEVEL(irq));
415#else
416 << Bomb! Must redefine this for more than 2 CPUS. >>
417#endif
418}
419
420#endif
421
422extern void smp_call_function_interrupt(void);
423
424void install_cpuintr(int cpu)
425{
426#ifdef CONFIG_SMP
427#if (CPUS_PER_NODE == 2)
428 static int done = 0;
429
430
431
432
433
434
435 if (done == 0) {
436 int j;
437
438 if (request_irq(CPU_RESCHED_A_IRQ, handle_resched_intr,
439 0, "resched", 0))
440 panic("intercpu intr unconnectible");
441 if (request_irq(CPU_RESCHED_B_IRQ, handle_resched_intr,
442 0, "resched", 0))
443 panic("intercpu intr unconnectible");
444 if (request_irq(CPU_CALL_A_IRQ, smp_call_function_interrupt,
445 0, "callfunc", 0))
446 panic("intercpu intr unconnectible");
447 if (request_irq(CPU_CALL_B_IRQ, smp_call_function_interrupt,
448 0, "callfunc", 0))
449 panic("intercpu intr unconnectible");
450
451 for (j = 0; j < PERNODE_LEVELS; j++)
452 LEVEL_TO_IRQ(0, j) = -1;
453 LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ)) =
454 CPU_RESCHED_A_IRQ;
455 LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_RESCHED_B_IRQ)) =
456 CPU_RESCHED_B_IRQ;
457 LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ)) =
458 CPU_CALL_A_IRQ;
459 LEVEL_TO_IRQ(0, FAST_IRQ_TO_LEVEL(CPU_CALL_B_IRQ)) =
460 CPU_CALL_B_IRQ;
461 for (j = 1; j < MAX_COMPACT_NODES; j++)
462 memcpy(&node_level_to_irq[j][0],
463 &node_level_to_irq[0][0],
464 sizeof(node_level_to_irq[0][0])*PERNODE_LEVELS);
465
466 done = 1;
467 }
468
469 intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_RESCHED_A_IRQ +
470 cputoslice(cpu)));
471 intr_connect_level(cpu, FAST_IRQ_TO_LEVEL(CPU_CALL_A_IRQ +
472 cputoslice(cpu)));
473#else
474#error Must redefine this for more than 2 CPUS.
475#endif
476#endif
477}
478
479void install_tlbintr(int cpu)
480{
481#if 0
482 int intr_bit = N_INTPEND_BITS + TLB_INTR_A + cputoslice(cpu);
483
484 intr_connect_level(cpu, intr_bit);
485#endif
486}
487