more changes on original files
[linux-2.4.git] / arch / ia64 / sn / io / sn2 / shub_intr.c
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
36 hub_intr_init(vertex_hdl_t hubv)
37 {
38 }
39
40 xwidgetnum_t
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
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
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
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
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
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
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
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