summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>2025-12-19 19:40:36 +0200
committerBjorn Helgaas <bhelgaas@google.com>2026-01-27 16:36:53 -0600
commit5d413c735175fd3a862cd747b330d0097f74abce (patch)
treeeddbc2af4dd3b03602d40d338ef35b2dabc9a9bf
parent3cbb40c3d46415bff4ba4b75ccc96007217112f0 (diff)
PCI: Move CardBus bridge scanning to setup-cardbus.c
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
-rw-r--r--drivers/pci/pci.h16
-rw-r--r--drivers/pci/probe.c73
-rw-r--r--drivers/pci/setup-cardbus.c118
3 files changed, 149 insertions, 58 deletions
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index b20ff7ef20ff..c586bf8a9da9 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -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 */
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 388bcf3a41f1..8f5436456c7a 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -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);
diff --git a/drivers/pci/setup-cardbus.c b/drivers/pci/setup-cardbus.c
index 93a2b43c637b..1ebd13a1f730 100644
--- a/drivers/pci/setup-cardbus.c
+++ b/drivers/pci/setup-cardbus.c
@@ -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;
+}