A bus reset can trigger a presence detection change and result in a suprise hotplug. This is generally not what we want to happen when trying to reset a device. Disable the presence detection control on on bridges around bus reset. Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx> --- v2: Still hasn't resolved any controversy of this patch, but writing the presence detect changed bit to the slot status to clear it resolves the stray remove/add I was still seeing occasionally on v1. drivers/pci/pci.c | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 5cb5820..efccc97 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3229,8 +3229,8 @@ static int pci_pm_reset(struct pci_dev *dev, int probe) static int pci_parent_bus_reset(struct pci_dev *dev, int probe) { - u16 ctrl; - struct pci_dev *pdev; + u16 ctrl, flags, sltctl = 0; + struct pci_dev *pdev, *bridge; if (pci_is_root_bus(dev->bus) || dev->subordinate || !dev->bus->self) return -ENOTTY; @@ -3242,15 +3242,37 @@ static int pci_parent_bus_reset(struct pci_dev *dev, int probe) if (probe) return 0; - pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl); + bridge = dev->bus->self; + + /* + * If the parent device supports a slot with presence detection + * change enabled, holding the bus in reset can trigger that and + * cause an unwanted surprise removal. Disable presence detection + * around the bus reset. + */ + pcie_capability_read_word(bridge, PCI_EXP_FLAGS, &flags); + if (flags & PCI_EXP_FLAGS_SLOT) { + pcie_capability_read_word(bridge, PCI_EXP_SLTCTL, &sltctl); + if (sltctl & PCI_EXP_SLTCTL_PDCE) + pcie_capability_write_word(bridge, PCI_EXP_SLTCTL, + sltctl & ~PCI_EXP_SLTCTL_PDCE); + } + + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, &ctrl); ctrl |= PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, ctrl); msleep(100); ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, ctrl); msleep(100); + if (sltctl & PCI_EXP_SLTCTL_PDCE) { + pcie_capability_write_word(bridge, + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); + pcie_capability_write_word(bridge, PCI_EXP_SLTCTL, sltctl); + } + return 0; } -- 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