lkml.org 
[lkml]   [2010]   [Jan]   [10]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 3/9] PCI PM: Make it possible to force using INTx for PCIe PME signaling
    Date
    From: Rafael J. Wysocki <rjw@sisk.pl>

    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>
    ---
    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



    \
     
     \ /
      Last update: 2010-01-10 15:09    [W:0.032 / U:0.020 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site