#include <linux/sfi_acpi.h>
#include <linux/bitmap.h>
#include <linux/dmi.h>
-#include <linux/sort.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <asm/acpi.h>
/* Indicate if the mmcfg resources have been placed into the resource table. */
static int __initdata pci_mmcfg_resources_inserted;
+LIST_HEAD(pci_mmcfg_list);
+
+static __init void pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
+{
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ list_del(&cfg->list);
+ kfree(cfg);
+}
+
static __init void free_all_mmcfg(void)
{
- int i;
- struct pci_mmcfg_region *cfg;
+ struct pci_mmcfg_region *cfg, *tmp;
pci_mmcfg_arch_free();
- for (i = 0; i < pci_mmcfg_config_num; i++) {
- cfg = &pci_mmcfg_config[i];
- if (cfg->res.parent)
- release_resource(&cfg->res);
+ list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
+ pci_mmconfig_remove(cfg);
+}
+
+static __init void list_add_sorted(struct pci_mmcfg_region *new)
+{
+ struct pci_mmcfg_region *cfg;
+
+ /* keep list sorted by segment and starting bus number */
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ if (cfg->segment > new->segment ||
+ (cfg->segment == new->segment &&
+ cfg->start_bus >= new->start_bus)) {
+ list_add_tail(&new->list, &cfg->list);
+ return;
+ }
}
- pci_mmcfg_config_num = 0;
- kfree(pci_mmcfg_config);
- pci_mmcfg_config = NULL;
+ list_add_tail(&new->list, &pci_mmcfg_list);
}
static __init struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start,
int end, u64 addr)
{
struct pci_mmcfg_region *new;
- int new_num = pci_mmcfg_config_num + 1;
- int i = pci_mmcfg_config_num;
int num_buses;
struct resource *res;
if (addr == 0)
return NULL;
- new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL);
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
if (!new)
return NULL;
- if (pci_mmcfg_config) {
- memcpy(new, pci_mmcfg_config,
- sizeof(pci_mmcfg_config[0]) * new_num);
- kfree(pci_mmcfg_config);
- }
- pci_mmcfg_config = new;
- pci_mmcfg_config_num++;
-
- new = &pci_mmcfg_config[i];
-
new->address = addr;
new->segment = segment;
new->start_bus = start;
new->end_bus = end;
+ list_add_sorted(new);
+
num_buses = end - start + 1;
res = &new->res;
res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
"PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
res->name = new->name;
- return &pci_mmcfg_config[i];
+ printk(KERN_INFO PREFIX "MMCONFIG for domain %04x [bus %02x-%02x] at "
+ "%pR (base %#lx)\n", segment, start, end, &new->res,
+ (unsigned long) addr);
+
+ return new;
+}
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+ struct pci_mmcfg_region *cfg;
+
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ if (cfg->segment == segment &&
+ cfg->start_bus <= bus && bus <= cfg->end_bus)
+ return cfg;
+
+ return NULL;
}
static const char __init *pci_mmcfg_e7520(void)
/*
* do check if amd fam10h already took over
*/
- if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked)
+ if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
return NULL;
mcp55_checked = true;
0x0369, pci_mmcfg_nvidia_mcp55 },
};
-static int __init cmp_mmcfg(const void *x1, const void *x2)
-{
- const struct pci_mmcfg_region *m1 = x1;
- const struct pci_mmcfg_region *m2 = x2;
- int start1, start2;
-
- start1 = m1->start_bus;
- start2 = m2->start_bus;
-
- return start1 - start2;
-}
-
static void __init pci_mmcfg_check_end_bus_number(void)
{
- int i;
struct pci_mmcfg_region *cfg, *cfgx;
- /* sort them at first */
- sort(pci_mmcfg_config, pci_mmcfg_config_num,
- sizeof(pci_mmcfg_config[0]), cmp_mmcfg, NULL);
-
- /* last one*/
- if (pci_mmcfg_config_num > 0) {
- i = pci_mmcfg_config_num - 1;
- cfg = &pci_mmcfg_config[i];
+ /* Fixup overlaps */
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
if (cfg->end_bus < cfg->start_bus)
cfg->end_bus = 255;
- }
-
- /* don't overlap please */
- for (i = 0; i < pci_mmcfg_config_num - 1; i++) {
- cfg = &pci_mmcfg_config[i];
- cfgx = &pci_mmcfg_config[i+1];
- if (cfg->end_bus < cfg->start_bus)
- cfg->end_bus = 255;
+ /* Don't access the list head ! */
+ if (cfg->list.next == &pci_mmcfg_list)
+ break;
+ cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
if (cfg->end_bus >= cfgx->start_bus)
cfg->end_bus = cfgx->start_bus - 1;
}
name = pci_mmcfg_probes[i].probe();
if (name)
- printk(KERN_INFO "PCI: Found %s with MMCONFIG support.\n",
+ printk(KERN_INFO PREFIX "%s with MMCONFIG support\n",
name);
}
/* some end_bus_number is crazy, fix it */
pci_mmcfg_check_end_bus_number();
- return pci_mmcfg_config_num != 0;
+ return !list_empty(&pci_mmcfg_list);
}
static void __init pci_mmcfg_insert_resources(void)
{
- int i;
struct pci_mmcfg_region *cfg;
- for (i = 0; i < pci_mmcfg_config_num; i++) {
- cfg = &pci_mmcfg_config[i];
+ list_for_each_entry(cfg, &pci_mmcfg_list, list)
insert_resource(&iomem_resource, &cfg->res);
- }
/* Mark that the resources have been inserted. */
pci_mmcfg_resources_inserted = 1;
typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
static int __init is_mmconf_reserved(check_reserved_t is_reserved,
- int i, struct pci_mmcfg_region *cfg, int with_e820)
+ struct pci_mmcfg_region *cfg, int with_e820)
{
u64 addr = cfg->res.start;
u64 size = resource_size(&cfg->res);
}
if (size >= (16UL<<20) || size == old_size) {
- printk(KERN_NOTICE
- "PCI: MCFG area at %Lx reserved in %s\n",
- addr, with_e820?"E820":"ACPI motherboard resources");
+ printk(KERN_INFO PREFIX "MMCONFIG at %pR reserved in %s\n",
+ &cfg->res,
+ with_e820 ? "E820" : "ACPI motherboard resources");
valid = 1;
if (old_size != size) {
snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
"PCI MMCONFIG %04x [bus %02x-%02x]",
cfg->segment, cfg->start_bus, cfg->end_bus);
- printk(KERN_NOTICE "PCI: updated MCFG configuration %d: base %lx "
- "segment %hu buses %u - %u\n",
- i, (unsigned long)cfg->address, cfg->segment,
- (unsigned int)cfg->start_bus,
- (unsigned int)cfg->end_bus);
+ printk(KERN_INFO PREFIX
+ "MMCONFIG for %04x [bus%02x-%02x] "
+ "at %pR (base %#lx) (size reduced!)\n",
+ cfg->segment, cfg->start_bus, cfg->end_bus,
+ &cfg->res, (unsigned long) cfg->address);
}
}
static void __init pci_mmcfg_reject_broken(int early)
{
struct pci_mmcfg_region *cfg;
- int i;
-
- if (pci_mmcfg_config_num == 0)
- return;
- for (i = 0; i < pci_mmcfg_config_num; i++) {
+ list_for_each_entry(cfg, &pci_mmcfg_list, list) {
int valid = 0;
- cfg = &pci_mmcfg_config[i];
- printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx "
- "segment %hu buses %u - %u\n",
- i, (unsigned long)cfg->address, cfg->segment,
- (unsigned int)cfg->start_bus,
- (unsigned int)cfg->end_bus);
-
if (!early && !acpi_disabled)
- valid = is_mmconf_reserved(is_acpi_reserved, i, cfg, 0);
+ valid = is_mmconf_reserved(is_acpi_reserved, cfg, 0);
if (valid)
continue;
if (!early)
- printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %Lx is not"
- " reserved in ACPI motherboard resources\n",
- cfg->address);
+ printk(KERN_ERR FW_BUG PREFIX
+ "MMCONFIG at %pR not reserved in "
+ "ACPI motherboard resources\n", &cfg->res);
/* Don't try to do this check unless configuration
type 1 is available. how about type 2 ?*/
if (raw_pci_ops)
- valid = is_mmconf_reserved(e820_all_mapped, i, cfg, 1);
+ valid = is_mmconf_reserved(e820_all_mapped, cfg, 1);
if (!valid)
goto reject;
return;
reject:
- printk(KERN_INFO "PCI: Not using MMCONFIG.\n");
+ printk(KERN_INFO PREFIX "not using MMCONFIG\n");
free_all_mmcfg();
}
static int __initdata known_bridge;
-/* The physical address of the MMCONFIG aperture. Set from ACPI tables. */
-struct pci_mmcfg_region *pci_mmcfg_config;
-int pci_mmcfg_config_num;
-
static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
struct acpi_mcfg_allocation *cfg)
{
return 0;
}
- printk(KERN_ERR PREFIX "MCFG region for %04x:%02x-%02x at %#llx "
+ printk(KERN_ERR PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
"is above 4GB, ignored\n", cfg->pci_segment,
cfg->start_bus_number, cfg->end_bus_number, cfg->address);
return -EINVAL;
pci_mmcfg_reject_broken(early);
- if (pci_mmcfg_config_num == 0)
+ if (list_empty(&pci_mmcfg_list))
return;
if (pci_mmcfg_arch_init())
*/
if ((pci_mmcfg_resources_inserted == 1) ||
(pci_probe & PCI_PROBE_MMCONF) == 0 ||
- (pci_mmcfg_config_num == 0))
+ list_empty(&pci_mmcfg_list))
return 1;
/*