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