PCI/portdrv: Only disable pciehp interrupts early when needed
authorFeng Tang <feng.tang@linux.alibaba.com>
Mon, 3 Mar 2025 02:36:30 +0000 (10:36 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 4 Mar 2025 23:17:01 +0000 (17:17 -0600)
Firmware developers reported that Linux issues two PCIe hotplug commands in
very short intervals on an ARM server, which doesn't comply with the PCIe
spec.  According to PCIe r6.1, sec 6.7.3.2, if the Command Completed event
is supported, software must wait for a command to complete or wait at
least 1 second before sending a new command.

In the failure case, the first PCIe hotplug command is from
get_port_device_capability(), which sends a command to disable PCIe hotplug
interrupts without waiting for its completion, and the second command comes
from pcie_enable_notification() of pciehp driver, which enables hotplug
interrupts again.

Fix this by only disabling the hotplug interrupts when the pciehp driver is
not enabled.

Link: https://lore.kernel.org/r/20250303023630.78397-1-feng.tang@linux.alibaba.com
Fixes: 2bd50dd800b5 ("PCI: PCIe: Disable PCIe port services during port initialization")
Suggested-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Feng Tang <feng.tang@linux.alibaba.com>
[bhelgaas: commit log]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
Reviewed-by: Kuppuswamy Sathyanarayanan <sathyanarayanan.kuppuswamy@linux.intel.com>
drivers/pci/pcie/portdrv.c

index 02e73099bad0532466fa10f549cc3c5013aa1bbb..e8318fd5f6ed537a1b236a3a0f054161d5710abd 100644 (file)
@@ -228,10 +228,12 @@ static int get_port_device_capability(struct pci_dev *dev)
 
                /*
                 * Disable hot-plug interrupts in case they have been enabled
-                * by the BIOS and the hot-plug service driver is not loaded.
+                * by the BIOS and the hot-plug service driver won't be loaded
+                * to handle them.
                 */
-               pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
-                         PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
+               if (!IS_ENABLED(CONFIG_HOTPLUG_PCI_PCIE))
+                       pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
+                               PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
        }
 
 #ifdef CONFIG_PCIEAER