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 + -
显示快捷键?