device/pci_device.c: Scan only one device for PCIe

Only scan one device if it's a PCIe downstream port.

A PCIe downstream port normally leads to a link with only device 0 on
it. As an optimization, scan only for device 0 in that case.

Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
Change-Id: Id184d03b33e1742b18efb3f11aa9b2f81fa03806
Reviewed-on: https://review.coreboot.org/c/coreboot/+/56788
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Yu-Ping Wu <yupingso@google.com>
diff --git a/src/device/pci_device.c b/src/device/pci_device.c
index 4b5e73b..1075ef7 100644
--- a/src/device/pci_device.c
+++ b/src/device/pci_device.c
@@ -1204,6 +1204,30 @@
 }
 
 /**
+ * A PCIe Downstream Port normally leads to a Link with only Device 0 on it
+ * (PCIe spec r5.0, sec 7.3.1).  As an optimization, scan only for Device 0 in
+ * that situation.
+ *
+ * @param bus Pointer to the bus structure.
+ */
+static bool pci_bus_only_one_child(struct bus *bus)
+{
+	struct device *bridge = bus->dev;
+	u16 pcie_pos, pcie_flags_reg;
+	int pcie_type;
+
+	pcie_pos = pci_find_capability(bridge, PCI_CAP_ID_PCIE);
+	if (!pcie_pos)
+		return false;
+
+	pcie_flags_reg = pci_read_config16(bridge, pcie_pos + PCI_EXP_FLAGS);
+
+	pcie_type = (pcie_flags_reg & PCI_EXP_FLAGS_TYPE) >> 4;
+
+	return pciexp_is_downstream_port(pcie_type);
+}
+
+/**
  * Scan a PCI bus.
  *
  * Determine the existence of devices and bridges on a PCI bus. If there are
@@ -1232,6 +1256,9 @@
 
 	post_code(0x24);
 
+	if (pci_bus_only_one_child(bus))
+		max_devfn = MIN(max_devfn, 0x07);
+
 	/*
 	 * Probe all devices/functions on this bus with some optimization for
 	 * non-existence and single function devices.
diff --git a/src/include/device/pci_def.h b/src/include/device/pci_def.h
index 22a5390..0611436 100644
--- a/src/include/device/pci_def.h
+++ b/src/include/device/pci_def.h
@@ -385,6 +385,7 @@
 #define  PCI_EXP_TYPE_UPSTREAM	0x5	/* Upstream Port */
 #define  PCI_EXP_TYPE_DOWNSTREAM 0x6	/* Downstream Port */
 #define  PCI_EXP_TYPE_PCI_BRIDGE 0x7	/* PCI/PCI-X Bridge */
+#define  PCI_EXP_TYPE_PCIE_BRIDGE 0x8   /* PCI/PCI-X to PCIe Bridge */
 #define PCI_EXP_FLAGS_SLOT	0x0100	/* Slot implemented */
 #define PCI_EXP_FLAGS_IRQ	0x3e00	/* Interrupt message number */
 #define PCI_EXP_DEVCAP		4	/* Device capabilities */
diff --git a/src/include/device/pciexp.h b/src/include/device/pciexp.h
index 014fcb1..fbc769e 100644
--- a/src/include/device/pciexp.h
+++ b/src/include/device/pciexp.h
@@ -31,4 +31,12 @@
 extern struct device_operations default_pciexp_hotplug_ops_bus;
 
 unsigned int pciexp_find_extended_cap(struct device *dev, unsigned int cap);
+
+static inline bool pciexp_is_downstream_port(int type)
+{
+	return type == PCI_EXP_TYPE_ROOT_PORT ||
+	       type == PCI_EXP_TYPE_DOWNSTREAM ||
+	       type == PCI_EXP_TYPE_PCIE_BRIDGE;
+}
+
 #endif /* DEVICE_PCIEXP_H */