Commit 30386853 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/hotplug'

  - Disable in-band presence detection when possible (Alexandru Gagniuc)

  - Poll for presence detect if in-band presence detection is disabled
    (Alexandru Gagniuc)

  - Add DMI table of systems that don't support in-band presence detection
    (Stuart Hayes)

  - Fix indefinite pciehp wait caused by race in handling sysfs requests
    (Lukas Wunner)

  - Fix pciehp MSI interrupt race that caused us to miss interrupts (Stuart
    Hayes)

* pci/hotplug:
  PCI: pciehp: Fix MSI interrupt race
  PCI: pciehp: Fix indefinite wait on sysfs requests
  PCI: pciehp: Add DMI table for in-band presence detection disabled
  PCI: pciehp: Wait for PDS if in-band presence is disabled
  PCI: pciehp: Disable in-band presence detect when possible
parents eb81b249 8edf5332
...@@ -84,6 +84,7 @@ struct controller { ...@@ -84,6 +84,7 @@ struct controller {
struct pcie_device *pcie; struct pcie_device *pcie;
u32 slot_cap; /* capabilities and quirks */ u32 slot_cap; /* capabilities and quirks */
unsigned int inband_presence_disabled:1;
u16 slot_ctrl; /* control register access */ u16 slot_ctrl; /* control register access */
struct mutex ctrl_lock; struct mutex ctrl_lock;
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#define dev_fmt(fmt) "pciehp: " fmt #define dev_fmt(fmt) "pciehp: " fmt
#include <linux/dmi.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
...@@ -26,6 +27,24 @@ ...@@ -26,6 +27,24 @@
#include "../pci.h" #include "../pci.h"
#include "pciehp.h" #include "pciehp.h"
static const struct dmi_system_id inband_presence_disabled_dmi_table[] = {
/*
* Match all Dell systems, as some Dell systems have inband
* presence disabled on NVMe slots (but don't support the bit to
* report it). Setting inband presence disabled should have no
* negative effect, except on broken hotplug slots that never
* assert presence detect--and those will still work, they will
* just have a bit of extra delay before being probed.
*/
{
.ident = "Dell System",
.matches = {
DMI_MATCH(DMI_OEM_STRING, "Dell System"),
},
},
{}
};
static inline struct pci_dev *ctrl_dev(struct controller *ctrl) static inline struct pci_dev *ctrl_dev(struct controller *ctrl)
{ {
return ctrl->pcie->port; return ctrl->pcie->port;
...@@ -252,6 +271,22 @@ static bool pci_bus_check_dev(struct pci_bus *bus, int devfn) ...@@ -252,6 +271,22 @@ static bool pci_bus_check_dev(struct pci_bus *bus, int devfn)
return found; return found;
} }
static void pcie_wait_for_presence(struct pci_dev *pdev)
{
int timeout = 1250;
u16 slot_status;
do {
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &slot_status);
if (slot_status & PCI_EXP_SLTSTA_PDS)
return;
msleep(10);
timeout -= 10;
} while (timeout > 0);
pci_info(pdev, "Timeout waiting for Presence Detect\n");
}
int pciehp_check_link_status(struct controller *ctrl) int pciehp_check_link_status(struct controller *ctrl)
{ {
struct pci_dev *pdev = ctrl_dev(ctrl); struct pci_dev *pdev = ctrl_dev(ctrl);
...@@ -261,6 +296,9 @@ int pciehp_check_link_status(struct controller *ctrl) ...@@ -261,6 +296,9 @@ int pciehp_check_link_status(struct controller *ctrl)
if (!pcie_wait_for_link(pdev, true)) if (!pcie_wait_for_link(pdev, true))
return -1; return -1;
if (ctrl->inband_presence_disabled)
pcie_wait_for_presence(pdev);
found = pci_bus_check_dev(ctrl->pcie->port->subordinate, found = pci_bus_check_dev(ctrl->pcie->port->subordinate,
PCI_DEVFN(0, 0)); PCI_DEVFN(0, 0));
...@@ -527,7 +565,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) ...@@ -527,7 +565,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
struct controller *ctrl = (struct controller *)dev_id; struct controller *ctrl = (struct controller *)dev_id;
struct pci_dev *pdev = ctrl_dev(ctrl); struct pci_dev *pdev = ctrl_dev(ctrl);
struct device *parent = pdev->dev.parent; struct device *parent = pdev->dev.parent;
u16 status, events; u16 status, events = 0;
/* /*
* Interrupts only occur in D3hot or shallower and only if enabled * Interrupts only occur in D3hot or shallower and only if enabled
...@@ -552,6 +590,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) ...@@ -552,6 +590,7 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
} }
} }
read_status:
pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status); pcie_capability_read_word(pdev, PCI_EXP_SLTSTA, &status);
if (status == (u16) ~0) { if (status == (u16) ~0) {
ctrl_info(ctrl, "%s: no response from device\n", __func__); ctrl_info(ctrl, "%s: no response from device\n", __func__);
...@@ -564,24 +603,37 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id) ...@@ -564,24 +603,37 @@ static irqreturn_t pciehp_isr(int irq, void *dev_id)
* Slot Status contains plain status bits as well as event * Slot Status contains plain status bits as well as event
* notification bits; right now we only want the event bits. * notification bits; right now we only want the event bits.
*/ */
events = status & (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | status &= PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD |
PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_PDC | PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_DLLSC); PCI_EXP_SLTSTA_DLLSC;
/* /*
* If we've already reported a power fault, don't report it again * If we've already reported a power fault, don't report it again
* until we've done something to handle it. * until we've done something to handle it.
*/ */
if (ctrl->power_fault_detected) if (ctrl->power_fault_detected)
events &= ~PCI_EXP_SLTSTA_PFD; status &= ~PCI_EXP_SLTSTA_PFD;
events |= status;
if (!events) { if (!events) {
if (parent) if (parent)
pm_runtime_put(parent); pm_runtime_put(parent);
return IRQ_NONE; return IRQ_NONE;
} }
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events); if (status) {
pcie_capability_write_word(pdev, PCI_EXP_SLTSTA, events);
/*
* In MSI mode, all event bits must be zero before the port
* will send a new interrupt (PCIe Base Spec r5.0 sec 6.7.3.4).
* So re-read the Slot Status register in case a bit was set
* between read and write.
*/
if (pci_dev_msi_enabled(pdev) && !pciehp_poll_mode)
goto read_status;
}
ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events); ctrl_dbg(ctrl, "pending interrupts %#06x from Slot Status\n", events);
if (parent) if (parent)
pm_runtime_put(parent); pm_runtime_put(parent);
...@@ -625,17 +677,15 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) ...@@ -625,17 +677,15 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) { if (atomic_fetch_and(~RERUN_ISR, &ctrl->pending_events) & RERUN_ISR) {
ret = pciehp_isr(irq, dev_id); ret = pciehp_isr(irq, dev_id);
enable_irq(irq); enable_irq(irq);
if (ret != IRQ_WAKE_THREAD) { if (ret != IRQ_WAKE_THREAD)
pci_config_pm_runtime_put(pdev); goto out;
return ret;
}
} }
synchronize_hardirq(irq); synchronize_hardirq(irq);
events = atomic_xchg(&ctrl->pending_events, 0); events = atomic_xchg(&ctrl->pending_events, 0);
if (!events) { if (!events) {
pci_config_pm_runtime_put(pdev); ret = IRQ_NONE;
return IRQ_NONE; goto out;
} }
/* Check Attention Button Pressed */ /* Check Attention Button Pressed */
...@@ -664,10 +714,12 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id) ...@@ -664,10 +714,12 @@ static irqreturn_t pciehp_ist(int irq, void *dev_id)
pciehp_handle_presence_or_link_change(ctrl, events); pciehp_handle_presence_or_link_change(ctrl, events);
up_read(&ctrl->reset_lock); up_read(&ctrl->reset_lock);
ret = IRQ_HANDLED;
out:
pci_config_pm_runtime_put(pdev); pci_config_pm_runtime_put(pdev);
ctrl->ist_running = false; ctrl->ist_running = false;
wake_up(&ctrl->requester); wake_up(&ctrl->requester);
return IRQ_HANDLED; return ret;
} }
static int pciehp_poll(void *data) static int pciehp_poll(void *data)
...@@ -848,7 +900,7 @@ static inline void dbg_ctrl(struct controller *ctrl) ...@@ -848,7 +900,7 @@ static inline void dbg_ctrl(struct controller *ctrl)
struct controller *pcie_init(struct pcie_device *dev) struct controller *pcie_init(struct pcie_device *dev)
{ {
struct controller *ctrl; struct controller *ctrl;
u32 slot_cap, link_cap; u32 slot_cap, slot_cap2, link_cap;
u8 poweron; u8 poweron;
struct pci_dev *pdev = dev->port; struct pci_dev *pdev = dev->port;
struct pci_bus *subordinate = pdev->subordinate; struct pci_bus *subordinate = pdev->subordinate;
...@@ -883,6 +935,16 @@ struct controller *pcie_init(struct pcie_device *dev) ...@@ -883,6 +935,16 @@ struct controller *pcie_init(struct pcie_device *dev)
ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE; ctrl->state = list_empty(&subordinate->devices) ? OFF_STATE : ON_STATE;
up_read(&pci_bus_sem); up_read(&pci_bus_sem);
pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP2, &slot_cap2);
if (slot_cap2 & PCI_EXP_SLTCAP2_IBPD) {
pcie_write_cmd_nowait(ctrl, PCI_EXP_SLTCTL_IBPD_DISABLE,
PCI_EXP_SLTCTL_IBPD_DISABLE);
ctrl->inband_presence_disabled = 1;
}
if (dmi_first_match(inband_presence_disabled_dmi_table))
ctrl->inband_presence_disabled = 1;
/* Check if Data Link Layer Link Active Reporting is implemented */ /* Check if Data Link Layer Link Active Reporting is implemented */
pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap); pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &link_cap);
...@@ -892,7 +954,7 @@ struct controller *pcie_init(struct pcie_device *dev) ...@@ -892,7 +954,7 @@ struct controller *pcie_init(struct pcie_device *dev)
PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC | PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_CC |
PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC); PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC);
ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c LLActRep%c%s\n", ctrl_info(ctrl, "Slot #%d AttnBtn%c PwrCtrl%c MRL%c AttnInd%c PwrInd%c HotPlug%c Surprise%c Interlock%c NoCompl%c IbPresDis%c LLActRep%c%s\n",
(slot_cap & PCI_EXP_SLTCAP_PSN) >> 19, (slot_cap & PCI_EXP_SLTCAP_PSN) >> 19,
FLAG(slot_cap, PCI_EXP_SLTCAP_ABP), FLAG(slot_cap, PCI_EXP_SLTCAP_ABP),
FLAG(slot_cap, PCI_EXP_SLTCAP_PCP), FLAG(slot_cap, PCI_EXP_SLTCAP_PCP),
...@@ -903,6 +965,7 @@ struct controller *pcie_init(struct pcie_device *dev) ...@@ -903,6 +965,7 @@ struct controller *pcie_init(struct pcie_device *dev)
FLAG(slot_cap, PCI_EXP_SLTCAP_HPS), FLAG(slot_cap, PCI_EXP_SLTCAP_HPS),
FLAG(slot_cap, PCI_EXP_SLTCAP_EIP), FLAG(slot_cap, PCI_EXP_SLTCAP_EIP),
FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS), FLAG(slot_cap, PCI_EXP_SLTCAP_NCCS),
FLAG(slot_cap2, PCI_EXP_SLTCAP2_IBPD),
FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC), FLAG(link_cap, PCI_EXP_LNKCAP_DLLLARC),
pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : ""); pdev->broken_cmd_compl ? " (with Cmd Compl erratum)" : "");
......
...@@ -605,6 +605,7 @@ ...@@ -605,6 +605,7 @@
#define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */ #define PCI_EXP_SLTCTL_PWR_OFF 0x0400 /* Power Off */
#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ #define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */
#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ #define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */
#define PCI_EXP_SLTCTL_IBPD_DISABLE 0x4000 /* In-band PD disable */
#define PCI_EXP_SLTSTA 26 /* Slot Status */ #define PCI_EXP_SLTSTA 26 /* Slot Status */
#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ #define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */
#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ #define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */
...@@ -680,6 +681,7 @@ ...@@ -680,6 +681,7 @@
#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */ #define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */ #define PCI_CAP_EXP_ENDPOINT_SIZEOF_V2 52 /* v2 endpoints with link end here */
#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */ #define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
#define PCI_EXP_SLTCAP2_IBPD 0x00000001 /* In-band PD Disable Supported */
#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */ #define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */ #define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment