ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / ia64 / sn / io / sn2 / shub_intr.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.  All Rights Reserved.
7  */
8
9 #include <linux/types.h>
10 #include <linux/slab.h>
11 #include <asm/sn/types.h>
12 #include <asm/sn/sgi.h>
13 #include <asm/sn/driver.h>
14 #include <asm/param.h>
15 #include <asm/sn/pio.h>
16 #include <asm/sn/xtalk/xwidget.h>
17 #include <asm/sn/io.h>
18 #include <asm/sn/sn_private.h>
19 #include <asm/sn/addrs.h>
20 #include <asm/sn/hcl.h>
21 #include <asm/sn/hcl_util.h>
22 #include <asm/sn/intr.h>
23 #include <asm/sn/xtalk/xtalkaddrs.h>
24 #include <asm/sn/klconfig.h>
25 #include <asm/sn/sn2/shub_mmr.h>
26 #include <asm/sn/sn_cpuid.h>
27 #include <asm/sn/pci/pcibr.h>
28 #include <asm/sn/pci/pcibr_private.h>
29
30 /* ARGSUSED */
31 void
32 hub_intr_init(vertex_hdl_t hubv)
33 {
34 }
35
36 xwidgetnum_t
37 hub_widget_id(nasid_t nasid)
38 {
39
40         if (!(nasid & 1)) {
41                 hubii_wcr_t     ii_wcr; /* the control status register */
42                 ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid,IIO_WCR);
43                 return ii_wcr.wcr_fields_s.wcr_widget_id;
44         } else {
45                 /* ICE does not have widget id. */
46                 return(-1);
47         }
48 }
49
50 static hub_intr_t
51 do_hub_intr_alloc(vertex_hdl_t dev,
52                 device_desc_t dev_desc,
53                 vertex_hdl_t owner_dev,
54                 int uncond_nothread)
55 {
56         cpuid_t         cpu;
57         int             vector;
58         hub_intr_t      intr_hdl;
59         cnodeid_t       cnode;
60         int             cpuphys, slice;
61         int             nasid;
62         iopaddr_t       xtalk_addr;
63         struct xtalk_intr_s     *xtalk_info;
64         xwidget_info_t  xwidget_info;
65
66         cpu = intr_heuristic(dev, -1, &vector);
67         if (cpu == CPU_NONE) {
68                 printk("Unable to allocate interrupt for 0x%p\n", (void *)owner_dev);
69                 return(0);
70         }
71
72         cpuphys = cpu_physical_id(cpu);
73         slice = cpu_physical_id_to_slice(cpuphys);
74         nasid = cpu_physical_id_to_nasid(cpuphys);
75         cnode = cpuid_to_cnodeid(cpu);
76
77         if (slice) {
78                 xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
79         } else {
80                 xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
81         }
82
83         intr_hdl = kmalloc(sizeof(struct hub_intr_s), GFP_KERNEL);
84         ASSERT_ALWAYS(intr_hdl);
85         memset(intr_hdl, 0, sizeof(struct hub_intr_s));
86
87         xtalk_info = &intr_hdl->i_xtalk_info;
88         xtalk_info->xi_dev = dev;
89         xtalk_info->xi_vector = vector;
90         xtalk_info->xi_addr = xtalk_addr;
91
92         xwidget_info = xwidget_info_get(dev);
93         if (xwidget_info) {
94                 xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
95         }
96
97         intr_hdl->i_cpuid = cpu;
98         intr_hdl->i_bit = vector;
99         intr_hdl->i_flags |= HUB_INTR_IS_ALLOCED;
100
101         return intr_hdl;
102 }
103
104 hub_intr_t
105 hub_intr_alloc(vertex_hdl_t dev,
106                 device_desc_t dev_desc,
107                 vertex_hdl_t owner_dev)
108 {
109         return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
110 }
111
112 hub_intr_t
113 hub_intr_alloc_nothd(vertex_hdl_t dev,
114                 device_desc_t dev_desc,
115                 vertex_hdl_t owner_dev)
116 {
117         return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
118 }
119
120 void
121 hub_intr_free(hub_intr_t intr_hdl)
122 {
123         cpuid_t         cpu = intr_hdl->i_cpuid;
124         int             vector = intr_hdl->i_bit;
125         xtalk_intr_t    xtalk_info;
126
127         if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) {
128                 xtalk_info = &intr_hdl->i_xtalk_info;
129                 xtalk_info->xi_dev = 0;
130                 xtalk_info->xi_vector = 0;
131                 xtalk_info->xi_addr = 0;
132                 hub_intr_disconnect(intr_hdl);
133         }
134
135         if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED) {
136                 kfree(intr_hdl);
137         }
138         intr_unreserve_level(cpu, vector);
139 }
140
141 int
142 hub_intr_connect(hub_intr_t intr_hdl,
143                 intr_func_t intr_func,          /* xtalk intr handler */
144                 void *intr_arg,                 /* arg to intr handler */
145                 xtalk_intr_setfunc_t setfunc,
146                 void *setfunc_arg)
147 {
148         int             rv;
149         cpuid_t         cpu = intr_hdl->i_cpuid;
150         int             vector = intr_hdl->i_bit;
151
152         ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
153
154         rv = intr_connect_level(cpu, vector);
155         if (rv < 0)
156                 return rv;
157
158         intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
159         intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
160
161         if (setfunc) {
162                 (*setfunc)((xtalk_intr_t)intr_hdl);
163         }
164
165         intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
166
167         return 0;
168 }
169
170 /*
171  * Disassociate handler with the specified interrupt.
172  */
173 void
174 hub_intr_disconnect(hub_intr_t intr_hdl)
175 {
176         /*REFERENCED*/
177         int rv;
178         cpuid_t cpu = intr_hdl->i_cpuid;
179         int bit = intr_hdl->i_bit;
180         xtalk_intr_setfunc_t setfunc;
181
182         setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
183
184         /* TBD: send disconnected interrupts somewhere harmless */
185         if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
186
187         rv = intr_disconnect_level(cpu, bit);
188         ASSERT(rv == 0);
189         intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
190 }
191
192 /* 
193  * Redirect an interrupt to another cpu.
194  */
195
196 void
197 sn_shub_redirect_intr(pcibr_intr_t intr, unsigned long cpu)
198 {
199         unsigned long bit;
200         int cpuphys, slice;
201         nasid_t nasid;
202         unsigned long xtalk_addr;
203         int             irq;
204         int             i;
205         int             old_cpu;
206         int             new_cpu;
207
208         cpuphys = cpu_physical_id(cpu);
209         slice = cpu_physical_id_to_slice(cpuphys);
210         nasid = cpu_physical_id_to_nasid(cpuphys);
211
212         for (i = CPUS_PER_NODE - 1; i >= 0; i--) {
213                 new_cpu = nasid_slice_to_cpuid(nasid, i);
214                 if (new_cpu == NR_CPUS) {
215                         continue;
216                 }
217
218                 if (!cpu_online(new_cpu)) {
219                         continue;
220                 }
221                 break;
222         }
223
224         if (enable_shub_wars_1_1() && slice != i) {
225                 printk("smp_affinity WARNING: SHUB 1.1 present: cannot target cpu %d, targeting cpu %d instead.\n",(int)cpu, new_cpu);
226                 cpu = new_cpu;
227                 slice = i;
228         }
229
230         if (slice) {    
231                 xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
232         } else {
233                 xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
234         }
235
236         for (bit = 0; bit < 8; bit++) {
237                 if (intr->bi_ibits & (1 << bit) ) {
238                         /* Disable interrupts. */
239                         pcireg_intr_enable_bit_clr(intr->bi_soft, bit);
240                         /* Reset Host address (Interrupt destination) */
241                         pcireg_intr_addr_addr_set(intr->bi_soft, bit, xtalk_addr);
242                         /* Enable interrupt */
243                         pcireg_intr_enable_bit_set(intr->bi_soft, bit);
244                         /* Force an interrupt, just in case. */
245                         pcireg_force_intr_set(intr->bi_soft, bit);
246                 }
247         }
248         irq = intr->bi_irq;
249         old_cpu = intr->bi_cpu;
250         if (pdacpu(cpu)->sn_first_irq == 0 || pdacpu(cpu)->sn_first_irq > irq) {
251                 pdacpu(cpu)->sn_first_irq = irq;
252         }
253         if (pdacpu(cpu)->sn_last_irq < irq) {
254                 pdacpu(cpu)->sn_last_irq = irq;
255         }
256         pdacpu(old_cpu)->sn_num_irqs--;
257         pdacpu(cpu)->sn_num_irqs++;
258         intr->bi_cpu = (int)cpu;
259 }