ipath_driver.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 1,993 行 · 第 1/4 页

C
1,993
字号
/* * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */#include <linux/spinlock.h>#include <linux/idr.h>#include <linux/pci.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/vmalloc.h>#include "ipath_kernel.h"#include "ips_common.h"#include "ipath_layer.h"static void ipath_update_pio_bufs(struct ipath_devdata *);const char *ipath_get_unit_name(int unit){	static char iname[16];	snprintf(iname, sizeof iname, "infinipath%u", unit);	return iname;}EXPORT_SYMBOL_GPL(ipath_get_unit_name);#define DRIVER_LOAD_MSG "PathScale " IPATH_DRV_NAME " loaded: "#define PFX IPATH_DRV_NAME ": "/* * The size has to be longer than this string, so we can append * board/chip information to it in the init code. */const char ipath_core_version[] = IPATH_IDSTR "\n";static struct idr unit_table;DEFINE_SPINLOCK(ipath_devs_lock);LIST_HEAD(ipath_dev_list);wait_queue_head_t ipath_sma_state_wait;unsigned ipath_debug = __IPATH_INFO;module_param_named(debug, ipath_debug, uint, S_IWUSR | S_IRUGO);MODULE_PARM_DESC(debug, "mask for debug prints");EXPORT_SYMBOL_GPL(ipath_debug);MODULE_LICENSE("GPL");MODULE_AUTHOR("PathScale <support@pathscale.com>");MODULE_DESCRIPTION("Pathscale InfiniPath driver");const char *ipath_ibcstatus_str[] = {	"Disabled",	"LinkUp",	"PollActive",	"PollQuiet",	"SleepDelay",	"SleepQuiet",	"LState6",		/* unused */	"LState7",		/* unused */	"CfgDebounce",	"CfgRcvfCfg",	"CfgWaitRmt",	"CfgIdle",	"RecovRetrain",	"LState0xD",		/* unused */	"RecovWaitRmt",	"RecovIdle",};/* * These variables are initialized in the chip-specific files * but are defined here. */u16 ipath_gpio_sda_num, ipath_gpio_scl_num;u64 ipath_gpio_sda, ipath_gpio_scl;u64 infinipath_i_bitsextant;ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;static void __devexit ipath_remove_one(struct pci_dev *);static int __devinit ipath_init_one(struct pci_dev *,				    const struct pci_device_id *);/* Only needed for registration, nothing else needs this info */#define PCI_VENDOR_ID_PATHSCALE 0x1fc1#define PCI_DEVICE_ID_INFINIPATH_HT 0xd#define PCI_DEVICE_ID_INFINIPATH_PE800 0x10static const struct pci_device_id ipath_pci_tbl[] = {	{ PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE, PCI_DEVICE_ID_INFINIPATH_HT) },	{ PCI_DEVICE(PCI_VENDOR_ID_PATHSCALE, PCI_DEVICE_ID_INFINIPATH_PE800) },	{ 0, }};MODULE_DEVICE_TABLE(pci, ipath_pci_tbl);static struct pci_driver ipath_driver = {	.name = IPATH_DRV_NAME,	.probe = ipath_init_one,	.remove = __devexit_p(ipath_remove_one),	.id_table = ipath_pci_tbl,};/* * This is where port 0's rcvhdrtail register is written back; we also * want nothing else sharing the cache line, so make it a cache line * in size.  Used for all units. */volatile __le64 *ipath_port0_rcvhdrtail;dma_addr_t ipath_port0_rcvhdrtail_dma;static int port0_rcvhdrtail_refs;static inline void read_bars(struct ipath_devdata *dd, struct pci_dev *dev,			     u32 *bar0, u32 *bar1){	int ret;	ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, bar0);	if (ret)		ipath_dev_err(dd, "failed to read bar0 before enable: "			      "error %d\n", -ret);	ret = pci_read_config_dword(dev, PCI_BASE_ADDRESS_1, bar1);	if (ret)		ipath_dev_err(dd, "failed to read bar1 before enable: "			      "error %d\n", -ret);	ipath_dbg("Read bar0 %x bar1 %x\n", *bar0, *bar1);}static void ipath_free_devdata(struct pci_dev *pdev,			       struct ipath_devdata *dd){	unsigned long flags;	pci_set_drvdata(pdev, NULL);	if (dd->ipath_unit != -1) {		spin_lock_irqsave(&ipath_devs_lock, flags);		idr_remove(&unit_table, dd->ipath_unit);		list_del(&dd->ipath_list);		spin_unlock_irqrestore(&ipath_devs_lock, flags);	}	dma_free_coherent(&pdev->dev, sizeof(*dd), dd, dd->ipath_dma_addr);}static struct ipath_devdata *ipath_alloc_devdata(struct pci_dev *pdev){	unsigned long flags;	struct ipath_devdata *dd;	dma_addr_t dma_addr;	int ret;	if (!idr_pre_get(&unit_table, GFP_KERNEL)) {		dd = ERR_PTR(-ENOMEM);		goto bail;	}	dd = dma_alloc_coherent(&pdev->dev, sizeof(*dd), &dma_addr,				GFP_KERNEL);	if (!dd) {		dd = ERR_PTR(-ENOMEM);		goto bail;	}	dd->ipath_dma_addr = dma_addr;	dd->ipath_unit = -1;	spin_lock_irqsave(&ipath_devs_lock, flags);	ret = idr_get_new(&unit_table, dd, &dd->ipath_unit);	if (ret < 0) {		printk(KERN_ERR IPATH_DRV_NAME		       ": Could not allocate unit ID: error %d\n", -ret);		ipath_free_devdata(pdev, dd);		dd = ERR_PTR(ret);		goto bail_unlock;	}	dd->pcidev = pdev;	pci_set_drvdata(pdev, dd);	list_add(&dd->ipath_list, &ipath_dev_list);bail_unlock:	spin_unlock_irqrestore(&ipath_devs_lock, flags);bail:	return dd;}static inline struct ipath_devdata *__ipath_lookup(int unit){	return idr_find(&unit_table, unit);}struct ipath_devdata *ipath_lookup(int unit){	struct ipath_devdata *dd;	unsigned long flags;	spin_lock_irqsave(&ipath_devs_lock, flags);	dd = __ipath_lookup(unit);	spin_unlock_irqrestore(&ipath_devs_lock, flags);	return dd;}int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp){	int nunits, npresent, nup;	struct ipath_devdata *dd;	unsigned long flags;	u32 maxports;	nunits = npresent = nup = maxports = 0;	spin_lock_irqsave(&ipath_devs_lock, flags);	list_for_each_entry(dd, &ipath_dev_list, ipath_list) {		nunits++;		if ((dd->ipath_flags & IPATH_PRESENT) && dd->ipath_kregbase)			npresent++;		if (dd->ipath_lid &&		    !(dd->ipath_flags & (IPATH_DISABLED | IPATH_LINKDOWN					 | IPATH_LINKUNK)))			nup++;		if (dd->ipath_cfgports > maxports)			maxports = dd->ipath_cfgports;	}	spin_unlock_irqrestore(&ipath_devs_lock, flags);	if (npresentp)		*npresentp = npresent;	if (nupp)		*nupp = nup;	if (maxportsp)		*maxportsp = maxports;	return nunits;}static int init_port0_rcvhdrtail(struct pci_dev *pdev){	int ret;	mutex_lock(&ipath_mutex);	if (!ipath_port0_rcvhdrtail) {		ipath_port0_rcvhdrtail =			dma_alloc_coherent(&pdev->dev,					   IPATH_PORT0_RCVHDRTAIL_SIZE,					   &ipath_port0_rcvhdrtail_dma,					   GFP_KERNEL);		if (!ipath_port0_rcvhdrtail) {			ret = -ENOMEM;			goto bail;		}	}	port0_rcvhdrtail_refs++;	ret = 0;bail:	mutex_unlock(&ipath_mutex);	return ret;}static void cleanup_port0_rcvhdrtail(struct pci_dev *pdev){	mutex_lock(&ipath_mutex);	if (!--port0_rcvhdrtail_refs) {		dma_free_coherent(&pdev->dev, IPATH_PORT0_RCVHDRTAIL_SIZE,				  (void *) ipath_port0_rcvhdrtail,				  ipath_port0_rcvhdrtail_dma);		ipath_port0_rcvhdrtail = NULL;	}	mutex_unlock(&ipath_mutex);}/* * These next two routines are placeholders in case we don't have per-arch * code for controlling write combining.  If explicit control of write * combining is not available, performance will probably be awful. */int __attribute__((weak)) ipath_enable_wc(struct ipath_devdata *dd){	return -EOPNOTSUPP;}void __attribute__((weak)) ipath_disable_wc(struct ipath_devdata *dd){}static int __devinit ipath_init_one(struct pci_dev *pdev,				    const struct pci_device_id *ent){	int ret, len, j;	struct ipath_devdata *dd;	unsigned long long addr;	u32 bar0 = 0, bar1 = 0;	u8 rev;	ret = init_port0_rcvhdrtail(pdev);	if (ret < 0) {		printk(KERN_ERR IPATH_DRV_NAME		       ": Could not allocate port0_rcvhdrtail: error %d\n",		       -ret);		goto bail;	}	dd = ipath_alloc_devdata(pdev);	if (IS_ERR(dd)) {		ret = PTR_ERR(dd);		printk(KERN_ERR IPATH_DRV_NAME		       ": Could not allocate devdata: error %d\n", -ret);		goto bail_rcvhdrtail;	}	ipath_cdbg(VERBOSE, "initializing unit #%u\n", dd->ipath_unit);	read_bars(dd, pdev, &bar0, &bar1);	ret = pci_enable_device(pdev);	if (ret) {		/* This can happen iff:		 *		 * We did a chip reset, and then failed to reprogram the		 * BAR, or the chip reset due to an internal error.  We then		 * unloaded the driver and reloaded it.		 *		 * Both reset cases set the BAR back to initial state.  For		 * the latter case, the AER sticky error bit at offset 0x718		 * should be set, but the Linux kernel doesn't yet know		 * about that, it appears.  If the original BAR was retained		 * in the kernel data structures, this may be OK.		 */		ipath_dev_err(dd, "enable unit %d failed: error %d\n",			      dd->ipath_unit, -ret);		goto bail_devdata;	}	addr = pci_resource_start(pdev, 0);	len = pci_resource_len(pdev, 0);	ipath_cdbg(VERBOSE, "regbase (0) %llx len %d irq %x, vend %x/%x "		   "driver_data %lx\n", addr, len, pdev->irq, ent->vendor,		   ent->device, ent->driver_data);	read_bars(dd, pdev, &bar0, &bar1);	if (!bar1 && !(bar0 & ~0xf)) {		if (addr) {			dev_info(&pdev->dev, "BAR is 0 (probable RESET), "				 "rewriting as %llx\n", addr);			ret = pci_write_config_dword(				pdev, PCI_BASE_ADDRESS_0, addr);			if (ret) {				ipath_dev_err(dd, "rewrite of BAR0 "					      "failed: err %d\n", -ret);				goto bail_disable;			}			ret = pci_write_config_dword(				pdev, PCI_BASE_ADDRESS_1, addr >> 32);			if (ret) {				ipath_dev_err(dd, "rewrite of BAR1 "					      "failed: err %d\n", -ret);				goto bail_disable;			}		} else {			ipath_dev_err(dd, "BAR is 0 (probable RESET), "				      "not usable until reboot\n");			ret = -ENODEV;			goto bail_disable;		}	}	ret = pci_request_regions(pdev, IPATH_DRV_NAME);	if (ret) {		dev_info(&pdev->dev, "pci_request_regions unit %u fails: "			 "err %d\n", dd->ipath_unit, -ret);		goto bail_disable;	}	ret = pci_set_dma_mask(pdev, DMA_64BIT_MASK);	if (ret) {		/*		 * if the 64 bit setup fails, try 32 bit.  Some systems		 * do not setup 64 bit maps on systems with 2GB or less		 * memory installed.		 */		ret = pci_set_dma_mask(pdev, DMA_32BIT_MASK);		if (ret) {			dev_info(&pdev->dev, "pci_set_dma_mask unit %u "				 "fails: %d\n", dd->ipath_unit, ret);			goto bail_regions;		}		else			ipath_dbg("No 64bit DMA mask, used 32 bit mask\n");	}	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:		ipath_init_ht400_funcs(dd);		break;	case PCI_DEVICE_ID_INFINIPATH_PE800:		ipath_init_pe800_funcs(dd);		break;	default:		ipath_dev_err(dd, "Found unknown PathScale 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 %lx, end %lx, len %lx\n",			   j, pdev->resource[j].start,			   pdev->resource[j].end,			   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_deviceid = ent->device;	/* save for later use */	dd->ipath_vendorid = ent->vendor;	ret = pci_read_config_byte(pdev, PCI_REVISION_ID, &rev);	if (ret) {		ipath_dev_err(dd, "Failed to read PCI revision ID unit "			      "%u: err %d\n", dd->ipath_unit, -ret);		goto bail_regions;	/* shouldn't ever happen */	}	dd->ipath_pcirev = rev;	dd->ipath_kregbase = ioremap_nocache(addr, len);	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 */	dd->ipath_kregvirt = (u64 __iomem *) phys_to_virt(addr);	ipath_cdbg(VERBOSE, "mapped io addr %llx to kregbase %p "

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?