ipath_driver.c
来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,993 行 · 第 1/4 页
C
1,993 行
ret = -ENODEV; break; } } while (1); return ret;}void ipath_set_ib_lstate(struct ipath_devdata *dd, int which){ static const char *what[4] = { [0] = "DOWN", [INFINIPATH_IBCC_LINKCMD_INIT] = "INIT", [INFINIPATH_IBCC_LINKCMD_ARMED] = "ARMED", [INFINIPATH_IBCC_LINKCMD_ACTIVE] = "ACTIVE" }; ipath_cdbg(SMA, "Trying to move unit %u to %s, current ltstate " "is %s\n", dd->ipath_unit, what[(which >> INFINIPATH_IBCC_LINKCMD_SHIFT) & INFINIPATH_IBCC_LINKCMD_MASK], ipath_ibcstatus_str[ (ipath_read_kreg64 (dd, dd->ipath_kregs->kr_ibcstatus) >> INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) & INFINIPATH_IBCS_LINKTRAININGSTATE_MASK]); ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, dd->ipath_ibcctrl | which);}/** * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register * @dd: the infinipath device * @regno: the register number to read * @port: the port containing the register * * Registers that vary with the chip implementation constants (port) * use this routine. */u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno, unsigned port){ u16 where; if (port < dd->ipath_portcnt && (regno == dd->ipath_kregs->kr_rcvhdraddr || regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) where = regno + port; else where = -1; return ipath_read_kreg64(dd, where);}/** * ipath_write_kreg_port - write a device's per-port 64-bit kernel register * @dd: the infinipath device * @regno: the register number to write * @port: the port containing the register * @value: the value to write * * Registers that vary with the chip implementation constants (port) * use this routine. */void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno, unsigned port, u64 value){ u16 where; if (port < dd->ipath_portcnt && (regno == dd->ipath_kregs->kr_rcvhdraddr || regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) where = regno + port; else where = -1; ipath_write_kreg(dd, where, value);}/** * ipath_shutdown_device - shut down a device * @dd: the infinipath device * * This is called to make the device quiet when we are about to * unload the driver, and also when the device is administratively * disabled. It does not free any data structures. * Everything it does has to be setup again by ipath_init_chip(dd,1) */void ipath_shutdown_device(struct ipath_devdata *dd){ u64 val; ipath_dbg("Shutting down the device\n"); dd->ipath_flags |= IPATH_LINKUNK; dd->ipath_flags &= ~(IPATH_INITTED | IPATH_LINKDOWN | IPATH_LINKINIT | IPATH_LINKARMED | IPATH_LINKACTIVE); *dd->ipath_statusp &= ~(IPATH_STATUS_IB_CONF | IPATH_STATUS_IB_READY); /* mask interrupts, but not errors */ ipath_write_kreg(dd, dd->ipath_kregs->kr_intmask, 0ULL); dd->ipath_rcvctrl = 0; ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, dd->ipath_rcvctrl); /* * gracefully stop all sends allowing any in progress to trickle out * first. */ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, 0ULL); /* flush it */ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch); /* * enough for anything that's going to trickle out to have actually * done so. */ udelay(5); /* * abort any armed or launched PIO buffers that didn't go. (self * clearing). Will cause any packet currently being transmitted to * go out with an EBP, and may also cause a short packet error on * the receiver. */ ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, INFINIPATH_S_ABORT); ipath_set_ib_lstate(dd, INFINIPATH_IBCC_LINKINITCMD_DISABLE << INFINIPATH_IBCC_LINKINITCMD_SHIFT); /* * we are shutting down, so tell the layered driver. We don't do * this on just a link state change, much like ethernet, a cable * unplug, etc. doesn't change driver state */ ipath_layer_intr(dd, IPATH_LAYER_INT_IF_DOWN); /* disable IBC */ dd->ipath_control &= ~INFINIPATH_C_LINKENABLE; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, dd->ipath_control); /* * clear SerdesEnable and turn the leds off; do this here because * we are unloading, so don't count on interrupts to move along * Turn the LEDs off explictly for the same reason. */ dd->ipath_f_quiet_serdes(dd); dd->ipath_f_setextled(dd, 0, 0); if (dd->ipath_stats_timer_active) { del_timer_sync(&dd->ipath_stats_timer); dd->ipath_stats_timer_active = 0; } /* * clear all interrupts and errors, so that the next time the driver * is loaded or device is enabled, we know that whatever is set * happened while we were unloaded */ ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrclear, ~0ULL & ~INFINIPATH_HWE_MEMBISTFAILED); ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, -1LL); ipath_write_kreg(dd, dd->ipath_kregs->kr_intclear, -1LL);}/** * ipath_free_pddata - free a port's allocated data * @dd: the infinipath device * @port: the port * @freehdrq: free the port data structure if true * * when closing, free up any allocated data for a port, if the * reference count goes to zero * Note: this also optionally frees the portdata itself! * Any changes here have to be matched up with the reinit case * of ipath_init_chip(), which calls this routine on reinit after reset. */void ipath_free_pddata(struct ipath_devdata *dd, u32 port, int freehdrq){ struct ipath_portdata *pd = dd->ipath_pd[port]; if (!pd) return; if (freehdrq) /* * only clear and free portdata if we are going to also * release the hdrq, otherwise we leak the hdrq on each * open/close cycle */ dd->ipath_pd[port] = NULL; if (freehdrq && pd->port_rcvhdrq) { ipath_cdbg(VERBOSE, "free closed port %d rcvhdrq @ %p " "(size=%lu)\n", pd->port_port, pd->port_rcvhdrq, (unsigned long) pd->port_rcvhdrq_size); dma_free_coherent(&dd->pcidev->dev, pd->port_rcvhdrq_size, pd->port_rcvhdrq, pd->port_rcvhdrq_phys); pd->port_rcvhdrq = NULL; } if (port && pd->port_rcvegrbuf) { /* always free this */ if (pd->port_rcvegrbuf) { unsigned e; for (e = 0; e < pd->port_rcvegrbuf_chunks; e++) { void *base = pd->port_rcvegrbuf[e]; size_t size = pd->port_rcvegrbuf_size; ipath_cdbg(VERBOSE, "egrbuf free(%p, %lu), " "chunk %u/%u\n", base, (unsigned long) size, e, pd->port_rcvegrbuf_chunks); dma_free_coherent( &dd->pcidev->dev, size, base, pd->port_rcvegrbuf_phys[e]); } vfree(pd->port_rcvegrbuf); pd->port_rcvegrbuf = NULL; vfree(pd->port_rcvegrbuf_phys); pd->port_rcvegrbuf_phys = NULL; } pd->port_rcvegrbuf_chunks = 0; } else if (port == 0 && dd->ipath_port0_skbs) { unsigned e; struct sk_buff **skbs = dd->ipath_port0_skbs; dd->ipath_port0_skbs = NULL; ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs " "@ %p\n", pd->port_port, skbs); for (e = 0; e < dd->ipath_rcvegrcnt; e++) if (skbs[e]) dev_kfree_skb(skbs[e]); vfree(skbs); } if (freehdrq) { kfree(pd->port_tid_pg_list); kfree(pd); }}static int __init infinipath_init(void){ int ret; ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ipath_core_version); /* * These must be called before the driver is registered with * the PCI subsystem. */ idr_init(&unit_table); if (!idr_pre_get(&unit_table, GFP_KERNEL)) { ret = -ENOMEM; goto bail; } ret = pci_register_driver(&ipath_driver); if (ret < 0) { printk(KERN_ERR IPATH_DRV_NAME ": Unable to register driver: error %d\n", -ret); goto bail_unit; } ret = ipath_driver_create_group(&ipath_driver.driver); if (ret < 0) { printk(KERN_ERR IPATH_DRV_NAME ": Unable to create driver " "sysfs entries: error %d\n", -ret); goto bail_pci; } ret = ipath_init_ipathfs(); if (ret < 0) { printk(KERN_ERR IPATH_DRV_NAME ": Unable to create " "ipathfs: error %d\n", -ret); goto bail_group; } goto bail;bail_group: ipath_driver_remove_group(&ipath_driver.driver);bail_pci: pci_unregister_driver(&ipath_driver);bail_unit: idr_destroy(&unit_table);bail: return ret;}static void cleanup_device(struct ipath_devdata *dd){ int port; ipath_shutdown_device(dd); 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_kregvirt = 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_pageshadow) { struct page **tmpp = dd->ipath_pageshadow; 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; 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); vfree(dd->ipath_pageshadow); dd->ipath_pageshadow = NULL; } /* * free any resources still in use (usually just kernel ports) * at unload */ for (port = 0; port < dd->ipath_cfgports; port++) ipath_free_pddata(dd, port, 1); kfree(dd->ipath_pd); /* * debuggability, in case some cleanup path tries to use it * after this */ dd->ipath_pd = NULL;}static void __exit infinipath_cleanup(void){ struct ipath_devdata *dd, *tmp; unsigned long flags; ipath_exit_ipathfs(); ipath_driver_remove_group(&ipath_driver.driver); spin_lock_irqsave(&ipath_devs_lock, flags); /* * 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 */ list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) { spin_unlock_irqrestore(&ipath_devs_lock, flags); if (dd->ipath_kregbase) cleanup_device(dd); if (dd->pcidev) { if (dd->pcidev->irq) { ipath_cdbg(VERBOSE, "unit %u free_irq of irq %x\n", dd->ipath_unit, dd->pcidev->irq); free_irq(dd->pcidev->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); dd->pcidev = NULL; } spin_lock_irqsave(&ipath_devs_lock, flags); } spin_unlock_irqrestore(&ipath_devs_lock, flags); ipath_cdbg(VERBOSE, "Unregistering pci driver\n"); pci_unregister_driver(&ipath_driver); idr_destroy(&unit_table);}/** * ipath_reset_device - reset the chip if possible * @unit: the device to reset * * Whether or not reset is successful, we attempt to re-initialize the chip * (that is, much like a driver unload/reload). We clear the INITTED flag * so that the various entry points will fail until we reinitialize. For * now, we only allow this if no user ports are open that use chip resources */int ipath_reset_device(int unit){ int ret, i; struct ipath_devdata *dd = ipath_lookup(unit); if (!dd) { ret = -ENODEV; goto bail; } dev_info(&dd->pcidev->dev, "Reset on unit %u requested\n", unit); if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT)) { dev_info(&dd->pcidev->dev, "Invalid unit number %u or " "not initialized or not present\n", unit); ret = -ENXIO; goto bail; } if (dd->ipath_pd) for (i = 1; i < dd->ipath_cfgports; i++) { if (dd->ipath_pd[i] && dd->ipath_pd[i]->port_cnt) { ipath_dbg("unit %u port %d is in use " "(PID %u cmd %s), can't reset\n", unit, i, dd->ipath_pd[i]->port_pid, dd->ipath_pd[i]->port_comm); ret = -EBUSY; goto bail; } } dd->ipath_flags &= ~IPATH_INITTED; ret = dd->ipath_f_reset(dd); if (ret != 1) ipath_dbg("reset was not successful\n"); ipath_dbg("Trying to reinitialize unit %u after reset attempt\n", unit); ret = ipath_init_chip(dd, 1); if (ret) ipath_dev_err(dd, "Reinitialize unit %u after " "reset failed with %d\n", unit, ret); else dev_info(&dd->pcidev->dev, "Reinitialized unit %u after " "resetting\n", unit);bail: return ret;}module_init(infinipath_init);module_exit(infinipath_cleanup);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?