diff options
| -rw-r--r-- | Documentation/kernel-parameters.txt | 2 | ||||
| -rw-r--r-- | drivers/pci/pcie/pme/pcie_pme.c | 14 | ||||
| -rw-r--r-- | drivers/pci/pcie/portdrv.h | 17 | ||||
| -rw-r--r-- | drivers/pci/pcie/portdrv_core.c | 12 | ||||
| -rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 27 |
5 files changed, 69 insertions, 3 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 747a2c8f62f3..516225a864f9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt | |||
| @@ -2003,6 +2003,8 @@ and is between 256 and 4096 characters. It is defined in the file | |||
| 2003 | force Use native PCIe PME signaling even if the BIOS refuses | 2003 | force Use native PCIe PME signaling even if the BIOS refuses |
| 2004 | to allow the kernel to control the relevant PCIe config | 2004 | to allow the kernel to control the relevant PCIe config |
| 2005 | registers. | 2005 | registers. |
| 2006 | nomsi Do not use MSI for native PCIe PME signaling (this makes | ||
| 2007 | all PCIe root ports use INTx for everything). | ||
| 2006 | 2008 | ||
| 2007 | pcmv= [HW,PCMCIA] BadgePAD 4 | 2009 | pcmv= [HW,PCMCIA] BadgePAD 4 |
| 2008 | 2010 | ||
diff --git a/drivers/pci/pcie/pme/pcie_pme.c b/drivers/pci/pcie/pme/pcie_pme.c index b5f96fb3cd83..51a69061b120 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 | */ |
| 54 | static bool pcie_pme_force_enable; | 54 | static 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 | */ | ||
| 62 | bool pcie_pme_msi_disabled; | ||
| 63 | |||
| 56 | static int __init pcie_pme_setup(char *str) | 64 | static 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 | */ |
| 74 | static bool pcie_pme_platform_setup(struct pcie_device *srv) | 84 | static 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 | ||
| 79 | struct pcie_pme_service_data { | 91 | struct pcie_pme_service_data { |
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h index aaeb9d21cba5..813a5c3427b6 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); | |||
| 30 | extern int __must_check pcie_port_bus_register(void); | 30 | extern int __must_check pcie_port_bus_register(void); |
| 31 | extern void pcie_port_bus_unregister(void); | 31 | extern void pcie_port_bus_unregister(void); |
| 32 | 32 | ||
| 33 | #ifdef CONFIG_PCIE_PME | ||
| 34 | extern bool pcie_pme_msi_disabled; | ||
| 35 | |||
| 36 | static inline void pcie_pme_disable_msi(void) | ||
| 37 | { | ||
| 38 | pcie_pme_msi_disabled = true; | ||
| 39 | } | ||
| 40 | |||
| 41 | static inline bool pcie_pme_no_msi(void) | ||
| 42 | { | ||
| 43 | return pcie_pme_msi_disabled; | ||
| 44 | } | ||
| 45 | #else /* !CONFIG_PCIE_PME */ | ||
| 46 | static inline void pcie_pme_disable_msi(void) {} | ||
| 47 | static 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 b174188ac121..0d34ff415399 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 | */ |
| 187 | static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask) | 187 | static 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 13c8972886e6..127e8f169d9c 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 | ||
| 277 | static 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 | |||
| 285 | static 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 | |||
| 276 | static int __init pcie_portdrv_init(void) | 301 | static 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); |
