/* $Id$ * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc. * Copyright (C) 2000 by Colin Ngam */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int maxnodes; /* #define IOGRAPH_DEBUG */ #ifdef IOGRAPH_DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif /* IOGRAPH_DEBUG */ /* #define PROBE_TEST */ /* At most 2 hubs can be connected to an xswitch */ #define NUM_XSWITCH_VOLUNTEER 2 /* * Track which hubs have volunteered to manage devices hanging off of * a Crosstalk Switch (e.g. xbow). This structure is allocated, * initialized, and hung off the xswitch vertex early on when the * xswitch vertex is created. */ typedef struct xswitch_vol_s { mutex_t xswitch_volunteer_mutex; int xswitch_volunteer_count; devfs_handle_t xswitch_volunteer[NUM_XSWITCH_VOLUNTEER]; } *xswitch_vol_t; void xswitch_vertex_init(devfs_handle_t xswitch) { xswitch_vol_t xvolinfo; int rc; xvolinfo = kmalloc(sizeof(struct xswitch_vol_s), GFP_KERNEL); mutex_init(&xvolinfo->xswitch_volunteer_mutex); xvolinfo->xswitch_volunteer_count = 0; rc = hwgraph_info_add_LBL(xswitch, INFO_LBL_XSWITCH_VOL, (arbitrary_info_t)xvolinfo); ASSERT(rc == GRAPH_SUCCESS); rc = rc; } /* * When assignment of hubs to widgets is complete, we no longer need the * xswitch volunteer structure hanging around. Destroy it. */ static void xswitch_volunteer_delete(devfs_handle_t xswitch) { xswitch_vol_t xvolinfo; int rc; rc = hwgraph_info_remove_LBL(xswitch, INFO_LBL_XSWITCH_VOL, (arbitrary_info_t *)&xvolinfo); #ifdef LATER ASSERT(rc == GRAPH_SUCCESS); rc = rc; #endif kfree(xvolinfo); } /* * A Crosstalk master volunteers to manage xwidgets on the specified xswitch. */ /* ARGSUSED */ static void volunteer_for_widgets(devfs_handle_t xswitch, devfs_handle_t master) { xswitch_vol_t xvolinfo = NULL; (void)hwgraph_info_get_LBL(xswitch, INFO_LBL_XSWITCH_VOL, (arbitrary_info_t *)&xvolinfo); if (xvolinfo == NULL) { #ifdef LATER if (!is_headless_node_vertex(master)) { #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("volunteer for widgets: vertex %v has no info label", xswitch); #else PRINT_WARNING("volunteer for widgets: vertex 0x%x has no info label", xswitch); #endif } #endif /* LATER */ return; } mutex_lock(&xvolinfo->xswitch_volunteer_mutex); ASSERT(xvolinfo->xswitch_volunteer_count < NUM_XSWITCH_VOLUNTEER); xvolinfo->xswitch_volunteer[xvolinfo->xswitch_volunteer_count] = master; xvolinfo->xswitch_volunteer_count++; mutex_unlock(&xvolinfo->xswitch_volunteer_mutex); } extern int xbow_port_io_enabled(nasid_t nasid, int widgetnum); /* * Assign all the xwidgets hanging off the specified xswitch to the * Crosstalk masters that have volunteered for xswitch duty. */ /* ARGSUSED */ static void assign_widgets_to_volunteers(devfs_handle_t xswitch, devfs_handle_t hubv) { int curr_volunteer, num_volunteer; xwidgetnum_t widgetnum; xswitch_info_t xswitch_info; xswitch_vol_t xvolinfo = NULL; nasid_t nasid; hubinfo_t hubinfo; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; xswitch_info = xswitch_info_get(xswitch); ASSERT(xswitch_info != NULL); (void)hwgraph_info_get_LBL(xswitch, INFO_LBL_XSWITCH_VOL, (arbitrary_info_t *)&xvolinfo); if (xvolinfo == NULL) { #ifdef LATER if (!is_headless_node_vertex(hubv)) { #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("assign_widgets_to_volunteers:vertex %v has " " no info label", xswitch); #else PRINT_WARNING("assign_widgets_to_volunteers:vertex 0x%x has " " no info label", xswitch); #endif } #endif /* LATER */ return; } num_volunteer = xvolinfo->xswitch_volunteer_count; ASSERT(num_volunteer > 0); curr_volunteer = 0; /* Assign master hub for xswitch itself. */ if (HUB_WIDGET_ID_MIN > 0) { hubv = xvolinfo->xswitch_volunteer[0]; xswitch_info_master_assignment_set(xswitch_info, (xwidgetnum_t)0, hubv); } /* * TBD: Use administrative information to alter assignment of * widgets to hubs. */ for (widgetnum=HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) { #ifndef BRINGUP int i; #endif /* * Ignore disabled/empty ports. */ if (!xbow_port_io_enabled(nasid, widgetnum)) continue; /* * If this is the master IO board, assign it to the same * hub that owned it in the prom. */ if (is_master_nasid_widget(nasid, widgetnum)) { int i; for (i=0; ixswitch_volunteer[i]; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; if (nasid == get_console_nasid()) goto do_assignment; } #ifdef LATER PRINT_PANIC("Nasid == %d, console nasid == %d", nasid, get_console_nasid()); #endif } /* * Do a round-robin assignment among the volunteer nodes. */ hubv = xvolinfo->xswitch_volunteer[curr_volunteer]; curr_volunteer = (curr_volunteer + 1) % num_volunteer; /* fall through */ do_assignment: /* * At this point, we want to make hubv the master of widgetnum. */ xswitch_info_master_assignment_set(xswitch_info, widgetnum, hubv); } xswitch_volunteer_delete(xswitch); } /* * Early iograph initialization. Called by master CPU in mlreset(). * Useful for including iograph.o in kernel.o. */ void iograph_early_init(void) { /* * Need new way to get this information .. */ cnodeid_t cnode; nasid_t nasid; lboard_t *board; /* * Init. the board-to-hwgraph link early, so FRU analyzer * doesn't trip on leftover values if we panic early on. */ for(cnode = 0; cnode < numnodes; cnode++) { nasid = COMPACT_TO_NASID_NODEID(cnode); board = (lboard_t *)KL_CONFIG_INFO(nasid); DBG("iograph_early_init: Found board 0x%p\n", board); /* Check out all the board info stored on a node */ while(board) { board->brd_graph_link = GRAPH_VERTEX_NONE; board = KLCF_NEXT(board); DBG("iograph_early_init: Found board 0x%p\n", board); } } hubio_init(); } #ifdef LATER /* There is an identical definition of this in os/scheduler/runq.c */ #define INIT_COOKIE(cookie) cookie.must_run = 0; cookie.cpu = PDA_RUNANYWHERE /* * These functions absolutely doesn't belong here. It's here, though, * until the scheduler provides a platform-independent version * that works the way it should. The interface will definitely change, * too. Currently used only in this file and by io/cdl.c in order to * bind various I/O threads to a CPU on the proper node. */ cpu_cookie_t setnoderun(cnodeid_t cnodeid) { int i; cpuid_t cpunum; cpu_cookie_t cookie; INIT_COOKIE(cookie); if (cnodeid == CNODEID_NONE) return(cookie); /* * Do a setmustrun to one of the CPUs on the specified * node. */ if ((cpunum = CNODE_TO_CPU_BASE(cnodeid)) == CPU_NONE) { return(cookie); } cpunum += CNODE_NUM_CPUS(cnodeid) - 1; for (i = 0; i < CNODE_NUM_CPUS(cnodeid); i++, cpunum--) { if (cpu_enabled(cpunum)) { cookie = setmustrun(cpunum); break; } } return(cookie); } void restorenoderun(cpu_cookie_t cookie) { restoremustrun(cookie); } #endif /* LATER */ #ifdef LINUX_KERNEL_THREADS static struct semaphore io_init_sema; #endif /* * Let boot processor know that we're done initializing our node's IO * and then exit. */ /* ARGSUSED */ static void io_init_done(cnodeid_t cnodeid,cpu_cookie_t c) { /* Let boot processor know that we're done. */ #ifdef LINUX_KERNEL_THREADS up(&io_init_sema); #endif #ifdef LATER /* This is for the setnoderun done when the io_init thread * started */ restorenoderun(c); sthread_exit(); #endif } /* * Probe to see if this hub's xtalk link is active. If so, * return the Crosstalk Identification of the widget that we talk to. * This is called before any of the Crosstalk infrastructure for * this hub is set up. It's usually called on the node that we're * probing, but not always. * * TBD: Prom code should actually do this work, and pass through * hwid for our use. */ static void early_probe_for_widget(devfs_handle_t hubv, xwidget_hwid_t hwid) { hubreg_t llp_csr_reg; nasid_t nasid; hubinfo_t hubinfo; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; llp_csr_reg = REMOTE_HUB_L(nasid, IIO_LLP_CSR); /* * If link is up, read the widget's part number. * A direct connect widget must respond to widgetnum=0. */ if (llp_csr_reg & IIO_LLP_CSR_IS_UP) { /* TBD: Put hub into "indirect" mode */ /* * We're able to read from a widget because our hub's * WIDGET_ID was set up earlier. */ widgetreg_t widget_id = *(volatile widgetreg_t *) (RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID); DBG("early_probe_for_widget: Hub Vertex 0x%p is UP widget_id = 0x%x Register 0x%p\n", hubv, widget_id, (volatile widgetreg_t *)(RAW_NODE_SWIN_BASE(nasid, 0x0) + WIDGET_ID) ); hwid->part_num = XWIDGET_PART_NUM(widget_id); hwid->rev_num = XWIDGET_REV_NUM(widget_id); hwid->mfg_num = XWIDGET_MFG_NUM(widget_id); /* TBD: link reset */ } else { hwid->part_num = XWIDGET_PART_NUM_NONE; hwid->rev_num = XWIDGET_REV_NUM_NONE; hwid->mfg_num = XWIDGET_MFG_NUM_NONE; } } /* Add inventory information to the widget vertex * Right now (module,slot,revision) is being * added as inventory information. */ static void xwidget_inventory_add(devfs_handle_t widgetv, lboard_t *board, struct xwidget_hwid_s hwid) { if (!board) return; /* Donot add inventory information for the baseio * on a speedo with an xbox. It has already been * taken care of in SN00_vmc. * Speedo with xbox's baseio comes in at slot io1 (widget 9) */ device_inventory_add(widgetv,INV_IOBD,board->brd_type, board->brd_module, SLOTNUM_GETSLOT(board->brd_slot), hwid.rev_num); } /* * io_xswitch_widget_init * */ /* defined in include/linux/ctype.h */ /* #define toupper(c) (islower(c) ? (c) - 'a' + 'A' : (c)) */ void io_xswitch_widget_init(devfs_handle_t xswitchv, devfs_handle_t hubv, xwidgetnum_t widgetnum, async_attach_t aa) { xswitch_info_t xswitch_info; xwidgetnum_t hub_widgetid; devfs_handle_t widgetv; cnodeid_t cnode; widgetreg_t widget_id; nasid_t nasid, peer_nasid; struct xwidget_hwid_s hwid; hubinfo_t hubinfo; /*REFERENCED*/ int rc; char slotname[SLOTNUM_MAXLENGTH]; char pathname[128]; char new_name[64]; moduleid_t module; slotid_t slot; lboard_t *board = NULL; char buffer[16]; DBG("\nio_xswitch_widget_init: hubv 0x%p, xswitchv 0x%p, widgetnum 0x%x\n", hubv, xswitchv, widgetnum); /* * Verify that xswitchv is indeed an attached xswitch. */ xswitch_info = xswitch_info_get(xswitchv); ASSERT(xswitch_info != NULL); hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; cnode = NASID_TO_COMPACT_NODEID(nasid); hub_widgetid = hubinfo->h_widgetid; /* Who's the other guy on out crossbow (if anyone) */ peer_nasid = NODEPDA(cnode)->xbow_peer; if (peer_nasid == INVALID_NASID) /* If I don't have a peer, use myself. */ peer_nasid = nasid; /* Check my xbow structure and my peer's */ if (!xbow_port_io_enabled(nasid, widgetnum) && !xbow_port_io_enabled(peer_nasid, widgetnum)) { return; } if (xswitch_info_link_ok(xswitch_info, widgetnum)) { char name[4]; /* * If the current hub is not supposed to be the master * for this widgetnum, then skip this widget. */ if (xswitch_info_master_assignment_get(xswitch_info, widgetnum) != hubv) { return; } module = NODEPDA(cnode)->module_id; #ifdef XBRIDGE_REGS_SIM /* hardwire for now...could do this with something like: * xbow_soft_t soft = hwgraph_fastinfo_get(vhdl); * xbow_t xbow = soft->base; * xbowreg_t xwidget_id = xbow->xb_wid_id; * but I don't feel like figuring out vhdl right now.. * and I know for a fact the answer is 0x2d000049 */ DBG("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: reading xwidget id: hardwired to xbridge (0x2d000049).\n"); DBG("XWIDGET_PART_NUM(0x2d000049)= 0x%x\n", XWIDGET_PART_NUM(0x2d000049)); if (XWIDGET_PART_NUM(0x2d000049)==XXBOW_WIDGET_PART_NUM) { #else if (nasid_has_xbridge(nasid)) { #endif /* XBRIDGE_REGS_SIM */ board = find_lboard_module_class( (lboard_t *)KL_CONFIG_INFO(nasid), module, KLTYPE_IOBRICK); DBG("io_xswitch_widget_init: Board 0x%p\n", board); { lboard_t dummy; if (board) { DBG("io_xswitch_widget_init: Found KLTYPE_IOBRICK Board 0x%p brd_type 0x%x\n", board, board->brd_type); } else { DBG("io_xswitch_widget_init: FIXME did not find IOBOARD\n"); board = &dummy; } } /* * BRINGUP * Make sure we really want to say xbrick, pbrick, * etc. rather than XIO, graphics, etc. */ #ifdef SUPPORT_PRINTING_M_FORMAT sprintf(pathname, EDGE_LBL_MODULE "/%M/" "%cbrick" "/%s/%d", NODEPDA(cnode)->module_id, #else memset(buffer, 0, 16); format_module_id(buffer, NODEPDA(cnode)->module_id, MODULE_FORMAT_BRIEF); sprintf(pathname, EDGE_LBL_MODULE "/%s/" "%cbrick" "/%s/%d", buffer, #endif #ifdef BRINGUP (board->brd_type == KLTYPE_IBRICK) ? 'I' : (board->brd_type == KLTYPE_PBRICK) ? 'P' : (board->brd_type == KLTYPE_XBRICK) ? 'X' : '?', #else toupper(MODULE_GET_BTCHAR(NODEPDA(cnode)->module_id)), #endif /* BRINGUP */ EDGE_LBL_XTALK, widgetnum); } DBG("io_xswitch_widget_init: path= %s\n", pathname); rc = hwgraph_path_add(hwgraph_root, pathname, &widgetv); ASSERT(rc == GRAPH_SUCCESS); /* This is needed to let the user programs to map the * module,slot numbers to the corresponding widget numbers * on the crossbow. */ rc = device_master_set(hwgraph_connectpt_get(widgetv), hubv); /* If we are looking at the global master io6 * then add information about the version of * the io6prom as a part of "detailed inventory" * information. */ if (is_master_baseio(nasid, NODEPDA(cnode)->module_id, #ifdef BRINGUP get_widget_slotnum(0,widgetnum))) { #else <<< BOMB! >>> Need a new way to get slot numbers on IP35/IP37 #endif extern void klhwg_baseio_inventory_add(devfs_handle_t, cnodeid_t); module = NODEPDA(cnode)->module_id; #ifdef XBRIDGE_REGS_SIM DBG("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: reading xwidget id: hardwired to xbridge (0x2d000049).\n"); if (XWIDGET_PART_NUM(0x2d000049)==XXBOW_WIDGET_PART_NUM) { #else if (nasid_has_xbridge(nasid)) { #endif /* XBRIDGE_REGS_SIM */ board = find_lboard_module( (lboard_t *)KL_CONFIG_INFO(nasid), module); /* * BRINGUP * Change iobrick to correct i/o brick */ #ifdef SUPPORT_PRINTING_M_FORMAT sprintf(pathname, EDGE_LBL_MODULE "/%M/" #else sprintf(pathname, EDGE_LBL_MODULE "/%x/" #endif "iobrick" "/%s/%d", NODEPDA(cnode)->module_id, EDGE_LBL_XTALK, widgetnum); } else { #ifdef BRINGUP slot = get_widget_slotnum(0, widgetnum); #else <<< BOMB! Need a new way to get slot numbers on IP35/IP37 #endif board = get_board_name(nasid, module, slot, new_name); /* * Create the vertex for the widget, * using the decimal * widgetnum as the name of the primary edge. */ #ifdef SUPPORT_PRINTING_M_FORMAT sprintf(pathname, EDGE_LBL_MODULE "/%M/" EDGE_LBL_SLOT "/%s/%s", NODEPDA(cnode)->module_id, slotname, new_name); #else memset(buffer, 0, 16); format_module_id(buffer, NODEPDA(cnode)->module_id, MODULE_FORMAT_BRIEF); sprintf(pathname, EDGE_LBL_MODULE "/%s/" EDGE_LBL_SLOT "/%s/%s", buffer, slotname, new_name); #endif } rc = hwgraph_path_add(hwgraph_root, pathname, &widgetv); DBG("io_xswitch_widget_init: (2) path= %s\n", pathname); /* * This is a weird ass code needed for error injection * purposes. */ rc = device_master_set(hwgraph_connectpt_get(widgetv), hubv); klhwg_baseio_inventory_add(widgetv,cnode); } sprintf(name, "%d", widgetnum); DBG("io_xswitch_widget_init: FIXME hwgraph_edge_add %s xswitchv 0x%p, widgetv 0x%p\n", name, xswitchv, widgetv); rc = hwgraph_edge_add(xswitchv, widgetv, name); /* * crosstalk switch code tracks which * widget is attached to each link. */ xswitch_info_vhdl_set(xswitch_info, widgetnum, widgetv); /* * Peek at the widget to get its crosstalk part and * mfgr numbers, then present it to the generic xtalk * bus provider to have its driver attach routine * called (or not). */ #ifdef XBRIDGE_REGS_SIM widget_id = 0x2d000049; DBG("io_xswitch_widget_init: XBRIDGE_REGS_SIM FIXME: id hardwired to widget_id\n"); #else widget_id = XWIDGET_ID_READ(nasid, widgetnum); #endif /* XBRIDGE_REGS_SIM */ hwid.part_num = XWIDGET_PART_NUM(widget_id); hwid.rev_num = XWIDGET_REV_NUM(widget_id); hwid.mfg_num = XWIDGET_MFG_NUM(widget_id); /* Store some inventory information about * the xwidget in the hardware graph. */ xwidget_inventory_add(widgetv,board,hwid); (void)xwidget_register(&hwid, widgetv, widgetnum, hubv, hub_widgetid, aa); #ifdef SN0_USE_BTE bte_bpush_war(cnode, (void *)board); #endif } } static void io_init_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnode) { xwidgetnum_t widgetnum; async_attach_t aa; aa = async_attach_new(); DBG("io_init_xswitch_widgets: xswitchv 0x%p for cnode %d\n", xswitchv, cnode); for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) { io_xswitch_widget_init(xswitchv, cnodeid_to_vertex(cnode), widgetnum, aa); } /* * Wait for parallel attach threads, if any, to complete. */ async_attach_waitall(aa); async_attach_free(aa); } /* * For each PCI bridge connected to the xswitch, add a link from the * board's klconfig info to the bridge's hwgraph vertex. This lets * the FRU analyzer find the bridge without traversing the hardware * graph and risking hangs. */ static void io_link_xswitch_widgets(devfs_handle_t xswitchv, cnodeid_t cnodeid) { xwidgetnum_t widgetnum; char pathname[128]; devfs_handle_t vhdl; nasid_t nasid, peer_nasid; lboard_t *board; /* And its connected hub's nasids */ nasid = COMPACT_TO_NASID_NODEID(cnodeid); peer_nasid = NODEPDA(cnodeid)->xbow_peer; /* * Look for paths matching "/pci" under xswitchv. * For every widget, init. its lboard's hwgraph link. If the * board has a PCI bridge, point the link to it. */ for (widgetnum = HUB_WIDGET_ID_MIN; widgetnum <= HUB_WIDGET_ID_MAX; widgetnum++) { sprintf(pathname, "%d", widgetnum); if (hwgraph_traverse(xswitchv, pathname, &vhdl) != GRAPH_SUCCESS) continue; #if defined (CONFIG_SGI_IP35) || defined (CONFIG_IA64_SGI_SN1) || defined (CONFIG_IA64_GENERIC) board = find_lboard_module((lboard_t *)KL_CONFIG_INFO(nasid), NODEPDA(cnodeid)->module_id); #else { slotid_t slot; slot = get_widget_slotnum(xbow_num, widgetnum); board = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(nasid), NODEPDA(cnodeid)->module_id, slot); } #endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */ if (board == NULL && peer_nasid != INVALID_NASID) { /* * Try to find the board on our peer */ #if defined (CONFIG_SGI_IP35) || defined (CONFIG_IA64_SGI_SN1) || defined (CONFIG_IA64_GENERIC) board = find_lboard_module( (lboard_t *)KL_CONFIG_INFO(peer_nasid), NODEPDA(cnodeid)->module_id); #else board = find_lboard_modslot((lboard_t *)KL_CONFIG_INFO(peer_nasid), NODEPDA(cnodeid)->module_id, slot); #endif /* CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 */ } if (board == NULL) { #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("Could not find PROM info for vertex %v, " "FRU analyzer may fail", vhdl); #else PRINT_WARNING("Could not find PROM info for vertex 0x%x, " "FRU analyzer may fail", vhdl); #endif return; } sprintf(pathname, "%d/"EDGE_LBL_PCI, widgetnum); if (hwgraph_traverse(xswitchv, pathname, &vhdl) == GRAPH_SUCCESS) board->brd_graph_link = vhdl; else board->brd_graph_link = GRAPH_VERTEX_NONE; } } /* * Initialize all I/O on the specified node. */ static void io_init_node(cnodeid_t cnodeid) { /*REFERENCED*/ devfs_handle_t hubv, switchv, widgetv; struct xwidget_hwid_s hwid; hubinfo_t hubinfo; int is_xswitch; nodepda_t *npdap; struct semaphore *peer_sema = 0; uint32_t widget_partnum; nodepda_router_info_t *npda_rip; cpu_cookie_t c = 0; extern int hubdev_docallouts(devfs_handle_t); #ifdef LATER /* Try to execute on the node that we're initializing. */ c = setnoderun(cnodeid); #endif npdap = NODEPDA(cnodeid); /* * Get the "top" vertex for this node's hardware * graph; it will carry the per-hub hub-specific * data, and act as the crosstalk provider master. * It's canonical path is probably something of the * form /hw/module/%M/slot/%d/node */ hubv = cnodeid_to_vertex(cnodeid); DBG("io_init_node: Initialize IO for cnode %d hubv(node) 0x%p npdap 0x%p\n", cnodeid, hubv, npdap); ASSERT(hubv != GRAPH_VERTEX_NONE); hubdev_docallouts(hubv); /* * Set up the dependent routers if we have any. */ npda_rip = npdap->npda_rip_first; while(npda_rip) { /* If the router info has not been initialized * then we need to do the router initialization */ if (!npda_rip->router_infop) { router_init(cnodeid,0,npda_rip); } npda_rip = npda_rip->router_next; } /* * Read mfg info on this hub */ #ifdef LATER printk("io_init_node: FIXME need to implement HUB_VERTEX_MFG_INFO\n"); HUB_VERTEX_MFG_INFO(hubv); #endif /* LATER */ /* * If nothing connected to this hub's xtalk port, we're done. */ early_probe_for_widget(hubv, &hwid); if (hwid.part_num == XWIDGET_PART_NUM_NONE) { #ifdef PROBE_TEST if ((cnodeid == 1) || (cnodeid == 2)) { int index; for (index = 0; index < 600; index++) DBG("Interfering with device probing!!!\n"); } #endif /* io_init_done takes cpu cookie as 2nd argument * to do a restorenoderun for the setnoderun done * at the start of this thread */ DBG("**** io_init_node: Node's 0x%p hub widget has XWIDGET_PART_NUM_NONE ****\n", hubv); return; /* NOTREACHED */ } /* * attach our hub_provider information to hubv, * so we can use it as a crosstalk provider "master" * vertex. */ xtalk_provider_register(hubv, &hub_provider); xtalk_provider_startup(hubv); /* * Create a vertex to represent the crosstalk bus * attached to this hub, and a vertex to be used * as the connect point for whatever is out there * on the other side of our crosstalk connection. * * Crosstalk Switch drivers "climb up" from their * connection point to try and take over the switch * point. * * Of course, the edges and verticies may already * exist, in which case our net effect is just to * associate the "xtalk_" driver with the connection * point for the device. */ (void)hwgraph_path_add(hubv, EDGE_LBL_XTALK, &switchv); DBG("io_init_node: Created 'xtalk' entry to '../node/' xtalk vertex 0x%p\n", switchv); ASSERT(switchv != GRAPH_VERTEX_NONE); (void)hwgraph_edge_add(hubv, switchv, EDGE_LBL_IO); DBG("io_init_node: Created symlink 'io' from ../node/io to ../node/xtalk \n"); /* * We need to find the widget id and update the basew_id field * accordingly. In particular, SN00 has direct connected bridge, * and hence widget id is Not 0. */ widget_partnum = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + WIDGET_ID))) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT; if (widget_partnum == BRIDGE_WIDGET_PART_NUM || widget_partnum == XBRIDGE_WIDGET_PART_NUM){ npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID); DBG("io_init_node: Found XBRIDGE widget_partnum= 0x%x\n", widget_partnum); } else if (widget_partnum == XBOW_WIDGET_PART_NUM || widget_partnum == XXBOW_WIDGET_PART_NUM) { /* * Xbow control register does not have the widget ID field. * So, hard code the widget ID to be zero. */ DBG("io_init_node: Found XBOW widget_partnum= 0x%x\n", widget_partnum); npdap->basew_id = 0; #if defined(BRINGUP) } else if (widget_partnum == XG_WIDGET_PART_NUM) { /* * OK, WTF do we do here if we have an XG direct connected to a HUB/Bedrock??? * So, hard code the widget ID to be zero? */ npdap->basew_id = 0; npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID); #endif } else { npdap->basew_id = (((*(volatile int32_t *)(NODE_SWIN_BASE(COMPACT_TO_NASID_NODEID(cnodeid), 0) + BRIDGE_WID_CONTROL))) & WIDGET_WIDGET_ID); panic(" ****io_init_node: Unknown Widget Part Number 0x%x Widgt ID 0x%x attached to Hubv 0x%p ****\n", widget_partnum, npdap->basew_id, hubv); /*NOTREACHED*/ } { char widname[10]; sprintf(widname, "%x", npdap->basew_id); (void)hwgraph_path_add(switchv, widname, &widgetv); DBG("io_init_node: Created '%s' to '..node/xtalk/' vertex 0x%p\n", widname, widgetv); ASSERT(widgetv != GRAPH_VERTEX_NONE); } nodepda->basew_xc = widgetv; is_xswitch = xwidget_hwid_is_xswitch(&hwid); /* * Try to become the master of the widget. If this is an xswitch * with multiple hubs connected, only one will succeed. Mastership * of an xswitch is used only when touching registers on that xswitch. * The slave xwidgets connected to the xswitch can be owned by various * masters. */ if (device_master_set(widgetv, hubv) == 0) { /* Only one hub (thread) per Crosstalk device or switch makes * it to here. */ /* * Initialize whatever xwidget is hanging off our hub. * Whatever it is, it's accessible through widgetnum 0. */ hubinfo_get(hubv, &hubinfo); (void)xwidget_register(&hwid, widgetv, npdap->basew_id, hubv, hubinfo->h_widgetid, NULL); if (!is_xswitch) { /* io_init_done takes cpu cookie as 2nd argument * to do a restorenoderun for the setnoderun done * at the start of this thread */ io_init_done(cnodeid,c); /* NOTREACHED */ } /* * Special handling for Crosstalk Switches (e.g. xbow). * We need to do things in roughly the following order: * 1) Initialize xswitch hardware (done above) * 2) Determine which hubs are available to be widget masters * 3) Discover which links are active from the xswitch * 4) Assign xwidgets hanging off the xswitch to hubs * 5) Initialize all xwidgets on the xswitch */ volunteer_for_widgets(switchv, hubv); /* If there's someone else on this crossbow, recognize him */ if (npdap->xbow_peer != INVALID_NASID) { nodepda_t *peer_npdap = NODEPDA(NASID_TO_COMPACT_NODEID(npdap->xbow_peer)); peer_sema = &peer_npdap->xbow_sema; volunteer_for_widgets(switchv, peer_npdap->node_vertex); } assign_widgets_to_volunteers(switchv, hubv); /* Signal that we're done */ if (peer_sema) { mutex_unlock(peer_sema); } } else { /* Wait 'til master is done assigning widgets. */ mutex_lock(&npdap->xbow_sema); } #ifdef PROBE_TEST if ((cnodeid == 1) || (cnodeid == 2)) { int index; for (index = 0; index < 500; index++) DBG("Interfering with device probing!!!\n"); } #endif /* Now both nodes can safely inititialize widgets */ io_init_xswitch_widgets(switchv, cnodeid); io_link_xswitch_widgets(switchv, cnodeid); /* io_init_done takes cpu cookie as 2nd argument * to do a restorenoderun for the setnoderun done * at the start of this thread */ io_init_done(cnodeid,c); DBG("\nio_init_node: DONE INITIALIZED ALL I/O FOR CNODEID %d\n\n", cnodeid); } #define IOINIT_STKSZ (16 * 1024) #define __DEVSTR1 "/../.master/" #define __DEVSTR2 "/target/" #define __DEVSTR3 "/lun/0/disk/partition/" #define __DEVSTR4 "/../ef" #if CONFIG_SGI_IP35 || CONFIG_IA64_SGI_SN1 || CONFIG_IA64_GENERIC /* * Currently, we need to allow for 5 IBrick slots with 1 FC each * plus an internal 1394. * * ioconfig starts numbering SCSI's at NUM_BASE_IO_SCSI_CTLR. */ #define NUM_BASE_IO_SCSI_CTLR 6 #endif /* * This tells ioconfig where it can start numbering scsi controllers. * Below this base number, platform-specific handles the numbering. * XXX Irix legacy..controller numbering should be part of devfsd's job */ int num_base_io_scsi_ctlr = 2; /* used by syssgi */ devfs_handle_t base_io_scsi_ctlr_vhdl[NUM_BASE_IO_SCSI_CTLR]; static devfs_handle_t baseio_enet_vhdl,baseio_console_vhdl; /* * Put the logical controller number information in the * scsi controller vertices for each scsi controller that * is in a "fixed position". */ static void scsi_ctlr_nums_add(devfs_handle_t pci_vhdl) { { int i; num_base_io_scsi_ctlr = NUM_BASE_IO_SCSI_CTLR; /* Initialize base_io_scsi_ctlr_vhdl array */ for (i=0; i extern devfs_handle_t ioc3_console_vhdl_get(void); devfs_handle_t sys_critical_graph_root = GRAPH_VERTEX_NONE; /* Define the system critical vertices and connect them through * a canonical parent-child relationships for easy traversal * during io error handling. */ static void sys_critical_graph_init(void) { devfs_handle_t bridge_vhdl,master_node_vhdl; devfs_handle_t xbow_vhdl = GRAPH_VERTEX_NONE; extern devfs_handle_t hwgraph_root; devfs_handle_t pci_slot_conn; int slot; devfs_handle_t baseio_console_conn; DBG("sys_critical_graph_init: FIXME.\n"); baseio_console_conn = hwgraph_connectpt_get(baseio_console_vhdl); if (baseio_console_conn == NULL) { return; } /* Get the vertex handle for the baseio bridge */ bridge_vhdl = device_master_get(baseio_console_conn); /* Get the master node of the baseio card */ master_node_vhdl = cnodeid_to_vertex( master_node_get(baseio_console_vhdl)); /* Add the "root->node" part of the system critical graph */ sys_critical_graph_vertex_add(hwgraph_root,master_node_vhdl); /* Check if we have a crossbow */ if (hwgraph_traverse(master_node_vhdl, EDGE_LBL_XTALK"/0", &xbow_vhdl) == GRAPH_SUCCESS) { /* We have a crossbow.Add "node->xbow" part of the system * critical graph. */ sys_critical_graph_vertex_add(master_node_vhdl,xbow_vhdl); /* Add "xbow->baseio bridge" of the system critical graph */ sys_critical_graph_vertex_add(xbow_vhdl,bridge_vhdl); hwgraph_vertex_unref(xbow_vhdl); } else /* We donot have a crossbow. Add "node->baseio_bridge" * part of the system critical graph. */ sys_critical_graph_vertex_add(master_node_vhdl,bridge_vhdl); /* Add all the populated PCI slot vertices to the system critical * graph with the bridge vertex as the parent. */ for (slot = 0 ; slot < 8; slot++) { char slot_edge[10]; sprintf(slot_edge,"%d",slot); if (hwgraph_traverse(bridge_vhdl,slot_edge, &pci_slot_conn) != GRAPH_SUCCESS) continue; sys_critical_graph_vertex_add(bridge_vhdl,pci_slot_conn); hwgraph_vertex_unref(pci_slot_conn); } hwgraph_vertex_unref(bridge_vhdl); /* Add the "ioc3 pci connection point -> console ioc3" part * of the system critical graph */ if (hwgraph_traverse(baseio_console_vhdl,"..",&pci_slot_conn) == GRAPH_SUCCESS) { sys_critical_graph_vertex_add(pci_slot_conn, baseio_console_vhdl); hwgraph_vertex_unref(pci_slot_conn); } /* Add the "ethernet pci connection point -> base ethernet" part of * the system critical graph */ if (hwgraph_traverse(baseio_enet_vhdl,"..",&pci_slot_conn) == GRAPH_SUCCESS) { sys_critical_graph_vertex_add(pci_slot_conn, baseio_enet_vhdl); hwgraph_vertex_unref(pci_slot_conn); } /* Add the "scsi controller pci connection point -> base scsi * controller" part of the system critical graph */ if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[0], "../..",&pci_slot_conn) == GRAPH_SUCCESS) { sys_critical_graph_vertex_add(pci_slot_conn, base_io_scsi_ctlr_vhdl[0]); hwgraph_vertex_unref(pci_slot_conn); } if (hwgraph_traverse(base_io_scsi_ctlr_vhdl[1], "../..",&pci_slot_conn) == GRAPH_SUCCESS) { sys_critical_graph_vertex_add(pci_slot_conn, base_io_scsi_ctlr_vhdl[1]); hwgraph_vertex_unref(pci_slot_conn); } hwgraph_vertex_unref(baseio_console_conn); } static void baseio_ctlr_num_set(void) { char name[MAXDEVNAME]; devfs_handle_t console_vhdl, pci_vhdl, enet_vhdl; DBG("baseio_ctlr_num_set; FIXME\n"); console_vhdl = ioc3_console_vhdl_get(); if (console_vhdl == GRAPH_VERTEX_NONE) return; /* Useful for setting up the system critical graph */ baseio_console_vhdl = console_vhdl; vertex_to_name(console_vhdl,name,MAXDEVNAME); strcat(name,__DEVSTR1); pci_vhdl = hwgraph_path_to_vertex(name); scsi_ctlr_nums_add(pci_vhdl); /* Unref the pci_vhdl due to the reference by hwgraph_path_to_vertex */ hwgraph_vertex_unref(pci_vhdl); vertex_to_name(console_vhdl, name, MAXDEVNAME); strcat(name, __DEVSTR4); enet_vhdl = hwgraph_path_to_vertex(name); /* Useful for setting up the system critical graph */ baseio_enet_vhdl = enet_vhdl; device_controller_num_set(enet_vhdl, 0); /* Unref the enet_vhdl due to the reference by hwgraph_path_to_vertex */ hwgraph_vertex_unref(enet_vhdl); } /* #endif */ void sn00_rrb_alloc(devfs_handle_t vhdl, int *vendor_list) { /* REFERENCED */ int rtn_val; /* ** sn00 population: errb orrb ** 0- ql 3+? ** 1- ql 2 ** 2- ioc3 ethernet 2+? ** 3- ioc3 secondary 1 ** 4- 0 ** 5- PCI slot ** 6- PCI slot ** 7- PCI slot */ /* The following code implements this heuristic for getting * maximum usage out of the rrbs * * constraints: * 8 bit ql1 needs 1+1 * ql0 or ql5,6,7 wants 1+2 * ethernet wants 2 or more * * rules for even rrbs: * if nothing in slot 6 * 4 rrbs to 0 and 2 (0xc8889999) * else * 3 2 3 to slots 0 2 6 (0xc8899bbb) * * rules for odd rrbs * if nothing in slot 5 or 7 (0xc8889999) * 4 rrbs to 1 and 3 * else if 1 thing in 5 or 7 (0xc8899aaa) or (0xc8899bbb) * 3 2 3 to slots 1 3 5|7 * else * 2 1 3 2 to slots 1 3 5 7 (note: if there's a ql card in 7 this * (0xc89aaabb) may short what it wants therefore the * rule should be to plug pci slots in order) */ if (vendor_list[6] != PCIIO_VENDOR_ID_NONE) { /* something in slot 6 */ rtn_val = pcibr_alloc_all_rrbs(vhdl, 0, 3,1, 2,0, 0,0, 3,0); } else { rtn_val = pcibr_alloc_all_rrbs(vhdl, 0, 4,1, 4,0, 0,0, 0,0); } if (rtn_val) PRINT_WARNING("sn00_rrb_alloc: pcibr_alloc_all_rrbs failed"); if ((vendor_list[5] != PCIIO_VENDOR_ID_NONE) && (vendor_list[7] != PCIIO_VENDOR_ID_NONE)) { /* soemthing in slot 5 and 7 */ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 2,1, 1,0, 3,0, 2,0); } else if (vendor_list[5] != PCIIO_VENDOR_ID_NONE) { /* soemthing in slot 5 but not 7 */ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 3,1, 2,0, 3,0, 0,0); } else if (vendor_list[7] != PCIIO_VENDOR_ID_NONE) { /* soemthing in slot 7 but not 5 */ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 3,1, 2,0, 0,0, 3,0); } else { /* nothing in slot 5 or 7 */ rtn_val = pcibr_alloc_all_rrbs(vhdl, 1, 4,1, 4,0, 0,0, 0,0); } if (rtn_val) PRINT_WARNING("sn00_rrb_alloc: pcibr_alloc_all_rrbs failed"); } /* * Initialize all I/O devices. Starting closest to nodes, probe and * initialize outward. */ void init_all_devices(void) { /* Governor on init threads..bump up when safe * (beware many devfs races) */ #ifdef LATER int io_init_node_threads = 2; #endif cnodeid_t cnodeid, active; #ifdef LINUX_KERNEL_THREADS sema_init(&io_init_sema, 0); #endif active = 0; for (cnodeid = 0; cnodeid < maxnodes; cnodeid++) { #ifdef LINUX_KERNEL_THREADS char thread_name[16]; extern int io_init_pri; /* * Spawn a service thread for each node to initialize all * I/O on that node. Each thread attempts to bind itself * to the node whose I/O it's initializing. */ sprintf(thread_name, "IO_init[%d]", cnodeid); (void)sthread_create(thread_name, 0, IOINIT_STKSZ, 0, io_init_pri, KT_PS, (st_func_t *)io_init_node, (void *)(long)cnodeid, 0, 0, 0); #else DBG("init_all_devices: Calling io_init_node() for cnode %d\n", cnodeid); io_init_node(cnodeid); DBG("init_all_devices: Done io_init_node() for cnode %d\n", cnodeid); #endif /* LINUX_KERNEL_THREADS */ #ifdef LINUX_KERNEL_THREADS /* Limit how many nodes go at once, to not overload hwgraph */ /* TBD: Should timeout */ DBG("started thread for cnode %d\n", cnodeid); active++; if (io_init_node_threads && active >= io_init_node_threads) { down(&io_init_sema); active--; } #endif /* LINUX_KERNEL_THREADS */ } #ifdef LINUX_KERNEL_THREADS /* Wait until all IO_init threads are done */ while (active > 0) { #ifdef AA_DEBUG DBG("waiting, %d still active\n", active); #endif down(&io_init_sema); active--; } #endif /* LINUX_KERNEL_THREADS */ for (cnodeid = 0; cnodeid < maxnodes; cnodeid++) /* * Update information generated by IO init. */ update_node_information(cnodeid); baseio_ctlr_num_set(); /* Setup the system critical graph (which is a subgraph of the * main hwgraph). This information is useful during io error * handling. */ sys_critical_graph_init(); #if HWG_PRINT hwgraph_print(); #endif } #define toint(x) ((int)(x) - (int)('0')) void devnamefromarcs(char *devnm) { int val; char tmpnm[MAXDEVNAME]; char *tmp1, *tmp2; val = strncmp(devnm, "dks", 3); if (val != 0) return; tmp1 = devnm + 3; if (!isdigit(*tmp1)) return; val = 0; while (isdigit(*tmp1)) { val = 10*val+toint(*tmp1); tmp1++; } if(*tmp1 != 'd') return; else tmp1++; if ((val < 0) || (val >= NUM_BASE_IO_SCSI_CTLR)) { int i; int viable_found = 0; DBG("Only controller numbers 0..%d are supported for\n", NUM_BASE_IO_SCSI_CTLR-1); DBG("prom \"root\" variables of the form dksXdXsX.\n"); DBG("To use another disk you must use the full hardware graph path\n\n"); DBG("Possible controller numbers for use in 'dksXdXsX' on this system: "); for (i=0; i XBOW_PORT_F) return 0; /* Find "brick" in the path name */ bp = strstr(hw_path_name, "brick"); if (bp == NULL) return 0; /* Find preceding slash */ sp = bp; while (sp > hw_path_name) { sp--; if (*sp == '/') break; } /* Invalid if no preceding slash */ if (!sp) return 0; /* Bump slash pointer to "brick" prefix */ sp++; /* * Verify "brick" prefix length; valid exaples: * 'I' from "/Ibrick" * 'P' from "/Pbrick" * 'X' from "/Xbrick" */ if ((bp - sp) != 1) return 0; return (io_brick_map_widget(*sp, widget_num)); }