aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2011-10-03 17:16:33 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2011-10-14 12:05:31 -0400
commit379021d5c0899fcf9410cae4ca7a59a5a94ca769 (patch)
tree9c91ffb80fcb143b94c20922cb27d60d2c7e6654 /drivers/pci
parent3e309cdf07c930f29a4e0f233e47d399bea34c68 (diff)
PCI / PM: Extend PME polling to all PCI devices
The land of PCI power management is a land of sorrow and ugliness, especially in the area of signaling events by devices. There are devices that set their PME Status bits, but don't really bother to send a PME message or assert PME#. There are hardware vendors who don't connect PME# lines to the system core logic (they know who they are). There are PCI Express Root Ports that don't bother to trigger interrupts when they receive PME messages from the devices below. There are ACPI BIOSes that forget to provide _PRW methods for devices capable of signaling wakeup. Finally, there are BIOSes that do provide _PRW methods for such devices, but then don't bother to call Notify() for those devices from the corresponding _Lxx/_Exx GPE-handling methods. In all of these cases the kernel doesn't have a chance to receive a proper notification that it should wake up a device, so devices stay in low-power states forever. Worse yet, in some cases they continuously send PME Messages that are silently ignored, because the kernel simply doesn't know that it should clear the device's PME Status bit. This problem was first observed for "parallel" (non-Express) PCI devices on add-on cards and Matthew Garrett addressed it by adding code that polls PME Status bits of such devices, if they are enabled to signal PME, to the kernel. Recently, however, it has turned out that PCI Express devices are also affected by this issue and that it is not limited to add-on devices, so it seems necessary to extend the PME polling to all PCI devices, including PCI Express and planar ones. Still, it would be wasteful to poll the PME Status bits of devices that are known to receive proper PME notifications, so make the kernel (1) poll the PME Status bits of all PCI and PCIe devices enabled to signal PME and (2) disable the PME Status polling for devices for which correct PME notifications are received. Tested-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/pci-acpi.c3
-rw-r--r--drivers/pci/pci.c41
-rw-r--r--drivers/pci/pcie/pme.c9
3 files changed, 32 insertions, 21 deletions
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index d36f41ea8cbf..cd3c4f1cdf1b 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -46,6 +46,9 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
46 struct pci_dev *pci_dev = context; 46 struct pci_dev *pci_dev = context;
47 47
48 if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) { 48 if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) {
49 if (pci_dev->pme_poll)
50 pci_dev->pme_poll = false;
51
49 pci_wakeup_event(pci_dev); 52 pci_wakeup_event(pci_dev);
50 pci_check_pme_status(pci_dev); 53 pci_check_pme_status(pci_dev);
51 pm_runtime_resume(&pci_dev->dev); 54 pm_runtime_resume(&pci_dev->dev);
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index e9651f0a8817..7cd417e94058 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -1407,13 +1407,16 @@ bool pci_check_pme_status(struct pci_dev *dev)
1407/** 1407/**
1408 * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set. 1408 * pci_pme_wakeup - Wake up a PCI device if its PME Status bit is set.
1409 * @dev: Device to handle. 1409 * @dev: Device to handle.
1410 * @ign: Ignored. 1410 * @pme_poll_reset: Whether or not to reset the device's pme_poll flag.
1411 * 1411 *
1412 * Check if @dev has generated PME and queue a resume request for it in that 1412 * Check if @dev has generated PME and queue a resume request for it in that
1413 * case. 1413 * case.
1414 */ 1414 */
1415static int pci_pme_wakeup(struct pci_dev *dev, void *ign) 1415static int pci_pme_wakeup(struct pci_dev *dev, void *pme_poll_reset)
1416{ 1416{
1417 if (pme_poll_reset && dev->pme_poll)
1418 dev->pme_poll = false;
1419
1417 if (pci_check_pme_status(dev)) { 1420 if (pci_check_pme_status(dev)) {
1418 pci_wakeup_event(dev); 1421 pci_wakeup_event(dev);
1419 pm_request_resume(&dev->dev); 1422 pm_request_resume(&dev->dev);
@@ -1428,7 +1431,7 @@ static int pci_pme_wakeup(struct pci_dev *dev, void *ign)
1428void pci_pme_wakeup_bus(struct pci_bus *bus) 1431void pci_pme_wakeup_bus(struct pci_bus *bus)
1429{ 1432{
1430 if (bus) 1433 if (bus)
1431 pci_walk_bus(bus, pci_pme_wakeup, NULL); 1434 pci_walk_bus(bus, pci_pme_wakeup, (void *)true);
1432} 1435}
1433 1436
1434/** 1437/**
@@ -1446,31 +1449,26 @@ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state)
1446 1449
1447static void pci_pme_list_scan(struct work_struct *work) 1450static void pci_pme_list_scan(struct work_struct *work)
1448{ 1451{
1449 struct pci_pme_device *pme_dev; 1452 struct pci_pme_device *pme_dev, *n;
1450 1453
1451 mutex_lock(&pci_pme_list_mutex); 1454 mutex_lock(&pci_pme_list_mutex);
1452 if (!list_empty(&pci_pme_list)) { 1455 if (!list_empty(&pci_pme_list)) {
1453 list_for_each_entry(pme_dev, &pci_pme_list, list) 1456 list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
1454 pci_pme_wakeup(pme_dev->dev, NULL); 1457 if (pme_dev->dev->pme_poll) {
1455 schedule_delayed_work(&pci_pme_work, msecs_to_jiffies(PME_TIMEOUT)); 1458 pci_pme_wakeup(pme_dev->dev, NULL);
1459 } else {
1460 list_del(&pme_dev->list);
1461 kfree(pme_dev);
1462 }
1463 }
1464 if (!list_empty(&pci_pme_list))
1465 schedule_delayed_work(&pci_pme_work,
1466 msecs_to_jiffies(PME_TIMEOUT));
1456 } 1467 }
1457 mutex_unlock(&pci_pme_list_mutex); 1468 mutex_unlock(&pci_pme_list_mutex);
1458} 1469}
1459 1470
1460/** 1471/**
1461 * pci_external_pme - is a device an external PCI PME source?
1462 * @dev: PCI device to check
1463 *
1464 */
1465
1466static bool pci_external_pme(struct pci_dev *dev)
1467{
1468 if (pci_is_pcie(dev) || dev->bus->number == 0)
1469 return false;
1470 return true;
1471}
1472
1473/**
1474 * pci_pme_active - enable or disable PCI device's PME# function 1472 * pci_pme_active - enable or disable PCI device's PME# function
1475 * @dev: PCI device to handle. 1473 * @dev: PCI device to handle.
1476 * @enable: 'true' to enable PME# generation; 'false' to disable it. 1474 * @enable: 'true' to enable PME# generation; 'false' to disable it.
@@ -1503,7 +1501,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable)
1503 hit, and the power savings from the devices will still be a 1501 hit, and the power savings from the devices will still be a
1504 win. */ 1502 win. */
1505 1503
1506 if (pci_external_pme(dev)) { 1504 if (dev->pme_poll) {
1507 struct pci_pme_device *pme_dev; 1505 struct pci_pme_device *pme_dev;
1508 if (enable) { 1506 if (enable) {
1509 pme_dev = kmalloc(sizeof(struct pci_pme_device), 1507 pme_dev = kmalloc(sizeof(struct pci_pme_device),
@@ -1821,6 +1819,7 @@ void pci_pm_init(struct pci_dev *dev)
1821 (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "", 1819 (pmc & PCI_PM_CAP_PME_D3) ? " D3hot" : "",
1822 (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : ""); 1820 (pmc & PCI_PM_CAP_PME_D3cold) ? " D3cold" : "");
1823 dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT; 1821 dev->pme_support = pmc >> PCI_PM_CAP_PME_SHIFT;
1822 dev->pme_poll = true;
1824 /* 1823 /*
1825 * Make device's PM flags reflect the wake-up capability, but 1824 * Make device's PM flags reflect the wake-up capability, but
1826 * let the user space enable it to wake up the system as needed. 1825 * let the user space enable it to wake up the system as needed.
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 0057344a3fcb..001f1b78f39c 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -84,6 +84,9 @@ static bool pcie_pme_walk_bus(struct pci_bus *bus)
84 list_for_each_entry(dev, &bus->devices, bus_list) { 84 list_for_each_entry(dev, &bus->devices, bus_list) {
85 /* Skip PCIe devices in case we started from a root port. */ 85 /* Skip PCIe devices in case we started from a root port. */
86 if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) { 86 if (!pci_is_pcie(dev) && pci_check_pme_status(dev)) {
87 if (dev->pme_poll)
88 dev->pme_poll = false;
89
87 pci_wakeup_event(dev); 90 pci_wakeup_event(dev);
88 pm_request_resume(&dev->dev); 91 pm_request_resume(&dev->dev);
89 ret = true; 92 ret = true;
@@ -142,6 +145,9 @@ static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
142 145
143 /* First, check if the PME is from the root port itself. */ 146 /* First, check if the PME is from the root port itself. */
144 if (port->devfn == devfn && port->bus->number == busnr) { 147 if (port->devfn == devfn && port->bus->number == busnr) {
148 if (port->pme_poll)
149 port->pme_poll = false;
150
145 if (pci_check_pme_status(port)) { 151 if (pci_check_pme_status(port)) {
146 pm_request_resume(&port->dev); 152 pm_request_resume(&port->dev);
147 found = true; 153 found = true;
@@ -187,6 +193,9 @@ static void pcie_pme_handle_request(struct pci_dev *port, u16 req_id)
187 /* The device is there, but we have to check its PME status. */ 193 /* The device is there, but we have to check its PME status. */
188 found = pci_check_pme_status(dev); 194 found = pci_check_pme_status(dev);
189 if (found) { 195 if (found) {
196 if (dev->pme_poll)
197 dev->pme_poll = false;
198
190 pci_wakeup_event(dev); 199 pci_wakeup_event(dev);
191 pm_request_resume(&dev->dev); 200 pm_request_resume(&dev->dev);
192 } 201 }