From: Rafael J. Wysocki <rjw@xxxxxxx> Apparently, some machines may have problems with PCI run-time power management if MSIs are used for the native PCIe PME signaling. In particular, on the MSI Wind U-100 PCIe PME interrupts are not generated by a PCIe root port after a resume from suspend to RAM, if the system wake-up was triggered by a PME from the device attached to this port. [It doesn't help to free the interrupt on suspend and request it back on resume, even if that is done along with disabling the MSI and re-enabling it, respectively.] However, if INTx interrupts are used for this purpose on the same machine, everything works just fine. For this reason, add a kernel command line switch allowing one to request that MSIs be not used for the native PCIe PME signaling, introduce a DMI table allowing us to blacklist machines that need this switch to be set by default and put the MSI Wind U-100 into this table. Signed-off-by: Rafael J. Wysocki <rjw@xxxxxxx> --- Documentation/kernel-parameters.txt | 2 ++ drivers/pci/pcie/pme/pcie_pme.c | 14 +++++++++++++- drivers/pci/pcie/portdrv.h | 17 +++++++++++++++++ drivers/pci/pcie/portdrv_core.c | 12 ++++++++++-- drivers/pci/pcie/portdrv_pci.c | 27 +++++++++++++++++++++++++++ 5 files changed, 69 insertions(+), 3 deletions(-) Index: linux-2.6/drivers/pci/pcie/pme/pcie_pme.c =================================================================== --- linux-2.6.orig/drivers/pci/pcie/pme/pcie_pme.c +++ linux-2.6/drivers/pci/pcie/pme/pcie_pme.c @@ -53,12 +53,22 @@ static bool pcie_pme_disabled; */ static bool pcie_pme_force_enable; +/* + * 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 + * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based + * wake-up from system sleep states. + */ +bool pcie_pme_msi_disabled; + static int __init pcie_pme_setup(char *str) { if (!strcmp(str, "off")) pcie_pme_disabled = true; else if (!strcmp(str, "force")) pcie_pme_force_enable = true; + else if (!strcmp(str, "nomsi")) + pcie_pme_msi_disabled = true; return 1; } __setup("pcie_pme=", pcie_pme_setup); @@ -73,7 +83,9 @@ __setup("pcie_pme=", pcie_pme_setup); */ static bool pcie_pme_platform_setup(struct pcie_device *srv) { - return !pcie_pme_platform_notify(srv) || pcie_pme_force_enable; + if (!pcie_pme_platform_notify(srv)) + return true; + return pcie_pme_force_enable; } struct pcie_pme_service_data { Index: linux-2.6/drivers/pci/pcie/portdrv_core.c =================================================================== --- linux-2.6.orig/drivers/pci/pcie/portdrv_core.c +++ linux-2.6/drivers/pci/pcie/portdrv_core.c @@ -186,16 +186,24 @@ static int pcie_port_enable_msix(struct */ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) { - int i, irq; + int i, irq = -1; + + /* We have to use INTx if MSI cannot be used for PCIe PME. */ + if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) { + if (dev->pin) + irq = dev->irq; + goto no_msi; + } /* Try to use MSI-X if supported */ if (!pcie_port_enable_msix(dev, irqs, mask)) return 0; + /* We're not going to use MSI-X, so try MSI and fall back to INTx */ - irq = -1; if (!pci_enable_msi(dev) || dev->pin) irq = dev->irq; + no_msi: for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) irqs[i] = irq; irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; Index: linux-2.6/drivers/pci/pcie/portdrv_pci.c =================================================================== --- linux-2.6.orig/drivers/pci/pcie/portdrv_pci.c +++ linux-2.6/drivers/pci/pcie/portdrv_pci.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/pcieport_if.h> #include <linux/aer.h> +#include <linux/dmi.h> #include "portdrv.h" #include "aer/aerdrv.h" @@ -273,10 +274,36 @@ static struct pci_driver pcie_portdriver .driver.pm = PCIE_PORTDRV_PM_OPS, }; +static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d) +{ + pr_notice("%s detected: will not use MSI for PCIe PME signaling\n", + d->ident); + pcie_pme_disable_msi(); + return 0; +} + +static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = { + /* + * Boxes that should not use MSI for PCIe PME signaling. + */ + { + .callback = dmi_pcie_pme_disable_msi, + .ident = "MSI Wind U-100", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, + "MICRO-STAR INTERNATIONAL CO., LTD"), + DMI_MATCH(DMI_PRODUCT_NAME, "U-100"), + }, + }, + {} +}; + static int __init pcie_portdrv_init(void) { int retval; + dmi_check_system(pcie_portdrv_dmi_table); + retval = pcie_port_bus_register(); if (retval) { printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); Index: linux-2.6/drivers/pci/pcie/portdrv.h =================================================================== --- linux-2.6.orig/drivers/pci/pcie/portdrv.h +++ linux-2.6/drivers/pci/pcie/portdrv.h @@ -30,4 +30,21 @@ extern void pcie_port_device_remove(stru extern int __must_check pcie_port_bus_register(void); extern void pcie_port_bus_unregister(void); +#ifdef CONFIG_PCIE_PME +extern bool pcie_pme_msi_disabled; + +static inline void pcie_pme_disable_msi(void) +{ + pcie_pme_msi_disabled = true; +} + +static inline bool pcie_pme_no_msi(void) +{ + return pcie_pme_msi_disabled; +} +#else /* !CONFIG_PCIE_PME */ +static inline void pcie_pme_disable_msi(void) {} +static inline bool pcie_pme_no_msi(void) { return false; } +#endif /* !CONFIG_PCIE_PME */ + #endif /* _PORTDRV_H_ */ Index: linux-2.6/Documentation/kernel-parameters.txt =================================================================== --- linux-2.6.orig/Documentation/kernel-parameters.txt +++ linux-2.6/Documentation/kernel-parameters.txt @@ -1994,6 +1994,8 @@ and is between 256 and 4096 characters. force Use native PCIe PME signaling even if the BIOS refuses to allow the kernel to control the relevant PCIe config registers. + nomsi Do not use MSI for native PCIe PME signaling (this makes + all PCIe root ports use INTx for everything). pcmv= [HW,PCMCIA] BadgePAD 4 _______________________________________________ linux-pm mailing list linux-pm@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/linux-pm