diff options
Diffstat (limited to 'drivers/pci/pci.c')
-rw-r--r-- | drivers/pci/pci.c | 92 |
1 files changed, 72 insertions, 20 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d3eab057b2d..fd47ac0c473 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -13,6 +13,7 @@ #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> @@ -34,8 +35,7 @@ unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; * 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 __devinit -pci_bus_max_busnr(struct pci_bus* bus) +unsigned char pci_bus_max_busnr(struct pci_bus* bus) { struct list_head *tmp; unsigned char max, n; @@ -891,31 +891,76 @@ pci_disable_device(struct pci_dev *dev) } /** - * 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. + * pcibios_set_pcie_reset_state - set reset state for device dev + * @dev: the PCI-E device reset + * @state: Reset state to enter into + * + * + * Sets the PCI-E reset state for the device. This is the default + * implementation. Architecture implementations can override this. + */ +int __attribute__ ((weak)) pcibios_set_pcie_reset_state(struct pci_dev *dev, + enum pcie_reset_state state) +{ + return -EINVAL; +} + +/** + * pci_set_pcie_reset_state - set reset state for device dev + * @dev: the PCI-E device reset + * @state: Reset state to enter into * - * -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. - * + * + * Sets the PCI reset state for the device. + */ +int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state) +{ + return pcibios_set_pcie_reset_state(dev, state); +} + +/** + * pci_enable_wake - enable PCI device as wakeup event source + * @dev: PCI device affected + * @state: PCI state from which device will issue wakeup events + * @enable: True to enable event generation; false to disable + * + * This enables the device as a wakeup event source, or disables it. + * When such events involves platform-specific hooks, those hooks are + * called automatically by this routine. + * + * Devices with legacy power management (no standard PCI PM capabilities) + * always require such platform hooks. Depending on the platform, devices + * supporting the standard PCI PME# signal may require such platform hooks; + * they always update bits in config space to allow PME# generation. + * + * -EIO is returned if the device can't ever be a wakeup event source. + * -EINVAL is returned if the device can't generate wakeup events from + * the specified PCI state. Returns zero if the operation is successful. */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) { int pm; + int status; u16 value; + /* Note that drivers should verify device_may_wakeup(&dev->dev) + * before calling this function. Platform code should report + * errors when drivers try to enable wakeup on devices that + * can't issue wakeups, or on which wakeups were disabled by + * userspace updating the /sys/devices.../power/wakeup file. + */ + + status = call_platform_enable_wakeup(&dev->dev, enable); + /* 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; + /* If device doesn't support PM Capabilities, but caller wants to + * disable wake events, it's a NOP. Otherwise fail unless the + * platform hooks handled this legacy device already. + */ + if (!pm) + return enable ? status : 0; /* Check device's ability to generate PME# */ pci_read_config_word(dev,pm+PCI_PM_PMC,&value); @@ -924,8 +969,14 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) value >>= ffs(PCI_PM_CAP_PME_MASK) - 1; /* First bit of mask */ /* Check if it can generate PME# from requested state. */ - if (!value || !(value & (1 << state))) + if (!value || !(value & (1 << state))) { + /* if it can't, revert what the platform hook changed, + * always reporting the base "EINVAL, can't PME#" error + */ + if (enable) + call_platform_enable_wakeup(&dev->dev, 0); return enable ? -EINVAL : 0; + } pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); @@ -936,7 +987,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) value &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, pm + PCI_PM_CTRL, value); - + return 0; } @@ -1271,7 +1322,7 @@ pci_intx(struct pci_dev *pdev, int enable) /** * pci_msi_off - disables any msi or msix capabilities - * @pdev: the PCI device to operate on + * @dev: the PCI device to operate on * * If you want to use msi see pci_enable_msi and friends. * This is a lower level primitive that allows us to disable @@ -1403,4 +1454,5 @@ EXPORT_SYMBOL(pci_set_power_state); EXPORT_SYMBOL(pci_save_state); EXPORT_SYMBOL(pci_restore_state); EXPORT_SYMBOL(pci_enable_wake); +EXPORT_SYMBOL_GPL(pci_set_pcie_reset_state); |