/* $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 xtalk_provider_t hub_provider; /* * Perform any initializations needed to support hub-based I/O. * Called once during startup. */ void hubio_init(void) { #ifdef LATER /* This isn't needed unless we port the entire sio driver ... */ extern void early_brl1_port_init( void ); early_brl1_port_init(); #endif } /* * Implementation of hub iobus operations. * * Hub provides a crosstalk "iobus" on IP27 systems. These routines * provide a platform-specific implementation of xtalk used by all xtalk * cards on IP27 systems. * * Called from corresponding xtalk_* routines. */ /* PIO MANAGEMENT */ /* For mapping system virtual address space to xtalk space on a specified widget */ /* * Setup pio structures needed for a particular hub. */ static void hub_pio_init(devfs_handle_t hubv) { xwidgetnum_t widget; hubinfo_t hubinfo; nasid_t nasid; int bigwin; hub_piomap_t hub_piomap; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; /* Initialize small window piomaps for this hub */ for (widget=0; widget <= HUB_WIDGET_ID_MAX; widget++) { hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget); hub_piomap->hpio_xtalk_info.xp_target = widget; hub_piomap->hpio_xtalk_info.xp_xtalk_addr = 0; hub_piomap->hpio_xtalk_info.xp_mapsz = SWIN_SIZE; hub_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_SWIN_BASE(nasid, widget); hub_piomap->hpio_hub = hubv; hub_piomap->hpio_flags = HUB_PIOMAP_IS_VALID; } /* Initialize big window piomaps for this hub */ for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) { hub_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin); hub_piomap->hpio_xtalk_info.xp_mapsz = BWIN_SIZE; hub_piomap->hpio_hub = hubv; hub_piomap->hpio_holdcnt = 0; hub_piomap->hpio_flags = HUB_PIOMAP_IS_BIGWINDOW; IIO_ITTE_DISABLE(nasid, bigwin); } hub_set_piomode(nasid, HUB_PIO_CONVEYOR); mutex_spinlock_init(&hubinfo->h_bwlock); /* * If this lock can be acquired from interrupts or bh's, add SV_INTS or SV_BHS, * respectively, to the flags here. */ sv_init(&hubinfo->h_bwwait, &hubinfo->h_bwlock, SV_ORDER_FIFO | SV_MON_SPIN); } /* * Create a caddr_t-to-xtalk_addr mapping. * * Use a small window if possible (that's the usual case), but * manage big windows if needed. Big window mappings can be * either FIXED or UNFIXED -- we keep at least 1 big window available * for UNFIXED mappings. * * Returns an opaque pointer-sized type which can be passed to * other hub_pio_* routines on success, or NULL if the request * cannot be satisfied. */ /* ARGSUSED */ hub_piomap_t hub_piomap_alloc(devfs_handle_t dev, /* set up mapping for this device */ device_desc_t dev_desc, /* device descriptor */ iopaddr_t xtalk_addr, /* map for this xtalk_addr range */ size_t byte_count, size_t byte_count_max, /* maximum size of a mapping */ unsigned flags) /* defined in sys/pio.h */ { xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); hubinfo_t hubinfo; hub_piomap_t bw_piomap; int bigwin, free_bw_index; nasid_t nasid; volatile hubreg_t junk; unsigned long s; /* sanity check */ if (byte_count_max > byte_count) return(NULL); hubinfo_get(hubv, &hubinfo); /* If xtalk_addr range is mapped by a small window, we don't have * to do much */ if (xtalk_addr + byte_count <= SWIN_SIZE) return(hubinfo_swin_piomap_get(hubinfo, (int)widget)); /* We need to use a big window mapping. */ /* * TBD: Allow requests that would consume multiple big windows -- * split the request up and use multiple mapping entries. * For now, reject requests that span big windows. */ if ((xtalk_addr % BWIN_SIZE) + byte_count > BWIN_SIZE) return(NULL); /* Round xtalk address down for big window alignement */ xtalk_addr = xtalk_addr & ~(BWIN_SIZE-1); /* * Check to see if an existing big window mapping will suffice. */ tryagain: free_bw_index = -1; s = mutex_spinlock(&hubinfo->h_bwlock); for (bigwin=0; bigwin < HUB_NUM_BIG_WINDOW; bigwin++) { bw_piomap = hubinfo_bwin_piomap_get(hubinfo, bigwin); /* If mapping is not valid, skip it */ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_VALID)) { free_bw_index = bigwin; continue; } /* * If mapping is UNFIXED, skip it. We don't allow sharing * of UNFIXED mappings, because this would allow starvation. */ if (!(bw_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED)) continue; if ( xtalk_addr == bw_piomap->hpio_xtalk_info.xp_xtalk_addr && widget == bw_piomap->hpio_xtalk_info.xp_target) { bw_piomap->hpio_holdcnt++; mutex_spinunlock(&hubinfo->h_bwlock, s); return(bw_piomap); } } /* * None of the existing big window mappings will work for us -- * we need to establish a new mapping. */ /* Insure that we don't consume all big windows with FIXED mappings */ if (flags & PIOMAP_FIXED) { if (hubinfo->h_num_big_window_fixed < HUB_NUM_BIG_WINDOW-1) { ASSERT(free_bw_index >= 0); hubinfo->h_num_big_window_fixed++; } else { bw_piomap = NULL; goto done; } } else /* PIOMAP_UNFIXED */ { if (free_bw_index < 0) { if (flags & PIOMAP_NOSLEEP) { bw_piomap = NULL; goto done; } sv_wait(&hubinfo->h_bwwait, 0, 0); goto tryagain; } } /* OK! Allocate big window free_bw_index for this mapping. */ /* * The code below does a PIO write to setup an ITTE entry. * We need to prevent other CPUs from seeing our updated memory * shadow of the ITTE (in the piomap) until the ITTE entry is * actually set up; otherwise, another CPU might attempt a PIO * prematurely. * * Also, the only way we can know that an entry has been received * by the hub and can be used by future PIO reads/writes is by * reading back the ITTE entry after writing it. * * For these two reasons, we PIO read back the ITTE entry after * we write it. */ nasid = hubinfo->h_nasid; IIO_ITTE_PUT(nasid, free_bw_index, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr); junk = HUB_L(IIO_ITTE_GET(nasid, free_bw_index)); bw_piomap = hubinfo_bwin_piomap_get(hubinfo, free_bw_index); bw_piomap->hpio_xtalk_info.xp_dev = dev; bw_piomap->hpio_xtalk_info.xp_target = widget; bw_piomap->hpio_xtalk_info.xp_xtalk_addr = xtalk_addr; bw_piomap->hpio_xtalk_info.xp_kvaddr = (caddr_t)NODE_BWIN_BASE(nasid, free_bw_index); bw_piomap->hpio_holdcnt++; bw_piomap->hpio_bigwin_num = free_bw_index; if (flags & PIOMAP_FIXED) bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED; else bw_piomap->hpio_flags |= HUB_PIOMAP_IS_VALID; done: mutex_spinunlock(&hubinfo->h_bwlock, s); return(bw_piomap); } /* * hub_piomap_free destroys a caddr_t-to-xtalk pio mapping and frees * any associated mapping resources. * * If this * piomap was handled with a small window, or if it was handled * in a big window that's still in use by someone else, then there's * nothing to do. On the other hand, if this mapping was handled * with a big window, AND if we were the final user of that mapping, * then destroy the mapping. */ void hub_piomap_free(hub_piomap_t hub_piomap) { devfs_handle_t hubv; hubinfo_t hubinfo; nasid_t nasid; unsigned long s; /* * Small windows are permanently mapped to corresponding widgets, * so there're no resources to free. */ if (!(hub_piomap->hpio_flags & HUB_PIOMAP_IS_BIGWINDOW)) return; ASSERT(hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID); ASSERT(hub_piomap->hpio_holdcnt > 0); hubv = hub_piomap->hpio_hub; hubinfo_get(hubv, &hubinfo); nasid = hubinfo->h_nasid; s = mutex_spinlock(&hubinfo->h_bwlock); /* * If this is the last hold on this mapping, free it. */ if (--hub_piomap->hpio_holdcnt == 0) { IIO_ITTE_DISABLE(nasid, hub_piomap->hpio_bigwin_num ); if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_FIXED) { hub_piomap->hpio_flags &= ~(HUB_PIOMAP_IS_VALID | HUB_PIOMAP_IS_FIXED); hubinfo->h_num_big_window_fixed--; ASSERT(hubinfo->h_num_big_window_fixed >= 0); } else hub_piomap->hpio_flags &= ~HUB_PIOMAP_IS_VALID; (void)sv_signal(&hubinfo->h_bwwait); } mutex_spinunlock(&hubinfo->h_bwlock, s); } /* * Establish a mapping to a given xtalk address range using the resources * allocated earlier. */ caddr_t hub_piomap_addr(hub_piomap_t hub_piomap, /* mapping resources */ iopaddr_t xtalk_addr, /* map for this xtalk address */ size_t byte_count) /* map this many bytes */ { /* Verify that range can be mapped using the specified piomap */ if (xtalk_addr < hub_piomap->hpio_xtalk_info.xp_xtalk_addr) return(0); if (xtalk_addr + byte_count > ( hub_piomap->hpio_xtalk_info.xp_xtalk_addr + hub_piomap->hpio_xtalk_info.xp_mapsz)) return(0); if (hub_piomap->hpio_flags & HUB_PIOMAP_IS_VALID) return(hub_piomap->hpio_xtalk_info.xp_kvaddr + (xtalk_addr % hub_piomap->hpio_xtalk_info.xp_mapsz)); else return(0); } /* * Driver indicates that it's done with PIO's from an earlier piomap_addr. */ /* ARGSUSED */ void hub_piomap_done(hub_piomap_t hub_piomap) /* done with these mapping resources */ { /* Nothing to do */ } /* * For translations that require no mapping resources, supply a kernel virtual * address that maps to the specified xtalk address range. */ /* ARGSUSED */ caddr_t hub_piotrans_addr( devfs_handle_t dev, /* translate to this device */ device_desc_t dev_desc, /* device descriptor */ iopaddr_t xtalk_addr, /* Crosstalk address */ size_t byte_count, /* map this many bytes */ unsigned flags) /* (currently unused) */ { xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); hub_piomap_t hub_piomap; hubinfo_t hubinfo; hubinfo_get(hubv, &hubinfo); if (xtalk_addr + byte_count <= SWIN_SIZE) { hub_piomap = hubinfo_swin_piomap_get(hubinfo, (int)widget); return(hub_piomap_addr(hub_piomap, xtalk_addr, byte_count)); } else return(0); } /* DMA MANAGEMENT */ /* Mapping from crosstalk space to system physical space */ /* * There's not really very much to do here, since crosstalk maps * directly to system physical space. It's quite possible that this * DMA layer will be bypassed in performance kernels. */ /* ARGSUSED */ static void hub_dma_init(devfs_handle_t hubv) { } /* * Allocate resources needed to set up DMA mappings up to a specified size * on a specified adapter. * * We don't actually use the adapter ID for anything. It's just the adapter * that the lower level driver plans to use for DMA. */ /* ARGSUSED */ hub_dmamap_t hub_dmamap_alloc( devfs_handle_t dev, /* set up mappings for this device */ device_desc_t dev_desc, /* device descriptor */ size_t byte_count_max, /* max size of a mapping */ unsigned flags) /* defined in dma.h */ { hub_dmamap_t dmamap; xwidget_info_t widget_info = xwidget_info_get(dev); xwidgetnum_t widget = xwidget_info_id_get(widget_info); devfs_handle_t hubv = xwidget_info_master_get(widget_info); dmamap = kern_malloc(sizeof(struct hub_dmamap_s)); dmamap->hdma_xtalk_info.xd_dev = dev; dmamap->hdma_xtalk_info.xd_target = widget; dmamap->hdma_hub = hubv; dmamap->hdma_flags = HUB_DMAMAP_IS_VALID; if (flags & XTALK_FIXED) dmamap->hdma_flags |= HUB_DMAMAP_IS_FIXED; return(dmamap); } /* * Destroy a DMA mapping from crosstalk space to system address space. * There is no actual mapping hardware to destroy, but we at least mark * the dmamap INVALID and free the space that it took. */ void hub_dmamap_free(hub_dmamap_t hub_dmamap) { hub_dmamap->hdma_flags &= ~HUB_DMAMAP_IS_VALID; kern_free(hub_dmamap); } /* * Establish a DMA mapping using the resources allocated in a previous dmamap_alloc. * Return an appropriate crosstalk address range that maps to the specified physical * address range. */ /* ARGSUSED */ extern iopaddr_t hub_dmamap_addr( hub_dmamap_t dmamap, /* use these mapping resources */ paddr_t paddr, /* map for this address */ size_t byte_count) /* map this many bytes */ { devfs_handle_t vhdl; ASSERT(dmamap->hdma_flags & HUB_DMAMAP_IS_VALID); if (dmamap->hdma_flags & HUB_DMAMAP_USED) { /* If the map is FIXED, re-use is OK. */ if (!(dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) { vhdl = dmamap->hdma_xtalk_info.xd_dev; #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("%v: hub_dmamap_addr re-uses dmamap.\n",vhdl); #else PRINT_WARNING("0x%x: hub_dmamap_addr re-uses dmamap.\n", vhdl); #endif } } else { dmamap->hdma_flags |= HUB_DMAMAP_USED; } /* There isn't actually any DMA mapping hardware on the hub. */ return(paddr); } /* * Establish a DMA mapping using the resources allocated in a previous dmamap_alloc. * Return an appropriate crosstalk address list that maps to the specified physical * address list. */ /* ARGSUSED */ alenlist_t hub_dmamap_list(hub_dmamap_t hub_dmamap, /* use these mapping resources */ alenlist_t palenlist, /* map this area of memory */ unsigned flags) { devfs_handle_t vhdl; ASSERT(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_VALID); if (hub_dmamap->hdma_flags & HUB_DMAMAP_USED) { /* If the map is FIXED, re-use is OK. */ if (!(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) { vhdl = hub_dmamap->hdma_xtalk_info.xd_dev; #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("%v: hub_dmamap_list re-uses dmamap\n",vhdl); #else PRINT_WARNING("0x%x: hub_dmamap_list re-uses dmamap\n", vhdl); #endif } } else { hub_dmamap->hdma_flags |= HUB_DMAMAP_USED; } /* There isn't actually any DMA mapping hardware on the hub. */ return(palenlist); } /* * Driver indicates that it has completed whatever DMA it may have started * after an earlier dmamap_addr or dmamap_list call. */ void hub_dmamap_done(hub_dmamap_t hub_dmamap) /* done with these mapping resources */ { devfs_handle_t vhdl; if (hub_dmamap->hdma_flags & HUB_DMAMAP_USED) { hub_dmamap->hdma_flags &= ~HUB_DMAMAP_USED; } else { /* If the map is FIXED, re-done is OK. */ if (!(hub_dmamap->hdma_flags & HUB_DMAMAP_IS_FIXED)) { vhdl = hub_dmamap->hdma_xtalk_info.xd_dev; #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("%v: hub_dmamap_done already done with dmamap\n",vhdl); #else PRINT_WARNING("0x%x: hub_dmamap_done already done with dmamap\n", vhdl); #endif } } } /* * Translate a single system physical address into a crosstalk address. */ /* ARGSUSED */ iopaddr_t hub_dmatrans_addr( devfs_handle_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ paddr_t paddr, /* system physical address */ size_t byte_count, /* length */ unsigned flags) /* defined in dma.h */ { /* no translation needed */ return(paddr); } /* * Translate a list of IP27 addresses and lengths into a list of crosstalk * addresses and lengths. No actual hardware mapping takes place; the hub * has no DMA mapping registers -- crosstalk addresses map directly. */ /* ARGSUSED */ alenlist_t hub_dmatrans_list( devfs_handle_t dev, /* translate for this device */ device_desc_t dev_desc, /* device descriptor */ alenlist_t palenlist, /* system address/length list */ unsigned flags) /* defined in dma.h */ { /* no translation needed */ return(palenlist); } /*ARGSUSED*/ void hub_dmamap_drain( hub_dmamap_t map) { /* XXX- flush caches, if cache coherency WAR is needed */ } /*ARGSUSED*/ void hub_dmaaddr_drain( devfs_handle_t vhdl, paddr_t addr, size_t bytes) { /* XXX- flush caches, if cache coherency WAR is needed */ } /*ARGSUSED*/ void hub_dmalist_drain( devfs_handle_t vhdl, alenlist_t list) { /* XXX- flush caches, if cache coherency WAR is needed */ } /* INTERRUPT MANAGEMENT */ /* ARGSUSED */ static void hub_intr_init(devfs_handle_t hubv) { } /* * hub_device_desc_update * Update the passed in device descriptor with the actual the * target cpu number and interrupt priority level. * NOTE : These might be the same as the ones passed in thru * the descriptor. */ static void hub_device_desc_update(device_desc_t dev_desc, ilvl_t intr_swlevel, cpuid_t cpu) { char cpuname[40]; /* Store the interrupt priority level in the device descriptor */ device_desc_intr_swlevel_set(dev_desc, intr_swlevel); /* Convert the cpuid to the vertex handle in the hwgraph and * save it in the device descriptor. */ sprintf(cpuname,"/hw/cpunum/%ld",cpu); device_desc_intr_target_set(dev_desc, hwgraph_path_to_dev(cpuname)); } int allocate_my_bit = INTRCONNECT_ANYBIT; /* * Allocate resources required for an interrupt as specified in dev_desc. * Returns a hub interrupt handle on success, or 0 on failure. */ static hub_intr_t do_hub_intr_alloc(devfs_handle_t dev, /* which crosstalk device */ device_desc_t dev_desc, /* device descriptor */ devfs_handle_t owner_dev, /* owner of this interrupt, if known */ int uncond_nothread) /* unconditionally non-threaded */ { cpuid_t cpu = (cpuid_t)0; /* cpu to receive interrupt */ int cpupicked = 0; int bit; /* interrupt vector */ /*REFERENCED*/ int intr_resflags = 0; hub_intr_t intr_hdl; cnodeid_t nodeid; /* node to receive interrupt */ /*REFERENCED*/ nasid_t nasid; /* nasid to receive interrupt */ struct xtalk_intr_s *xtalk_info; iopaddr_t xtalk_addr; /* xtalk addr on hub to set intr */ xwidget_info_t xwidget_info; /* standard crosstalk widget info handle */ char *intr_name = NULL; ilvl_t intr_swlevel; extern int default_intr_pri; #ifdef CONFIG_IA64_SGI_SN1 extern void synergy_intr_alloc(int, int); #endif /* * If caller didn't explicily specify a device descriptor, see if there's * a default descriptor associated with the device. */ if (!dev_desc) dev_desc = device_desc_default_get(dev); if (dev_desc) { intr_name = device_desc_intr_name_get(dev_desc); intr_swlevel = device_desc_intr_swlevel_get(dev_desc); if (dev_desc->flags & D_INTR_ISERR) { intr_resflags = II_ERRORINT; } else if (!uncond_nothread && !(dev_desc->flags & D_INTR_NOTHREAD)) { intr_resflags = II_THREADED; } else { /* Neither an error nor a thread. */ intr_resflags = 0; } } else { intr_swlevel = default_intr_pri; if (!uncond_nothread) intr_resflags = II_THREADED; } /* XXX - Need to determine if the interrupt should be threaded. */ /* If the cpu has not been picked already then choose a candidate * interrupt target and reserve the interrupt bit */ #if defined(NEW_INTERRUPTS) if (!cpupicked) { cpu = intr_heuristic(dev,dev_desc,allocate_my_bit, intr_resflags,owner_dev, intr_name,&bit); } #endif /* At this point we SHOULD have a valid cpu */ if (cpu == CPU_NONE) { #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("%v hub_intr_alloc could not allocate interrupt\n", owner_dev); #else PRINT_WARNING("0x%x hub_intr_alloc could not allocate interrupt\n", owner_dev); #endif return(0); } /* If the cpu has been picked already (due to the bridge data * corruption bug) then try to reserve an interrupt bit . */ #if defined(NEW_INTERRUPTS) if (cpupicked) { bit = intr_reserve_level(cpu, allocate_my_bit, intr_resflags, owner_dev, intr_name); if (bit < 0) { #if defined(SUPPORT_PRINTING_V_FORMAT) PRINT_WARNING("Could not reserve an interrupt bit for cpu " " %d and dev %v\n", cpu,owner_dev); #else PRINT_WARNING("Could not reserve an interrupt bit for cpu " " %d and dev 0x%x\n", cpu, owner_dev); #endif return(0); } } #endif /* NEW_INTERRUPTS */ nodeid = cpuid_to_cnodeid(cpu); nasid = cpuid_to_nasid(cpu); xtalk_addr = HUBREG_AS_XTALKADDR(nasid, PIREG(PI_INT_PEND_MOD, cpuid_to_subnode(cpu))); /* * Allocate an interrupt handle, and fill it in. There are two * pieces to an interrupt handle: the piece needed by generic * xtalk code which is used by crosstalk device drivers, and * the piece needed by low-level IP27 hardware code. */ intr_hdl = kmem_alloc_node(sizeof(struct hub_intr_s), KM_NOSLEEP, nodeid); ASSERT_ALWAYS(intr_hdl); /* * Fill in xtalk information for generic xtalk interfaces that * operate on xtalk_intr_hdl's. */ xtalk_info = &intr_hdl->i_xtalk_info; xtalk_info->xi_dev = dev; xtalk_info->xi_vector = bit; xtalk_info->xi_addr = xtalk_addr; /* * Regardless of which CPU we ultimately interrupt, a given crosstalk * widget always handles interrupts (and PIO and DMA) through its * designated "master" crosstalk provider. */ xwidget_info = xwidget_info_get(dev); if (xwidget_info) xtalk_info->xi_target = xwidget_info_masterid_get(xwidget_info); /* Fill in low level hub information for hub_* interrupt interface */ intr_hdl->i_swlevel = intr_swlevel; intr_hdl->i_cpuid = cpu; intr_hdl->i_bit = bit; intr_hdl->i_flags = HUB_INTR_IS_ALLOCED; /* Store the actual interrupt priority level & interrupt target * cpu back in the device descriptor. */ hub_device_desc_update(dev_desc, intr_swlevel, cpu); #ifdef CONFIG_IA64_SGI_SN1 synergy_intr_alloc((int)bit, (int)cpu); #endif return(intr_hdl); } /* * Allocate resources required for an interrupt as specified in dev_desc. * Returns a hub interrupt handle on success, or 0 on failure. */ hub_intr_t hub_intr_alloc( devfs_handle_t dev, /* which crosstalk device */ device_desc_t dev_desc, /* device descriptor */ devfs_handle_t owner_dev) /* owner of this interrupt, if known */ { return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 0)); } /* * Allocate resources required for an interrupt as specified in dev_desc. * Uncondtionally request non-threaded, regardless of what the device * descriptor might say. * Returns a hub interrupt handle on success, or 0 on failure. */ hub_intr_t hub_intr_alloc_nothd(devfs_handle_t dev, /* which crosstalk device */ device_desc_t dev_desc, /* device descriptor */ devfs_handle_t owner_dev) /* owner of this interrupt, if known */ { return(do_hub_intr_alloc(dev, dev_desc, owner_dev, 1)); } /* * Free resources consumed by intr_alloc. */ void hub_intr_free(hub_intr_t intr_hdl) { cpuid_t cpu = intr_hdl->i_cpuid; int bit = intr_hdl->i_bit; xtalk_intr_t xtalk_info; if (intr_hdl->i_flags & HUB_INTR_IS_CONNECTED) { /* Setting the following fields in the xtalk interrupt info * clears the interrupt target register in the xtalk user */ xtalk_info = &intr_hdl->i_xtalk_info; xtalk_info->xi_dev = NODEV; xtalk_info->xi_vector = 0; xtalk_info->xi_addr = 0; hub_intr_disconnect(intr_hdl); } if (intr_hdl->i_flags & HUB_INTR_IS_ALLOCED) kfree(intr_hdl); #if defined(NEW_INTERRUPTS) intr_unreserve_level(cpu, bit); #endif } /* * Associate resources allocated with a previous hub_intr_alloc call with the * described handler, arg, name, etc. */ /*ARGSUSED*/ int hub_intr_connect( hub_intr_t intr_hdl, /* xtalk intr resource handle */ intr_func_t intr_func, /* xtalk intr handler */ void *intr_arg, /* arg to intr handler */ xtalk_intr_setfunc_t setfunc, /* func to set intr hw */ void *setfunc_arg, /* arg to setfunc */ void *thread) /* intr thread to use */ { int rv; cpuid_t cpu = intr_hdl->i_cpuid; int bit = intr_hdl->i_bit; #ifdef CONFIG_IA64_SGI_SN1 extern int synergy_intr_connect(int, int); #endif ASSERT(intr_hdl->i_flags & HUB_INTR_IS_ALLOCED); #if defined(NEW_INTERRUPTS) rv = intr_connect_level(cpu, bit, intr_hdl->i_swlevel, intr_func, intr_arg, NULL); if (rv < 0) return(rv); #endif intr_hdl->i_xtalk_info.xi_setfunc = setfunc; intr_hdl->i_xtalk_info.xi_sfarg = setfunc_arg; if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl); intr_hdl->i_flags |= HUB_INTR_IS_CONNECTED; #ifdef CONFIG_IA64_SGI_SN1 return(synergy_intr_connect((int)bit, (int)cpu)); #endif } /* * Disassociate handler with the specified interrupt. */ void hub_intr_disconnect(hub_intr_t intr_hdl) { /*REFERENCED*/ int rv; cpuid_t cpu = intr_hdl->i_cpuid; int bit = intr_hdl->i_bit; xtalk_intr_setfunc_t setfunc; setfunc = intr_hdl->i_xtalk_info.xi_setfunc; /* TBD: send disconnected interrupts somewhere harmless */ if (setfunc) (*setfunc)((xtalk_intr_t)intr_hdl); #if defined(NEW_INTERRUPTS) rv = intr_disconnect_level(cpu, bit); ASSERT(rv == 0); #endif intr_hdl->i_flags &= ~HUB_INTR_IS_CONNECTED; } /* * Return a hwgraph vertex that represents the CPU currently * targeted by an interrupt. */ devfs_handle_t hub_intr_cpu_get(hub_intr_t intr_hdl) { cpuid_t cpuid = intr_hdl->i_cpuid; ASSERT(cpuid != CPU_NONE); return(cpuid_to_vertex(cpuid)); } /* CONFIGURATION MANAGEMENT */ /* * Perform initializations that allow this hub to start crosstalk support. */ void hub_provider_startup(devfs_handle_t hubv) { hub_pio_init(hubv); hub_dma_init(hubv); hub_intr_init(hubv); } /* * Shutdown crosstalk support from a hub. */ void hub_provider_shutdown(devfs_handle_t hub) { /* TBD */ xtalk_provider_unregister(hub); } /* * Check that an address is in teh real small window widget 0 space * or else in the big window we're using to emulate small window 0 * in the kernel. */ int hub_check_is_widget0(void *addr) { nasid_t nasid = NASID_GET(addr); if (((__psunsigned_t)addr >= RAW_NODE_SWIN_BASE(nasid, 0)) && ((__psunsigned_t)addr < RAW_NODE_SWIN_BASE(nasid, 1))) return 1; return 0; } /* * Check that two addresses use the same widget */ int hub_check_window_equiv(void *addra, void *addrb) { if (hub_check_is_widget0(addra) && hub_check_is_widget0(addrb)) return 1; /* XXX - Assume this is really a small window address */ if (WIDGETID_GET((__psunsigned_t)addra) == WIDGETID_GET((__psunsigned_t)addrb)) return 1; return 0; } /* * Determine whether two PCI addresses actually refer to the same device. * This only works if both addresses are in small windows. It's used to * determine whether prom addresses refer to particular PCI devices. */ /* * XXX - This won't work as written if we ever have more than two nodes * on a crossbow. In that case, we'll need an array or partners. */ int hub_check_pci_equiv(void *addra, void *addrb) { nasid_t nasida, nasidb; /* * This is for a permanent workaround that causes us to use a * big window in place of small window 0. */ if (!hub_check_window_equiv(addra, addrb)) return 0; /* If the offsets aren't the same, forget it. */ if (SWIN_WIDGETADDR((__psunsigned_t)addra) != (SWIN_WIDGETADDR((__psunsigned_t)addrb))) return 0; /* Now, check the nasids */ nasida = NASID_GET(addra); nasidb = NASID_GET(addrb); ASSERT(NASID_TO_COMPACT_NODEID(nasida) != INVALID_NASID); ASSERT(NASID_TO_COMPACT_NODEID(nasidb) != INVALID_NASID); /* * Either the NASIDs must be the same or they must be crossbow * partners (on the same crossbow). */ return (check_nasid_equiv(nasida, nasidb)); } /* * hub_setup_prb(nasid, prbnum, credits, conveyor) * * Put a PRB into fire-and-forget mode if conveyor isn't set. Otehrwise, * put it into conveyor belt mode with the specified number of credits. */ void hub_setup_prb(nasid_t nasid, int prbnum, int credits, int conveyor) { iprb_t prb; int prb_offset; #ifdef LATER extern int force_fire_and_forget; extern volatile int ignore_conveyor_override; if (force_fire_and_forget && !ignore_conveyor_override) if (conveyor == HUB_PIO_CONVEYOR) conveyor = HUB_PIO_FIRE_N_FORGET; #endif /* * Get the current register value. */ prb_offset = IIO_IOPRB(prbnum); prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset); /* * Clear out some fields. */ prb.iprb_ovflow = 1; prb.iprb_bnakctr = 0; prb.iprb_anakctr = 0; /* * Enable or disable fire-and-forget mode. */ prb.iprb_ff = ((conveyor == HUB_PIO_CONVEYOR) ? 0 : 1); /* * Set the appropriate number of PIO cresits for the widget. */ prb.iprb_xtalkctr = credits; /* * Store the new value to the register. */ REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval); } /* * hub_set_piomode() * * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget" * mode. To do this, we have to make absolutely sure that no PIOs * are in progress so we turn off access to all widgets for the duration * of the function. * * XXX - This code should really check what kind of widget we're talking * to. Bridges can only handle three requests, but XG will do more. * How many can crossbow handle to widget 0? We're assuming 1. * * XXX - There is a bug in the crossbow that link reset PIOs do not * return write responses. The easiest solution to this problem is to * leave widget 0 (xbow) in fire-and-forget mode at all times. This * only affects pio's to xbow registers, which should be rare. */ void hub_set_piomode(nasid_t nasid, int conveyor) { hubreg_t ii_iowa; int direct_connect; hubii_wcr_t ii_wcr; int prbnum; int cons_lock = 0; ASSERT(NASID_TO_COMPACT_NODEID(nasid) != INVALID_CNODEID); if (nasid == get_console_nasid()) { PUTBUF_LOCK(s); cons_lock = 1; } ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS); REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0); ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR); direct_connect = ii_wcr.iwcr_dir_con; if (direct_connect) { /* * Assume a bridge here. */ hub_setup_prb(nasid, 0, 3, conveyor); } else { /* * Assume a crossbow here. */ hub_setup_prb(nasid, 0, 1, conveyor); } for (prbnum = HUB_WIDGET_ID_MIN; prbnum <= HUB_WIDGET_ID_MAX; prbnum++) { /* * XXX - Here's where we should take the widget type into * when account assigning credits. */ /* Always set the PRBs in fire-and-forget mode */ hub_setup_prb(nasid, prbnum, 3, conveyor); } REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa); if (cons_lock) PUTBUF_UNLOCK(s); } /* Interface to allow special drivers to set hub specific * device flags. * Return 0 on failure , 1 on success */ int hub_widget_flags_set(nasid_t nasid, xwidgetnum_t widget_num, hub_widget_flags_t flags) { ASSERT((flags & HUB_WIDGET_FLAGS) == flags); if (flags & HUB_PIO_CONVEYOR) { hub_setup_prb(nasid,widget_num, 3,HUB_PIO_CONVEYOR); /* set the PRB in conveyor * belt mode with 3 credits */ } else if (flags & HUB_PIO_FIRE_N_FORGET) { hub_setup_prb(nasid,widget_num, 3,HUB_PIO_FIRE_N_FORGET); /* set the PRB in fire * and forget mode */ } return 1; } /* Interface to allow special drivers to set hub specific * device flags. * Return 0 on failure , 1 on success */ int hub_device_flags_set(devfs_handle_t widget_vhdl, hub_widget_flags_t flags) { xwidget_info_t widget_info = xwidget_info_get(widget_vhdl); xwidgetnum_t widget_num = xwidget_info_id_get(widget_info); devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info); hubinfo_t hub_info = 0; nasid_t nasid; unsigned long s; int rv; /* Use the nasid from the hub info hanging off the hub vertex * and widget number from the widget vertex */ hubinfo_get(hub_vhdl, &hub_info); /* Being over cautious by grabbing a lock */ s = mutex_spinlock(&hub_info->h_bwlock); nasid = hub_info->h_nasid; rv = hub_widget_flags_set(nasid,widget_num,flags); mutex_spinunlock(&hub_info->h_bwlock, s); return rv; } #if ((defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)) && defined(BRINGUP)) /* BRINGUP: This ought to be useful for IP27 too but, for now, * make it SN1 only because `ii_ixtt_u_t' is not in IP27/hubio.h * (or anywhere else :-). */ int hubii_ixtt_set(devfs_handle_t widget_vhdl, ii_ixtt_u_t *ixtt) { xwidget_info_t widget_info = xwidget_info_get(widget_vhdl); devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info); hubinfo_t hub_info = 0; nasid_t nasid; unsigned long s; /* Use the nasid from the hub info hanging off the hub vertex * and widget number from the widget vertex */ hubinfo_get(hub_vhdl, &hub_info); /* Being over cautious by grabbing a lock */ s = mutex_spinlock(&hub_info->h_bwlock); nasid = hub_info->h_nasid; REMOTE_HUB_S(nasid, IIO_IXTT, ixtt->ii_ixtt_regval); mutex_spinunlock(&hub_info->h_bwlock, s); return 0; } int hubii_ixtt_get(devfs_handle_t widget_vhdl, ii_ixtt_u_t *ixtt) { xwidget_info_t widget_info = xwidget_info_get(widget_vhdl); devfs_handle_t hub_vhdl = xwidget_info_master_get(widget_info); hubinfo_t hub_info = 0; nasid_t nasid; unsigned long s; /* Use the nasid from the hub info hanging off the hub vertex * and widget number from the widget vertex */ hubinfo_get(hub_vhdl, &hub_info); /* Being over cautious by grabbing a lock */ s = mutex_spinlock(&hub_info->h_bwlock); nasid = hub_info->h_nasid; ixtt->ii_ixtt_regval = REMOTE_HUB_L(nasid, IIO_IXTT); mutex_spinunlock(&hub_info->h_bwlock, s); return 0; } #endif /* CONFIG_IA64_SGI_SN1 */ /* * hub_device_inquiry * Find out the xtalk widget related information stored in this * hub's II. */ void hub_device_inquiry(devfs_handle_t xbus_vhdl, xwidgetnum_t widget) { devfs_handle_t xconn, hub_vhdl; char widget_name[8]; hubreg_t ii_iidem,ii_iiwa, ii_iowa; hubinfo_t hubinfo; nasid_t nasid; int d; sprintf(widget_name, "%d", widget); if (hwgraph_traverse(xbus_vhdl, widget_name, &xconn) != GRAPH_SUCCESS) return; hub_vhdl = device_master_get(xconn); if (hub_vhdl == GRAPH_VERTEX_NONE) return; hubinfo_get(hub_vhdl, &hubinfo); if (!hubinfo) return; nasid = hubinfo->h_nasid; ii_iidem = REMOTE_HUB_L(nasid, IIO_IIDEM); ii_iiwa = REMOTE_HUB_L(nasid, IIO_IIWA); ii_iowa = REMOTE_HUB_L(nasid, IIO_IOWA); #if defined(SUPPORT_PRINTING_V_FORMAT) printk("Inquiry Info for %v\n", xconn); #else printk("Inquiry Info for 0x%x\n", xconn); #endif printk("\tDevices shutdown [ "); for (d = 0 ; d <= 7 ; d++) if (!(ii_iidem & (IIO_IIDEM_WIDGETDEV_MASK(widget,d)))) printk(" %d", d); printk("]\n"); printk("\tInbound access ? %s\n", ii_iiwa & IIO_IIWA_WIDGET(widget) ? "yes" : "no"); printk("\tOutbound access ? %s\n", ii_iowa & IIO_IOWA_WIDGET(widget) ? "yes" : "no"); } /* * A pointer to this structure hangs off of every hub hwgraph vertex. * The generic xtalk layer may indirect through it to get to this specific * crosstalk bus provider. */ xtalk_provider_t hub_provider = { (xtalk_piomap_alloc_f *) hub_piomap_alloc, (xtalk_piomap_free_f *) hub_piomap_free, (xtalk_piomap_addr_f *) hub_piomap_addr, (xtalk_piomap_done_f *) hub_piomap_done, (xtalk_piotrans_addr_f *) hub_piotrans_addr, (xtalk_dmamap_alloc_f *) hub_dmamap_alloc, (xtalk_dmamap_free_f *) hub_dmamap_free, (xtalk_dmamap_addr_f *) hub_dmamap_addr, (xtalk_dmamap_list_f *) hub_dmamap_list, (xtalk_dmamap_done_f *) hub_dmamap_done, (xtalk_dmatrans_addr_f *) hub_dmatrans_addr, (xtalk_dmatrans_list_f *) hub_dmatrans_list, (xtalk_dmamap_drain_f *) hub_dmamap_drain, (xtalk_dmaaddr_drain_f *) hub_dmaaddr_drain, (xtalk_dmalist_drain_f *) hub_dmalist_drain, (xtalk_intr_alloc_f *) hub_intr_alloc, (xtalk_intr_alloc_f *) hub_intr_alloc_nothd, (xtalk_intr_free_f *) hub_intr_free, (xtalk_intr_connect_f *) hub_intr_connect, (xtalk_intr_disconnect_f *) hub_intr_disconnect, (xtalk_intr_cpu_get_f *) hub_intr_cpu_get, (xtalk_provider_startup_f *) hub_provider_startup, (xtalk_provider_shutdown_f *) hub_provider_shutdown, };