mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-14 15:54:15 +08:00
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>
This commit is contained in:
parent
c7f486567c
commit
c39fae1416
@ -2003,6 +2003,8 @@ and is between 256 and 4096 characters. It is defined in the file
|
|||||||
force Use native PCIe PME signaling even if the BIOS refuses
|
force Use native PCIe PME signaling even if the BIOS refuses
|
||||||
to allow the kernel to control the relevant PCIe config
|
to allow the kernel to control the relevant PCIe config
|
||||||
registers.
|
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
|
pcmv= [HW,PCMCIA] BadgePAD 4
|
||||||
|
|
||||||
|
@ -53,12 +53,22 @@ static bool pcie_pme_disabled;
|
|||||||
*/
|
*/
|
||||||
static bool pcie_pme_force_enable;
|
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)
|
static int __init pcie_pme_setup(char *str)
|
||||||
{
|
{
|
||||||
if (!strcmp(str, "off"))
|
if (!strcmp(str, "off"))
|
||||||
pcie_pme_disabled = true;
|
pcie_pme_disabled = true;
|
||||||
else if (!strcmp(str, "force"))
|
else if (!strcmp(str, "force"))
|
||||||
pcie_pme_force_enable = true;
|
pcie_pme_force_enable = true;
|
||||||
|
else if (!strcmp(str, "nomsi"))
|
||||||
|
pcie_pme_msi_disabled = true;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
__setup("pcie_pme=", pcie_pme_setup);
|
__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)
|
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 {
|
struct pcie_pme_service_data {
|
||||||
|
@ -30,4 +30,21 @@ extern void pcie_port_device_remove(struct pci_dev *dev);
|
|||||||
extern int __must_check pcie_port_bus_register(void);
|
extern int __must_check pcie_port_bus_register(void);
|
||||||
extern void pcie_port_bus_unregister(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_ */
|
#endif /* _PORTDRV_H_ */
|
||||||
|
@ -186,16 +186,24 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
|
|||||||
*/
|
*/
|
||||||
static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
|
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 */
|
/* Try to use MSI-X if supported */
|
||||||
if (!pcie_port_enable_msix(dev, irqs, mask))
|
if (!pcie_port_enable_msix(dev, irqs, mask))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* We're not going to use MSI-X, so try MSI and fall back to INTx */
|
/* 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)
|
if (!pci_enable_msi(dev) || dev->pin)
|
||||||
irq = dev->irq;
|
irq = dev->irq;
|
||||||
|
|
||||||
|
no_msi:
|
||||||
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
|
for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
|
||||||
irqs[i] = irq;
|
irqs[i] = irq;
|
||||||
irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
|
irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/pcieport_if.h>
|
#include <linux/pcieport_if.h>
|
||||||
#include <linux/aer.h>
|
#include <linux/aer.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
|
||||||
#include "portdrv.h"
|
#include "portdrv.h"
|
||||||
#include "aer/aerdrv.h"
|
#include "aer/aerdrv.h"
|
||||||
@ -273,10 +274,36 @@ static struct pci_driver pcie_portdriver = {
|
|||||||
.driver.pm = PCIE_PORTDRV_PM_OPS,
|
.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)
|
static int __init pcie_portdrv_init(void)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
dmi_check_system(pcie_portdrv_dmi_table);
|
||||||
|
|
||||||
retval = pcie_port_bus_register();
|
retval = pcie_port_bus_register();
|
||||||
if (retval) {
|
if (retval) {
|
||||||
printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
|
printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
|
||||||
|
Loading…
Reference in New Issue
Block a user