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
6 * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc. All Rights Reserved.
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>
32 hub_intr_init(vertex_hdl_t hubv)
37 hub_widget_id(nasid_t nasid)
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;
45 /* ICE does not have widget id. */
51 do_hub_intr_alloc(vertex_hdl_t dev,
52 device_desc_t dev_desc,
53 vertex_hdl_t owner_dev,
63 struct xtalk_intr_s *xtalk_info;
64 xwidget_info_t xwidget_info;
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);
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);
78 xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
80 xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
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));
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;
92 xwidget_info = xwidget_info_get(dev);
94 xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info);
97 intr_hdl->i_cpuid = cpu;
98 intr_hdl->i_bit = vector;
99 intr_hdl->i_flags |= HUB_INTR_IS_ALLOCED;
105 hub_intr_alloc(vertex_hdl_t dev,
106 device_desc_t dev_desc,
107 vertex_hdl_t owner_dev)
109 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0));
113 hub_intr_alloc_nothd(vertex_hdl_t dev,
114 device_desc_t dev_desc,
115 vertex_hdl_t owner_dev)
117 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1));
121 hub_intr_free(hub_intr_t intr_hdl)
123 cpuid_t cpu = intr_hdl->i_cpuid;
124 int vector = intr_hdl->i_bit;
125 xtalk_intr_t xtalk_info;
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);
135 if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED) {
138 intr_unreserve_level(cpu, vector);
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,
149 cpuid_t cpu = intr_hdl->i_cpuid;
150 int vector = intr_hdl->i_bit;
152 ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED);
154 rv = intr_connect_level(cpu, vector);
158 intr_hdl->i_xtalk_info.xi_setfunc = setfunc;
159 intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg;
162 (*setfunc)((xtalk_intr_t)intr_hdl);
165 intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED;
171 * Disassociate handler with the specified interrupt.
174 hub_intr_disconnect(hub_intr_t intr_hdl)
178 cpuid_t cpu = intr_hdl->i_cpuid;
179 int bit = intr_hdl->i_bit;
180 xtalk_intr_setfunc_t setfunc;
182 setfunc = intr_hdl->i_xtalk_info.xi_setfunc;
184 /* TBD: send disconnected interrupts somewhere harmless */
185 if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl);
187 rv = intr_disconnect_level(cpu, bit);
189 intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED;
193 * Redirect an interrupt to another cpu.
197 sn_shub_redirect_intr(pcibr_intr_t intr, unsigned long cpu)
202 unsigned long xtalk_addr;
208 cpuphys = cpu_physical_id(cpu);
209 slice = cpu_physical_id_to_slice(cpuphys);
210 nasid = cpu_physical_id_to_nasid(cpuphys);
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) {
218 if (!cpu_online(new_cpu)) {
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);
231 xtalk_addr = SH_II_INT1 | ((unsigned long)nasid << 36) | (1UL << 47);
233 xtalk_addr = SH_II_INT0 | ((unsigned long)nasid << 36) | (1UL << 47);
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);
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;
253 if (pdacpu(cpu)->sn_last_irq < irq) {
254 pdacpu(cpu)->sn_last_irq = irq;
256 pdacpu(old_cpu)->sn_num_irqs--;
257 pdacpu(cpu)->sn_num_irqs++;
258 intr->bi_cpu = (int)cpu;