]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
powerpc/pci: Hotplug driver bridge support
authorKrishna Kumar <krishnak@linux.ibm.com>
Mon, 1 Jul 2024 07:45:07 +0000 (13:15 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Thu, 4 Jul 2024 13:10:40 +0000 (23:10 +1000)
There is an issue with the hotplug operation when it's done on the
bridge/switch slot. The bridge-port and devices behind the bridge, which
become offline by hot-unplug operation, don't get hot-plugged/enabled
by doing hot-plug operation on that slot. Only the first port of the
bridge gets enabled and the remaining port/devices remain unplugged. The
hot plug/unplug operation is done by the hotplug driver (drivers/pci/
hotplug/pnv_php.c).

This behavior is due to missing code for the switch/bridge. The existing
driver depends on pci_hp_add_devices() function for device enablement.
This function calls pci_scan_slot() on only one device-node/port of the
bridge, not on all the siblings' device-node/port.

The missing code needs to be added which will find all the sibling
device-nodes/bridge-ports and will run explicit pci_scan_slot()
on those. A new function has been added for this purpose
which is invoked from pci_hp_add_devices(). This new function
traverse_siblings_and_scan_slot() gets all the sibling bridge-ports by
traversal and explicitly invokes pci_scan_slot() on them.

Tested-by: Shawn Anastasio <sanastasio@raptorengineering.com>
Signed-off-by: Krishna Kumar <krishnak@linux.ibm.com>
[mpe: Move the code into pci-hotplug.c]
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20240701074513.94873-3-krishnak@linux.ibm.com
arch/powerpc/kernel/pci-hotplug.c

index 0fe251c6ac2ce7a4e7462ce9b8b6285c77cd3851..9ea74973d78d5a20e619877d2d2d66a07c62fb4e 100644 (file)
@@ -93,6 +93,36 @@ void pci_hp_remove_devices(struct pci_bus *bus)
 }
 EXPORT_SYMBOL_GPL(pci_hp_remove_devices);
 
+static void traverse_siblings_and_scan_slot(struct device_node *start, struct pci_bus *bus)
+{
+       struct device_node *dn;
+       int slotno;
+
+       u32 class = 0;
+
+       if (!of_property_read_u32(start->child, "class-code", &class)) {
+               /* Call of pci_scan_slot for non-bridge/EP case */
+               if (!((class >> 8) == PCI_CLASS_BRIDGE_PCI)) {
+                       slotno = PCI_SLOT(PCI_DN(start->child)->devfn);
+                       pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
+                       return;
+               }
+       }
+
+       /* Iterate all siblings */
+       for_each_child_of_node(start, dn) {
+               class = 0;
+
+               if (!of_property_read_u32(start->child, "class-code", &class)) {
+                       /* Call of pci_scan_slot on each sibling-nodes/bridge-ports */
+                       if ((class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+                               slotno = PCI_SLOT(PCI_DN(dn)->devfn);
+                               pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
+                       }
+               }
+       }
+}
+
 /**
  * pci_hp_add_devices - adds new pci devices to bus
  * @bus: the indicated PCI bus
@@ -106,7 +136,7 @@ EXPORT_SYMBOL_GPL(pci_hp_remove_devices);
  */
 void pci_hp_add_devices(struct pci_bus *bus)
 {
-       int slotno, mode, max;
+       int mode, max;
        struct pci_dev *dev;
        struct pci_controller *phb;
        struct device_node *dn = pci_bus_to_OF_node(bus);
@@ -129,8 +159,7 @@ void pci_hp_add_devices(struct pci_bus *bus)
                 * order for fully rescan all the way down to pick them up.
                 * They can have been removed during partial hotplug.
                 */
-               slotno = PCI_SLOT(PCI_DN(dn->child)->devfn);
-               pci_scan_slot(bus, PCI_DEVFN(slotno, 0));
+               traverse_siblings_and_scan_slot(dn, bus);
                max = bus->busn_res.start;
                /*
                 * Scan bridges that are already configured. We don't touch