pci.c

来自「linux 内核源代码」· C语言 代码 · 共 1,653 行 · 第 1/3 页

C
1,653
字号
/* *	$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/kernel.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/pm.h>#include <linux/module.h>#include <linux/spinlock.h>#include <linux/string.h>#include <linux/log2.h>#include <asm/dma.h>	/* isa_dma_bridge_buggy */#include "pci.h"unsigned int pci_pm_d3_delay = 10;#ifdef CONFIG_PCI_DOMAINSint pci_domains_supported = 1;#endif#define DEFAULT_CARDBUS_IO_SIZE		(256)#define DEFAULT_CARDBUS_MEM_SIZE	(64*1024*1024)/* pci=cbmemsize=nnM,cbiosize=nn can override this */unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE;unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE;/** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search * * Given a PCI bus, returns the highest PCI bus number present in the set * including the given PCI bus and its list of child PCI buses. */unsigned char pci_bus_max_busnr(struct pci_bus* bus){	struct list_head *tmp;	unsigned char max, n;	max = bus->subordinate;	list_for_each(tmp, &bus->children) {		n = pci_bus_max_busnr(pci_bus_b(tmp));		if(n > max)			max = n;	}	return max;}EXPORT_SYMBOL_GPL(pci_bus_max_busnr);#if 0/** * pci_max_busnr - returns maximum PCI bus number * * Returns the highest PCI bus number present in the system global list of * PCI buses. */unsigned char __devinitpci_max_busnr(void){	struct pci_bus *bus = NULL;	unsigned char max, n;	max = 0;	while ((bus = pci_find_next_bus(bus)) != NULL) {		n = pci_bus_max_busnr(bus);		if(n > max)			max = n;	}	return max;}#endif  /*  0  */#define PCI_FIND_CAP_TTL	48static int __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn,				   u8 pos, int cap, int *ttl){	u8 id;	while ((*ttl)--) {		pci_bus_read_config_byte(bus, devfn, pos, &pos);		if (pos < 0x40)			break;		pos &= ~3;		pci_bus_read_config_byte(bus, devfn, pos + PCI_CAP_LIST_ID,					 &id);		if (id == 0xff)			break;		if (id == cap)			return pos;		pos += PCI_CAP_LIST_NEXT;	}	return 0;}static int __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn,			       u8 pos, int cap){	int ttl = PCI_FIND_CAP_TTL;	return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl);}int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap){	return __pci_find_next_cap(dev->bus, dev->devfn,				   pos + PCI_CAP_LIST_NEXT, cap);}EXPORT_SYMBOL_GPL(pci_find_next_capability);static int __pci_bus_find_cap_start(struct pci_bus *bus,				    unsigned int devfn, u8 hdr_type){	u16 status;	pci_bus_read_config_word(bus, devfn, PCI_STATUS, &status);	if (!(status & PCI_STATUS_CAP_LIST))		return 0;	switch (hdr_type) {	case PCI_HEADER_TYPE_NORMAL:	case PCI_HEADER_TYPE_BRIDGE:		return PCI_CAPABILITY_LIST;	case PCI_HEADER_TYPE_CARDBUS:		return PCI_CB_CAPABILITY_LIST;	default:		return 0;	}	return 0;}/** * 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  *  %PCI_CAP_ID_PCIX         PCI-X *  %PCI_CAP_ID_EXP          PCI Express */int pci_find_capability(struct pci_dev *dev, int cap){	int pos;	pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);	if (pos)		pos = __pci_find_next_cap(dev->bus, dev->devfn, pos, cap);	return pos;}/** * pci_bus_find_capability - query for devices' capabilities  * @bus:   the PCI bus to query * @devfn: PCI device to query * @cap:   capability code * * Like pci_find_capability() but works for pci devices that do not have a * pci_dev structure set up yet.  * * 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. */int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap){	int pos;	u8 hdr_type;	pci_bus_read_config_byte(bus, devfn, PCI_HEADER_TYPE, &hdr_type);	pos = __pci_bus_find_cap_start(bus, devfn, hdr_type & 0x7f);	if (pos)		pos = __pci_find_next_cap(bus, devfn, pos, cap);	return pos;}/** * pci_find_ext_capability - Find an extended capability * @dev: PCI device to query * @cap: capability code * * Returns the address of the requested extended capability structure * within the device's PCI configuration space or 0 if the device does * not support it.  Possible values for @cap: * *  %PCI_EXT_CAP_ID_ERR		Advanced Error Reporting *  %PCI_EXT_CAP_ID_VC		Virtual Channel *  %PCI_EXT_CAP_ID_DSN		Device Serial Number *  %PCI_EXT_CAP_ID_PWR		Power Budgeting */int pci_find_ext_capability(struct pci_dev *dev, int cap){	u32 header;	int ttl = 480; /* 3840 bytes, minimum 8 bytes per capability */	int pos = 0x100;	if (dev->cfg_size <= 256)		return 0;	if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)		return 0;	/*	 * If we have no capabilities, this is indicated by cap ID,	 * cap version and next pointer all being 0.	 */	if (header == 0)		return 0;	while (ttl-- > 0) {		if (PCI_EXT_CAP_ID(header) == cap)			return pos;		pos = PCI_EXT_CAP_NEXT(header);		if (pos < 0x100)			break;		if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)			break;	}	return 0;}EXPORT_SYMBOL_GPL(pci_find_ext_capability);static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap){	int rc, ttl = PCI_FIND_CAP_TTL;	u8 cap, mask;	if (ht_cap == HT_CAPTYPE_SLAVE || ht_cap == HT_CAPTYPE_HOST)		mask = HT_3BIT_CAP_MASK;	else		mask = HT_5BIT_CAP_MASK;	pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos,				      PCI_CAP_ID_HT, &ttl);	while (pos) {		rc = pci_read_config_byte(dev, pos + 3, &cap);		if (rc != PCIBIOS_SUCCESSFUL)			return 0;		if ((cap & mask) == ht_cap)			return pos;		pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn,					      pos + PCI_CAP_LIST_NEXT,					      PCI_CAP_ID_HT, &ttl);	}	return 0;}/** * pci_find_next_ht_capability - query a device's Hypertransport capabilities * @dev: PCI device to query * @pos: Position from which to continue searching * @ht_cap: Hypertransport capability code * * To be used in conjunction with pci_find_ht_capability() to search for * all capabilities matching @ht_cap. @pos should always be a value returned * from pci_find_ht_capability(). * * NB. To be 100% safe against broken PCI devices, the caller should take * steps to avoid an infinite loop. */int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap){	return __pci_find_next_ht_cap(dev, pos + PCI_CAP_LIST_NEXT, ht_cap);}EXPORT_SYMBOL_GPL(pci_find_next_ht_capability);/** * pci_find_ht_capability - query a device's Hypertransport capabilities * @dev: PCI device to query * @ht_cap: Hypertransport capability code * * Tell if a device supports a given Hypertransport capability. * Returns an address within the device's PCI configuration space * or 0 in case the device does not support the request capability. * The address points to the PCI capability, of type PCI_CAP_ID_HT, * which has a Hypertransport capability matching @ht_cap. */int pci_find_ht_capability(struct pci_dev *dev, int ht_cap){	int pos;	pos = __pci_bus_find_cap_start(dev->bus, dev->devfn, dev->hdr_type);	if (pos)		pos = __pci_find_next_ht_cap(dev, pos, ht_cap);	return pos;}EXPORT_SYMBOL_GPL(pci_find_ht_capability);/** * 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 < PCI_BUS_NUM_RESOURCES; 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_restore_bars - restore a devices BAR values (e.g. after wake-up) * @dev: PCI device to have its BARs restored * * Restore the BAR values for a given device, so as to make it * accessible by its driver. */voidpci_restore_bars(struct pci_dev *dev){	int i, numres;	switch (dev->hdr_type) {	case PCI_HEADER_TYPE_NORMAL:		numres = 6;		break;	case PCI_HEADER_TYPE_BRIDGE:		numres = 2;		break;	case PCI_HEADER_TYPE_CARDBUS:		numres = 1;		break;	default:		/* Should never get here, but just in case... */		return;	}	for (i = 0; i < numres; i ++)		pci_update_resource(dev, &dev->resource[i], i);}int (*platform_pci_set_power_state)(struct pci_dev *dev, pci_power_t t);/** * pci_set_power_state - Set the power state of a PCI device * @dev: PCI device to be suspended * @state: PCI power state (D0, D1, D2, D3hot, D3cold) 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, pci_power_t state){	int pm, need_restore = 0;	u16 pmcsr, pmc;	/* bound the state we're entering */	if (state > PCI_D3hot)		state = PCI_D3hot;	/*	 * If the device or the parent bridge can't support PCI PM, ignore	 * the request if we're doing anything besides putting it into D0	 * (which would only happen on boot).	 */	if ((state == PCI_D1 || state == PCI_D2) && pci_no_d1d2(dev))		return 0;	/* 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;	/* 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 != PCI_D0 && dev->current_state > state) {		printk(KERN_ERR "%s(): %s: state=%d, current state=%d\n",			__FUNCTION__, pci_name(dev), state, dev->current_state);		return -EINVAL;	} else if (dev->current_state == state)		return 0;        /* we're already there */	pci_read_config_word(dev,pm + PCI_PM_PMC,&pmc);	if ((pmc & PCI_PM_CAP_VER_MASK) > 3) {		printk(KERN_DEBUG		       "PCI: %s has unsupported PM cap regs version (%u)\n",		       pci_name(dev), pmc & PCI_PM_CAP_VER_MASK);		return -EIO;	}	/* check if this device supports the desired state */	if (state == PCI_D1 && !(pmc & PCI_PM_CAP_D1))		return -EIO;	else if (state == PCI_D2 && !(pmc & PCI_PM_CAP_D2))		return -EIO;	pci_read_config_word(dev, pm + PCI_PM_CTRL, &pmcsr);	/* If we're (effectively) in D3, force entire word to 0.	 * This doesn't affect PME_Status, disables PME_En, and	 * sets PowerState to 0.	 */	switch (dev->current_state) {	case PCI_D0:	case PCI_D1:	case PCI_D2:		pmcsr &= ~PCI_PM_CTRL_STATE_MASK;		pmcsr |= state;		break;	case PCI_UNKNOWN: /* Boot-up */		if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot		 && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET))			need_restore = 1;		/* Fall-through: force to D0 */	default:		pmcsr = 0;		break;	}	/* 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 == PCI_D3hot || dev->current_state == PCI_D3hot)		msleep(pci_pm_d3_delay);	else if (state == PCI_D2 || dev->current_state == PCI_D2)		udelay(200);	/*	 * Give firmware a chance to be called, such as ACPI _PRx, _PSx	 * Firmware method after native method ?	 */	if (platform_pci_set_power_state)		platform_pci_set_power_state(dev, state);	dev->current_state = state;	/* According to section 5.4.1 of the "PCI BUS POWER MANAGEMENT	 * INTERFACE SPECIFICATION, REV. 1.2", a device transitioning	 * from D3hot to D0 _may_ perform an internal reset, thereby	 * going to "D0 Uninitialized" rather than "D0 Initialized".	 * For example, at least some versions of the 3c905B and the	 * 3c556B exhibit this behaviour.	 *	 * At least some laptop BIOSen (e.g. the Thinkpad T21) leave	 * devices in a D3hot state at boot.  Consequently, we need to	 * restore at least the BARs so that the device will be	 * accessible to its driver.	 */	if (need_restore)		pci_restore_bars(dev);	return 0;}pci_power_t (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state); /** * pci_choose_state - Choose the power state of a PCI device * @dev: PCI device to be suspended * @state: target sleep state for the whole system. This is the value *	that is passed to suspend() function. * * Returns PCI power state suitable for given device and given system * message. */pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state){	pci_power_t ret;	if (!pci_find_capability(dev, PCI_CAP_ID_PM))		return PCI_D0;	if (platform_pci_choose_state) {		ret = platform_pci_choose_state(dev, state);		if (ret != PCI_POWER_ERROR)			return ret;	}	switch (state.event) {	case PM_EVENT_ON:		return PCI_D0;	case PM_EVENT_FREEZE:	case PM_EVENT_PRETHAW:		/* REVISIT both freeze and pre-thaw "should" use D0 */	case PM_EVENT_SUSPEND:		return PCI_D3hot;	default:		printk("Unrecognized suspend event %d\n", state.event);		BUG();	}	return PCI_D0;}EXPORT_SYMBOL(pci_choose_state);static int pci_save_pcie_state(struct pci_dev *dev){	int pos, i = 0;

⌨️ 快捷键说明

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