⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pci.c

📁 PCI总线在DOS操作系统下的驱动程序源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
					list_del(&d->global_list);
					list_add_tail(&d->global_list, &sorted_devices);
					if (d == dev)
						found = 1;
					break;
				}
			}

			if (ln == &pci_devices) 
			{
				#ifdef DEBUG_VERSION
					safe_printf("PCI: BIOS reporting unknown device %02x:%02x\n", bus, devfn);
				#endif

				/*
				 * We must not continue scanning as several buggy BIOSes
				 * return garbage after the last device. Grr.
				 */
				break;
			}
		}

		if (!found) 
		{
			#ifdef DEBUG_VERSION
				safe_printf("PCI: Device %02x:%02x not found by BIOS\n",
					dev->bus->number, dev->devfn);
			#endif

			list_del(&dev->global_list);
			list_add_tail(&dev->global_list, &sorted_devices);
		}
	}

	list_splice(&sorted_devices, &pci_devices);
}

#ifdef CONFIG_PM

/*
 * PCI Power management..
 *
 * This needs to be done centralized, so that we power manage PCI
 * devices in the right order: we should not shut down PCI bridges
 * before we've shut down the devices behind them, and we should
 * not wake up devices before we've woken up the bridge to the
 * device.. Eh?
 *
 * We do not touch devices that don't have a driver that exports
 * a suspend/resume function. That is just too dangerous. If the default
 * PCI suspend/resume functions work for a device, the driver can
 * easily implement them (ie just have a suspend function that calls
 * the pci_set_power_state() function).
 */

long pci_pm_save_state_device(struct pci_dev far * dev,u32 state)
{
	long error = 0;
	if (dev) 
	{
		struct pci_driver far * driver = dev->driver;
		if (driver && driver->save_state) 
			error = driver->save_state(dev,state);
	}

	return error;
}

long pci_pm_suspend_device(struct pci_dev far * dev,u32 state)
{
	int error = 0;

	if (dev) 
	{
		struct pci_driver far * driver = dev->driver;
		if (driver && driver->suspend)
			error = driver->suspend(dev,state);
	}

	return error;
}

long pci_pm_resume_device(struct pci_dev far * dev)
{
	int error = 0;

	if (dev) 
	{
		struct pci_driver far * driver = dev->driver;
		if (driver && driver->resume)
			error = driver->resume(dev);
	}

	return error;
}

long pci_pm_save_state_bus(struct pci_bus far * bus,u32 state)
{
	struct list_head *list;
	int error = 0;

	list_for_each(list,&bus->children) 
	{
		error = pci_pm_save_state_bus(pci_bus_b(list),state);
		if (error) 
			return error;
	}

	list_for_each(list,&bus->devices) 
	{
		error = pci_pm_save_state_device(pci_dev_b(list),state);
		if (error) 
			return error;
	}

	return 0;
}

long pci_pm_suspend_bus(struct pci_bus *bus, u32 state)
{
	struct list_head far * list;

	/* Walk the bus children list */
	list_for_each(list,&bus->children) 
		pci_pm_suspend_bus(pci_bus_b(list),state);

	/* Walk the device children list */
	list_for_each(list,&bus->devices)
		pci_pm_suspend_device(pci_dev_b(list),state);

	return 0;
}

long pci_pm_resume_bus(struct pci_bus far * bus)
{
	struct list_head far * list;

	/* Walk the device children list */
	list_for_each(list,&bus->devices)
		pci_pm_resume_device(pci_dev_b(list));

	/* And then walk the bus children */
	list_for_each(list,&bus->children)
		pci_pm_resume_bus(pci_bus_b(list));

	return 0;
}

long pci_pm_save_state(u32 state)
{
	struct list_head far * list;
	struct pci_bus far * bus;
	int error = 0;

	list_for_each(list, &pci_root_buses) 
	{
		bus = pci_bus_b(list);
		error = pci_pm_save_state_bus(bus,state);
		if (!error)
			error = pci_pm_save_state_device(bus->self,state);
	}

	return error;
}

long pci_pm_suspend(u32 state)
{
	struct list_head *list;
	struct pci_bus *bus;

	list_for_each(list,&pci_root_buses) 
	{
		bus = pci_bus_b(list);
		pci_pm_suspend_bus(bus,state);
		pci_pm_suspend_device(bus->self,state);
	}

	return 0;
}

long pci_pm_resume(void)
{
	struct list_head far * list;
	struct pci_bus far * bus;

	list_for_each(list,&pci_root_buses) 
	{
		bus = pci_bus_b(list);
		pci_pm_resume_device(bus->self);
		pci_pm_resume_bus(bus);
	}

	return 0;
}

long pci_pm_callback(struct pm_dev far * pm_device,pm_request_t rqst,void *data)
{
	int error = 0;

	switch (rqst) 
	{
		case PM_SAVE_STATE:
			error = pci_pm_save_state((u32)data);
			break;
		case PM_SUSPEND:
			error = pci_pm_suspend((u32)data);
			break;
		case PM_RESUME:
			error = pci_pm_resume();
			break;
		default: 
			break;
	};

	return error;
}

#endif


void pci_init(void)
{
	struct pci_dev *dev;

	#ifdef CONFIG_PM
		pm_active = 0;
	#endif

	pcibios_init();

	pci_for_each_dev(dev) 
	{
		pci_fixup_device(PCI_FIXUP_FINAL, dev);
	}

	#ifdef CONFIG_PM
		pm_register(PM_PCI_DEV,0,pci_pm_callback);
		pm_active = 1;
	#endif
}

void pci_free_resources(struct pci_dev far * dev)
{
	unsigned short i;

	for (i = 0; i < PCI_NUM_RESOURCES; i++) 
	{
		struct resource *res = dev->resource + i;

		if (res->parent)
			release_resource(res);
	}
}

void pci_remove_device(struct pci_dev far * 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);
}

void pci_exit(void)
{
	struct pci_dev far * dev;
	struct pci_dev far * dev1;
	struct pci_bus far * bus;
	struct pci_bus far * bus1;
	struct list_head far * ln;
	struct list_head far * ln1;
	struct pci_driver far * drv;

	for(ln = pci_drivers.next; ln != &pci_drivers;) 
	{
		drv = list_entry(ln, struct pci_driver, node);
		ln1 = ln->next;
		pci_unregister_driver(drv);		
		ln = ln1;
	}

	list_del(&pci_drivers);

	for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices);)
	{
		dev1 = pci_dev_g(dev->global_list.next);
		pci_remove_device(dev);
		free(dev);
		dev = dev1;
	}

	list_del(&pci_devices);

	for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses);)
	{
		bus1 = pci_bus_b(bus->node.next);

		list_del(&bus->node);
		list_del(&bus->children);
		list_del(&bus->devices);

		free(bus);

		bus = bus1;
	}

	list_del(&pci_root_buses);

	if(pirq_table)
		free(pirq_table);

	#ifdef CONFIG_PM
		pm_active = 0;
	#endif
}

/**
 * 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 far * pci_find_class(unsigned long class, const struct pci_dev far * from)
{
	struct list_head far * n = from ? from->global_list.next : pci_devices.next;

	while (n != &pci_devices) 
	{
		struct pci_dev far * 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 
 */
long pci_find_capability(struct pci_dev far * dev, long 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_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.
 */

long pci_set_power_state(struct pci_dev far * dev, long state)
{
	long 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;
}

#ifdef	CONFIG_PM

/**
 * 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).
 */
long pci_save_state(struct pci_dev far * dev, u32 far * buffer)
{
	int i;

	if (buffer) 
	{

⌨️ 快捷键说明

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