📄 pcibr_slot.c
字号:
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(IS_PIC_SOFT(pcibr_soft), 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)); } /* 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 (IS_PCIX(pcibr_soft)) { 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(IS_PIC_SOFT(pcibr_soft), 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(IS_PIC_SOFT(pcibr_soft), 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(IS_PIC_SOFT(pcibr_soft), 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(IS_PIC_SOFT(pcibr_soft), wptr, (win * 4), 4, ~0); /* write 1's */ size = do_pcibr_config_get(IS_PIC_SOFT(pcibr_soft), 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 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 */ do_pcibr_config_set(IS_PIC_SOFT(pcibr_soft), 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(1, 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(1, cfgw, PCI_CAPABILITIES_PTR, 1) & 0xfc); while (cap_nxt && (defend_against_circular_linkedlist <= 48)) { cap_id = do_pcibr_config_get(1, cfgw, cap_nxt, 1); if (cap_id == capability) { return ((cfg_p)((char *)cfgw + cap_nxt)); } cap_nxt = (do_pcibr_config_get(1, 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(devfs_handle_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);}/* * pcibr_slot_pcix_rbar_init * Allocate RBARs to the PCI-X functions on a given device */intpcibr_slot_pcix_rbar_init(pcibr_soft_t pcibr_soft, pciio_slot_t slot){ pcibr_info_h pcibr_infoh; pcibr_info_t pcibr_info; char tmp_str[256]; int nfunc; int func; if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) return(EINVAL); if ((nfunc = pcibr_soft->bs_slot[slot].bss_ninfo) < 1) return(EINVAL); if (!(pcibr_infoh = pcibr_soft->bs_slot[slot].bss_infos)) return(EINVAL); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RBAR, pcibr_soft->bs_vhdl, "pcibr_slot_pcix_rbar_init for slot %d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot))); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RBAR, pcibr_soft->bs_vhdl, "\tslot/func\trequested\tgiven\tinuse\tavail\n")); for (func = 0; func < nfunc; ++func) { cap_pcix_type0_t *pcix_cap_p; cap_pcix_stat_reg_t *pcix_statreg_p; cap_pcix_cmd_reg_t *pcix_cmdreg_p; int num_rbar; if (!(pcibr_info = pcibr_infoh[func])) continue; if (pcibr_info->f_vendor == PCIIO_VENDOR_ID_NONE) continue; if (!(pcix_cap_p = pcibr_info->f_pcix_cap)) continue; pcix_statreg_p = &pcix_cap_p->pcix_type0_status; pcix_cmdreg_p = &pcix_cap_p->pcix_type0_command; /* If there are enough RBARs to satify the number of "max outstanding * transactions" each function requested (bs_pcix_rbar_percent_allowed * is 100%), then give each function what it requested, otherwise give * the functions a "percentage of what they requested". */ if (pcibr_soft->bs_pcix_rbar_percent_allowed >= 100) { pcix_cmdreg_p->max_split = pcix_statreg_p->max_out_split; num_rbar = max_splittrans_to_numbuf[pcix_cmdreg_p->max_split]; pcibr_soft->bs_pcix_rbar_inuse += num_rbar; pcibr_soft->bs_pcix_rbar_avail -= num_rbar; pcix_cmdreg_p->max_mem_read_cnt = pcix_statreg_p->max_mem_read_cnt; } else { int index; /* index into max_splittrans_to_numbuf table */ int max_out; /* max outstanding transactions given to func */ /* Calculate the percentage of RBARs this function can have. * NOTE: Every function gets at least 1 RBAR (thus the "+1"). * bs_pcix_rbar_percent_allowed is the percentage of what was * requested less this 1 RBAR that all functions automatically * gets */ max_out = ((max_splittrans_to_numbuf[pcix_statreg_p->max_out_split] * pcibr_soft->bs_pcix_rbar_percent_allowed) / 100) + 1; /* round down the newly caclulated max_out to a valid number in * max_splittrans_to_numbuf[] */ for (index = 0; index < MAX_SPLIT_TABLE-1; index++) if (max_splittrans_to_numbuf[index + 1] > max_out) break; pcix_cmdreg_p->max_split = index; num_rbar = max_splittrans_to_numbuf[pcix_cmdreg_p->max_split]; pcibr_soft->bs_pcix_rbar_inuse += num_rbar; pcibr_soft->bs_pcix_rbar_avail -= num_rbar; pcix_cmdreg_p->max_mem_read_cnt = pcix_statreg_p->max_mem_read_cnt; } /* * The kernel only allows functions to have so many variable args, * attempting to call PCIBR_DEBUG_ALWAYS() with more than 5 printf * arguments fails so sprintf() it into a temporary string. */ if (pcibr_debug_mask & PCIBR_DEBUG_RBAR) { sprintf(tmp_str,"\t %d/%d \t %d \t %d \t %d \t %d\n", PCIBR_DEVICE_TO_SLOT(pcibr_soft, slot), func, max_splittrans_to_numbuf[pcix_statreg_p->max_out_split], max_splittrans_to_numbuf[pcix_cmdreg_p->max_split], pcibr_soft->bs_pcix_rbar_inuse, pcibr_soft->bs_pcix_rbar_avail); PCIBR_DEBUG_ALWAYS((PCIBR_DEBUG_RBAR, pcibr_soft->bs_vhdl, "%s", tmp_str)); } } return(0);}int as_debug = 0;/* * pcibr_slot_addr_space_init * Reserve chunks of PCI address space as required by * the base registers in the card. */intpcibr_slot_addr_space_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; size_t align_slot; iopaddr_t mask; int nbars; int nfunc; int func; int win; int rc = 0; pcibr_soft = pcibr_soft_get(pcibr_vhdl); if (!pcibr_soft) return(EINVAL); if (!PCIBR_VALID_SLOT(pcibr_soft, slot)) return(EINVAL); bridge = pcibr_soft->bs_base; /* allocate address space, * for windows that have not been * previously assigned. */ if (pcibr_soft->bs_slot[slot].has_host) { return(0); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -