📄 pcibr_slot.c
字号:
/* Acquire read access to the bus */ mrlock(pcibr_soft->bs_bus_lock, MR_ACCESS, PZERO); error = pcibr_slot_info_return(pcibr_soft, slot, respp); /* Release the bus lock */ mrunlock(pcibr_soft->bs_bus_lock); return(error); } /* Return information for all the slots */ for (tmp_slot = 0; tmp_slot < 8; tmp_slot++) { if (size < sizeof(*respp)) { return(PCI_RESP_AREA_TOO_SMALL); } /* Acquire read access to the bus */ mrlock(pcibr_soft->bs_bus_lock, MR_ACCESS, PZERO); error = pcibr_slot_info_return(pcibr_soft, tmp_slot, respp); /* Release the bus lock */ mrunlock(pcibr_soft->bs_bus_lock); if (error) { return(error); } ++respp; size -= sizeof(*respp); } return(error);}#endif /* LATER *//* FIXME: there should be a better way to do this. * pcibr_attach() needs PCI_ADDR_SPACE_LIMITS_STORE *//* * PCI_ADDR_SPACE_LIMITS_LOAD * Gets the current values of * pci io base, * pci io last, * pci low memory base, * pci low memory last, * pci high memory base, * pci high memory last */#define PCI_ADDR_SPACE_LIMITS_LOAD() \ pci_io_fb = pcibr_soft->bs_spinfo.pci_io_base; \ pci_io_fl = pcibr_soft->bs_spinfo.pci_io_last; \ pci_lo_fb = pcibr_soft->bs_spinfo.pci_swin_base; \ pci_lo_fl = pcibr_soft->bs_spinfo.pci_swin_last; \ pci_hi_fb = pcibr_soft->bs_spinfo.pci_mem_base; \ pci_hi_fl = pcibr_soft->bs_spinfo.pci_mem_last;/* * PCI_ADDR_SPACE_LIMITS_STORE * Sets the current values of * pci io base, * pci io last, * pci low memory base, * pci low memory last, * pci high memory base, * pci high memory last */#define PCI_ADDR_SPACE_LIMITS_STORE() \ pcibr_soft->bs_spinfo.pci_io_base = pci_io_fb; \ pcibr_soft->bs_spinfo.pci_io_last = pci_io_fl; \ pcibr_soft->bs_spinfo.pci_swin_base = pci_lo_fb; \ pcibr_soft->bs_spinfo.pci_swin_last = pci_lo_fl; \ pcibr_soft->bs_spinfo.pci_mem_base = pci_hi_fb; \ pcibr_soft->bs_spinfo.pci_mem_last = pci_hi_fl;#define PCI_ADDR_SPACE_LIMITS_PRINT() \ printf("+++++++++++++++++++++++\n" \ "IO base 0x%x last 0x%x\n" \ "SWIN base 0x%x last 0x%x\n" \ "MEM base 0x%x last 0x%x\n" \ "+++++++++++++++++++++++\n", \ pcibr_soft->bs_spinfo.pci_io_base, \ pcibr_soft->bs_spinfo.pci_io_last, \ pcibr_soft->bs_spinfo.pci_swin_base, \ pcibr_soft->bs_spinfo.pci_swin_last, \ pcibr_soft->bs_spinfo.pci_mem_base, \ pcibr_soft->bs_spinfo.pci_mem_last);/* * 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(devfs_handle_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; cfg_p wptr; int win; pciio_space_t space; iopaddr_t pci_io_fb, pci_io_fl; iopaddr_t pci_lo_fb, pci_lo_fl; iopaddr_t pci_hi_fb, pci_hi_fl; int nfunc; pciio_function_t rfunc; int func; devfs_handle_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(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); } /* Check for a slot with any system critical functions */ if (pcibr_is_slot_sys_critical(pcibr_vhdl, slot)) return(EPERM); /* Load the current values of allocated PCI address spaces */ PCI_ADDR_SPACE_LIMITS_LOAD(); /* Try to read the device-id/vendor-id from the config space */ cfgw = bridge->b_type0_cfg_dev[slot].l; if (pcibr_probe_slot(bridge, cfgw, &idword)) return(ENODEV); slotp = &pcibr_soft->bs_slot[slot]; slotp->slot_status |= SLOT_POWER_UP; vendor = 0xFFFF & idword; /* If the vendor id is not valid then the slot is not populated * and we are done. */ if (vendor == 0xFFFF) return(ENODEV); device = 0xFFFF & (idword >> 16); 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 = bridge->b_type0_cfg_dev[slot].f[func].l; if (pcibr_probe_slot(bridge, cfgw, &idwords[func])) { pfail |= 1 << func; continue; } vendor = 0xFFFF & idwords[func]; if (vendor == 0xFFFF) { pfail |= 1 << func; continue; } nfunc = func + 1; rfunc = 0; } cfgw = bridge->b_type0_cfg_dev[slot].l; } 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 = bridge->b_type0_cfg_dev[slot].f[func].l; 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); continue; }#if DEBUG && ATTACH_DEBUG printk(KERN_NOTICE "%s pcibr: pci slot %d func %d: vendor 0x%x device 0x%x", pcibr_soft->bs_name, slot, func, vendor, device);#endif pcibr_info = pcibr_device_info_new (pcibr_soft, slot, rfunc, vendor, device); conn_vhdl = pciio_device_info_register(pcibr_vhdl, &pcibr_info->f_c); if (func == 0) slotp->slot_conn = conn_vhdl;#ifdef SN1_LITTLE_ENDIAN cmd_reg = cfgw[(PCI_CFG_COMMAND ^ 4) / 4];#else cmd_reg = cfgw[PCI_CFG_COMMAND / 4];#endif wptr = cfgw + PCI_CFG_BASE_ADDR_0 / 4; for (win = 0; win < PCI_CFG_BASE_ADDRS; ++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. */#ifdef SN1_LITTLE_ENDIAN base = wptr[((win*4)^4)/4];#else base = wptr[win];#endif 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) &&#ifdef SN1_LITTLE_ENDIAN (wptr[(((win + 1)*4)^4)/4] != 0)) {#else (wptr[win + 1] != 0)) {#endif /* LITTLE_ENDIAN */ base = 0; /* outside permissable range */ } } if (base != 0) { /* estimate size */ size = base & -base; } else { /* calculate size */#ifdef SN1_LITTLE_ENDIAN wptr[((win*4)^4)/4] = ~0; /* turn on all bits */ size = wptr[((win*4)^4)/4]; /* get stored bits */#else wptr[win] = ~0; /* turn on all bits */ size = wptr[win]; /* get stored bits */#endif /* LITTLE_ENDIAN */ 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 this window already has PCI space * allocated for it, "subtract" that space from * our running freeblocks. Don't worry about * overlaps in existing allocated windows; we * may be overstating their sizes anyway. */ if (base && size) { if (space == PCIIO_SPACE_IO) { pcibr_freeblock_sub(&pci_io_fb, &pci_io_fl, base, size); } else { pcibr_freeblock_sub(&pci_lo_fb, &pci_lo_fl, base, size); pcibr_freeblock_sub(&pci_hi_fb, &pci_hi_fl, base, size); } }#if defined(IOC3_VENDOR_ID_NUM) && defined(IOC3_DEVICE_ID_NUM) /* * IOC3 BASE_ADDR* BUG WORKAROUND * * If we write to BASE1 on the IOC3, the * data in BASE0 is replaced. The * original workaround was to remember * the value of BASE0 and restore it * when we ran off the end of the BASE * registers; however, a later * workaround was added (I think it was * rev 1.44) to avoid setting up * anything but BASE0, with the comment * that writing all ones to BASE1 set * the enable-parity-error test feature * in IOC3's SCR bit 14. * * So, unless we defer doing any PCI * space allocation until drivers * attach, and set up a way for drivers * (the IOC3 in paricular) to tell us * generically to keep our hands off * BASE registers, we gotta "know" about * the IOC3 here. * * Too bad the PCI folks didn't reserve the * all-zero value for 'no BASE here' (it is a * valid code for an uninitialized BASE in * 32-bit PCI memory space). */ if ((vendor == IOC3_VENDOR_ID_NUM) && (device == IOC3_DEVICE_ID_NUM)) break;#endif if (code == PCI_BA_MEM_64BIT) { win++; /* skip upper half */#ifdef SN1_LITTLE_ENDIAN wptr[((win*4)^4)/4] = 0; /* which must be zero */#else wptr[win] = 0; /* which must be zero */#endif /* LITTLE_ENDIAN */ } } /* next win */ } /* next func */ /* Store back the values for allocated PCI address spaces */ PCI_ADDR_SPACE_LIMITS_STORE(); return(0);} /* * pcibr_slot_info_free * Remove all the PCI infrastructural information associated * with a particular PCI device. */intpcibr_slot_info_free(devfs_handle_t pcibr_vhdl,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -