PCI/PCIe: Clear Root PME Status bits early during system resume
authorRafael J. Wysocki <rjw@sisk.pl>
Sun, 19 Dec 2010 14:57:16 +0000 (15:57 +0100)
committerJesse Barnes <jbarnes@virtuousgeek.org>
Thu, 23 Dec 2010 20:54:03 +0000 (12:54 -0800)
I noticed that PCI Express PMEs don't work on my Toshiba Portege R500
after the system has been woken up from a sleep state by a PME
(through Wake-on-LAN).  After some investigation it turned out that
the BIOS didn't clear the Root PME Status bit in the root port that
received the wakeup PME and since the Requester ID was also set in
the port's Root Status register, any subsequent PMEs didn't trigger
interrupts.

This problem can be avoided by clearing the Root PME Status bits in
all PCI Express root ports during early resume.  For this purpose,
add an early resume routine to the PCIe port driver and make this
driver be always registered, even if pci_ports_disable is set (in
which case the driver's only function is to provide the early
resume callback).

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
drivers/pci/pcie/pme.c
drivers/pci/pcie/portdrv.h
drivers/pci/pcie/portdrv_core.c
drivers/pci/pcie/portdrv_pci.c
include/linux/pci_regs.h

index 2f3c904072273fcdaf5c5a660405281720dff7aa..073f0308c6b219f6f1cbb4f7bed32d3d59a0f1ec 100644 (file)
@@ -26,9 +26,6 @@
 #include "../pci.h"
 #include "portdrv.h"
 
-#define PCI_EXP_RTSTA_PME      0x10000 /* PME status */
-#define PCI_EXP_RTSTA_PENDING  0x20000 /* PME pending */
-
 /*
  * If this switch is set, MSI will not be used for PCIe PME signaling.  This
  * causes the PCIe port driver to use INTx interrupts only, but it turns out
@@ -73,22 +70,6 @@ void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
        pci_write_config_word(dev, rtctl_pos, rtctl);
 }
 
-/**
- * pcie_pme_clear_status - Clear root port PME interrupt status.
- * @dev: PCIe root port or event collector.
- */
-static void pcie_pme_clear_status(struct pci_dev *dev)
-{
-       int rtsta_pos;
-       u32 rtsta;
-
-       rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
-
-       pci_read_config_dword(dev, rtsta_pos, &rtsta);
-       rtsta |= PCI_EXP_RTSTA_PME;
-       pci_write_config_dword(dev, rtsta_pos, rtsta);
-}
-
 /**
  * pcie_pme_walk_bus - Scan a PCI bus for devices asserting PME#.
  * @bus: PCI bus to scan.
@@ -253,7 +234,7 @@ static void pcie_pme_work_fn(struct work_struct *work)
                         * Clear PME status of the port.  If there are other
                         * pending PMEs, the status will be set again.
                         */
-                       pcie_pme_clear_status(port);
+                       pcie_clear_root_pme_status(port);
 
                        spin_unlock_irq(&data->lock);
                        pcie_pme_handle_request(port, rtsta & 0xffff);
@@ -378,7 +359,7 @@ static int pcie_pme_probe(struct pcie_device *srv)
 
        port = srv->port;
        pcie_pme_interrupt_enable(port, false);
-       pcie_pme_clear_status(port);
+       pcie_clear_root_pme_status(port);
 
        ret = request_irq(srv->irq, pcie_pme_irq, IRQF_SHARED, "PCIe PME", srv);
        if (ret) {
@@ -402,7 +383,7 @@ static int pcie_pme_suspend(struct pcie_device *srv)
 
        spin_lock_irq(&data->lock);
        pcie_pme_interrupt_enable(port, false);
-       pcie_pme_clear_status(port);
+       pcie_clear_root_pme_status(port);
        data->noirq = true;
        spin_unlock_irq(&data->lock);
 
@@ -422,7 +403,7 @@ static int pcie_pme_resume(struct pcie_device *srv)
 
        spin_lock_irq(&data->lock);
        data->noirq = false;
-       pcie_pme_clear_status(port);
+       pcie_clear_root_pme_status(port);
        pcie_pme_interrupt_enable(port, true);
        spin_unlock_irq(&data->lock);
 
index 7b5aba0a3291107a231a8b6ee6a90cacf0c4557c..8fcc03598b293b883b77ccc842e101a27871872f 100644 (file)
@@ -35,6 +35,8 @@ extern void pcie_port_bus_unregister(void);
 
 struct pci_dev;
 
+extern void pcie_clear_root_pme_status(struct pci_dev *dev);
+
 #ifdef CONFIG_PCIE_PME
 extern bool pcie_pme_msi_disabled;
 
index a9c222d79ebc5dc32cd6c4fad37f616ac291ff6a..5130d0d22390bba5266fdfb18a10cb7aa2dccb7d 100644 (file)
@@ -241,17 +241,17 @@ static int get_port_device_capability(struct pci_dev *dev)
        int cap_mask;
        int err;
 
+       if (pcie_ports_disabled)
+               return 0;
+
        err = pcie_port_platform_notify(dev, &cap_mask);
-       if (pcie_ports_auto) {
-               if (err) {
-                       pcie_no_aspm();
-                       return 0;
-               }
-       } else {
+       if (!pcie_ports_auto) {
                cap_mask = PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP
                                | PCIE_PORT_SERVICE_VC;
                if (pci_aer_available())
                        cap_mask |= PCIE_PORT_SERVICE_AER;
+       } else if (err) {
+                       return 0;
        }
 
        pos = pci_pcie_cap(dev);
@@ -349,15 +349,18 @@ int pcie_port_device_register(struct pci_dev *dev)
        int status, capabilities, i, nr_service;
        int irqs[PCIE_PORT_DEVICE_MAXSERVICES];
 
-       /* Get and check PCI Express port services */
-       capabilities = get_port_device_capability(dev);
-       if (!capabilities)
-               return -ENODEV;
-
        /* Enable PCI Express port device */
        status = pci_enable_device(dev);
        if (status)
                return status;
+
+       /* Get and check PCI Express port services */
+       capabilities = get_port_device_capability(dev);
+       if (!capabilities) {
+               pcie_no_aspm();
+               return 0;
+       }
+
        pci_set_master(dev);
        /*
         * Initialize service irqs. Don't use service devices that
index f9033e190fb62060e701ebf44b0eb1d3dfbefa90..e0610bda1dea8ace0c03c202ff52e33a278036ff 100644 (file)
@@ -57,6 +57,22 @@ __setup("pcie_ports=", pcie_port_setup);
 
 /* global data */
 
+/**
+ * pcie_clear_root_pme_status - Clear root port PME interrupt status.
+ * @dev: PCIe root port or event collector.
+ */
+void pcie_clear_root_pme_status(struct pci_dev *dev)
+{
+       int rtsta_pos;
+       u32 rtsta;
+
+       rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
+
+       pci_read_config_dword(dev, rtsta_pos, &rtsta);
+       rtsta |= PCI_EXP_RTSTA_PME;
+       pci_write_config_dword(dev, rtsta_pos, rtsta);
+}
+
 static int pcie_portdrv_restore_config(struct pci_dev *dev)
 {
        int retval;
@@ -69,6 +85,20 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev)
 }
 
 #ifdef CONFIG_PM
+static int pcie_port_resume_noirq(struct device *dev)
+{
+       struct pci_dev *pdev = to_pci_dev(dev);
+
+       /*
+        * Some BIOSes forget to clear Root PME Status bits after system wakeup
+        * which breaks ACPI-based runtime wakeup on PCI Express, so clear those
+        * bits now just in case (shouldn't hurt).
+        */
+       if(pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+               pcie_clear_root_pme_status(pdev);
+       return 0;
+}
+
 static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .suspend        = pcie_port_device_suspend,
        .resume         = pcie_port_device_resume,
@@ -76,6 +106,7 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .thaw           = pcie_port_device_resume,
        .poweroff       = pcie_port_device_suspend,
        .restore        = pcie_port_device_resume,
+       .resume_noirq   = pcie_port_resume_noirq,
 };
 
 #define PCIE_PORTDRV_PM_OPS    (&pcie_portdrv_pm_ops)
@@ -327,10 +358,8 @@ static int __init pcie_portdrv_init(void)
 {
        int retval;
 
-       if (pcie_ports_disabled) {
-               pcie_no_aspm();
-               return -EACCES;
-       }
+       if (pcie_ports_disabled)
+               return pci_register_driver(&pcie_portdriver);
 
        dmi_check_system(pcie_portdrv_dmi_table);
 
index d4f2c80a6c3e111fdbd105afa96f75e5140ae40c..5b7e6b1ba54f4453deffe9291a0e4ab0ec412430 100644 (file)
 #define  PCI_EXP_RTCTL_CRSSVE  0x10    /* CRS Software Visibility Enable */
 #define PCI_EXP_RTCAP          30      /* Root Capabilities */
 #define PCI_EXP_RTSTA          32      /* Root Status */
+#define PCI_EXP_RTSTA_PME      0x10000 /* PME status */
+#define PCI_EXP_RTSTA_PENDING  0x20000 /* PME pending */
 #define PCI_EXP_DEVCAP2                36      /* Device Capabilities 2 */
 #define  PCI_EXP_DEVCAP2_ARI   0x20    /* Alternative Routing-ID */
 #define PCI_EXP_DEVCTL2                40      /* Device Control 2 */