📄 pci.c
字号:
if (pci_resource_flags(pdev, bar) & IORESOURCE_IO) { if (!request_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar), res_name)) goto err_out; } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { if (!request_mem_region(pci_resource_start(pdev, bar), pci_resource_len(pdev, bar), res_name)) goto err_out; } return 0;err_out: printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%lx@%lx for device %s\n", pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem", bar + 1, /* PCI BAR # */ pci_resource_len(pdev, bar), pci_resource_start(pdev, bar), pdev->slot_name); return -EBUSY;}/** * pci_release_regions - Release reserved PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved by pci_request_regions * * Releases all PCI I/O and memory resources previously reserved by a * successful call to pci_request_regions. Call this function only * after all use of the PCI regions has ceased. */void pci_release_regions(struct pci_dev *pdev){ int i; for (i = 0; i < 6; i++) pci_release_region(pdev, i);}/** * pci_request_regions - Reserved PCI I/O and memory resources * @pdev: PCI device whose resources are to be reserved * @res_name: Name to be associated with resource. * * Mark all PCI regions associated with PCI device @pdev as * being reserved by owner @res_name. Do not access any * address inside the PCI regions unless this call returns * successfully. * * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. */int pci_request_regions(struct pci_dev *pdev, char *res_name){ int i; for (i = 0; i < 6; i++) if(pci_request_region(pdev, i, res_name)) goto err_out; return 0;err_out: printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%lx@%lx for device %s\n", pci_resource_flags(pdev, i) & IORESOURCE_IO ? "I/O" : "mem", i + 1, /* PCI BAR # */ pci_resource_len(pdev, i), pci_resource_start(pdev, i), pdev->slot_name); while(--i >= 0) pci_release_region(pdev, i); return -EBUSY;}/* * Registration of PCI drivers and handling of hot-pluggable devices. */static LIST_HEAD(pci_drivers);/** * pci_match_device - Tell if a PCI device structure has a matching PCI device id structure * @ids: array of PCI device id structures to search in * @dev: the PCI device structure to match against * * Used by a driver to check whether a PCI device present in the * system is in its list of supported devices.Returns the matching * pci_device_id structure or %NULL if there is no match. */const struct pci_device_id *pci_match_device(const struct pci_device_id *ids, const struct pci_dev *dev){ while (ids->vendor || ids->subvendor || ids->class_mask) { if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) && (ids->device == PCI_ANY_ID || ids->device == dev->device) && (ids->subvendor == PCI_ANY_ID || ids->subvendor == dev->subsystem_vendor) && (ids->subdevice == PCI_ANY_ID || ids->subdevice == dev->subsystem_device) && !((ids->class ^ dev->class) & ids->class_mask)) return ids; ids++; } return NULL;}static intpci_announce_device(struct pci_driver *drv, struct pci_dev *dev){ const struct pci_device_id *id; int ret = 0; if (drv->id_table) { id = pci_match_device(drv->id_table, dev); if (!id) { ret = 0; goto out; } } else id = NULL; dev_probe_lock(); if (drv->probe(dev, id) >= 0) { dev->driver = drv; ret = 1; } dev_probe_unlock();out: return ret;}/** * pci_register_driver - register a new pci driver * @drv: the driver structure to register * * Adds the driver structure to the list of registered drivers * Returns the number of pci devices which were claimed by the driver * during registration. The driver remains registered even if the * return value is zero. */intpci_register_driver(struct pci_driver *drv){ struct pci_dev *dev; int count = 0; list_add_tail(&drv->node, &pci_drivers); pci_for_each_dev(dev) { if (!pci_dev_driver(dev)) count += pci_announce_device(drv, dev); } return count;}/** * pci_unregister_driver - unregister a pci driver * @drv: the driver structure to unregister * * Deletes the driver structure from the list of registered PCI drivers, * gives it a chance to clean up by calling its remove() function for * each device it was responsible for, and marks those devices as * driverless. */voidpci_unregister_driver(struct pci_driver *drv){ struct pci_dev *dev; list_del(&drv->node); pci_for_each_dev(dev) { if (dev->driver == drv) { if (drv->remove) drv->remove(dev); dev->driver = NULL; } }}#ifdef CONFIG_HOTPLUG#ifndef FALSE#define FALSE (0)#define TRUE (!FALSE)#endifstatic voidrun_sbin_hotplug(struct pci_dev *pdev, int insert){ int i; char *argv[3], *envp[8]; char id[20], sub_id[24], bus_id[24], class_id[20]; if (!hotplug_path[0]) return; sprintf(class_id, "PCI_CLASS=%04X", pdev->class); sprintf(id, "PCI_ID=%04X:%04X", pdev->vendor, pdev->device); sprintf(sub_id, "PCI_SUBSYS_ID=%04X:%04X", pdev->subsystem_vendor, pdev->subsystem_device); sprintf(bus_id, "PCI_SLOT_NAME=%s", pdev->slot_name); i = 0; argv[i++] = hotplug_path; argv[i++] = "pci"; argv[i] = 0; i = 0; /* minimal command environment */ envp[i++] = "HOME=/"; envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; /* other stuff we want to pass to /sbin/hotplug */ envp[i++] = class_id; envp[i++] = id; envp[i++] = sub_id; envp[i++] = bus_id; if (insert) envp[i++] = "ACTION=add"; else envp[i++] = "ACTION=remove"; envp[i] = 0; call_usermodehelper (argv [0], argv, envp);}/** * pci_announce_device_to_drivers - tell the drivers a new device has appeared * @dev: the device that has shown up * * Notifys the drivers that a new device has appeared, and also notifys * userspace through /sbin/hotplug. */voidpci_announce_device_to_drivers(struct pci_dev *dev){ struct list_head *ln; for(ln=pci_drivers.next; ln != &pci_drivers; ln=ln->next) { struct pci_driver *drv = list_entry(ln, struct pci_driver, node); if (drv->remove && pci_announce_device(drv, dev)) break; } /* notify userspace of new hotplug device */ run_sbin_hotplug(dev, TRUE);}/** * pci_insert_device - insert a hotplug device * @dev: the device to insert * @bus: where to insert it * * Add a new device to the device lists and notify userspace (/sbin/hotplug). */voidpci_insert_device(struct pci_dev *dev, struct pci_bus *bus){ list_add_tail(&dev->bus_list, &bus->devices); list_add_tail(&dev->global_list, &pci_devices);#ifdef CONFIG_PROC_FS pci_proc_attach_device(dev);#endif pci_announce_device_to_drivers(dev);}static voidpci_free_resources(struct pci_dev *dev){ int i; for (i = 0; i < PCI_NUM_RESOURCES; i++) { struct resource *res = dev->resource + i; if (res->parent) release_resource(res); }}/** * pci_remove_device - remove a hotplug device * @dev: the device to remove * * Delete the device structure from the device lists and * notify userspace (/sbin/hotplug). */voidpci_remove_device(struct pci_dev *dev){ if (dev->driver) { if (dev->driver->remove) dev->driver->remove(dev); dev->driver = NULL; } list_del(&dev->bus_list); list_del(&dev->global_list); pci_free_resources(dev);#ifdef CONFIG_PROC_FS pci_proc_detach_device(dev);#endif /* notify userspace of hotplug device removal */ run_sbin_hotplug(dev, FALSE);}#endifstatic struct pci_driver pci_compat_driver = { name: "compat"};/** * pci_dev_driver - get the pci_driver of a device * @dev: the device to query * * Returns the appropriate pci_driver structure or %NULL if there is no * registered driver for the device. */struct pci_driver *pci_dev_driver(const struct pci_dev *dev){ if (dev->driver) return dev->driver; else { int i; for(i=0; i<=PCI_ROM_RESOURCE; i++) if (dev->resource[i].flags & IORESOURCE_BUSY) return &pci_compat_driver; } return NULL;}/* * This interrupt-safe spinlock protects all accesses to PCI * configuration space. */static spinlock_t pci_lock = SPIN_LOCK_UNLOCKED;/* * Wrappers for all PCI configuration access functions. They just check * alignment, do locking and call the low-level functions pointed to * by pci_dev->ops. */#define PCI_byte_BAD 0#define PCI_word_BAD (pos & 1)#define PCI_dword_BAD (pos & 3)#define PCI_OP(rw,size,type) \int pci_##rw##_config_##size (struct pci_dev *dev, int pos, type value) \{ \ int res; \ unsigned long flags; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ spin_lock_irqsave(&pci_lock, flags); \ res = dev->bus->ops->rw##_##size(dev, pos, value); \ spin_unlock_irqrestore(&pci_lock, flags); \ return res; \}PCI_OP(read, byte, u8 *)PCI_OP(read, word, u16 *)PCI_OP(read, dword, u32 *)PCI_OP(write, byte, u8)PCI_OP(write, word, u16)PCI_OP(write, dword, u32)/** * pci_set_master - enables bus-mastering for device dev * @dev: the PCI device to enable * * Enables bus-mastering on the device and calls pcibios_set_master() * to do the needed arch specific settings. */voidpci_set_master(struct pci_dev *dev){ u16 cmd; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_MASTER)) { DBG("PCI: Enabling bus mastering for device %s\n", dev->slot_name); cmd |= PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, cmd); } pcibios_set_master(dev);}/** * pdev_set_mwi - arch helper function for pcibios_set_mwi * @dev: the PCI device for which MWI is enabled * * Helper function for implementation the arch-specific pcibios_set_mwi * function. Originally copied from drivers/net/acenic.c. * Copyright 1998-2001 by Jes Sorensen, <jes@trained-monkey.org>. * * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. */intpdev_set_mwi(struct pci_dev *dev){ int rc = 0; u8 cache_size; /* * Looks like this is necessary to deal with on all architectures, * even this %$#%$# N440BX Intel based thing doesn't get it right. * Ie. having two NICs in the machine, one will have the cache * line set at boot time, the other will not. */ pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &cache_size); cache_size <<= 2; if (cache_size != SMP_CACHE_BYTES) { printk(KERN_WARNING "PCI: %s PCI cache line size set incorrectly (%i bytes) by BIOS/FW.\n", dev->slot_name, cache_size); if (cache_size > SMP_CACHE_BYTES) { printk("PCI: %s cache line size too large - expecting %i.\n", dev->slot_name, SMP_CACHE_BYTES); rc = -EINVAL; } else { printk("PCI: %s PCI cache line size corrected to %i.\n", dev->slot_name, SMP_CACHE_BYTES); pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, SMP_CACHE_BYTES >> 2); } } return rc;}/** * pci_set_mwi - enables memory-write-invalidate PCI transaction * @dev: the PCI device for which MWI is enabled * * Enables the Memory-Write-Invalidate transaction in %PCI_COMMAND, * and then calls @pcibios_set_mwi to do the needed arch specific * operations or a generic mwi-prep function. * * RETURNS: An appriopriate -ERRNO error value on eror, or zero for success. */intpci_set_mwi(struct pci_dev *dev){ int rc; u16 cmd;#ifdef HAVE_ARCH_PCI_MWI rc = pcibios_set_mwi(dev);#else rc = pdev_set_mwi(dev);#endif if (rc) return rc; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (! (cmd & PCI_COMMAND_INVALIDATE)) { DBG("PCI: Enabling Mem-Wr-Inval for device %s\n", dev->slot_name); cmd |= PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); } return 0;}/** * pci_clear_mwi - disables Memory-Write-Invalidate for device dev * @dev: the PCI device to disable * * Disables PCI Memory-Write-Invalidate transaction on the device */voidpci_clear_mwi(struct pci_dev *dev){ u16 cmd; pci_read_config_word(dev, PCI_COMMAND, &cmd); if (cmd & PCI_COMMAND_INVALIDATE) { cmd &= ~PCI_COMMAND_INVALIDATE; pci_write_config_word(dev, PCI_COMMAND, cmd); }}intpci_set_dma_mask(struct pci_dev *dev, u64 mask){ if (!pci_dma_supported(dev, mask)) return -EIO; dev->dma_mask = mask; return 0;} intpci_dac_set_dma_mask(struct pci_dev *dev, u64 mask){ if (!pci_dac_dma_supported(dev, mask)) return -EIO; dev->dma_mask = mask; return 0;} /* * Translate the low bits of the PCI base * to the resource type */static inline unsigned int pci_calc_resource_flags(unsigned int flags){ if (flags & PCI_BASE_ADDRESS_SPACE_IO) return IORESOURCE_IO; if (flags & PCI_BASE_ADDRESS_MEM_PREFETCH) return IORESOURCE_MEM | IORESOURCE_PREFETCH; return IORESOURCE_MEM;}/* * Find the extent of a PCI decode, do sanity checks. */static u32 pci_size(u32 base, u32 maxbase, unsigned long mask){ u32 size = mask & maxbase; /* Find the significant bits */ if (!size) return 0; size = size & ~(size-1); /* Get the lowest of them to find the decode size */ size -= 1; /* extent = size - 1 */ if (base == maxbase && ((base | size) & mask) != mask) return 0; /* base == maxbase can be valid only if the BAR has been already programmed with all 1s */ return size;}static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom){ unsigned int pos, reg, next; u32 l, sz; struct resource *res; for(pos=0; pos<howmany; pos = next) { next = pos+1; res = &dev->resource[pos]; res->name = dev->name; reg = PCI_BASE_ADDRESS_0 + (pos << 2); pci_read_config_dword(dev, reg, &l); pci_write_config_dword(dev, reg, ~0); pci_read_config_dword(dev, reg, &sz); pci_write_config_dword(dev, reg, l); if (!sz || sz == 0xffffffff) continue; if (l == 0xffffffff) l = 0; if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { sz = pci_size(l, sz, PCI_BASE_ADDRESS_MEM_MASK); if (!sz) continue; res->start = l & PCI_BASE_ADDRESS_MEM_MASK; res->flags |= l & ~PCI_BASE_ADDRESS_MEM_MASK; } else { sz = pci_size(l, sz, PCI_BASE_ADDRESS_IO_MASK & 0xffff); if (!sz)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -