@@ -142,6 +142,11 @@ extern int __devinit pci_mmconfig_insert(struct device *dev,
u16 seg, u8 start,
u8 end, phys_addr_t addr);
extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+#if defined(CONFIG_ACPI) && defined(CONFIG_PCI_MMCONFIG)
+extern void pci_mmconfig_probe(u8 start);
+#else
+static inline void pci_mmconfig_probe(u8 start) { }
+#endif
extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
extern struct list_head pci_mmcfg_list;
@@ -49,6 +49,7 @@ void __devinit pcibios_scan_specific_bus(int busn)
l != 0x0000 && l != 0xffff) {
DBG("Found device at %02x:%02x [%04x]\n", busn, devfn, l);
printk(KERN_INFO "PCI: Discovered peer bus %02x\n", busn);
+ pci_mmconfig_probe(busn);
pci_scan_bus_on_node(busn, &pci_root_ops, node);
return;
}
@@ -459,6 +459,14 @@ static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
break;
}
+ /*
+ * For backward compatibility, we will adjust the MMCONFIG region
+ * at boot time if it's only partially reserved by firmware.
+ * For PCI host bridge hotplug at runtime, just reject it.
+ */
+ if (pci_mmcfg_running_state && old_size != size)
+ return 0;
+
if (size < (16UL<<20) && size != old_size)
return 0;
@@ -602,6 +610,16 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
return -ENODEV;
}
+ /*
+ * MMCFG information for host brideges will be added on demand
+ * by pci_root driver if ACPI is enabled. But there are special
+ * requirements for devices on segment 0, MMCFG information may
+ * be needed for fixing hardware quirks and probing for hidden
+ * buses.
+ */
+ if (!acpi_disabled && cfg->pci_segment)
+ continue;
+
if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
cfg->end_bus_number, cfg->address) == NULL) {
printk(KERN_WARNING PREFIX
@@ -614,7 +632,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
return 0;
}
-static void __init __pci_mmcfg_init(int early)
+static void __init __pci_mmcfg_init(int early, bool create_on_demand)
{
pci_mmcfg_reject_broken(early);
if (list_empty(&pci_mmcfg_list))
@@ -630,6 +648,13 @@ static void __init __pci_mmcfg_init(int early)
}
}
+ /*
+ * Avoid redundant pci_mmcfg_arch_map()/pci_mmcfg_arch_unmap() calls
+ * for each MMCONFIG entry if they will be created on demand.
+ */
+ if (create_on_demand)
+ free_all_mmcfg();
+
if (pci_mmcfg_arch_init())
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
else {
@@ -647,28 +672,37 @@ void __init pci_mmcfg_early_init(void)
known_bridge = 1;
else
acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
- __pci_mmcfg_init(1);
+ __pci_mmcfg_init(1, false);
}
}
void __init pci_mmcfg_late_init(void)
{
+ bool create_on_demand;
+
/* MMCONFIG disabled */
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
return;
- /* MMCONFIG already enabled */
- if (!(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
- return;
-
if (known_bridge)
return;
- acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
- __pci_mmcfg_init(0);
+ /*
+ * MMCONFIG information will be created on demand by pci_root driver
+ * when binding to ACPI host bridge deivces.
+ */
+ create_on_demand = (!acpi_disabled && !acpi_pci_cache_mcfg());
- if (!(pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF))
- acpi_pci_cache_mcfg();
+ /* pci_mmcfg_early_init() fails to setup MMCONFIG, try again. */
+ if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
+ acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+ __pci_mmcfg_init(0, create_on_demand);
+ /*
+ * Free MMCONFIG information created by pci_mmcfg_early_init()
+ * if MMCONFIG information will be created on demand.
+ */
+ } else if (create_on_demand)
+ free_all_mmcfg();
}
static int __init pci_mmcfg_late_insert_resources(void)
@@ -802,3 +836,25 @@ int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
return -ENOENT;
}
+
+#ifdef CONFIG_ACPI
+/* Probe MMCFG information for PCI bus blind probe */
+void __devinit pci_mmconfig_probe(u8 start)
+{
+ int end_bus, temp;
+ phys_addr_t addr;
+
+ if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
+ return;
+
+ addr = acpi_pci_root_get_mcfg_addr(NULL, 0, start, &end_bus);
+ if (addr && end_bus >= 0 && end_bus <= 255) {
+ for (temp = start + 1; temp <= end_bus; temp++)
+ if (pci_find_bus(0, temp))
+ break;
+
+ temp--;
+ pci_mmconfig_insert(NULL, 0, start, (u8) temp, addr);
+ }
+}
+#endif