1 /* $Id: shub_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 #include <linux/types.h>
11 #include <linux/slab.h>
12 #include <asm/sn/types.h>
13 #include <asm/sn/sgi.h>
14 #include <asm/sn/driver.h>
15 #include <asm/sn/iograph.h>
16 #include <asm/param.h>
17 #include <asm/sn/pio.h>
18 #include <asm/sn/xtalk/xwidget.h>
19 #include <asm/sn/io.h>
20 #include <asm/sn/sn_private.h>
21 #include <asm/sn/addrs.h>
22 #include <asm/sn/invent.h>
23 #include <asm/sn/hcl.h>
24 #include <asm/sn/hcl_util.h>
25 #include <asm/sn/intr.h>
26 #include <asm/sn/xtalk/xtalkaddrs.h>
27 #include <asm/sn/klconfig.h>
28 #include <asm/sn/sn2/shub_mmr.h>
29 #include <asm/sn/sn_cpuid.h>
30 #include <asm/sn/pci/pcibr.h>
31 #include <asm/sn/pci/pcibr_private.h>
32 #include <asm/sn/pci/bridge.h>
33 
34 /* ARGSUSED */
35 void
hub_intr_init(vertex_hdl_t hubv)36 hub_intr_init(vertex_hdl_t hubv)
37 {
38 }
39 
40 xwidgetnum_t
hub_widget_id(nasid_t nasid)41 hub_widget_id(nasid_t nasid)
42 {
43 
44 	if (!(nasid & 1)) {
45         	hubii_wcr_t     ii_wcr; /* the control status register */
46         	ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid,IIO_WCR);
47         	return ii_wcr.wcr_fields_s.wcr_widget_id;
48 	} else {
49 		/* ICE does not have widget id. */
50 		return(-1);
51 	}
52 }
53 
54 static hub_intr_t
do_hub_intr_alloc(vertex_hdl_t dev,device_desc_t dev_desc,vertex_hdl_t owner_dev,int uncond_nothread)55 do_hub_intr_alloc(vertex_hdl_t dev,
56 		device_desc_t dev_desc,
57 		vertex_hdl_t owner_dev,
58 		int uncond_nothread)
59 {
60 	cpuid_t		cpu = 0;
61 	int		vector;
62 	hub_intr_t	intr_hdl;
63 	cnodeid_t	cnode;
64 	int		cpuphys, slice;
65 	int		nasid;
66 	iopaddr_t	xtalk_addr;
67 	struct xtalk_intr_s	*xtalk_info;
68 	xwidget_info_t	xwidget_info;
69 	ilvl_t		intr_swlevel = 0;
70 
71 	cpu = intr_heuristic(dev, dev_desc, -1, 0, owner_dev, NULL, &vector);
72 
73 	if (cpu == CPU_NONE) {
74 		printk("Unable to allocate interrupt for 0x%p\n", (void *)owner_dev);
75 		return(0);
76 	}
77 
78 	cpuphys = cpu_physical_id(cpu);
79 	slice = cpu_physical_id_to_slice(cpuphys);
80 	nasid = cpu_physical_id_to_nasid(cpuphys);
81 	cnode = cpuid_to_cnodeid(cpu);
82 
83 	if (slice) {
84 		xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
85 	} else {
86 		xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
87 	}
88 
89 	intr_hdl = snia_kmem_alloc_node(sizeof(struct hub_intr_s), cnode);
90 	ASSERT_ALWAYS(intr_hdl);
91 
92 	xtalk_info = &intr_hdl->i_xtalk_info;
93 	xtalk_info->xi_dev = dev;
94 	xtalk_info->xi_vector = vector;
95 	xtalk_info->xi_addr = xtalk_addr;
96 
97 	xwidget_info = xwidget_info_get(dev);
98 	if (xwidget_info) {
99 		xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
100 	}
101 
102 	intr_hdl->i_swlevel = intr_swlevel;
103 	intr_hdl->i_cpuid = cpu;
104 	intr_hdl->i_bit = vector;
105 	intr_hdl->i_flags |= HUB_INTR_IS_ALLOCED;
106 
107 	return(intr_hdl);
108 }
109 
110 hub_intr_t
hub_intr_alloc(vertex_hdl_t dev,device_desc_t dev_desc,vertex_hdl_t owner_dev)111 hub_intr_alloc(vertex_hdl_t dev,
112 		device_desc_t dev_desc,
113 		vertex_hdl_t owner_dev)
114 {
115 	return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
116 }
117 
118 hub_intr_t
hub_intr_alloc_nothd(vertex_hdl_t dev,device_desc_t dev_desc,vertex_hdl_t owner_dev)119 hub_intr_alloc_nothd(vertex_hdl_t dev,
120 		device_desc_t dev_desc,
121 		vertex_hdl_t owner_dev)
122 {
123 	return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
124 }
125 
126 void
hub_intr_free(hub_intr_t intr_hdl)127 hub_intr_free(hub_intr_t intr_hdl)
128 {
129 	cpuid_t		cpu = intr_hdl->i_cpuid;
130 	int		vector = intr_hdl->i_bit;
131 	xtalk_intr_t	xtalk_info;
132 
133 	if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) {
134 		xtalk_info = &intr_hdl->i_xtalk_info;
135 		xtalk_info->xi_dev = NODEV;
136 		xtalk_info->xi_vector = 0;
137 		xtalk_info->xi_addr = 0;
138 		hub_intr_disconnect(intr_hdl);
139 	}
140 
141 	if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED) {
142 		kfree(intr_hdl);
143 	}
144 	intr_unreserve_level(cpu, vector);
145 }
146 
147 int
hub_intr_connect(hub_intr_t intr_hdl,intr_func_t intr_func,void * intr_arg,xtalk_intr_setfunc_t setfunc,void * setfunc_arg)148 hub_intr_connect(hub_intr_t intr_hdl,
149 		intr_func_t intr_func,          /* xtalk intr handler */
150 		void *intr_arg,                 /* arg to intr handler */
151 		xtalk_intr_setfunc_t setfunc,
152 		void *setfunc_arg)
153 {
154 	int		rv;
155 	cpuid_t		cpu = intr_hdl->i_cpuid;
156 	int 		vector = intr_hdl->i_bit;
157 
158 	ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
159 
160 	rv = intr_connect_level(cpu, vector, intr_hdl->i_swlevel, NULL);
161 	if (rv < 0) {
162 		return rv;
163 	}
164 
165 	intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
166 	intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
167 
168 	if (setfunc) {
169 		(*setfunc)((xtalk_intr_t)intr_hdl);
170 	}
171 
172 	intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
173 
174 	return 0;
175 }
176 
177 /*
178  * Disassociate handler with the specified interrupt.
179  */
180 void
hub_intr_disconnect(hub_intr_t intr_hdl)181 hub_intr_disconnect(hub_intr_t intr_hdl)
182 {
183 	/*REFERENCED*/
184 	int rv;
185 	cpuid_t cpu = intr_hdl->i_cpuid;
186 	int bit = intr_hdl->i_bit;
187 	xtalk_intr_setfunc_t setfunc;
188 
189 	setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
190 
191 	/* TBD: send disconnected interrupts somewhere harmless */
192 	if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
193 
194 	rv = intr_disconnect_level(cpu, bit);
195 	ASSERT(rv == 0);
196 	intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
197 }
198 
199 /*
200  * Redirect an interrupt to another cpu.
201  */
202 
203 void
sn_shub_redirect_intr(pcibr_intr_t intr,unsigned long cpu)204 sn_shub_redirect_intr(pcibr_intr_t intr, unsigned long cpu) {
205 	unsigned long bit;
206 	int cpuphys, slice;
207 	nasid_t nasid;
208 	unsigned long xtalk_addr;
209 	bridge_t	*bridge = intr->bi_soft->bs_base;
210 	picreg_t	int_enable;
211 	picreg_t	host_addr;
212 	int		irq;
213 
214 	cpuphys = cpu_physical_id(cpu);
215 	slice = cpu_physical_id_to_slice(cpuphys);
216 	nasid = cpu_physical_id_to_nasid(cpuphys);
217 
218 	if (slice) {
219 		xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
220 	} else {
221 		xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
222 	}
223 
224 	for (bit = 0; bit < 8; bit++) {
225 		if (intr->bi_ibits & (1 << bit) ) {
226 			/* Disable interrupts. */
227 			int_enable = bridge->p_int_enable_64;
228 			int_enable &= ~bit;
229 			bridge->p_int_enable_64 = int_enable;
230 			/* Reset Host address (Interrupt destination) */
231 			host_addr = bridge->p_int_addr_64[bit];
232 			host_addr &= ~((1UL << 48) - 1);
233 			host_addr |= xtalk_addr;
234 			bridge->p_int_addr_64[bit] = host_addr;
235 			/* Enable interrupt */
236 			int_enable |= bit;
237 			bridge->p_int_enable_64 = int_enable;
238 			/* Force an interrupt, just in case. */
239 			bridge->b_force_pin[bit].intr = 1;
240 		}
241 	}
242 	irq = intr->bi_irq;
243 	if (pdacpu(cpu).sn_first_irq == 0 || pdacpu(cpu).sn_first_irq > irq) {
244 		pdacpu(cpu).sn_first_irq = irq;
245 	}
246 	if (pdacpu(cpu).sn_last_irq < irq) {
247 		pdacpu(cpu).sn_last_irq = irq;
248 	}
249 	intr->bi_cpu = (int)cpu;
250 }
251 
252