ipath_driver.c
来自「linux 内核源代码」· C语言 代码 · 共 2,286 行 · 第 1/5 页
C
2,286 行
ipath_dbg("No 64bit DMA mask, used 32 bit mask\n"); ret = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); if (ret) dev_info(&pdev->dev, "Unable to set DMA consistent mask " "for unit %u: %d\n", dd->ipath_unit, ret); } } else { ret = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (ret) dev_info(&pdev->dev, "Unable to set DMA consistent mask " "for unit %u: %d\n", dd->ipath_unit, ret); } pci_set_master(pdev); /* * Save BARs to rewrite after device reset. Save all 64 bits of * BAR, just in case. */ dd->ipath_pcibar0 = addr; dd->ipath_pcibar1 = addr >> 32; dd->ipath_deviceid = ent->device; /* save for later use */ dd->ipath_vendorid = ent->vendor; /* setup the chip-specific functions, as early as possible. */ switch (ent->device) { case PCI_DEVICE_ID_INFINIPATH_HT:#ifdef CONFIG_HT_IRQ ipath_init_iba6110_funcs(dd); break;#else ipath_dev_err(dd, "QLogic HT device 0x%x cannot work if " "CONFIG_HT_IRQ is not enabled\n", ent->device); return -ENODEV;#endif case PCI_DEVICE_ID_INFINIPATH_PE800:#ifdef CONFIG_PCI_MSI ipath_init_iba6120_funcs(dd); break;#else ipath_dev_err(dd, "QLogic PCIE device 0x%x cannot work if " "CONFIG_PCI_MSI is not enabled\n", ent->device); return -ENODEV;#endif default: ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, " "failing\n", ent->device); return -ENODEV; } for (j = 0; j < 6; j++) { if (!pdev->resource[j].start) continue; ipath_cdbg(VERBOSE, "BAR %d start %llx, end %llx, len %llx\n", j, (unsigned long long)pdev->resource[j].start, (unsigned long long)pdev->resource[j].end, (unsigned long long)pci_resource_len(pdev, j)); } if (!addr) { ipath_dev_err(dd, "No valid address in BAR 0!\n"); ret = -ENODEV; goto bail_regions; } dd->ipath_pcirev = pdev->revision;#if defined(__powerpc__) /* There isn't a generic way to specify writethrough mappings */ dd->ipath_kregbase = __ioremap(addr, len, (_PAGE_NO_CACHE|_PAGE_WRITETHRU));#else dd->ipath_kregbase = ioremap_nocache(addr, len);#endif if (!dd->ipath_kregbase) { ipath_dbg("Unable to map io addr %llx to kvirt, failing\n", addr); ret = -ENOMEM; goto bail_iounmap; } dd->ipath_kregend = (u64 __iomem *) ((void __iomem *)dd->ipath_kregbase + len); dd->ipath_physaddr = addr; /* used for io_remap, etc. */ /* for user mmap */ ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p\n", addr, dd->ipath_kregbase); /* * clear ipath_flags here instead of in ipath_init_chip as it is set * by ipath_setup_htconfig. */ dd->ipath_flags = 0; dd->ipath_lli_counter = 0; dd->ipath_lli_errors = 0; if (dd->ipath_f_bus(dd, pdev)) ipath_dev_err(dd, "Failed to setup config space; " "continuing anyway\n"); /* * set up our interrupt handler; IRQF_SHARED probably not needed, * since MSI interrupts shouldn't be shared but won't hurt for now. * check 0 irq after we return from chip-specific bus setup, since * that can affect this due to setup */ if (!dd->ipath_irq) ipath_dev_err(dd, "irq is 0, BIOS error? Interrupts won't " "work\n"); else { ret = request_irq(dd->ipath_irq, ipath_intr, IRQF_SHARED, IPATH_DRV_NAME, dd); if (ret) { ipath_dev_err(dd, "Couldn't setup irq handler, " "irq=%d: %d\n", dd->ipath_irq, ret); goto bail_iounmap; } } ret = ipath_init_chip(dd, 0); /* do the chip-specific init */ if (ret) goto bail_irqsetup; ret = ipath_enable_wc(dd); if (ret) { ipath_dev_err(dd, "Write combining not enabled " "(err %d): performance may be poor\n", -ret); ret = 0; } ipath_verify_pioperf(dd); ipath_device_create_group(&pdev->dev, dd); ipathfs_add_device(dd); ipath_user_add(dd); ipath_diag_add(dd); ipath_register_ib_device(dd); /* Check that card status in STATUS_TIMEOUT seconds. */ schedule_delayed_work(&dd->status_work, HZ * STATUS_TIMEOUT); goto bail;bail_irqsetup: if (pdev->irq) free_irq(pdev->irq, dd);bail_iounmap: iounmap((volatile void __iomem *) dd->ipath_kregbase);bail_regions: pci_release_regions(pdev);bail_disable: pci_disable_device(pdev);bail_devdata: ipath_free_devdata(pdev, dd);bail: return ret;}static void __devexit cleanup_device(struct ipath_devdata *dd){ int port; if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { /* can't do anything more with chip; needs re-init */ *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; if (dd->ipath_kregbase) { /* * if we haven't already cleaned up before these are * to ensure any register reads/writes "fail" until * re-init */ dd->ipath_kregbase = NULL; dd->ipath_uregbase = 0; dd->ipath_sregbase = 0; dd->ipath_cregbase = 0; dd->ipath_kregsize = 0; } ipath_disable_wc(dd); } if (dd->ipath_pioavailregs_dma) { dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE, (void *) dd->ipath_pioavailregs_dma, dd->ipath_pioavailregs_phys); dd->ipath_pioavailregs_dma = NULL; } if (dd->ipath_dummy_hdrq) { dma_free_coherent(&dd->pcidev->dev, dd->ipath_pd[0]->port_rcvhdrq_size, dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys); dd->ipath_dummy_hdrq = NULL; } if (dd->ipath_pageshadow) { struct page **tmpp = dd->ipath_pageshadow; dma_addr_t *tmpd = dd->ipath_physshadow; int i, cnt = 0; ipath_cdbg(VERBOSE, "Unlocking any expTID pages still " "locked\n"); for (port = 0; port < dd->ipath_cfgports; port++) { int port_tidbase = port * dd->ipath_rcvtidcnt; int maxtid = port_tidbase + dd->ipath_rcvtidcnt; for (i = port_tidbase; i < maxtid; i++) { if (!tmpp[i]) continue; pci_unmap_page(dd->pcidev, tmpd[i], PAGE_SIZE, PCI_DMA_FROMDEVICE); ipath_release_user_pages(&tmpp[i], 1); tmpp[i] = NULL; cnt++; } } if (cnt) { ipath_stats.sps_pageunlocks += cnt; ipath_cdbg(VERBOSE, "There were still %u expTID " "entries locked\n", cnt); } if (ipath_stats.sps_pagelocks || ipath_stats.sps_pageunlocks) ipath_cdbg(VERBOSE, "%llu pages locked, %llu " "unlocked via ipath_m{un}lock\n", (unsigned long long) ipath_stats.sps_pagelocks, (unsigned long long) ipath_stats.sps_pageunlocks); ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", dd->ipath_pageshadow); tmpp = dd->ipath_pageshadow; dd->ipath_pageshadow = NULL; vfree(tmpp); } /* * free any resources still in use (usually just kernel ports) * at unload; we do for portcnt, not cfgports, because cfgports * could have changed while we were loaded. */ for (port = 0; port < dd->ipath_portcnt; port++) { struct ipath_portdata *pd = dd->ipath_pd[port]; dd->ipath_pd[port] = NULL; ipath_free_pddata(dd, pd); } kfree(dd->ipath_pd); /* * debuggability, in case some cleanup path tries to use it * after this */ dd->ipath_pd = NULL;}static void __devexit ipath_remove_one(struct pci_dev *pdev){ struct ipath_devdata *dd = pci_get_drvdata(pdev); ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd); /* * disable the IB link early, to be sure no new packets arrive, which * complicates the shutdown process */ ipath_shutdown_device(dd); cancel_delayed_work(&dd->status_work); flush_scheduled_work(); if (dd->verbs_dev) ipath_unregister_ib_device(dd->verbs_dev); ipath_diag_remove(dd); ipath_user_remove(dd); ipathfs_remove_device(dd); ipath_device_remove_group(&pdev->dev, dd); ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, " "unit %u\n", dd, (u32) dd->ipath_unit); cleanup_device(dd); /* * turn off rcv, send, and interrupts for all ports, all drivers * should also hard reset the chip here? * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs * for all versions of the driver, if they were allocated */ if (dd->ipath_irq) { ipath_cdbg(VERBOSE, "unit %u free irq %d\n", dd->ipath_unit, dd->ipath_irq); dd->ipath_f_free_irq(dd); } else ipath_dbg("irq is 0, not doing free_irq " "for unit %u\n", dd->ipath_unit); /* * we check for NULL here, because it's outside * the kregbase check, and we need to call it * after the free_irq. Thus it's possible that * the function pointers were never initialized. */ if (dd->ipath_f_cleanup) /* clean up chip-specific stuff */ dd->ipath_f_cleanup(dd); ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", dd->ipath_kregbase); iounmap((volatile void __iomem *) dd->ipath_kregbase); pci_release_regions(pdev); ipath_cdbg(VERBOSE, "calling pci_disable_device\n"); pci_disable_device(pdev); ipath_free_devdata(pdev, dd);}/* general driver use */DEFINE_MUTEX(ipath_mutex);static DEFINE_SPINLOCK(ipath_pioavail_lock);/** * ipath_disarm_piobufs - cancel a range of PIO buffers * @dd: the infinipath device * @first: the first PIO buffer to cancel * @cnt: the number of PIO buffers to cancel * * cancel a range of PIO buffers, used when they might be armed, but * not triggered. Used at init to ensure buffer state, and also user * process close, in case it died while writing to a PIO buffer * Also after errors. */void ipath_disarm_piobufs(struct ipath_devdata *dd, unsigned first, unsigned cnt){ unsigned i, last = first + cnt; u64 sendctrl, sendorig; ipath_cdbg(PKT, "disarm %u PIObufs first=%u\n", cnt, first); sendorig = dd->ipath_sendctrl; for (i = first; i < last; i++) { sendctrl = sendorig | INFINIPATH_S_DISARM | (i << INFINIPATH_S_DISARMPIOBUF_SHIFT); ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, sendctrl); } /* * Write it again with current value, in case ipath_sendctrl changed * while we were looping; no critical bits that would require * locking. * * disable PIOAVAILUPD, then re-enable, reading scratch in * between. This seems to avoid a chip timing race that causes * pioavail updates to memory to stop. */ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, sendorig & ~INFINIPATH_S_PIOBUFAVAILUPD); sendorig = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl);}/** * ipath_wait_linkstate - wait for an IB link state change to occur * @dd: the infinipath device * @state: the state to wait for * @msecs: the number of milliseconds to wait * * wait up to msecs milliseconds for IB link state change to occur for * now, take the easy polling route. Currently used only by * ipath_set_linkstate. Returns 0 if state reached, otherwise * -ETIMEDOUT state can have multiple states set, for any of several * transitions. */static int ipath_wait_linkstate(struct ipath_devdata *dd, u32 state, int msecs){ dd->ipath_state_wanted = state; wait_event_interruptible_timeout(ipath_state_wait, (dd->ipath_flags & state), msecs_to_jiffies(msecs)); dd->ipath_state_wanted = 0; if (!(dd->ipath_flags & state)) { u64 val; ipath_cdbg(VERBOSE, "Didn't reach linkstate %s within %u" " ms\n", /* test INIT ahead of DOWN, both can be set */ (state & IPATH_LINKINIT) ? "INIT" : ((state & IPATH_LINKDOWN) ? "DOWN" : ((state & IPATH_LINKARMED) ? "ARM" : "ACTIVE")), msecs); val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_ibcstatus); ipath_cdbg(VERBOSE, "ibcc=%llx ibcstatus=%llx (%s)\n", (unsigned long long) ipath_read_kreg64( dd, dd->ipath_kregs->kr_ibcctrl), (unsigned long long) val, ipath_ibcstatus_str[val & 0xf]); } return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT;}/* * Decode the error status into strings, deciding whether to always * print * it or not depending on "normal packet errors" vs everything * else. Return 1 if "real" errors, otherwise 0 if only packet * errors, so caller can decide what to print with the string. */int ipath_decode_err(char *buf, size_t blen, ipath_err_t err){ int iserr = 1; *buf = '\0'; if (err & INFINIPATH_E_PKTERRS) { if (!(err & ~INFINIPATH_E_PKTERRS)) iserr = 0; // if only packet errors. if (ipath_debug & __IPATH_ERRPKTDBG) { if (err & INFINIPATH_E_REBP) strlcat(buf, "EBP ", blen); if (err & INFINIPATH_E_RVCRC) strlcat(buf, "VCRC ", blen); if (err & INFINIPATH_E_RICRC) { strlcat(buf, "CRC ", blen); // clear for check below, so only once err &= INFINIPATH_E_RICRC; } if (err & INFINIPATH_E_RSHORTPKTLEN) strlcat(buf, "rshortpktlen ", blen); if (err & INFINIPATH_E_SDROPPEDDATAPKT) strlcat(buf, "sdroppeddatapkt ", blen); if (err & INFINIPATH_E_SPKTLEN) strlcat(buf, "spktlen ", blen); } if ((err & INFINIPATH_E_RICRC) && !(err&(INFINIPATH_E_RVCRC|INFINIPATH_E_REBP))) strlcat(buf, "CRC ", blen); if (!iserr) goto done; } if (err & INFINIPATH_E_RHDRLEN) strlcat(buf, "rhdrlen ", blen); if (err & INFINIPATH_E_RBADTID) strlcat(buf, "rbadtid ", blen); if (err & INFINIPATH_E_RBADVERSION) strlcat(buf, "rbadversion ", blen); if (err & INFINIPATH_E_RHDR) strlcat(buf, "rhdr ", blen); if (err & INFINIPATH_E_RLONGPKTLEN) strlcat(buf, "rlongpktlen ", blen); if (err & INFINIPATH_E_RMAXPKTLEN)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?