aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci-acpi.c3
-rw-r--r--drivers/pci/pci.c41
-rw-r--r--drivers/pci/pcie/pme.c9
-rw-r--r--include/linux/pci.h1
4 files changed, 33 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 }
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 4ff6d4e9455c..176c981a90d4 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -273,6 +273,7 @@ struct pci_dev {
273 unsigned int pme_support:5; /* Bitmask of states from which PME# 273 unsigned int pme_support:5; /* Bitmask of states from which PME#
274 can be generated */ 274 can be generated */
275 unsigned int pme_interrupt:1; 275 unsigned int pme_interrupt:1;
276 unsigned int pme_poll:1; /* Poll device's PME status bit */
276 unsigned int d1_support:1; /* Low power state D1 is supported */ 277 unsigned int d1_support:1; /* Low power state D1 is supported */
277 unsigned int d2_support:1; /* Low power state D2 is supported */ 278 unsigned int d2_support:1; /* Low power state D2 is supported */
278 unsigned int no_d1d2:1; /* Only allow D0 and D3 */ 279 unsigned int no_d1d2:1; /* Only allow D0 and D3 */