[PATCH] PCI: pciehp: Check for PCIe capabilities change after resume

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Len Brown reported that resume on a Dell XPS11 laptop takes longer than it
should.  The delay is caused by pciehp scanning for a device below a Root
Port that has nothing connected to it.

At boot-time, the 00:1c.0 Root Port's PCI Express Capabilities Register
(PCIe spec r3.0, sec 7.8.2) advertises a slot (PCI_EXP_FLAGS_SLOT is set),
so pciehp claims the port.

At resume-time, PCI_EXP_FLAGS_SLOT is clear, so the Root Port no longer
advertises a slot.  But pciehp doesn't notice that change, and it reads
Slot Status to see if anything changed.  Slot Status says a device is
present (Ports not connected to slots are required to report "Card Present"
per sec 7.8.11), so pciehp tries to bring up the link and scan for the
device, which accounts for the delay.

Per sec 7.8.2, the PCIe Capabilities Register is all read-only, so I
think the fact that it changes between boot- and resume-time is a
firmware defect.

Work around this by re-reading the Capabilites at resume-time and updating
the cached copy in pci_dev->pcie_flags_reg.  Then stop using pciehp on the
port if it no longer advertises a slot.

Reported-and-tested-by: Len Brown <lenb@xxxxxxxxxx>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=99751
Signed-off-by: Bjorn Helgaas <bhelgaas@xxxxxxxxxx>
---
 drivers/pci/hotplug/pciehp_core.c |   10 ++++++++++
 drivers/pci/pci-driver.c          |   13 +++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 7d32fa33..f5461cb 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -278,6 +278,9 @@ static void pciehp_remove(struct pcie_device *dev)
 {
 	struct controller *ctrl = get_service_data(dev);
 
+	if (!ctrl)
+		return;
+
 	cleanup_slot(ctrl);
 	pciehp_release_ctrl(ctrl);
 }
@@ -296,6 +299,13 @@ static int pciehp_resume(struct pcie_device *dev)
 
 	ctrl = get_service_data(dev);
 
+	if (!(pcie_caps_reg(dev->port) & PCI_EXP_FLAGS_SLOT)) {
+		dev_info(&dev->port->dev, "No longer supports hotplug\n");
+		pciehp_remove(dev);
+		set_service_data(dev, NULL);
+		return 0;
+	}
+
 	/* reinitialize the chipset's event detection logic */
 	pcie_enable_notification(ctrl);
 
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 1ccce1c..fe8e964 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -505,7 +505,20 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev)
 
 static void pci_pm_default_resume_early(struct pci_dev *pci_dev)
 {
+	u16 flags;
+
 	pci_power_up(pci_dev);
+
+	if (pci_dev->pcie_cap) {
+		pci_read_config_word(pci_dev, pci_dev->pcie_cap + PCI_EXP_FLAGS,
+				     &flags);
+		if (pci_dev->pcie_flags_reg != flags) {
+			dev_info(&pci_dev->dev, "PCIe Capabilities was %#06x, is %#06x after resume (possible firmware defect)\n",
+				 pci_dev->pcie_flags_reg, flags);
+			pci_dev->pcie_flags_reg = flags;
+		}
+	}
+
 	pci_restore_state(pci_dev);
 	pci_fixup_device(pci_fixup_resume_early, pci_dev);
 }

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux