/*
* $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...)
#endif
LIST_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
*/
int
pci_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.
*/
int
pci_set_power_state(struct pci_dev *dev, int state)
{
int pm;
u16 pmcsr;
/* bound the state we're entering */
if (state > 3) state = 3;
/* Val