aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2010-02-17 17:40:07 -0500
committerJesse Barnes <jbarnes@virtuousgeek.org>2010-02-22 19:20:39 -0500
commitc39fae1416d59fd565606793f090cebe3720d50d (patch)
treef53b3dc3202706c328c2306f168058ec2e9ae859 /drivers/pci/pcie
parentc7f486567c1d0acd2e4166c47069835b9f75e77b (diff)
PCI PM: Make it possible to force using INTx for PCIe PME signaling
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@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r--drivers/pci/pcie/pme/pcie_pme.c14
-rw-r--r--drivers/pci/pcie/portdrv.h17
-rw-r--r--drivers/pci/pcie/portdrv_core.c12
-rw-r--r--drivers/pci/pcie/portdrv_pci.c27
4 files changed, 67 insertions, 3 deletions
diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c
index b5f96fb3cd8..51a69061b12 100644
--- a/drivers/pci/pcie/pme/pcie_pme.c
+++ b/drivers/pci/pcie/pme/pcie_pme.c
@@ -53,12 +53,22 @@ static bool pcie_pme_disabled;
53 */ 53 */
54static bool pcie_pme_force_enable; 54static bool pcie_pme_force_enable;
55 55
56/*
57 * If this switch is set, MSI will not be used for PCIe PME signaling. This
58 * causes the PCIe port driver to use INTx interrupts only, but it turns out
59 * that using MSI for PCIe PME signaling doesn't play well with PCIe PME-based
60 * wake-up from system sleep states.
61 */
62bool pcie_pme_msi_disabled;
63
56static int __init pcie_pme_setup(char *str) 64static int __init pcie_pme_setup(char *str)
57{ 65{
58 if (!strcmp(str, "off")) 66 if (!strcmp(str, "off"))
59 pcie_pme_disabled = true; 67 pcie_pme_disabled = true;
60 else if (!strcmp(str, "force")) 68 else if (!strcmp(str, "force"))
61 pcie_pme_force_enable = true; 69 pcie_pme_force_enable = true;
70 else if (!strcmp(str, "nomsi"))
71 pcie_pme_msi_disabled = true;
62 return 1; 72 return 1;
63} 73}
64__setup("pcie_pme=", pcie_pme_setup); 74__setup("pcie_pme=", pcie_pme_setup);
@@ -73,7 +83,9 @@ __setup("pcie_pme=", pcie_pme_setup);
73 */ 83 */
74static bool pcie_pme_platform_setup(struct pcie_device *srv) 84static bool pcie_pme_platform_setup(struct pcie_device *srv)
75{ 85{
76 return !pcie_pme_platform_notify(srv) || pcie_pme_force_enable; 86 if (!pcie_pme_platform_notify(srv))
87 return true;
88 return pcie_pme_force_enable;
77} 89}
78 90
79struct pcie_pme_service_data { 91struct pcie_pme_service_data {
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index aaeb9d21cba..813a5c3427b 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -30,4 +30,21 @@ extern void pcie_port_device_remove(struct pci_dev *dev);
30extern int __must_check pcie_port_bus_register(void); 30extern int __must_check pcie_port_bus_register(void);
31extern void pcie_port_bus_unregister(void); 31extern void pcie_port_bus_unregister(void);
32 32
33#ifdef CONFIG_PCIE_PME
34extern bool pcie_pme_msi_disabled;
35
36static inline void pcie_pme_disable_msi(void)
37{
38 pcie_pme_msi_disabled = true;
39}
40
41static inline bool pcie_pme_no_msi(void)
42{
43 return pcie_pme_msi_disabled;
44}
45#else /* !CONFIG_PCIE_PME */
46static inline void pcie_pme_disable_msi(void) {}
47static inline bool pcie_pme_no_msi(void) { return false; }
48#endif /* !CONFIG_PCIE_PME */
49
33#endif /* _PORTDRV_H_ */ 50#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index b174188ac12..0d34ff41539 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -186,16 +186,24 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
186 */ 186 */
187static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) 187static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
188{ 188{
189 int i, irq; 189 int i, irq = -1;
190
191 /* We have to use INTx if MSI cannot be used for PCIe PME. */
192 if ((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) {
193 if (dev->pin)
194 irq = dev->irq;
195 goto no_msi;
196 }
190 197
191 /* Try to use MSI-X if supported */ 198 /* Try to use MSI-X if supported */
192 if (!pcie_port_enable_msix(dev, irqs, mask)) 199 if (!pcie_port_enable_msix(dev, irqs, mask))
193 return 0; 200 return 0;
201
194 /* We're not going to use MSI-X, so try MSI and fall back to INTx */ 202 /* We're not going to use MSI-X, so try MSI and fall back to INTx */
195 irq = -1;
196 if (!pci_enable_msi(dev) || dev->pin) 203 if (!pci_enable_msi(dev) || dev->pin)
197 irq = dev->irq; 204 irq = dev->irq;
198 205
206 no_msi:
199 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) 207 for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
200 irqs[i] = irq; 208 irqs[i] = irq;
201 irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1; 209 irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 13c8972886e..127e8f169d9 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -15,6 +15,7 @@
15#include <linux/slab.h> 15#include <linux/slab.h>
16#include <linux/pcieport_if.h> 16#include <linux/pcieport_if.h>
17#include <linux/aer.h> 17#include <linux/aer.h>
18#include <linux/dmi.h>
18 19
19#include "portdrv.h" 20#include "portdrv.h"
20#include "aer/aerdrv.h" 21#include "aer/aerdrv.h"
@@ -273,10 +274,36 @@ static struct pci_driver pcie_portdriver = {
273 .driver.pm = PCIE_PORTDRV_PM_OPS, 274 .driver.pm = PCIE_PORTDRV_PM_OPS,
274}; 275};
275 276
277static int __init dmi_pcie_pme_disable_msi(const struct dmi_system_id *d)
278{
279 pr_notice("%s detected: will not use MSI for PCIe PME signaling\n",
280 d->ident);
281 pcie_pme_disable_msi();
282 return 0;
283}
284
285static struct dmi_system_id __initdata pcie_portdrv_dmi_table[] = {
286 /*
287 * Boxes that should not use MSI for PCIe PME signaling.
288 */
289 {
290 .callback = dmi_pcie_pme_disable_msi,
291 .ident = "MSI Wind U-100",
292 .matches = {
293 DMI_MATCH(DMI_SYS_VENDOR,
294 "MICRO-STAR INTERNATIONAL CO., LTD"),
295 DMI_MATCH(DMI_PRODUCT_NAME, "U-100"),
296 },
297 },
298 {}
299};
300
276static int __init pcie_portdrv_init(void) 301static int __init pcie_portdrv_init(void)
277{ 302{
278 int retval; 303 int retval;
279 304
305 dmi_check_system(pcie_portdrv_dmi_table);
306
280 retval = pcie_port_bus_register(); 307 retval = pcie_port_bus_register();
281 if (retval) { 308 if (retval) {
282 printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); 309 printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);