Commit 71a83bd7 authored by Zheng Yan's avatar Zheng Yan Committed by Bjorn Helgaas

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: default avatarRafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: default avatarZheng Yan <zheng.z.yan@intel.com>
Signed-off-by: default avatarHuang Ying <ying.huang@intel.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent ee85f543
...@@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work) ...@@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work)
if (!list_empty(&pci_pme_list)) { if (!list_empty(&pci_pme_list)) {
list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
if (pme_dev->dev->pme_poll) { 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); pci_pme_wakeup(pme_dev->dev, NULL);
} else { } else {
list_del(&pme_dev->list); list_del(&pme_dev->list);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/pcieport_if.h> #include <linux/pcieport_if.h>
#include <linux/aer.h> #include <linux/aer.h>
...@@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev) ...@@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev)
return 0; 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 = { static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.suspend = pcie_port_device_suspend, .suspend = pcie_port_device_suspend,
.resume = pcie_port_device_resume, .resume = pcie_port_device_resume,
...@@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { ...@@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
.poweroff = pcie_port_device_suspend, .poweroff = pcie_port_device_suspend,
.restore = pcie_port_device_resume, .restore = pcie_port_device_resume,
.resume_noirq = pcie_port_resume_noirq, .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) #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
...@@ -116,6 +128,14 @@ static const struct dev_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 #define PCIE_PORTDRV_PM_OPS NULL
#endif /* !PM */ #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 * pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed * @dev: PCI-Express port device being probed
...@@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev, ...@@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
return status; return status;
pci_save_state(dev); pci_save_state(dev);
if (!pci_match_id(port_runtime_pm_black_list, dev))
pm_runtime_put_noidle(&dev->dev);
return 0; return 0;
} }
static void pcie_portdrv_remove(struct pci_dev *dev) 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); pcie_port_device_remove(dev);
pci_disable_device(dev); pci_disable_device(dev);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment