1/* $Id: hub_intr.c,v 1.1.1.1 2008/10/15 03:26:03 james26_jang 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 30extern xtalk_provider_t hub_provider; 31 32/* ARGSUSED */ 33void 34hub_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 */ 45static void 46hub_device_desc_update(device_desc_t dev_desc, 47 ilvl_t intr_swlevel, 48 cpuid_t cpu) 49{ 50} 51 52int 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 */ 58static hub_intr_t 59do_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 98 /* If the cpu has not been picked already then choose a candidate 99 * interrupt target and reserve the interrupt bit 100 */ 101 if (!cpupicked) { 102 cpu = intr_heuristic(dev,dev_desc,allocate_my_bit, 103 intr_resflags,owner_dev, 104 intr_name,&bit); 105 } 106 107 /* At this point we SHOULD have a valid cpu */ 108 if (cpu == CPU_NONE) { 109#if defined(SUPPORT_PRINTING_V_FORMAT) 110 printk(KERN_WARNING "%v hub_intr_alloc could not allocate interrupt\n", 111 owner_dev); 112#else 113 printk(KERN_WARNING "%p hub_intr_alloc could not allocate interrupt\n", 114 (void *)owner_dev); 115#endif 116 return(0); 117 118 } 119 120 /* If the cpu has been picked already (due to the bridge data 121 * corruption bug) then try to reserve an interrupt bit . 122 */ 123 if (cpupicked) { 124 bit = intr_reserve_level(cpu, allocate_my_bit, 125 intr_resflags, 126 owner_dev, intr_name); 127 if (bit < 0) { 128#if defined(SUPPORT_PRINTING_V_FORMAT) 129 printk(KERN_WARNING "Could not reserve an interrupt bit for cpu " 130 " %d and dev %v\n", 131 cpu,owner_dev); 132#else 133 printk(KERN_WARNING "Could not reserve an interrupt bit for cpu " 134 " %d and dev %p\n", 135 (int)cpu, (void *)owner_dev); 136#endif 137 138 return(0); 139 } 140 } 141 142 nodeid = cpuid_to_cnodeid(cpu); 143 nasid = cpuid_to_nasid(cpu); 144 xtalk_addr = HUBREG_AS_XTALKADDR(nasid, PIREG(PI_INT_PEND_MOD, cpuid_to_subnode(cpu))); 145 146 /* 147 * Allocate an interrupt handle, and fill it in. There are two 148 * pieces to an interrupt handle: the piece needed by generic 149 * xtalk code which is used by crosstalk device drivers, and 150 * the piece needed by low-level IP27 hardware code. 151 */ 152 intr_hdl = snia_kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid); 153 ASSERT_ALWAYS(intr_hdl); 154 155 /* 156 * Fill in xtalk information for generic xtalk interfaces that 157 * operate on xtalk_intr_hdl's. 158 */ 159 xtalk_info = &intr_hdl->i_xtalk_info; 160 xtalk_info->xi_dev = dev; 161 xtalk_info->xi_vector = bit; 162 xtalk_info->xi_addr = xtalk_addr; 163 164 /* 165 * Regardless of which CPU we ultimately interrupt, a given crosstalk 166 * widget always handles interrupts (and PIO and DMA) through its 167 * designated "master" crosstalk provider. 168 */ 169 xwidget_info = xwidget_info_get(dev); 170 if (xwidget_info) 171 xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info); 172 173 /* Fill in low level hub information for hub_* interrupt interface */ 174 intr_hdl->i_swlevel = intr_swlevel; 175 intr_hdl->i_cpuid = cpu; 176 intr_hdl->i_bit = bit; 177 intr_hdl->i_flags = HUB_INTR_IS_ALLOCED; 178 179 /* Store the actual interrupt priority level & interrupt target 180 * cpu back in the device descriptor. 181 */ 182 hub_device_desc_update(dev_desc, intr_swlevel, cpu); 183 synergy_intr_alloc((int)bit, (int)cpu); 184 return(intr_hdl); 185} 186 187/* 188 * Allocate resources required for an interrupt as specified in dev_desc. 189 * Returns a hub interrupt handle on success, or 0 on failure. 190 */ 191hub_intr_t 192hub_intr_alloc( devfs_handle_t dev, /* which crosstalk device */ 193 device_desc_t dev_desc, /* device descriptor */ 194 devfs_handle_t owner_dev) /* owner of this interrupt, if known */ 195{ 196 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0)); 197} 198 199/* 200 * Allocate resources required for an interrupt as specified in dev_desc. 201 * Uncondtionally request non-threaded, regardless of what the device 202 * descriptor might say. 203 * Returns a hub interrupt handle on success, or 0 on failure. 204 */ 205hub_intr_t 206hub_intr_alloc_nothd(devfs_handle_t dev, /* which crosstalk device */ 207 device_desc_t dev_desc, /* device descriptor */ 208 devfs_handle_t owner_dev) /* owner of this interrupt, if known */ 209{ 210 return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1)); 211} 212 213/* 214 * Free resources consumed by intr_alloc. 215 */ 216void 217hub_intr_free(hub_intr_t intr_hdl) 218{ 219 cpuid_t cpu = intr_hdl->i_cpuid; 220 int bit = intr_hdl->i_bit; 221 xtalk_intr_t xtalk_info; 222 223 if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) { 224 /* Setting the following fields in the xtalk interrupt info 225 * clears the interrupt target register in the xtalk user 226 */ 227 xtalk_info = &intr_hdl->i_xtalk_info; 228 xtalk_info->xi_dev = NODEV; 229 xtalk_info->xi_vector = 0; 230 xtalk_info->xi_addr = 0; 231 hub_intr_disconnect(intr_hdl); 232 } 233 234 if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED) 235 kfree(intr_hdl); 236 237 intr_unreserve_level(cpu, bit); 238} 239 240 241/* 242 * Associate resources allocated with a previous hub_intr_alloc call with the 243 * described handler, arg, name, etc. 244 */ 245/*ARGSUSED*/ 246int 247hub_intr_connect( hub_intr_t intr_hdl, /* xtalk intr resource handle */ 248 xtalk_intr_setfunc_t setfunc, /* func to set intr hw */ 249 void *setfunc_arg) /* arg to setfunc */ 250{ 251 int rv; 252 cpuid_t cpu = intr_hdl->i_cpuid; 253 int bit = intr_hdl->i_bit; 254 extern int synergy_intr_connect(int, int); 255 256 ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED); 257 258 rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel, NULL); 259 if (rv < 0) 260 return(rv); 261 262 intr_hdl->i_xtalk_info.xi_setfunc = setfunc; 263 intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg; 264 265 if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl); 266 267 intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED; 268 return(synergy_intr_connect((int)bit, (int)cpu)); 269} 270 271 272/* 273 * Disassociate handler with the specified interrupt. 274 */ 275void 276hub_intr_disconnect(hub_intr_t intr_hdl) 277{ 278 /*REFERENCED*/ 279 int rv; 280 cpuid_t cpu = intr_hdl->i_cpuid; 281 int bit = intr_hdl->i_bit; 282 xtalk_intr_setfunc_t setfunc; 283 284 setfunc = intr_hdl->i_xtalk_info.xi_setfunc; 285 286 /* TBD: send disconnected interrupts somewhere harmless */ 287 if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl); 288 289 rv = intr_disconnect_level(cpu, bit); 290 ASSERT(rv == 0); 291 intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED; 292} 293 294 295/* 296 * Return a hwgraph vertex that represents the CPU currently 297 * targeted by an interrupt. 298 */ 299devfs_handle_t 300hub_intr_cpu_get(hub_intr_t intr_hdl) 301{ 302 cpuid_t cpuid = intr_hdl->i_cpuid; 303 ASSERT(cpuid != CPU_NONE); 304 305 return(cpuid_to_vertex(cpuid)); 306} 307