PCI/PM: add runtime PM support to PCIe port
This patch adds runtime PM support to PCIe port. This is needed by PCIe D3cold support, where PCIe device without ACPI node may be powered on/off by PCIe port. Because runtime suspend is broken for some chipsets, a black list is used to disable runtime PM support for these chipsets. Reviewed-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Zheng Yan <zheng.z.yan@intel.com> Signed-off-by: Huang Ying <ying.huang@intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
This commit is contained in:
parent
ee85f54371
commit
71a83bd727
|
@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work)
|
|||
if (!list_empty(&pci_pme_list)) {
|
||||
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
|
||||
if (pme_dev->dev->pme_poll) {
|
||||
struct pci_dev *bridge;
|
||||
|
||||
bridge = pme_dev->dev->bus->self;
|
||||
/*
|
||||
* If bridge is in low power state, the
|
||||
* configuration space of subordinate devices
|
||||
* may be not accessible
|
||||
*/
|
||||
if (bridge && bridge->current_state != PCI_D0)
|
||||
continue;
|
||||
pci_pme_wakeup(pme_dev->dev, NULL);
|
||||
} else {
|
||||
list_del(&pme_dev->list);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pcieport_if.h>
|
||||
#include <linux/aer.h>
|
||||
|
@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int pcie_port_runtime_pm(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define pcie_port_runtime_pm NULL
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
||||
.suspend = pcie_port_device_suspend,
|
||||
.resume = pcie_port_device_resume,
|
||||
|
@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
|||
.poweroff = pcie_port_device_suspend,
|
||||
.restore = pcie_port_device_resume,
|
||||
.resume_noirq = pcie_port_resume_noirq,
|
||||
.runtime_suspend = pcie_port_runtime_pm,
|
||||
.runtime_resume = pcie_port_runtime_pm,
|
||||
};
|
||||
|
||||
#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
|
||||
|
@ -116,6 +128,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
|
|||
#define PCIE_PORTDRV_PM_OPS NULL
|
||||
#endif /* !PM */
|
||||
|
||||
/*
|
||||
* PCIe port runtime suspend is broken for some chipsets, so use a
|
||||
* black list to disable runtime PM for these chipsets.
|
||||
*/
|
||||
static const struct pci_device_id port_runtime_pm_black_list[] = {
|
||||
{ /* end: all zeroes */ }
|
||||
};
|
||||
|
||||
/*
|
||||
* pcie_portdrv_probe - Probe PCI-Express port devices
|
||||
* @dev: PCI-Express port device being probed
|
||||
|
@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
|
|||
return status;
|
||||
|
||||
pci_save_state(dev);
|
||||
if (!pci_match_id(port_runtime_pm_black_list, dev))
|
||||
pm_runtime_put_noidle(&dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pcie_portdrv_remove(struct pci_dev *dev)
|
||||
{
|
||||
if (!pci_match_id(port_runtime_pm_black_list, dev))
|
||||
pm_runtime_get_noresume(&dev->dev);
|
||||
pcie_port_device_remove(dev);
|
||||
pci_disable_device(dev);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue