ipath_pe800.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,254 行 · 第 1/3 页
C
1,254 行
else ipath_dev_err(dd, "Can't find PCI Express " "capability!\n"); return 0;}static void ipath_init_pe_variables(void){ /* * bits for selecting i2c direction and values, * used for I2C serial flash */ ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM; ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM; ipath_gpio_sda = IPATH_GPIO_SDA; ipath_gpio_scl = IPATH_GPIO_SCL; /* variables for sanity checking interrupt and errors */ infinipath_hwe_bitsextant = (INFINIPATH_HWE_RXEMEMPARITYERR_MASK << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) | (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK << INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) | INFINIPATH_HWE_PCIE1PLLFAILED | INFINIPATH_HWE_PCIE0PLLFAILED | INFINIPATH_HWE_PCIEPOISONEDTLP | INFINIPATH_HWE_PCIECPLTIMEOUT | INFINIPATH_HWE_PCIEBUSPARITYXTLH | INFINIPATH_HWE_PCIEBUSPARITYXADM | INFINIPATH_HWE_PCIEBUSPARITYRADM | INFINIPATH_HWE_MEMBISTFAILED | INFINIPATH_HWE_COREPLL_FBSLIP | INFINIPATH_HWE_COREPLL_RFSLIP | INFINIPATH_HWE_SERDESPLLFAILED | INFINIPATH_HWE_IBCBUSTOSPCPARITYERR | INFINIPATH_HWE_IBCBUSFRSPCPARITYERR; infinipath_i_bitsextant = (INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) | (INFINIPATH_I_RCVAVAIL_MASK << INFINIPATH_I_RCVAVAIL_SHIFT) | INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT | INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO; infinipath_e_bitsextant = INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC | INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN | INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN | INFINIPATH_E_RSHORTPKTLEN | INFINIPATH_E_RUNEXPCHAR | INFINIPATH_E_RUNSUPVL | INFINIPATH_E_REBP | INFINIPATH_E_RIBFLOW | INFINIPATH_E_RBADVERSION | INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_RBADTID | INFINIPATH_E_RHDRLEN | INFINIPATH_E_RHDR | INFINIPATH_E_RIBLOSTLINK | INFINIPATH_E_SMINPKTLEN | INFINIPATH_E_SMAXPKTLEN | INFINIPATH_E_SUNDERRUN | INFINIPATH_E_SPKTLEN | INFINIPATH_E_SDROPPEDSMPPKT | INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SUNEXPERRPKTNUM | INFINIPATH_E_SUNSUPVL | INFINIPATH_E_IBSTATUSCHANGED | INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET | INFINIPATH_E_HARDWARE; infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK; infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;}/* setup the MSI stuff again after a reset. I'd like to just call * pci_enable_msi() and request_irq() again, but when I do that, * the MSI enable bit doesn't get set in the command word, and * we switch to to a different interrupt vector, which is confusing, * so I instead just do it all inline. Perhaps somehow can tie this * into the PCIe hotplug support at some point * Note, because I'm doing it all here, I don't call pci_disable_msi() * or free_irq() at the start of ipath_setup_pe_reset(). */static int ipath_reinit_msi(struct ipath_devdata *dd){ int pos; u16 control; int ret; if (!dd->ipath_msi_lo) { dev_info(&dd->pcidev->dev, "Can't restore MSI config, " "initial setup failed?\n"); ret = 0; goto bail; } if (!(pos = pci_find_capability(dd->pcidev, PCI_CAP_ID_MSI))) { ipath_dev_err(dd, "Can't find MSI capability, " "can't restore MSI settings\n"); ret = 0; goto bail; } ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n", dd->ipath_msi_lo, pos + PCI_MSI_ADDRESS_LO); pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_LO, dd->ipath_msi_lo); ipath_cdbg(VERBOSE, "Writing msi_lo 0x%x to config offset 0x%x\n", dd->ipath_msi_hi, pos + PCI_MSI_ADDRESS_HI); pci_write_config_dword(dd->pcidev, pos + PCI_MSI_ADDRESS_HI, dd->ipath_msi_hi); pci_read_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, &control); if (!(control & PCI_MSI_FLAGS_ENABLE)) { ipath_cdbg(VERBOSE, "MSI control at off %x was %x, " "setting MSI enable (%x)\n", pos + PCI_MSI_FLAGS, control, control | PCI_MSI_FLAGS_ENABLE); control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dd->pcidev, pos + PCI_MSI_FLAGS, control); } /* now rewrite the data (vector) info */ pci_write_config_word(dd->pcidev, pos + ((control & PCI_MSI_FLAGS_64BIT) ? 12 : 8), dd->ipath_msi_data); /* we restore the cachelinesize also, although it doesn't really * matter */ pci_write_config_byte(dd->pcidev, PCI_CACHE_LINE_SIZE, dd->ipath_pci_cacheline); /* and now set the pci master bit again */ pci_set_master(dd->pcidev); ret = 1;bail: return ret;}/* This routine sleeps, so it can only be called from user context, not * from interrupt context. If we need interrupt context, we can split * it into two routines.*/static int ipath_setup_pe_reset(struct ipath_devdata *dd){ u64 val; int i; int ret; /* Use ERROR so it shows up in logs, etc. */ ipath_dev_err(dd, "Resetting PE-800 unit %u\n", dd->ipath_unit); /* keep chip from being accessed in a few places */ dd->ipath_flags &= ~(IPATH_INITTED|IPATH_PRESENT); val = dd->ipath_control | INFINIPATH_C_RESET; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, val); mb(); for (i = 1; i <= 5; i++) { int r; /* allow MBIST, etc. to complete; longer on each retry. * We sometimes get machine checks from bus timeout if no * response, so for now, make it *really* long. */ msleep(1000 + (1 + i) * 2000); if ((r = pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_0, dd->ipath_pcibar0))) ipath_dev_err(dd, "rewrite of BAR0 failed: %d\n", r); if ((r = pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_1, dd->ipath_pcibar1))) ipath_dev_err(dd, "rewrite of BAR1 failed: %d\n", r); /* now re-enable memory access */ if ((r = pci_enable_device(dd->pcidev))) ipath_dev_err(dd, "pci_enable_device failed after " "reset: %d\n", r); /* whether it worked or not, mark as present, again */ dd->ipath_flags |= IPATH_PRESENT; val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_revision); if (val == dd->ipath_revision) { ipath_cdbg(VERBOSE, "Got matching revision " "register %llx on try %d\n", (unsigned long long) val, i); ret = ipath_reinit_msi(dd); goto bail; } /* Probably getting -1 back */ ipath_dbg("Didn't get expected revision register, " "got %llx, try %d\n", (unsigned long long) val, i + 1); } ret = 0; /* failed */bail: return ret;}/** * ipath_pe_put_tid - write a TID in chip * @dd: the infinipath device * @tidptr: pointer to the expected TID (in chip) to udpate * @tidtype: 0 for eager, 1 for expected * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing * * This exists as a separate routine to allow for special locking etc. * It's used for both the full cleanup on exit, as well as the normal * setup and teardown. */static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr, u32 type, unsigned long pa){ u32 __iomem *tidp32 = (u32 __iomem *)tidptr; unsigned long flags = 0; /* keep gcc quiet */ if (pa != dd->ipath_tidinvalid) { if (pa & ((1U << 11) - 1)) { dev_info(&dd->pcidev->dev, "BUG: physaddr %lx " "not 4KB aligned!\n", pa); return; } pa >>= 11; /* paranoia check */ if (pa & (7<<29)) ipath_dev_err(dd, "BUG: Physical page address 0x%lx " "has bits set in 31-29\n", pa); if (type == 0) pa |= dd->ipath_tidtemplate; else /* for now, always full 4KB page */ pa |= 2 << 29; } /* workaround chip bug 9437 by writing each TID twice * and holding a spinlock around the writes, so they don't * intermix with other TID (eager or expected) writes * Unfortunately, this call can be done from interrupt level * for the port 0 eager TIDs, so we have to use irqsave */ spin_lock_irqsave(&dd->ipath_tid_lock, flags); ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xfeeddeaf); if (dd->ipath_kregbase) writel(pa, tidp32); ipath_write_kreg(dd, dd->ipath_kregs->kr_scratch, 0xdeadbeef); mmiowb(); spin_unlock_irqrestore(&dd->ipath_tid_lock, flags);}/** * ipath_pe_clear_tid - clear all TID entries for a port, expected and eager * @dd: the infinipath device * @port: the port * * clear all TID entries for a port, expected and eager. * Used from ipath_close(). On PE800, TIDs are only 32 bits, * not 64, but they are still on 64 bit boundaries, so tidbase * is declared as u64 * for the pointer math, even though we write 32 bits */static void ipath_pe_clear_tids(struct ipath_devdata *dd, unsigned port){ u64 __iomem *tidbase; unsigned long tidinv; int i; if (!dd->ipath_kregbase) return; ipath_cdbg(VERBOSE, "Invalidate TIDs for port %u\n", port); tidinv = dd->ipath_tidinvalid; tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + dd->ipath_rcvtidbase + port * dd->ipath_rcvtidcnt * sizeof(*tidbase)); for (i = 0; i < dd->ipath_rcvtidcnt; i++) ipath_pe_put_tid(dd, &tidbase[i], 0, tidinv); tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) + dd->ipath_rcvegrbase + port * dd->ipath_rcvegrcnt * sizeof(*tidbase)); for (i = 0; i < dd->ipath_rcvegrcnt; i++) ipath_pe_put_tid(dd, &tidbase[i], 1, tidinv);}/** * ipath_pe_tidtemplate - setup constants for TID updates * @dd: the infinipath device * * We setup stuff that we use a lot, to avoid calculating each time */static void ipath_pe_tidtemplate(struct ipath_devdata *dd){ u32 egrsize = dd->ipath_rcvegrbufsize; /* For now, we always allocate 4KB buffers (at init) so we can * receive max size packets. We may want a module parameter to * specify 2KB or 4KB and/or make be per port instead of per device * for those who want to reduce memory footprint. Note that the * ipath_rcvhdrentsize size must be large enough to hold the largest * IB header (currently 96 bytes) that we expect to handle (plus of * course the 2 dwords of RHF). */ if (egrsize == 2048) dd->ipath_tidtemplate = 1U << 29; else if (egrsize == 4096) dd->ipath_tidtemplate = 2U << 29; else { egrsize = 4096; dev_info(&dd->pcidev->dev, "BUG: unsupported egrbufsize " "%u, using %u\n", dd->ipath_rcvegrbufsize, egrsize); dd->ipath_tidtemplate = 2U << 29; } dd->ipath_tidinvalid = 0;}static int ipath_pe_early_init(struct ipath_devdata *dd){ dd->ipath_flags |= IPATH_4BYTE_TID; /* * For openib, we need to be able to handle an IB header of 96 bytes * or 24 dwords. HT-400 has arbitrary sized receive buffers, so we * made them the same size as the PIO buffers. The PE-800 does not * handle arbitrary size buffers, so we need the header large enough * to handle largest IB header, but still have room for a 2KB MTU * standard IB packet. */ dd->ipath_rcvhdrentsize = 24; dd->ipath_rcvhdrsize = IPATH_DFLT_RCVHDRSIZE; /* For HT-400, we allocate a somewhat overly large eager buffer, * such that we can guarantee that we can receive the largest packet * that we can send out. To truly support a 4KB MTU, we need to * bump this to a larger value. We'll do this when I get around to * testing 4KB sends on the PE-800, which I have not yet done. */ dd->ipath_rcvegrbufsize = 2048; /* * the min() check here is currently a nop, but it may not always * be, depending on just how we do ipath_rcvegrbufsize */ dd->ipath_ibmaxlen = min(dd->ipath_piosize2k, dd->ipath_rcvegrbufsize + (dd->ipath_rcvhdrentsize << 2)); dd->ipath_init_ibmaxlen = dd->ipath_ibmaxlen; /* * For PE-800, we can request a receive interrupt for 1 or * more packets from current offset. For now, we set this * up for a single packet, to match the HT-400 behavior. */ dd->ipath_rhdrhead_intr_off = 1ULL<<32; ipath_get_eeprom_info(dd); return 0;}int __attribute__((weak)) ipath_unordered_wc(void){ return 0;}/** * ipath_init_pe_get_base_info - set chip-specific flags for user code * @dd: the infinipath device * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs * HyperTransport can affect some user packet algorithims. */static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase){ struct ipath_base_info *kinfo = kbase; if (ipath_unordered_wc()) { kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER; ipath_cdbg(PROC, "Intel processor, forcing WC order\n"); } else ipath_cdbg(PROC, "Not Intel processor, WC ordered\n"); kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE; return 0;}/** * ipath_init_pe800_funcs - set up the chip-specific function pointers * @dd: the infinipath device * * This is global, and is called directly at init to set up the * chip-specific function pointers for later use. */void ipath_init_pe800_funcs(struct ipath_devdata *dd){ dd->ipath_f_intrsetup = ipath_pe_intconfig; dd->ipath_f_bus = ipath_setup_pe_config; dd->ipath_f_reset = ipath_setup_pe_reset; dd->ipath_f_get_boardname = ipath_pe_boardname; dd->ipath_f_init_hwerrors = ipath_pe_init_hwerrors; dd->ipath_f_early_init = ipath_pe_early_init; dd->ipath_f_handle_hwerrors = ipath_pe_handle_hwerrors; dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes; dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes; dd->ipath_f_clear_tids = ipath_pe_clear_tids; dd->ipath_f_put_tid = ipath_pe_put_tid; dd->ipath_f_cleanup = ipath_setup_pe_cleanup; dd->ipath_f_setextled = ipath_setup_pe_setextled; dd->ipath_f_get_base_info = ipath_pe_get_base_info; /* initialize chip-specific variables */ dd->ipath_f_tidtemplate = ipath_pe_tidtemplate; /* * setup the register offsets, since they are different for each * chip */ dd->ipath_kregs = &ipath_pe_kregs; dd->ipath_cregs = &ipath_pe_cregs; ipath_init_pe_variables();}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?