aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2012-06-22 22:23:51 -0400
committerBjorn Helgaas <bhelgaas@google.com>2012-06-23 12:50:59 -0400
commit448bd857d48e69b33ef323739dc6d8ca20d4cda7 (patch)
tree4c1178f9c7dd2d78af2ac1ed26b214b04be1554a /drivers/pci/pcie
parent8497f696686ae1ab3f01e5956046d59844b9f500 (diff)
PCI/PM: add PCIe runtime D3cold support
This patch adds runtime D3cold support and corresponding ACPI platform support. This patch only enables runtime D3cold support; it does not enable D3cold support during system suspend/hibernate. D3cold is the deepest power saving state for a PCIe device, where its main power is removed. While it is in D3cold, you can't access the device at all, not even its configuration space (which is still accessible in D3hot). Therefore the PCI PM registers can not be used to transition into/out of the D3cold state; that must be done by platform logic such as ACPI _PR3. To support wakeup from D3cold, a system may provide auxiliary power, which allows a device to request wakeup using a Beacon or the sideband WAKE# signal. WAKE# is usually connected to platform logic such as ACPI GPE. This is quite different from other power saving states, where devices request wakeup via a PME message on the PCIe link. Some devices, such as those in plug-in slots, have no direct platform logic. For example, there is usually no ACPI _PR3 for them. D3cold support for these devices can be done via the PCIe Downstream Port leading to the device. When the PCIe port is powered on/off, the device is powered on/off too. Wakeup events from the device will be notified to the corresponding PCIe port. For more information about PCIe D3cold and corresponding ACPI support, please refer to: - PCI Express Base Specification Revision 2.0 - Advanced Configuration and Power Interface Specification Revision 5.0 [bhelgaas: changelog] Reviewed-by: Rafael J. Wysocki <rjw@sisk.pl> Originally-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>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/portdrv_pci.c44
1 files changed, 40 insertions, 4 deletions
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 7c576b9aa01..3a7eefcb270 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -101,12 +101,48 @@ static int pcie_port_resume_noirq(struct device *dev)
101} 101}
102 102
103#ifdef CONFIG_PM_RUNTIME 103#ifdef CONFIG_PM_RUNTIME
104static int pcie_port_runtime_pm(struct device *dev) 104struct d3cold_info {
105 bool no_d3cold;
106 unsigned int d3cold_delay;
107};
108
109static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
110{
111 struct d3cold_info *info = data;
112
113 info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
114 info->d3cold_delay);
115 if (pdev->no_d3cold)
116 info->no_d3cold = true;
117 return 0;
118}
119
120static int pcie_port_runtime_suspend(struct device *dev)
121{
122 struct pci_dev *pdev = to_pci_dev(dev);
123 struct d3cold_info d3cold_info = {
124 .no_d3cold = false,
125 .d3cold_delay = PCI_PM_D3_WAIT,
126 };
127
128 /*
129 * If any subordinate device disable D3cold, we should not put
130 * the port into D3cold. The D3cold delay of port should be
131 * the max of that of all subordinate devices.
132 */
133 pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
134 pdev->no_d3cold = d3cold_info.no_d3cold;
135 pdev->d3cold_delay = d3cold_info.d3cold_delay;
136 return 0;
137}
138
139static int pcie_port_runtime_resume(struct device *dev)
105{ 140{
106 return 0; 141 return 0;
107} 142}
108#else 143#else
109#define pcie_port_runtime_pm NULL 144#define pcie_port_runtime_suspend NULL
145#define pcie_port_runtime_resume NULL
110#endif 146#endif
111 147
112static const struct dev_pm_ops pcie_portdrv_pm_ops = { 148static const struct dev_pm_ops pcie_portdrv_pm_ops = {
@@ -117,8 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
117 .poweroff = pcie_port_device_suspend, 153 .poweroff = pcie_port_device_suspend,
118 .restore = pcie_port_device_resume, 154 .restore = pcie_port_device_resume,
119 .resume_noirq = pcie_port_resume_noirq, 155 .resume_noirq = pcie_port_resume_noirq,
120 .runtime_suspend = pcie_port_runtime_pm, 156 .runtime_suspend = pcie_port_runtime_suspend,
121 .runtime_resume = pcie_port_runtime_pm, 157 .runtime_resume = pcie_port_runtime_resume,
122}; 158};
123 159
124#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) 160#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)