📄 pcibr_slot.c
字号:
#endif error = pcibr_slot_info_return(pcibr_soft, tmp_slot, respp);#ifdef PCI_LATER /* Release the bus lock */ mrunlock(pcibr_soft->bs_bus_lock);#endif if (error) { return(error); } ++respp; size -= sizeof(*respp); } return(error);}#define PROBE_LOCK 0 /* FIXME: we're attempting to lock around accesses * to b_int_enable. This hangs pcibr_probe_slot() *//* * pcibr_slot_info_init * Probe for this slot and see if it is populated. * If it is populated initialize the generic PCI infrastructural * information associated with this particular PCI device. */intpcibr_slot_info_init(vertex_hdl_t pcibr_vhdl, pciio_slot_t slot){ pcibr_soft_t pcibr_soft; pcibr_info_h pcibr_infoh; pcibr_info_t pcibr_info; bridge_t *bridge; cfg_p cfgw; unsigned idword; unsigned pfail; unsigned idwords[8]; pciio_vendor_id_t vendor; pciio_device_id_t device; unsigned htype; unsigned lt_time; int nbars; cfg_p wptr; cfg_p pcix_cap; int win; pciio_space_t space; int nfunc; pciio_function_t rfunc; int func; vertex_hdl_t conn_vhdl; pcibr_soft_slot_t slotp; /* Get the basic software information required to proceed */ pcibr_soft = pcibr_soft_get(pcibr_vhdl); if (!pcibr_soft) return(EINVAL); bridge = pcibr_soft->bs_base; if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) return(EINVAL); /* If we have a host slot (eg:- IOC3 has 2 PCI slots and the initialization * is done by the host slot then we are done. */ if (pcibr_soft->bs_slot[slot].has_host) { return(0); } /* Try to read the device-id/vendor-id from the config space */ cfgw = pcibr_slot_config_addr(bridge, slot, 0);#if PROBE_LOCK s = pcibr_lock(pcibr_soft);#endif if (pcibr_probe_slot(bridge, cfgw, &idword)) return(ENODEV);#if PROBE_LOCK pcibr_unlock(pcibr_soft, s);#endif slotp = &pcibr_soft->bs_slot[slot]; slotp->slot_status |= SLOT_POWER_UP; vendor = 0xFFFF & idword; device = 0xFFFF & (idword >> 16); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_PROBE, pcibr_vhdl, "pcibr_slot_info_init: slot=%d, vendor=0x%x, device=0x%x\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), vendor, device)); /* If the vendor id is not valid then the slot is not populated * and we are done. */ if (vendor == 0xFFFF) return(ENODEV); htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1); nfunc = 1; rfunc = PCIIO_FUNC_NONE; pfail = 0; /* NOTE: if a card claims to be multifunction * but only responds to config space 0, treat * it as a unifunction card. */ if (htype & 0x80) { /* MULTIFUNCTION */ for (func = 1; func < 8; ++func) { cfgw = pcibr_func_config_addr(bridge, 0, slot, func, 0);#if PROBE_LOCK s = pcibr_lock(pcibr_soft);#endif if (pcibr_probe_slot(bridge, cfgw, &idwords[func])) { pfail |= 1 << func; continue; }#if PROBE_LOCK pcibr_unlock(pcibr_soft, s);#endif vendor = 0xFFFF & idwords[func]; if (vendor == 0xFFFF) { pfail |= 1 << func; continue; } nfunc = func + 1; rfunc = 0; } cfgw = pcibr_slot_config_addr(bridge, slot, 0); } NEWA(pcibr_infoh, nfunc); pcibr_soft->bs_slot[slot].bss_ninfo = nfunc; pcibr_soft->bs_slot[slot].bss_infos = pcibr_infoh; for (func = 0; func < nfunc; ++func) { unsigned cmd_reg; if (func) { if (pfail & (1 << func)) continue; idword = idwords[func]; cfgw = pcibr_func_config_addr(bridge, 0, slot, func, 0); device = 0xFFFF & (idword >> 16); htype = do_pcibr_config_get(cfgw, PCI_CFG_HEADER_TYPE, 1); rfunc = func; } htype &= 0x7f; if (htype != 0x00) { printk(KERN_WARNING "%s pcibr: pci slot %d func %d has strange header type 0x%x\n", pcibr_soft->bs_name, slot, func, htype); nbars = 2; } else { nbars = PCI_CFG_BASE_ADDRS; } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_CONFIG, pcibr_vhdl, "pcibr_slot_info_init: slot=%d, func=%d, cfgw=0x%x\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft,slot), func, cfgw));#ifdef PIC_LATER /* * Check for a Quad ATM PCI "card" and return all the PCI bus * memory and I/O space. This will work-around an apparent * hardware problem with the Quad ATM XIO card handling large * PIO addresses. Releasing all the space for use by the card * will lower the PIO addresses with the PCI bus address space. * This is OK since the PROM did not assign any BAR addresses. * * Only release all the PCI bus addresses once. * */ if ((vendor == LINC_VENDOR_ID_NUM) && (device == LINC_DEVICE_ID_NUM)) { iopaddr_t prom_base_addr = pcibr_soft->bs_xid << 24; int prom_base_size = 0x1000000; if (!(pcibr_soft->bs_bus_addr_status & PCIBR_BUS_ADDR_MEM_FREED)) { pciio_device_win_populate(&pcibr_soft->bs_mem_win_map, prom_base_addr, prom_base_size); pcibr_soft->bs_bus_addr_status |= PCIBR_BUS_ADDR_MEM_FREED; } if (!(pcibr_soft->bs_bus_addr_status & PCIBR_BUS_ADDR_IO_FREED)) { pciio_device_win_populate(&pcibr_soft->bs_io_win_map, prom_base_addr, prom_base_size); pcibr_soft->bs_bus_addr_status |= PCIBR_BUS_ADDR_IO_FREED; } }#endif /* PIC_LATER */ /* * If the latency timer has already been set, by prom or by the * card itself, use that value. Otherwise look at the device's * 'min_gnt' and attempt to calculate a latency time. * * NOTE: For now if the device is on the 'real time' arbitration * ring we don't set the latency timer. * * WAR: SGI's IOC3 and RAD devices target abort if you write a * single byte into their config space. So don't set the Latency * Timer for these devices */ lt_time = do_pcibr_config_get(cfgw, PCI_CFG_LATENCY_TIMER, 1); if ((lt_time == 0) && !(bridge->b_device[slot].reg & BRIDGE_DEV_RT) && (device == 0x5 /* RAD_DEV */)) { unsigned min_gnt; unsigned min_gnt_mult; /* 'min_gnt' indicates how long of a burst period a device * needs in increments of 250ns. But latency timer is in * PCI clock cycles, so a conversion is needed. */ min_gnt = do_pcibr_config_get(cfgw, PCI_MIN_GNT, 1); if (IS_133MHZ(pcibr_soft)) min_gnt_mult = 32; /* 250ns @ 133MHz in clocks */ else if (IS_100MHZ(pcibr_soft)) min_gnt_mult = 24; /* 250ns @ 100MHz in clocks */ else if (IS_66MHZ(pcibr_soft)) min_gnt_mult = 16; /* 250ns @ 66MHz, in clocks */ else min_gnt_mult = 8; /* 250ns @ 33MHz, in clocks */ if ((min_gnt != 0) && ((min_gnt * min_gnt_mult) < 256)) lt_time = (min_gnt * min_gnt_mult); else lt_time = 4 * min_gnt_mult; /* 1 micro second */ do_pcibr_config_set(cfgw, PCI_CFG_LATENCY_TIMER, 1, lt_time); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_CONFIG, pcibr_vhdl, "pcibr_slot_info_init: set Latency Timer for slot=%d, " "func=%d, to 0x%x\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), func, lt_time)); } /* In our architecture the setting of the cacheline size isn't * beneficial for cards in PCI mode, but in PCI-X mode devices * can optionally use the cacheline size value for internal * device optimizations (See 7.1.5 of the PCI-X v1.0 spec). * NOTE: cachline size is in doubleword increments */ if (IS_PCIX(pcibr_soft)) { if (!do_pcibr_config_get(cfgw, PCI_CFG_CACHE_LINE, 1)) { do_pcibr_config_set(cfgw, PCI_CFG_CACHE_LINE, 1, 0x20); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_CONFIG, pcibr_vhdl, "pcibr_slot_info_init: set CacheLine for slot=%d, " "func=%d, to 0x20\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), func)); } /* Get the PCI-X capability if running in PCI-X mode. If the func * doesnt have a pcix capability, allocate a PCIIO_VENDOR_ID_NONE * pcibr_info struct so the device driver for that function is not * called. */ if (!(pcix_cap = pcibr_find_capability(cfgw, PCI_CAP_PCIX))) { printk(KERN_WARNING#if defined(SUPPORT_PRINTING_V_FORMAT) "%v: Bus running in PCI-X mode, But card in slot %d, " "func %d not PCI-X capable\n", pcibr_vhdl, slot, func);#else "0x%lx: Bus running in PCI-X mode, But card in slot %d, " "func %d not PCI-X capable\n", (unsigned long)pcibr_vhdl, slot, func);#endif pcibr_device_info_new(pcibr_soft, slot, PCIIO_FUNC_NONE, PCIIO_VENDOR_ID_NONE, PCIIO_DEVICE_ID_NONE); continue; } PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_CONFIG, pcibr_vhdl, "pcibr_slot_info_init: PCI-X capability at 0x%x for " "slot=%d, func=%d\n", pcix_cap, PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), func)); } else { pcix_cap = NULL; } pcibr_info = pcibr_device_info_new (pcibr_soft, slot, rfunc, vendor, device); /* Keep a running total of the number of PIC-X functions on the bus * and the number of max outstanding split trasnactions that they * have requested. NOTE: "pcix_cap != NULL" implies IS_PCIX() */ pcibr_info->f_pcix_cap = (cap_pcix_type0_t *)pcix_cap; if (pcibr_info->f_pcix_cap) { int max_out; /* max outstanding splittrans from status reg */ pcibr_soft->bs_pcix_num_funcs++; max_out = pcibr_info->f_pcix_cap->pcix_type0_status.max_out_split; pcibr_soft->bs_pcix_split_tot += max_splittrans_to_numbuf[max_out]; } conn_vhdl = pciio_device_info_register(pcibr_vhdl, &pcibr_info->f_c); if (func == 0) slotp->slot_conn = conn_vhdl; cmd_reg = do_pcibr_config_get(cfgw, PCI_CFG_COMMAND, 4); wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4; for (win = 0; win < nbars; ++win) { iopaddr_t base, mask, code; size_t size; /* * GET THE BASE & SIZE OF THIS WINDOW: * * The low two or four bits of the BASE register * determines which address space we are in; the * rest is a base address. BASE registers * determine windows that are power-of-two sized * and naturally aligned, so we can get the size * of a window by writing all-ones to the * register, reading it back, and seeing which * bits are used for decode; the least * significant nonzero bit is also the size of * the window. * * WARNING: someone may already have allocated * some PCI space to this window, and in fact * PIO may be in process at this very moment * from another processor (or even from this * one, if we get interrupted)! So, if the BASE * already has a nonzero address, be generous * and use the LSBit of that address as the * size; this could overstate the window size. * Usually, when one card is set up, all are set * up; so, since we don't bitch about * overlapping windows, we are ok. * * UNFORTUNATELY, some cards do not clear their * BASE registers on reset. I have two heuristics * that can detect such cards: first, if the * decode enable is turned off for the space * that the window uses, we can disregard the * initial value. second, if the address is * outside the range that we use, we can disregard * it as well. * * This is looking very PCI generic. Except for * knowing how many slots and where their config * spaces are, this window loop and the next one * could probably be shared with other PCI host * adapters. It would be interesting to see if * this could be pushed up into pciio, when we * start supporting more PCI providers. */ base = do_pcibr_config_get(wptr, (win * 4), 4); if (base & PCI_BA_IO_SPACE) { /* BASE is in I/O space. */ space = PCIIO_SPACE_IO; mask = -4; code = base & 3; base = base & mask; if (base == 0) { ; /* not assigned */ } else if (!(cmd_reg & PCI_CMD_IO_SPACE)) { base = 0; /* decode not enabled */ } } else { /* BASE is in MEM space. */ space = PCIIO_SPACE_MEM; mask = -16; code = base & PCI_BA_MEM_LOCATION; /* extract BAR type */ base = base & mask; if (base == 0) { ; /* not assigned */ } else if (!(cmd_reg & PCI_CMD_MEM_SPACE)) { base = 0; /* decode not enabled */ } else if (base & 0xC0000000) { base = 0; /* outside permissable range */ } else if ((code == PCI_BA_MEM_64BIT) && (do_pcibr_config_get(wptr, ((win + 1)*4), 4) != 0)) { base = 0; /* outside permissable range */ } } if (base != 0) { /* estimate size */ size = base & -base; } else { /* calculate size */ do_pcibr_config_set(wptr, (win * 4), 4, ~0); /* write 1's */ size = do_pcibr_config_get(wptr, (win * 4), 4); /* read back */ size &= mask; /* keep addr */ size &= -size; /* keep lsbit */ if (size == 0) continue; } pcibr_info->f_window[win].w_space = space; pcibr_info->f_window[win].w_base = base; pcibr_info->f_window[win].w_size = size; if (code == PCI_BA_MEM_64BIT) { win++; /* skip upper half */ do_pcibr_config_set(wptr, (win * 4), 4, 0); /* must be zero */ } } /* next win */ } /* next func */ return(0);} /* * pcibr_find_capability * Walk the list of capabilities (if it exists) looking for * the requested capability. Return a cfg_p pointer to the * capability if found, else return NULL */cfg_ppcibr_find_capability(cfg_p cfgw, unsigned capability){ unsigned cap_nxt; unsigned cap_id; int defend_against_circular_linkedlist = 0; /* Check to see if there is a capabilities pointer in the cfg header */ if (!(do_pcibr_config_get(cfgw, PCI_CFG_STATUS, 2) & PCI_STAT_CAP_LIST)) { return (NULL); } /* * Read up the capabilities head pointer from the configuration header. * Capabilities are stored as a linked list in the lower 48 dwords of * config space and are dword aligned. (Note: spec states the least two * significant bits of the next pointer must be ignored, so we mask * with 0xfc). */ cap_nxt = (do_pcibr_config_get(cfgw, PCI_CAPABILITIES_PTR, 1) & 0xfc); while (cap_nxt && (defend_against_circular_linkedlist <= 48)) { cap_id = do_pcibr_config_get(cfgw, cap_nxt, 1); if (cap_id == capability) { return ((cfg_p)((char *)cfgw + cap_nxt)); } cap_nxt = (do_pcibr_config_get(cfgw, cap_nxt+1, 1) & 0xfc); defend_against_circular_linkedlist++; } return (NULL);}/* * pcibr_slot_info_free * Remove all the PCI infrastructural information associated * with a particular PCI device. */intpcibr_slot_info_free(vertex_hdl_t pcibr_vhdl, pciio_slot_t slot){ pcibr_soft_t pcibr_soft; pcibr_info_h pcibr_infoh; int nfunc; pcibr_soft = pcibr_soft_get(pcibr_vhdl); if (!pcibr_soft) return(EINVAL); if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) return(EINVAL); nfunc = pcibr_soft->bs_slot[slot].bss_ninfo; pcibr_device_info_free(pcibr_vhdl, slot); pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos; DELA(pcibr_infoh,nfunc); pcibr_soft->bs_slot[slot].bss_ninfo = 0; return(0);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -