1 /* $Id: hub_intr.c,v 1.1 2002/02/28 17:31:25 marcelo Exp $
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
7 * Copyright (C) 1992-1997, 2000-2002 Silicon Graphics, Inc. All Rights Reserved.
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/sn_cpuid.h>
30 extern xtalk_provider_t hub_provider;
34 hub_intr_init(devfs_handle_t hubv)
39 * hub_device_desc_update
40 * Update the passed in device descriptor with the actual the
41 * target cpu number and interrupt priority level.
42 * NOTE : These might be the same as the ones passed in thru
46 hub_device_desc_update(device_desc_t dev_desc,
52 int allocate_my_bit = INTRCONNECT_ANYBIT;
55 * Allocate resources required for an interrupt as specified in dev_desc.
56 * Returns a hub interrupt handle on success, or 0 on failure.
59 do_hub_intr_alloc(devfs_handle_t dev, /* which crosstalk device */
60 device_desc_t dev_desc, /* device descriptor */
61 devfs_handle_t owner_dev, /* owner of this interrupt, if known */
62 int uncond_nothread) /* unconditionally non-threaded */
64 cpuid_t cpu = (cpuid_t)0; /* cpu to receive interrupt */
66 int bit; /* interrupt vector */
68 int intr_resflags = 0;
70 cnodeid_t nodeid; /* node to receive interrupt */
72 nasid_t nasid; /* nasid to receive interrupt */
73 struct xtalk_intr_s *xtalk_info;
74 iopaddr_t xtalk_addr; /* xtalk addr on hub to set intr */
75 xwidget_info_t xwidget_info; /* standard crosstalk widget info handle */
76 char *intr_name = NULL;
77 ilvl_t intr_swlevel = (ilvl_t)0;
78 extern int default_intr_pri;
79 extern void synergy_intr_alloc(int, int);
83 if (dev_desc->flags & D_INTR_ISERR) {
84 intr_resflags = II_ERRORINT;
85 } else if (!uncond_nothread && !(dev_desc->flags & D_INTR_NOTHREAD)) {
86 intr_resflags = II_THREADED;
88 /* Neither an error nor a thread. */
92 intr_swlevel = default_intr_pri;
94 intr_resflags = II_THREADED;
97 /* XXX - Need to determine if the interrupt should be threaded. */
99 /* If the cpu has not been picked already then choose a candidate
100 * interrupt target and reserve the interrupt bit
103 cpu = intr_heuristic(dev,dev_desc,allocate_my_bit,
104 intr_resflags,owner_dev,
108 /* At this point we SHOULD have a valid cpu */
109 if (cpu == CPU_NONE) {
110 #if defined(SUPPORT_PRINTING_V_FORMAT)
111 printk(KERN_WARNING "%v hub_intr_alloc could not allocate interrupt\n",
114 printk(KERN_WARNING "%p hub_intr_alloc could not allocate interrupt\n",
121 /* If the cpu has been picked already (due to the bridge data
122 * corruption bug) then try to reserve an interrupt bit .
125 bit = intr_reserve_level(cpu, allocate_my_bit,
127 owner_dev, intr_name);
129 #if defined(SUPPORT_PRINTING_V_FORMAT)
130 printk(KERN_WARNING "Could not reserve an interrupt bit for cpu "
134 printk(KERN_WARNING "Could not reserve an interrupt bit for cpu "
136 (int)cpu, (void *)owner_dev);
143 nodeid = cpuid_to_cnodeid(cpu);
144 nasid = cpuid_to_nasid(cpu);
145 xtalk_addr = HUBREG_AS_XTALKADDR(nasid, PIREG(PI_INT_PEND_MOD, cpuid_to_subnode(cpu)));
148 * Allocate an interrupt handle, and fill it in. There are two
149 * pieces to an interrupt handle: the piece needed by generic
150 * xtalk code which is used by crosstalk device drivers, and
151 * the piece needed by low-level IP27 hardware code.
153 intr_hdl = snia_kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid);
154 ASSERT_ALWAYS(intr_hdl);
157 * Fill in xtalk information for generic xtalk interfaces that
158 * operate on xtalk_intr_hdl's.
160 xtalk_info = &intr_hdl->i_xtalk_info;
161 xtalk_info->xi_dev = dev;
162 xtalk_info->xi_vector = bit;
163 xtalk_info->xi_addr = xtalk_addr;
166 * Regardless of which CPU we ultimately interrupt, a given crosstalk
167 * widget always handles interrupts (and PIO and DMA) through its
168 * designated "master" crosstalk provider.
170 xwidget_info = xwidget_info_get(dev);
172 xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
174 /* Fill in low level hub information for hub_* interrupt interface */
175 intr_hdl->i_swlevel = intr_swlevel;
176 intr_hdl->i_cpuid = cpu;
177 intr_hdl->i_bit = bit;
178 intr_hdl->i_flags = HUB_INTR_IS_ALLOCED;
180 /* Store the actual interrupt priority level & interrupt target
181 * cpu back in the device descriptor.
183 hub_device_desc_update(dev_desc, intr_swlevel, cpu);
184 synergy_intr_alloc((int)bit, (int)cpu);
189 * Allocate resources required for an interrupt as specified in dev_desc.
190 * Returns a hub interrupt handle on success, or 0 on failure.
193 hub_intr_alloc( devfs_handle_t dev, /* which crosstalk device */
194 device_desc_t dev_desc, /* device descriptor */
195 devfs_handle_t owner_dev) /* owner of this interrupt, if known */
197 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
201 * Allocate resources required for an interrupt as specified in dev_desc.
202 * Uncondtionally request non-threaded, regardless of what the device
203 * descriptor might say.
204 * Returns a hub interrupt handle on success, or 0 on failure.
207 hub_intr_alloc_nothd(devfs_handle_t dev, /* which crosstalk device */
208 device_desc_t dev_desc, /* device descriptor */
209 devfs_handle_t owner_dev) /* owner of this interrupt, if known */
211 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
215 * Free resources consumed by intr_alloc.
218 hub_intr_free(hub_intr_t intr_hdl)
220 cpuid_t cpu = intr_hdl->i_cpuid;
221 int bit = intr_hdl->i_bit;
222 xtalk_intr_t xtalk_info;
224 if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) {
225 /* Setting the following fields in the xtalk interrupt info
226 * clears the interrupt target register in the xtalk user
228 xtalk_info = &intr_hdl->i_xtalk_info;
229 xtalk_info->xi_dev = NODEV;
230 xtalk_info->xi_vector = 0;
231 xtalk_info->xi_addr = 0;
232 hub_intr_disconnect(intr_hdl);
235 if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED)
238 intr_unreserve_level(cpu, bit);
243 * Associate resources allocated with a previous hub_intr_alloc call with the
244 * described handler, arg, name, etc.
248 hub_intr_connect( hub_intr_t intr_hdl, /* xtalk intr resource handle */
249 xtalk_intr_setfunc_t setfunc, /* func to set intr hw */
250 void *setfunc_arg) /* arg to setfunc */
253 cpuid_t cpu = intr_hdl->i_cpuid;
254 int bit = intr_hdl->i_bit;
255 extern int synergy_intr_connect(int, int);
257 ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
259 rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel, NULL);
263 intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
264 intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
266 if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
268 intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
269 return(synergy_intr_connect((int)bit, (int)cpu));
274 * Disassociate handler with the specified interrupt.
277 hub_intr_disconnect(hub_intr_t intr_hdl)
281 cpuid_t cpu = intr_hdl->i_cpuid;
282 int bit = intr_hdl->i_bit;
283 xtalk_intr_setfunc_t setfunc;
285 setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
287 /* TBD: send disconnected interrupts somewhere harmless */
288 if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
290 rv = intr_disconnect_level(cpu, bit);
292 intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
297 * Return a hwgraph vertex that represents the CPU currently
298 * targeted by an interrupt.
301 hub_intr_cpu_get(hub_intr_t intr_hdl)
303 cpuid_t cpuid = intr_hdl->i_cpuid;
304 ASSERT(cpuid != CPU_NONE);
306 return(cpuid_to_vertex(cpuid));