📄 pci.c
字号:
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 + -