1 /* $Id: ml_SN_intr.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
2 *
3 * This file is subject to the terms and conditions of the GNU General Public
4 * License. See the file "COPYING" in the main directory of this archive
5 * for more details.
6 *
7 * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
8 */
9
10 /*
11 * intr.c-
12 * This file contains all of the routines necessary to set up and
13 * handle interrupts on an IPXX board.
14 */
15
16 #ident "$Revision: 1.1 $"
17
18 #include <linux/types.h>
19 #include <linux/slab.h>
20 #include <linux/interrupt.h>
21 #include <asm/smp.h>
22 #include <asm/irq.h>
23 #include <asm/hw_irq.h>
24 #include <asm/sn/sgi.h>
25 #include <asm/sn/iograph.h>
26 #include <asm/sn/invent.h>
27 #include <asm/sn/hcl.h>
28 #include <asm/sn/labelcl.h>
29 #include <asm/sn/io.h>
30 #include <asm/sn/sn_private.h>
31 #include <asm/sn/klconfig.h>
32 #include <asm/sn/sn_cpuid.h>
33 #include <asm/sn/pci/pciio.h>
34 #include <asm/sn/pci/pcibr.h>
35 #include <asm/sn/xtalk/xtalk.h>
36 #include <asm/sn/pci/pcibr_private.h>
37 #include <asm/sn/intr.h>
38 #include <asm/sn/sn2/shub_mmr_t.h>
39 #include <asm/sn/sn2/shubio.h>
40 #include <asm/sal.h>
41 #include <asm/sn/sn_sal.h>
42
43 extern irqpda_t *irqpdaindr;
44 extern cnodeid_t master_node_get(vertex_hdl_t vhdl);
45 extern nasid_t master_nasid;
46
47 // Initialize some shub registers for interrupts, both IO and error.
48 //
49
50
51
52 void
intr_init_vecblk(nodepda_t * npda,cnodeid_t node,int sn)53 intr_init_vecblk( nodepda_t *npda,
54 cnodeid_t node,
55 int sn)
56 {
57 int nasid = cnodeid_to_nasid(node);
58 sh_ii_int0_config_u_t ii_int_config;
59 cpuid_t cpu;
60 cpuid_t cpu0, cpu1;
61 nodepda_t *lnodepda;
62 sh_ii_int0_enable_u_t ii_int_enable;
63 sh_int_node_id_config_u_t node_id_config;
64 sh_local_int5_config_u_t local5_config;
65 sh_local_int5_enable_u_t local5_enable;
66 extern void sn_init_cpei_timer(void);
67 static int timer_added = 0;
68
69
70 if (is_headless_node(node) ) {
71 int cnode;
72 struct ia64_sal_retval ret_stuff;
73
74 // retarget all interrupts on this node to the master node.
75 node_id_config.sh_int_node_id_config_regval = 0;
76 node_id_config.sh_int_node_id_config_s.node_id = master_nasid;
77 node_id_config.sh_int_node_id_config_s.id_sel = 1;
78 HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_INT_NODE_ID_CONFIG),
79 node_id_config.sh_int_node_id_config_regval);
80 cnode = nasid_to_cnodeid(master_nasid);
81 lnodepda = NODEPDA(cnode);
82 cpu = lnodepda->node_first_cpu;
83 cpu = cpu_physical_id(cpu);
84 SAL_CALL(ret_stuff, SN_SAL_REGISTER_CE, nasid, cpu, master_nasid,0,0,0,0);
85 if (ret_stuff.status < 0) {
86 printk("%s: SN_SAL_REGISTER_CE SAL_CALL failed\n",__FUNCTION__);
87 }
88 } else {
89 lnodepda = NODEPDA(node);
90 cpu = lnodepda->node_first_cpu;
91 cpu = cpu_physical_id(cpu);
92 }
93
94 // Get the physical id's of the cpu's on this node.
95 cpu0 = nasid_slice_to_cpu_physical_id(nasid, 0);
96 cpu1 = nasid_slice_to_cpu_physical_id(nasid, 2);
97
98 HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_ERROR_MASK), 0);
99 HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_PI_CRBP_ERROR_MASK), 0);
100
101 // Config and enable UART interrupt, all nodes.
102
103 local5_config.sh_local_int5_config_regval = 0;
104 local5_config.sh_local_int5_config_s.idx = SGI_UART_VECTOR;
105 local5_config.sh_local_int5_config_s.pid = cpu;
106 HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT5_CONFIG),
107 local5_config.sh_local_int5_config_regval);
108
109 local5_enable.sh_local_int5_enable_regval = 0;
110 local5_enable.sh_local_int5_enable_s.uart_int = 1;
111 HUB_S( (unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_LOCAL_INT5_ENABLE),
112 local5_enable.sh_local_int5_enable_regval);
113
114
115 // The II_INT_CONFIG register for cpu 0.
116 ii_int_config.sh_ii_int0_config_regval = 0;
117 ii_int_config.sh_ii_int0_config_s.type = 0;
118 ii_int_config.sh_ii_int0_config_s.agt = 0;
119 ii_int_config.sh_ii_int0_config_s.pid = cpu0;
120 ii_int_config.sh_ii_int0_config_s.base = 0;
121
122 HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_CONFIG),
123 ii_int_config.sh_ii_int0_config_regval);
124
125
126 // The II_INT_CONFIG register for cpu 1.
127 ii_int_config.sh_ii_int0_config_regval = 0;
128 ii_int_config.sh_ii_int0_config_s.type = 0;
129 ii_int_config.sh_ii_int0_config_s.agt = 0;
130 ii_int_config.sh_ii_int0_config_s.pid = cpu1;
131 ii_int_config.sh_ii_int0_config_s.base = 0;
132
133 HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_CONFIG),
134 ii_int_config.sh_ii_int0_config_regval);
135
136
137 // Enable interrupts for II_INT0 and 1.
138 ii_int_enable.sh_ii_int0_enable_regval = 0;
139 ii_int_enable.sh_ii_int0_enable_s.ii_enable = 1;
140
141 HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT0_ENABLE),
142 ii_int_enable.sh_ii_int0_enable_regval);
143 HUB_S((unsigned long *)GLOBAL_MMR_ADDR(nasid, SH_II_INT1_ENABLE),
144 ii_int_enable.sh_ii_int0_enable_regval);
145
146
147 if (!timer_added) { // can only init the timer once.
148 timer_added = 1;
149 sn_init_cpei_timer();
150 }
151 }
152
153 // (Un)Reserve an irq on this cpu.
154
155 static int
do_intr_reserve_level(cpuid_t cpu,int bit,int reserve)156 do_intr_reserve_level(cpuid_t cpu,
157 int bit,
158 int reserve)
159 {
160 int i;
161 irqpda_t *irqs = irqpdaindr;
162 int min_shared;
163
164 if (reserve) {
165 if (bit < 0) {
166 for (i = IA64_SN2_FIRST_DEVICE_VECTOR; i <= IA64_SN2_LAST_DEVICE_VECTOR; i++) {
167 if (irqs->irq_flags[i] == 0) {
168 bit = i;
169 break;
170 }
171 }
172 }
173 if (bit < 0) { /* ran out of irqs. Have to share. This will be rare. */
174 min_shared = 256;
175 for (i=IA64_SN2_FIRST_DEVICE_VECTOR; i < IA64_SN2_LAST_DEVICE_VECTOR; i++) {
176 /* Share with the same device class */
177 if (irqpdaindr->current->vendor == irqpdaindr->device_dev[i]->vendor &&
178 irqpdaindr->current->device == irqpdaindr->device_dev[i]->device &&
179 irqpdaindr->share_count[i] < min_shared) {
180 min_shared = irqpdaindr->share_count[i];
181 bit = i;
182 }
183 }
184 min_shared = 256;
185 if (bit < 0) { /* didn't find a matching device, just pick one. This will be */
186 /* exceptionally rare. */
187 for (i=IA64_SN2_FIRST_DEVICE_VECTOR; i < IA64_SN2_LAST_DEVICE_VECTOR; i++) {
188 if (irqpdaindr->share_count[i] < min_shared) {
189 min_shared = irqpdaindr->share_count[i];
190 bit = i;
191 }
192 }
193 }
194 irqpdaindr->share_count[bit]++;
195 }
196 if (irqs->irq_flags[bit] & SN2_IRQ_SHARED) {
197 irqs->irq_flags[bit] |= SN2_IRQ_RESERVED;
198 return bit;
199 }
200 if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED) {
201 return -1;
202 } else {
203 irqs->num_irq_used++;
204 irqs->irq_flags[bit] |= SN2_IRQ_RESERVED;
205 return bit;
206 }
207 } else {
208 if (irqs->irq_flags[bit] & SN2_IRQ_RESERVED) {
209 irqs->num_irq_used--;
210 irqs->irq_flags[bit] &= ~SN2_IRQ_RESERVED;
211 return bit;
212 } else {
213 return -1;
214 }
215 }
216 }
217
218 int
intr_reserve_level(cpuid_t cpu,int bit,int resflags,vertex_hdl_t owner_dev,char * name)219 intr_reserve_level(cpuid_t cpu,
220 int bit,
221 int resflags,
222 vertex_hdl_t owner_dev,
223 char *name)
224 {
225 return(do_intr_reserve_level(cpu, bit, 1));
226 }
227
228 void
intr_unreserve_level(cpuid_t cpu,int bit)229 intr_unreserve_level(cpuid_t cpu,
230 int bit)
231 {
232 (void)do_intr_reserve_level(cpu, bit, 0);
233 }
234
235 // Mark an irq on this cpu as (dis)connected.
236
237 static int
do_intr_connect_level(cpuid_t cpu,int bit,int connect)238 do_intr_connect_level(cpuid_t cpu,
239 int bit,
240 int connect)
241 {
242 irqpda_t *irqs = irqpdaindr;
243
244 if (connect) {
245 if (irqs->irq_flags[bit] & SN2_IRQ_SHARED) {
246 irqs->irq_flags[bit] |= SN2_IRQ_CONNECTED;
247 return bit;
248 }
249 if (irqs->irq_flags[bit] & SN2_IRQ_CONNECTED) {
250 return -1;
251 } else {
252 irqs->irq_flags[bit] |= SN2_IRQ_CONNECTED;
253 return bit;
254 }
255 } else {
256 if (irqs->irq_flags[bit] & SN2_IRQ_CONNECTED) {
257 irqs->irq_flags[bit] &= ~SN2_IRQ_CONNECTED;
258 return bit;
259 } else {
260 return -1;
261 }
262 }
263 return(bit);
264 }
265
266 int
intr_connect_level(cpuid_t cpu,int bit,ilvl_t is,intr_func_t intr_prefunc)267 intr_connect_level(cpuid_t cpu,
268 int bit,
269 ilvl_t is,
270 intr_func_t intr_prefunc)
271 {
272 return(do_intr_connect_level(cpu, bit, 1));
273 }
274
275 int
intr_disconnect_level(cpuid_t cpu,int bit)276 intr_disconnect_level(cpuid_t cpu,
277 int bit)
278 {
279 return(do_intr_connect_level(cpu, bit, 0));
280 }
281
282 // Choose a cpu on this node.
283 // We choose the one with the least number of int's assigned to it.
284
285 static cpuid_t
do_intr_cpu_choose(cnodeid_t cnode)286 do_intr_cpu_choose(cnodeid_t cnode) {
287 cpuid_t cpu, best_cpu = CPU_NONE;
288 int slice, min_count = 1000;
289 irqpda_t *irqs;
290
291 for (slice = CPUS_PER_NODE - 1; slice >= 0; slice--) {
292 int intrs;
293
294 cpu = cnode_slice_to_cpuid(cnode, slice);
295 if (cpu == smp_num_cpus) {
296 continue;
297 }
298
299 if (!cpu_online(cpu)) {
300 continue;
301 }
302
303 irqs = irqpdaindr;
304 intrs = irqs->num_irq_used;
305
306 if (min_count > intrs) {
307 min_count = intrs;
308 best_cpu = cpu;
309 if ( enable_shub_wars_1_1() ) {
310 /* Rather than finding the best cpu, always return the first cpu*/
311 /* This forces all interrupts to the same cpu */
312 break;
313 }
314 }
315 }
316 return best_cpu;
317 }
318
319 static cpuid_t
intr_cpu_choose_from_node(cnodeid_t cnode)320 intr_cpu_choose_from_node(cnodeid_t cnode)
321 {
322 return(do_intr_cpu_choose(cnode));
323 }
324
325 // See if we can use this cpu/vect.
326
327 static cpuid_t
intr_bit_reserve_test(cpuid_t cpu,int favor_subnode,cnodeid_t cnode,int req_bit,int resflags,vertex_hdl_t owner_dev,char * name,int * resp_bit)328 intr_bit_reserve_test(cpuid_t cpu,
329 int favor_subnode,
330 cnodeid_t cnode,
331 int req_bit,
332 int resflags,
333 vertex_hdl_t owner_dev,
334 char *name,
335 int *resp_bit)
336 {
337 ASSERT( (cpu == CPU_NONE) || (cnode == CNODEID_NONE) );
338
339 if (cnode != CNODEID_NONE) {
340 cpu = intr_cpu_choose_from_node(cnode);
341 }
342
343 if (cpu != CPU_NONE) {
344 *resp_bit = do_intr_reserve_level(cpu, req_bit, 1);
345 if (*resp_bit >= 0) {
346 return(cpu);
347 }
348 }
349 return CPU_NONE;
350 }
351
352 // Find the node to assign for this interrupt.
353
354 cpuid_t
intr_heuristic(vertex_hdl_t dev,device_desc_t dev_desc,int req_bit,int resflags,vertex_hdl_t owner_dev,char * name,int * resp_bit)355 intr_heuristic(vertex_hdl_t dev,
356 device_desc_t dev_desc,
357 int req_bit,
358 int resflags,
359 vertex_hdl_t owner_dev,
360 char *name,
361 int *resp_bit)
362 {
363 cpuid_t cpuid;
364 cpuid_t candidate = CPU_NONE;
365 cnodeid_t candidate_node;
366 vertex_hdl_t pconn_vhdl;
367 pcibr_soft_t pcibr_soft;
368 int bit;
369 static cnodeid_t last_node = 0;
370
371 /* SN2 + pcibr addressing limitation */
372 /* Due to this limitation, all interrupts from a given bridge must go to the name node.*/
373 /* The interrupt must also be targetted for the same processor. */
374 /* This limitation does not exist on PIC. */
375 /* But, the processor limitation will stay. The limitation will be similar to */
376 /* the bedrock/xbridge limit regarding PI's */
377
378 if ( (hwgraph_edge_get(dev, EDGE_LBL_PCI, &pconn_vhdl) == GRAPH_SUCCESS) &&
379 ( (pcibr_soft = pcibr_soft_get(pconn_vhdl) ) != NULL) ) {
380 if (pcibr_soft->bsi_err_intr) {
381 candidate = ((hub_intr_t)pcibr_soft->bsi_err_intr)->i_cpuid;
382 }
383 }
384
385
386 if (candidate != CPU_NONE) {
387 // The cpu was chosen already when we assigned the error interrupt.
388 bit = intr_reserve_level(candidate,
389 req_bit,
390 resflags,
391 owner_dev,
392 name);
393 if (bit < 0) {
394 cpuid = CPU_NONE;
395 } else {
396 cpuid = candidate;
397 *resp_bit = bit;
398 }
399 } else {
400 // Need to choose one. Try the controlling c-brick first.
401 cpuid = intr_bit_reserve_test(CPU_NONE,
402 0,
403 master_node_get(dev),
404 req_bit,
405 0,
406 owner_dev,
407 name,
408 resp_bit);
409 }
410
411 if (cpuid != CPU_NONE) {
412 return cpuid;
413 }
414
415 if (candidate != CPU_NONE) {
416 printk("Cannot target interrupt to target node (%ld).\n",candidate);
417 return CPU_NONE;
418 } else {
419 printk("Cannot target interrupt to closest node (0x%x) 0x%p\n",
420 master_node_get(dev), (void *)owner_dev);
421 }
422
423 // We couldn't put it on the closest node. Try to find another one.
424 // Do a stupid round-robin assignment of the node.
425
426 {
427 int i;
428
429 if (last_node >= numnodes) last_node = 0;
430 for (i = 0, candidate_node = last_node; i < numnodes; candidate_node++,i++) {
431 if (candidate_node == numnodes) candidate_node = 0;
432 cpuid = intr_bit_reserve_test(CPU_NONE,
433 0,
434 candidate_node,
435 req_bit,
436 0,
437 owner_dev,
438 name,
439 resp_bit);
440 if (cpuid != CPU_NONE) {
441 last_node = candidate_node + 1;
442 return cpuid;
443 }
444 }
445 }
446
447 printk("cannot target interrupt: 0x%p\n",(void *)owner_dev);
448 return CPU_NONE;
449 }
450