📄 pci.c
字号:
/* * $Id: pci.c,v 1.91 1999/01/21 13:34:01 davem Exp $ * * PCI Bus Services, see include/linux/pci.h for further explanation. * * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, * David Mosberger-Tang * * Copyright 1997 -- 2000 Martin Mares <mj@ucw.cz> */#include <linux/config.h>#include <linux/module.h>#include <linux/types.h>#include <linux/kernel.h>#include <linux/pci.h>#include <linux/string.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/spinlock.h>#include <linux/pm.h>#include <linux/kmod.h> /* for hotplug_path */#include <linux/bitops.h>#include <linux/delay.h>#include <linux/cache.h>#include <asm/page.h>#include <asm/dma.h> /* isa_dma_bridge_buggy */#undef DEBUG#ifdef DEBUG#define DBG(x...) printk(x)#else#define DBG(x...)#endifLIST_HEAD(pci_root_buses);LIST_HEAD(pci_devices);/** * pci_find_slot - locate PCI device from a given PCI slot * @bus: number of PCI bus on which desired PCI device resides * @devfn: encodes number of PCI slot in which the desired PCI * device resides and the logical device number within that slot * in case of multi-function devices. * * Given a PCI bus and slot/function number, the desired PCI device * is located in system global list of PCI devices. If the device * is found, a pointer to its data structure is returned. If no * device is found, %NULL is returned. */struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn){ struct pci_dev *dev; pci_for_each_dev(dev) { if (dev->bus->number == bus && dev->devfn == devfn) return dev; } return NULL;}/** * pci_find_subsys - begin or continue searching for a PCI device by vendor/subvendor/device/subdevice id * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids * @ss_vendor: PCI subsystem vendor id to match, or %PCI_ANY_ID to match all vendor ids * @ss_device: PCI subsystem device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is * found with a matching @vendor, @device, @ss_vendor and @ss_device, a pointer to its * device structure is returned. Otherwise, %NULL is returned. * A new search is initiated by passing %NULL to the @from argument. * Otherwise if @from is not %NULL, searches continue from next device on the global list. */struct pci_dev *pci_find_subsys(unsigned int vendor, unsigned int device, unsigned int ss_vendor, unsigned int ss_device, const struct pci_dev *from){ struct list_head *n = from ? from->global_list.next : pci_devices.next; while (n != &pci_devices) { struct pci_dev *dev = pci_dev_g(n); if ((vendor == PCI_ANY_ID || dev->vendor == vendor) && (device == PCI_ANY_ID || dev->device == device) && (ss_vendor == PCI_ANY_ID || dev->subsystem_vendor == ss_vendor) && (ss_device == PCI_ANY_ID || dev->subsystem_device == ss_device)) return dev; n = n->next; } return NULL;}/** * pci_find_device - begin or continue searching for a PCI device by vendor/device id * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is * found with a matching @vendor and @device, a pointer to its device structure is * returned. Otherwise, %NULL is returned. * A new search is initiated by passing %NULL to the @from argument. * Otherwise if @from is not %NULL, searches continue from next device on the global list. */struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, const struct pci_dev *from){ return pci_find_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from);}/** * pci_find_class - begin or continue searching for a PCI device by class * @class: search for a PCI device with this class designation * @from: Previous PCI device found in search, or %NULL for new search. * * Iterates through the list of known PCI devices. If a PCI device is * found with a matching @class, a pointer to its device structure is * returned. Otherwise, %NULL is returned. * A new search is initiated by passing %NULL to the @from argument. * Otherwise if @from is not %NULL, searches continue from next device * on the global list. */struct pci_dev *pci_find_class(unsigned int class, const struct pci_dev *from){ struct list_head *n = from ? from->global_list.next : pci_devices.next; while (n != &pci_devices) { struct pci_dev *dev = pci_dev_g(n); if (dev->class == class) return dev; n = n->next; } return NULL;}/** * pci_find_capability - query for devices' capabilities * @dev: PCI device to query * @cap: capability code * * Tell if a device supports a given PCI capability. * Returns the address of the requested capability structure within the * device's PCI configuration space or 0 in case the device does not * support it. Possible values for @cap: * * %PCI_CAP_ID_PM Power Management * * %PCI_CAP_ID_AGP Accelerated Graphics Port * * %PCI_CAP_ID_VPD Vital Product Data * * %PCI_CAP_ID_SLOTID Slot Identification * * %PCI_CAP_ID_MSI Message Signalled Interrupts * * %PCI_CAP_ID_CHSWP CompactPCI HotSwap */intpci_find_capability(struct pci_dev *dev, int cap){ u16 status; u8 pos, id; int ttl = 48; pci_read_config_word(dev, PCI_STATUS, &status); if (!(status & PCI_STATUS_CAP_LIST)) return 0; switch (dev->hdr_type) { case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_BRIDGE: pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos); break; case PCI_HEADER_TYPE_CARDBUS: pci_read_config_byte(dev, PCI_CB_CAPABILITY_LIST, &pos); break; default: return 0; } while (ttl-- && pos >= 0x40) { pos &= ~3; pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id); if (id == 0xff) break; if (id == cap) return pos; pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos); } return 0;}/** * pci_find_parent_resource - return resource region of parent bus of given region * @dev: PCI device structure contains resources to be searched * @res: child resource record for which parent is sought * * For given resource region of given device, return the resource * region of parent bus the given region is contained in or where * it should be allocated from. */struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res){ const struct pci_bus *bus = dev->bus; int i; struct resource *best = NULL; for(i=0; i<4; i++) { struct resource *r = bus->resource[i]; if (!r) continue; if (res->start && !(res->start >= r->start && res->end <= r->end)) continue; /* Not contained */ if ((res->flags ^ r->flags) & (IORESOURCE_IO | IORESOURCE_MEM)) continue; /* Wrong type */ if (!((res->flags ^ r->flags) & IORESOURCE_PREFETCH)) return r; /* Exact match */ if ((res->flags & IORESOURCE_PREFETCH) && !(r->flags & IORESOURCE_PREFETCH)) best = r; /* Approximating prefetchable by non-prefetchable */ } return best;}/** * pci_set_power_state - Set the power state of a PCI device * @dev: PCI device to be suspended * @state: Power state we're entering * * Transition a device to a new power state, using the Power Management * Capabilities in the device's config space. * * RETURN VALUE: * -EINVAL if trying to enter a lower state than we're already in. * 0 if we're already in the requested state. * -EIO if device does not support PCI PM. * 0 if we can successfully change the power state. */intpci_set_power_state(struct pci_dev *dev, int state){ int pm; u16 pmcsr; /* bound the state we're entering */ if (state > 3) state = 3; /* Validate current state: * Can enter D0 from any state, but if we can only go deeper * to sleep if we're already in a low power state */ if (state > 0 && dev->current_state > state) return -EINVAL; else if (dev->current_state == state) return 0; /* we're already there */ /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); /* abort if the device doesn't support PM capabilities */ if (!pm) return -EIO; /* check if this device supports the desired state */ if (state == 1 || state == 2) { u16 pmc; pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc); if (state == 1 && !(pmc & PCI_PM_CAP_D1)) return -EIO; else if (state == 2 && !(pmc & PCI_PM_CAP_D2)) return -EIO; } /* If we're in D3, force entire word to 0. * This doesn't affect PME_Status, disables PME_En, and * sets PowerState to 0. */ if (dev->current_state >= 3) pmcsr = 0; else { pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr); pmcsr &= ~PCI_PM_CTRL_STATE_MASK; pmcsr |= state; } /* enter specified state */ pci_write_config_word(dev, pm + PCI_PM_CTRL, pmcsr); /* Mandatory power management transition delays */ /* see PCI PM 1.1 5.6.1 table 18 */ if(state == 3 || dev->current_state == 3) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(HZ/100); } else if(state == 2 || dev->current_state == 2) udelay(200); dev->current_state = state; return 0;}/** * pci_save_state - save the PCI configuration space of a device before suspending * @dev: - PCI device that we're dealing with * @buffer: - buffer to hold config space context * * @buffer must be large enough to hold the entire PCI 2.2 config space * (>= 64 bytes). */intpci_save_state(struct pci_dev *dev, u32 *buffer){ int i; if (buffer) { /* XXX: 100% dword access ok here? */ for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&buffer[i]); } return 0;}/** * pci_restore_state - Restore the saved state of a PCI device * @dev: - PCI device that we're dealing with * @buffer: - saved PCI config space * */int pci_restore_state(struct pci_dev *dev, u32 *buffer){ int i; if (buffer) { for (i = 0; i < 16; i++) pci_write_config_dword(dev,i * 4, buffer[i]); } /* * otherwise, write the context information we know from bootup. * This works around a problem where warm-booting from Windows * combined with a D3(hot)->D0 transition causes PCI config * header data to be forgotten. */ else { for (i = 0; i < 6; i ++) pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + (i * 4), dev->resource[i].start); pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); } return 0;}int pci_compare_state(struct pci_dev *dev, u32 *buffer){ int i; unsigned int temp; if (buffer) { for (i = 0; i < 16; i++) { pci_read_config_dword(dev,i*4,&temp); if (temp!=buffer[i]) return 1; } } return 0;}int pci_generic_suspend_save(struct pci_dev *pdev, u32 state){ if (pdev) pci_save_state(pdev,pdev->saved_state); return 0;}int pci_generic_resume_restore(struct pci_dev *pdev){ if (pdev) pci_restore_state(pdev,pdev->saved_state); return 0; }int pci_generic_resume_compare(struct pci_dev *pdev){ int retval=0; if (pdev) retval = pci_compare_state(pdev,pdev->saved_state); return retval; }EXPORT_SYMBOL(pci_generic_suspend_save);EXPORT_SYMBOL(pci_generic_resume_restore);EXPORT_SYMBOL(pci_generic_resume_compare);/** * pci_enable_device - Initialize device before it's used by a driver. * @dev: PCI device to be initialized * * Initialize device before it's used by a driver. Ask low-level code * to enable I/O and memory. Wake up the device if it was suspended. * Beware, this function can fail. */intpci_enable_device(struct pci_dev *dev){ int err; pci_set_power_state(dev, 0); if ((err = pcibios_enable_device(dev)) < 0) return err; return 0;}/** * pci_disable_device - Disable PCI device after use * @dev: PCI device to be disabled * * Signal to the system that the PCI device is not in use by the system * anymore. This only involves disabling PCI bus-mastering, if active. */voidpci_disable_device(struct pci_dev *dev){ u16 pci_command; pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, pci_command); }}/** * pci_enable_wake - enable device to generate PME# when suspended * @dev: - PCI device to operate on * @state: - Current state of device. * @enable: - Flag to enable or disable generation * * Set the bits in the device's PM Capabilities to generate PME# when * the system is suspended. * * -EIO is returned if device doesn't have PM Capabilities. * -EINVAL is returned if device supports it, but can't generate wake events. * 0 if operation is successful. * */int pci_enable_wake(struct pci_dev *dev, u32 state, int enable){ int pm; u16 value; /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); /* If device doesn't support PM Capabilities, but request is to disable * wake events, it's a nop; otherwise fail */ if (!pm) return enable ? -EIO : 0; /* Check device's ability to generate PME# */ pci_read_config_word(dev,pm+PCI_PM_PMC,&value); value &= PCI_PM_CAP_PME_MASK; value >>= ffs(value); /* First bit of mask */ /* Check if it can generate PME# from requested state. */ if (!value || !(value & (1 << state))) return enable ? -EINVAL : 0; pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); /* Clear PME_Status by writing 1 to it and enable PME# */ value |= PCI_PM_CTRL_PME_STATUS | PCI_PM_CTRL_PME_ENABLE; if (!enable) value &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, pm + PCI_PM_CTRL, value); return 0;}intpci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge){ u8 pin; pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (!pin) return -1; pin--; while (dev->bus->self) { pin = (pin + PCI_SLOT(dev->devfn)) % 4; dev = dev->bus->self; } *bridge = dev; return pin;}/** * 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++) { if (pci_resource_len(pdev, i) == 0) continue; if (pci_resource_flags(pdev, i) & IORESOURCE_IO) release_region(pci_resource_start(pdev, i), pci_resource_len(pdev, i)); else if (pci_resource_flags(pdev, i) & IORESOURCE_MEM) release_mem_region(pci_resource_start(pdev, i), pci_resource_len(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. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -