]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: Move CardBus bridge scanning to setup-cardbus.c
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Fri, 19 Dec 2025 17:40:36 +0000 (19:40 +0200)
committerBjorn Helgaas <bhelgaas@google.com>
Tue, 27 Jan 2026 22:36:53 +0000 (16:36 -0600)
The PCI core's pci_scan_bridge_extend() contains convoluted logic specific
to setting up bus numbers for legacy CardBus bridges. Extract the CardBus
specific part out into setup-cardbus.c to make the core code cleaner and
allow omitting CardBus bridge support from modern systems.

Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/20251219174036.16738-24-ilpo.jarvinen@linux.intel.com
drivers/pci/pci.h
drivers/pci/probe.c
drivers/pci/setup-cardbus.c

index b20ff7ef20ff263bee16b740723d47da629373b0..c586bf8a9da987b3cd5d7c94bb4660cc4cbfe21e 100644 (file)
@@ -242,6 +242,7 @@ void pci_config_pm_runtime_put(struct pci_dev *dev);
 void pci_pm_power_up_and_verify_state(struct pci_dev *pci_dev);
 void pci_pm_init(struct pci_dev *dev);
 void pci_ea_init(struct pci_dev *dev);
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub);
 void pci_msi_init(struct pci_dev *dev);
 void pci_msix_init(struct pci_dev *dev);
 bool pci_bridge_d3_possible(struct pci_dev *dev);
@@ -377,10 +378,17 @@ extern unsigned long pci_hotplug_mmio_size;
 extern unsigned long pci_hotplug_mmio_pref_size;
 extern unsigned long pci_hotplug_bus_size;
 
+static inline bool pci_is_cardbus_bridge(struct pci_dev *dev)
+{
+       return dev->hdr_type == PCI_HEADER_TYPE_CARDBUS;
+}
 #ifdef CONFIG_CARDBUS
 unsigned long pci_cardbus_resource_alignment(struct resource *res);
 int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
                                struct list_head *realloc_head);
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+                                  u32 buses, int max,
+                                  unsigned int available_buses, int pass);
 int pci_setup_cardbus(char *str);
 
 #else
@@ -393,6 +401,14 @@ static inline int pci_bus_size_cardbus_bridge(struct pci_bus *bus,
 {
        return -EOPNOTSUPP;
 }
+static inline int pci_cardbus_scan_bridge_extend(struct pci_bus *bus,
+                                                struct pci_dev *dev,
+                                                u32 buses, int max,
+                                                unsigned int available_buses,
+                                                int pass)
+{
+       return max;
+}
 static inline int pci_setup_cardbus(char *str) { return -ENOENT; }
 #endif /* CONFIG_CARDBUS */
 
index 388bcf3a41f14ee34f392f1859b0577d8f6b5980..8f5436456c7af5ea8f0c28e0262b9217024d80e0 100644 (file)
@@ -25,9 +25,6 @@
 #include <linux/bitfield.h>
 #include "pci.h"
 
-#define CARDBUS_LATENCY_TIMER  176     /* secondary latency timer */
-#define CARDBUS_RESERVE_BUSNR  3
-
 static struct resource busn_resource = {
        .name   = "PCI busn",
        .start  = 0,
@@ -1343,7 +1340,7 @@ void pbus_validate_busn(struct pci_bus *bus)
  * and subordinate bus numbers, return true with the bus numbers in @sec
  * and @sub.  Otherwise return false.
  */
-static bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
+bool pci_ea_fixed_busnrs(struct pci_dev *dev, u8 *sec, u8 *sub)
 {
        int ea, offset;
        u32 dw;
@@ -1397,8 +1394,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
                                  int pass)
 {
        struct pci_bus *child;
-       int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
-       u32 buses, i, j = 0;
+       u32 buses;
        u16 bctl;
        u8 primary, secondary, subordinate;
        int broken = 0;
@@ -1442,8 +1438,15 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
        pci_write_config_word(dev, PCI_BRIDGE_CONTROL,
                              bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
 
-       if ((secondary || subordinate) && !pcibios_assign_all_busses() &&
-           !is_cardbus && !broken) {
+       if (pci_is_cardbus_bridge(dev)) {
+               max = pci_cardbus_scan_bridge_extend(bus, dev, buses, max,
+                                                    available_buses,
+                                                    pass);
+               goto out;
+       }
+
+       if ((secondary || subordinate) &&
+           !pcibios_assign_all_busses() && !broken) {
                unsigned int cmax, buses;
 
                /*
@@ -1485,7 +1488,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
                 * do in the second pass.
                 */
                if (!pass) {
-                       if (pcibios_assign_all_busses() || broken || is_cardbus)
+                       if (pcibios_assign_all_busses() || broken)
 
                                /*
                                 * Temporarily disable forwarding of the
@@ -1532,55 +1535,11 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
                        FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
                        FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
 
-               /*
-                * yenta.c forces a secondary latency timer of 176.
-                * Copy that behaviour here.
-                */
-               if (is_cardbus) {
-                       buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
-                       buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK,
-                                           CARDBUS_LATENCY_TIMER);
-               }
-
                /* We need to blast all three values with a single write */
                pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
 
-               if (!is_cardbus) {
-                       child->bridge_ctl = bctl;
-                       max = pci_scan_child_bus_extend(child, available_buses);
-               } else {
-
-                       /*
-                        * For CardBus bridges, we leave 4 bus numbers as
-                        * cards with a PCI-to-PCI bridge can be inserted
-                        * later.
-                        */
-                       for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
-                               struct pci_bus *parent = bus;
-                               if (pci_find_bus(pci_domain_nr(bus),
-                                                       max+i+1))
-                                       break;
-                               while (parent->parent) {
-                                       if ((!pcibios_assign_all_busses()) &&
-                                           (parent->busn_res.end > max) &&
-                                           (parent->busn_res.end <= max+i)) {
-                                               j = 1;
-                                       }
-                                       parent = parent->parent;
-                               }
-                               if (j) {
-
-                                       /*
-                                        * Often, there are two CardBus
-                                        * bridges -- try to leave one
-                                        * valid bus number for each one.
-                                        */
-                                       i /= 2;
-                                       break;
-                               }
-                       }
-                       max += i;
-               }
+               child->bridge_ctl = bctl;
+               max = pci_scan_child_bus_extend(child, available_buses);
 
                /*
                 * Set subordinate bus number to its real value.
@@ -1592,9 +1551,7 @@ static int pci_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
                pci_bus_update_busn_res_end(child, max);
                pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
        }
-
-       scnprintf(child->name, sizeof(child->name),
-                 (is_cardbus ? "PCI CardBus %04x:%02x" : "PCI Bus %04x:%02x"),
+       scnprintf(child->name, sizeof(child->name), "PCI Bus %04x:%02x",
                  pci_domain_nr(bus), child->number);
 
        pbus_validate_busn(child);
index 93a2b43c637bbf63465d5afec6f6b0437857e846..1ebd13a1f7302369019607f232fcc60825317ca3 100644 (file)
@@ -3,14 +3,19 @@
  * Cardbus bridge setup routines.
  */
 
+#include <linux/bitfield.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <linux/sizes.h>
+#include <linux/sprintf.h>
 #include <linux/types.h>
 
 #include "pci.h"
 
+#define CARDBUS_LATENCY_TIMER          176     /* secondary latency timer */
+#define CARDBUS_RESERVE_BUSNR          3
+
 #define DEFAULT_CARDBUS_IO_SIZE                SZ_256
 #define DEFAULT_CARDBUS_MEM_SIZE       SZ_64M
 /* pci=cbmemsize=nnM,cbiosize=nn can override this */
@@ -186,3 +191,116 @@ int pci_setup_cardbus(char *str)
 
        return -ENOENT;
 }
+
+int pci_cardbus_scan_bridge_extend(struct pci_bus *bus, struct pci_dev *dev,
+                                  u32 buses, int max,
+                                  unsigned int available_buses, int pass)
+{
+       struct pci_bus *child;
+       bool fixed_buses;
+       u8 fixed_sec, fixed_sub;
+       int next_busnr;
+       u32 i, j = 0;
+
+       /*
+        * We need to assign a number to this bus which we always do in the
+        * second pass.
+        */
+       if (!pass) {
+               /*
+                * Temporarily disable forwarding of the configuration
+                * cycles on all bridges in this bus segment to avoid
+                * possible conflicts in the second pass between two bridges
+                * programmed with overlapping bus ranges.
+                */
+               pci_write_config_dword(dev, PCI_PRIMARY_BUS,
+                                      buses & PCI_SEC_LATENCY_TIMER_MASK);
+               return max;
+       }
+
+       /* Clear errors */
+       pci_write_config_word(dev, PCI_STATUS, 0xffff);
+
+       /* Read bus numbers from EA Capability (if present) */
+       fixed_buses = pci_ea_fixed_busnrs(dev, &fixed_sec, &fixed_sub);
+       if (fixed_buses)
+               next_busnr = fixed_sec;
+       else
+               next_busnr = max + 1;
+
+       /*
+        * Prevent assigning a bus number that already exists. This can
+        * happen when a bridge is hot-plugged, so in this case we only
+        * re-scan this bus.
+        */
+       child = pci_find_bus(pci_domain_nr(bus), next_busnr);
+       if (!child) {
+               child = pci_add_new_bus(bus, dev, next_busnr);
+               if (!child)
+                       return max;
+               pci_bus_insert_busn_res(child, next_busnr, bus->busn_res.end);
+       }
+       max++;
+       if (available_buses)
+               available_buses--;
+
+       buses = (buses & PCI_SEC_LATENCY_TIMER_MASK) |
+               FIELD_PREP(PCI_PRIMARY_BUS_MASK, child->primary) |
+               FIELD_PREP(PCI_SECONDARY_BUS_MASK, child->busn_res.start) |
+               FIELD_PREP(PCI_SUBORDINATE_BUS_MASK, child->busn_res.end);
+
+       /*
+        * yenta.c forces a secondary latency timer of 176.
+        * Copy that behaviour here.
+        */
+       buses &= ~PCI_SEC_LATENCY_TIMER_MASK;
+       buses |= FIELD_PREP(PCI_SEC_LATENCY_TIMER_MASK, CARDBUS_LATENCY_TIMER);
+
+       /* We need to blast all three values with a single write */
+       pci_write_config_dword(dev, PCI_PRIMARY_BUS, buses);
+
+       /*
+        * For CardBus bridges, we leave 4 bus numbers as cards with a
+        * PCI-to-PCI bridge can be inserted later.
+        */
+       for (i = 0; i < CARDBUS_RESERVE_BUSNR; i++) {
+               struct pci_bus *parent = bus;
+
+               if (pci_find_bus(pci_domain_nr(bus), max + i + 1))
+                       break;
+
+               while (parent->parent) {
+                       if (!pcibios_assign_all_busses() &&
+                           (parent->busn_res.end > max) &&
+                           (parent->busn_res.end <= max + i)) {
+                               j = 1;
+                       }
+                       parent = parent->parent;
+               }
+               if (j) {
+                       /*
+                        * Often, there are two CardBus bridges -- try to
+                        * leave one valid bus number for each one.
+                        */
+                       i /= 2;
+                       break;
+               }
+       }
+       max += i;
+
+       /*
+        * Set subordinate bus number to its real value. If fixed
+        * subordinate bus number exists from EA capability then use it.
+        */
+       if (fixed_buses)
+               max = fixed_sub;
+       pci_bus_update_busn_res_end(child, max);
+       pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
+
+       scnprintf(child->name, sizeof(child->name), "PCI CardBus %04x:%02x",
+                 pci_domain_nr(bus), child->number);
+
+       pbus_validate_busn(child);
+
+       return max;
+}