Add version to PATCH_LOG and LAST_LOG.
[linux-flexiantxendom0-3.2.10.git] / arch / ia64 / sn / io / sn1 / hub_intr.c
1 /* $Id: hub_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-2002 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/sn_cpuid.h>
29
30 extern xtalk_provider_t hub_provider;
31
32 /* ARGSUSED */
33 void
34 hub_intr_init(devfs_handle_t hubv)
35 {
36 }
37
38 /*
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
43  *      the descriptor.
44  */
45 static void
46 hub_device_desc_update(device_desc_t    dev_desc, 
47                        ilvl_t           intr_swlevel,
48                        cpuid_t          cpu)
49 {
50 }
51
52 int allocate_my_bit = INTRCONNECT_ANYBIT;
53
54 /*
55  * Allocate resources required for an interrupt as specified in dev_desc.
56  * Returns a hub interrupt handle on success, or 0 on failure.
57  */
58 static hub_intr_t
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 */
63 {
64         cpuid_t cpu = (cpuid_t)0;                       /* cpu to receive interrupt */
65         int cpupicked = 0;
66         int bit;                        /* interrupt vector */
67         /*REFERENCED*/
68         int intr_resflags = 0;
69         hub_intr_t intr_hdl;
70         cnodeid_t nodeid;               /* node to receive interrupt */
71         /*REFERENCED*/
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);
80
81
82         if (dev_desc) {
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;
87                 } else {
88                         /* Neither an error nor a thread. */
89                         intr_resflags = 0;
90                 }
91         } else {
92                 intr_swlevel = default_intr_pri;
93                 if (!uncond_nothread)
94                         intr_resflags = II_THREADED;
95         }
96
97         /* XXX - Need to determine if the interrupt should be threaded. */
98
99         /* If the cpu has not been picked already then choose a candidate 
100          * interrupt target and reserve the interrupt bit 
101          */
102         if (!cpupicked) {
103                 cpu = intr_heuristic(dev,dev_desc,allocate_my_bit,
104                                      intr_resflags,owner_dev,
105                                      intr_name,&bit);
106         }
107
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",
112                         owner_dev);
113 #else
114                 printk(KERN_WARNING  "%p hub_intr_alloc could not allocate interrupt\n",
115                         (void *)owner_dev);
116 #endif
117                 return(0);
118
119         }
120
121         /* If the cpu has been picked already (due to the bridge data 
122          * corruption bug) then try to reserve an interrupt bit .
123          */
124         if (cpupicked) {
125                 bit = intr_reserve_level(cpu, allocate_my_bit, 
126                                          intr_resflags, 
127                                          owner_dev, intr_name);
128                 if (bit < 0) {
129 #if defined(SUPPORT_PRINTING_V_FORMAT)
130                         printk(KERN_WARNING  "Could not reserve an interrupt bit for cpu "
131                                 " %d and dev %v\n",
132                                 cpu,owner_dev);
133 #else
134                         printk(KERN_WARNING  "Could not reserve an interrupt bit for cpu "
135                                 " %d and dev %p\n",
136                                 (int)cpu, (void *)owner_dev);
137 #endif
138                                 
139                         return(0);
140                 }
141         }
142
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)));
146
147         /*
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.
152          */
153         intr_hdl = snia_kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid);
154         ASSERT_ALWAYS(intr_hdl);
155
156         /* 
157          * Fill in xtalk information for generic xtalk interfaces that
158          * operate on xtalk_intr_hdl's.
159          */
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;
164
165         /*
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.
169          */
170         xwidget_info = xwidget_info_get(dev);
171         if (xwidget_info)
172                 xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
173
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;
179
180         /* Store the actual interrupt priority level & interrupt target
181          * cpu back in the device descriptor.
182          */
183         hub_device_desc_update(dev_desc, intr_swlevel, cpu);
184         synergy_intr_alloc((int)bit, (int)cpu);
185         return(intr_hdl);
186 }
187
188 /*
189  * Allocate resources required for an interrupt as specified in dev_desc.
190  * Returns a hub interrupt handle on success, or 0 on failure.
191  */
192 hub_intr_t
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 */
196 {
197         return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
198 }
199
200 /*
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.
205  */
206 hub_intr_t
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 */
210 {
211         return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
212 }
213
214 /*
215  * Free resources consumed by intr_alloc.
216  */
217 void
218 hub_intr_free(hub_intr_t intr_hdl)
219 {
220         cpuid_t cpu = intr_hdl->i_cpuid;
221         int bit = intr_hdl->i_bit;
222         xtalk_intr_t xtalk_info;
223
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
227                  */
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);
233         }
234
235         if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED)
236                 kfree(intr_hdl);
237
238         intr_unreserve_level(cpu, bit);
239 }
240
241
242 /*
243  * Associate resources allocated with a previous hub_intr_alloc call with the
244  * described handler, arg, name, etc.
245  */
246 /*ARGSUSED*/
247 int
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 */
251 {
252         int rv;
253         cpuid_t cpu = intr_hdl->i_cpuid;
254         int bit = intr_hdl->i_bit;
255         extern int synergy_intr_connect(int, int);
256
257         ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
258
259         rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel, NULL);
260         if (rv < 0)
261                 return(rv);
262
263         intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
264         intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
265
266         if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
267
268         intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
269         return(synergy_intr_connect((int)bit, (int)cpu));
270 }
271
272
273 /*
274  * Disassociate handler with the specified interrupt.
275  */
276 void
277 hub_intr_disconnect(hub_intr_t intr_hdl)
278 {
279         /*REFERENCED*/
280         int rv;
281         cpuid_t cpu = intr_hdl->i_cpuid;
282         int bit = intr_hdl->i_bit;
283         xtalk_intr_setfunc_t setfunc;
284
285         setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
286
287         /* TBD: send disconnected interrupts somewhere harmless */
288         if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
289
290         rv = intr_disconnect_level(cpu, bit);
291         ASSERT(rv == 0);
292         intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
293 }
294
295
296 /*
297  * Return a hwgraph vertex that represents the CPU currently
298  * targeted by an interrupt.
299  */
300 devfs_handle_t
301 hub_intr_cpu_get(hub_intr_t intr_hdl)
302 {
303         cpuid_t cpuid = intr_hdl->i_cpuid;
304         ASSERT(cpuid != CPU_NONE);
305
306         return(cpuid_to_vertex(cpuid));
307 }