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