# HG changeset patch # User dkiper@xxxxxxxxxxxx # Date 1259016511 -3600 # Node ID 02e13e85fa55c25c232bd9dc41118d4069957195 # Parent 1db1bb63824b25f97d127449faeb3a56f1272c97 MMCONFIG PCI config space access driver backported from Linux Kernel Ver. 2.6.31.5 Signed-off-by: Daniel Kiper diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/Makefile --- a/arch/i386/pci/Makefile Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/i386/pci/Makefile Mon Nov 23 23:48:31 2009 +0100 @@ -1,7 +1,7 @@ obj-y := i386.o init.o obj-y := i386.o init.o obj-$(CONFIG_PCI_BIOS) += pcbios.o -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o mmconfig-shared.o obj-$(CONFIG_PCI_DIRECT) += direct.o # pcifront should be after pcbios.o, mmconfig.o, and direct.o as it should only diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/direct.c --- a/arch/i386/pci/direct.c Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/i386/pci/direct.c Mon Nov 23 23:48:31 2009 +0100 @@ -254,7 +254,18 @@ static int __init pci_check_type2(void) return works; } -void __init pci_direct_init(void) +void __init pci_direct_init(int type) +{ + if (type == 0) + return; + printk(KERN_INFO "PCI: Using configuration type %d\n", type); + if (type == 1) + raw_pci_ops = &pci_direct_conf1; + else + raw_pci_ops = &pci_direct_conf2; +} + +int __init pci_direct_probe(void) { struct resource *region, *region2; @@ -265,18 +276,17 @@ void __init pci_direct_init(void) goto type2; if (pci_check_type1()) { - printk(KERN_INFO "PCI: Using configuration type 1\n"); raw_pci_ops = &pci_direct_conf1; - return; + return 1; } release_resource(region); type2: if ((pci_probe & PCI_PROBE_CONF2) == 0) - return; + return 0; region = request_region(0xCF8, 4, "PCI conf2"); if (!region) - return; + return 0; region2 = request_region(0xC000, 0x1000, "PCI conf2"); if (!region2) goto fail2; @@ -284,10 +294,11 @@ void __init pci_direct_init(void) if (pci_check_type2()) { printk(KERN_INFO "PCI: Using configuration type 2\n"); raw_pci_ops = &pci_direct_conf2; - return; + return 2; } release_resource(region2); fail2: release_resource(region); -} + return 0; +} diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/init.c --- a/arch/i386/pci/init.c Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/i386/pci/init.c Mon Nov 23 23:48:31 2009 +0100 @@ -6,8 +6,13 @@ in the right sequence from here. */ static __init int pci_access_init(void) { + int type = 0; + +#ifdef CONFIG_PCI_DIRECT + type = pci_direct_probe(); +#endif #ifdef CONFIG_PCI_MMCONFIG - pci_mmcfg_init(); + pci_mmcfg_init(type); #endif if (raw_pci_ops) return 0; @@ -21,8 +26,12 @@ static __init int pci_access_init(void) * fails. */ #ifdef CONFIG_PCI_DIRECT - pci_direct_init(); + pci_direct_init(type); #endif + if (!raw_pci_ops) + printk(KERN_ERR + "PCI: Fatal: No config space access function found\n"); + return 0; } arch_initcall(pci_access_init); diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/mmconfig.c --- a/arch/i386/pci/mmconfig.c Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/i386/pci/mmconfig.c Mon Nov 23 23:48:31 2009 +0100 @@ -13,56 +13,47 @@ #include #include #include + #include "pci.h" -/* aperture is up to 256MB but BIOS may reserve less */ -#define MMCONFIG_APER_MIN (2 * 1024*1024) -#define MMCONFIG_APER_MAX (256 * 1024*1024) - +/* Assume systems with more busses have correct MCFG */ #define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG)) /* The base address of the last MMCONFIG device accessed */ static u32 mmcfg_last_accessed_device; +static int mmcfg_last_accessed_cpu; /* * Functions for accessing PCI configuration space with MMCONFIG accesses */ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) { - int cfg_num = -1; struct acpi_table_mcfg_config *cfg; + int cfg_num; - while (1) { - ++cfg_num; - if (cfg_num >= pci_mmcfg_config_num) { - break; - } + for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) { cfg = &pci_mmcfg_config[cfg_num]; - if (cfg->pci_segment_group_number != seg) - continue; - if ((cfg->start_bus_number <= bus) && + if (cfg->pci_segment_group_number == seg && + (cfg->start_bus_number <= bus) && (cfg->end_bus_number >= bus)) return cfg->base_address; } - - /* Handle more broken MCFG tables on Asus etc. - They only contain a single entry for bus 0-0. Assume - this applies to all busses. */ - cfg = &pci_mmcfg_config[0]; - if (pci_mmcfg_config_num == 1 && - cfg->pci_segment_group_number == 0 && - (cfg->start_bus_number | cfg->end_bus_number) == 0) - return cfg->base_address; /* Fall back to type 0 */ return 0; } -static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) +/* + * This is always called under pci_config_lock + */ +static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) { u32 dev_base = base | (bus << 20) | (devfn << 12); - if (dev_base != mmcfg_last_accessed_device) { + int cpu = smp_processor_id(); + if (dev_base != mmcfg_last_accessed_device || + cpu != mmcfg_last_accessed_cpu) { mmcfg_last_accessed_device = dev_base; + mmcfg_last_accessed_cpu = cpu; set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); } } @@ -77,9 +68,6 @@ err: *value = -1; err: *value = -1; return -EINVAL; } - - if (reg < 256) - return pci_conf1_read(seg,bus,devfn,reg,len,value); base = get_base_addr(seg, bus, devfn); if (!base) @@ -100,7 +88,6 @@ err: *value = -1; *value = readl(mmcfg_virt_addr + reg); break; } - spin_unlock_irqrestore(&pci_config_lock, flags); return 0; @@ -112,11 +99,8 @@ static int pci_mmcfg_write(unsigned int unsigned long flags; u32 base; - if ((bus > 255) || (devfn > 255) || (reg > 4095)) + if ((bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; - - if (reg < 256) - return pci_conf1_write(seg,bus,devfn,reg,len,value); base = get_base_addr(seg, bus, devfn); if (!base) @@ -137,7 +121,6 @@ static int pci_mmcfg_write(unsigned int writel(value, mmcfg_virt_addr + reg); break; } - spin_unlock_irqrestore(&pci_config_lock, flags); return 0; @@ -148,27 +131,13 @@ static struct pci_raw_ops pci_mmcfg = { .write = pci_mmcfg_write, }; -void __init pci_mmcfg_init(void) +int __init pci_mmcfg_arch_init(void) { - if ((pci_probe & PCI_PROBE_MMCONF) == 0) - return; - - acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); - if ((pci_mmcfg_config_num == 0) || - (pci_mmcfg_config == NULL) || - (pci_mmcfg_config[0].base_address == 0)) - return; - - if (!e820_all_mapped(pci_mmcfg_config[0].base_address, - pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN, - E820_RESERVED)) { - printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n", - pci_mmcfg_config[0].base_address); - printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); - return; - } - printk(KERN_INFO "PCI: Using MMCONFIG\n"); raw_pci_ops = &pci_mmcfg; - pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + return 1; } + +void __init pci_mmcfg_arch_free(void) +{ +} diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/pci.h --- a/arch/i386/pci/pci.h Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/i386/pci/pci.h Mon Nov 23 23:48:31 2009 +0100 @@ -81,7 +81,13 @@ extern int pci_conf1_read(unsigned int s extern int pci_conf1_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value); -extern void pci_direct_init(void); +extern int pci_direct_probe(void); +extern void pci_direct_init(int type); extern void pci_pcbios_init(void); -extern void pci_mmcfg_init(void); +extern void pci_mmcfg_init(int type); extern void pcibios_sort(void); + +/* pci-mmconfig.c */ + +extern int __init pci_mmcfg_arch_init(void); +extern void __init pci_mmcfg_arch_free(void); diff -r 1db1bb63824b -r 02e13e85fa55 arch/x86_64/pci/Makefile --- a/arch/x86_64/pci/Makefile Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/x86_64/pci/Makefile Mon Nov 23 23:48:31 2009 +0100 @@ -11,7 +11,7 @@ obj-$(CONFIG_ACPI) += acpi.o obj-$(CONFIG_ACPI) += acpi.o obj-y += legacy.o irq.o common.o # mmconfig has a 64bit special -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o mmconfig-shared.o obj-$(CONFIG_NUMA) += k8-bus.o @@ -28,3 +28,4 @@ fixup-y += ../../i386/pci/fixup.o fixup-y += ../../i386/pci/fixup.o i386-y += ../../i386/pci/i386.o init-y += ../../i386/pci/init.o +mmconfig-shared-y += ../../i386/pci/mmconfig-shared.o diff -r 1db1bb63824b -r 02e13e85fa55 arch/x86_64/pci/mmconfig.c --- a/arch/x86_64/pci/mmconfig.c Mon Nov 23 07:32:47 2009 +0000 +++ b/arch/x86_64/pci/mmconfig.c Mon Nov 23 23:48:31 2009 +0100 @@ -1,6 +1,6 @@ /* * mmconfig.c - Low-level direct PCI config space access via MMCONFIG - * + * * This is an 64bit optimized version that always keeps the full mmconfig * space mapped. This allows lockless config space operation. */ @@ -13,10 +13,6 @@ #include "pci.h" -/* aperture is up to 256MB but BIOS may reserve less */ -#define MMCONFIG_APER_MIN (2 * 1024*1024) -#define MMCONFIG_APER_MAX (256 * 1024*1024) - /* Static virtual mapping of the MMCONFIG aperture */ struct mmcfg_virt { struct acpi_table_mcfg_config *cfg; @@ -24,55 +20,18 @@ struct mmcfg_virt { }; static struct mmcfg_virt *pci_mmcfg_virt; -static inline int mcfg_broken(void) -{ - struct acpi_table_mcfg_config *cfg = &pci_mmcfg_config[0]; - - /* Handle more broken MCFG tables on Asus etc. - They only contain a single entry for bus 0-0. Assume - this applies to all busses. */ - if (pci_mmcfg_config_num == 1 && - cfg->pci_segment_group_number == 0 && - (cfg->start_bus_number | cfg->end_bus_number) == 0) - return 1; - return 0; -} - -static void __iomem * __init mcfg_ioremap(struct acpi_table_mcfg_config *cfg) -{ - void __iomem *addr; - u32 size; - - size = (cfg->end_bus_number + 1) << 20; - printk(KERN_INFO "%s: end_bus_number=%d\n", __func__, - cfg->end_bus_number); - addr = ioremap_nocache(cfg->base_address, size); - if (addr) { - printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", - cfg->base_address, cfg->base_address + size - 1); - } - return addr; -} - static char __iomem *get_virt(unsigned int seg, unsigned bus) { - int cfg_num = -1; struct acpi_table_mcfg_config *cfg; + int cfg_num; - while (1) { - ++cfg_num; - if (cfg_num >= pci_mmcfg_config_num) - break; + for (cfg_num = 0; cfg_num < pci_mmcfg_config_num; cfg_num++) { cfg = pci_mmcfg_virt[cfg_num].cfg; - if (cfg->pci_segment_group_number != seg) - continue; - if ((cfg->start_bus_number <= bus) && + if (cfg->pci_segment_group_number == seg && + (cfg->start_bus_number <= bus) && (cfg->end_bus_number >= bus)) return pci_mmcfg_virt[cfg_num].virt; } - - if (mcfg_broken()) - return pci_mmcfg_virt[0].virt; /* Fall back to type 0 */ return NULL; @@ -81,6 +40,7 @@ static char __iomem *pci_dev_base(unsign static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) { char __iomem *addr; + addr = get_virt(seg, bus); if (!addr) return NULL; @@ -97,9 +57,6 @@ err: *value = -1; err: *value = -1; return -EINVAL; } - - if (reg < 256) - return pci_conf1_read(seg,bus,devfn,reg,len,value); addr = pci_dev_base(seg, bus, devfn); if (!addr) @@ -129,9 +86,6 @@ static int pci_mmcfg_write(unsigned int if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) return -EINVAL; - if (reg < 256) - return pci_conf1_write(seg,bus,devfn,reg,len,value); - addr = pci_dev_base(seg, bus, devfn); if (!addr) return -EINVAL; @@ -156,44 +110,65 @@ static struct pci_raw_ops pci_mmcfg = { .write = pci_mmcfg_write, }; -void __init pci_mmcfg_init(void) +static void __iomem * __init mcfg_ioremap(struct acpi_table_mcfg_config *cfg) +{ + void __iomem *addr; + u64 start, size; + + start = cfg->start_bus_number; + start <<= 20; + start += cfg->base_address; + size = cfg->end_bus_number + 1 - cfg->start_bus_number; + size <<= 20; + addr = ioremap_nocache(start, size); + if (addr) { + printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", + start, start + size - 1); + addr -= cfg->start_bus_number << 20; + } + return addr; +} + +int __init pci_mmcfg_arch_init(void) { int i; - - if ((pci_probe & PCI_PROBE_MMCONF) == 0) - return; - - acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); - if ((pci_mmcfg_config_num == 0) || - (pci_mmcfg_config == NULL) || - (pci_mmcfg_config[0].base_address == 0)) - return; - - if (!e820_all_mapped(pci_mmcfg_config[0].base_address, - pci_mmcfg_config[0].base_address + MMCONFIG_APER_MIN, - E820_RESERVED)) { - printk(KERN_ERR "PCI: BIOS Bug: MCFG area at %x is not E820-reserved\n", - pci_mmcfg_config[0].base_address); - printk(KERN_ERR "PCI: Not using MMCONFIG.\n"); - return; + pci_mmcfg_virt = kzalloc(sizeof(*pci_mmcfg_virt) * + pci_mmcfg_config_num, GFP_KERNEL); + if (pci_mmcfg_virt == NULL) { + printk(KERN_ERR "PCI: Can not allocate memory for mmconfig structures\n"); + return 0; } - /* RED-PEN i386 doesn't do _nocache right now */ - pci_mmcfg_virt = kmalloc(sizeof(*pci_mmcfg_virt) * pci_mmcfg_config_num, GFP_KERNEL); - if (pci_mmcfg_virt == NULL) { - printk("PCI: Can not allocate memory for mmconfig structures\n"); - return; - } for (i = 0; i < pci_mmcfg_config_num; ++i) { pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i]; pci_mmcfg_virt[i].virt = mcfg_ioremap(&pci_mmcfg_config[i]); if (!pci_mmcfg_virt[i].virt) { - printk("PCI: Cannot map mmconfig aperture for segment %d\n", - pci_mmcfg_config[i].pci_segment_group_number); - return; + printk(KERN_ERR "PCI: Cannot map mmconfig aperture for " + "segment %d\n", + pci_mmcfg_config[i].pci_segment_group_number); + pci_mmcfg_arch_free(); + return 0; + } + } + raw_pci_ops = &pci_mmcfg; + return 1; +} + +void __init pci_mmcfg_arch_free(void) +{ + int i; + + if (pci_mmcfg_virt == NULL) + return; + + for (i = 0; i < pci_mmcfg_config_num; ++i) { + if (pci_mmcfg_virt[i].virt) { + iounmap(pci_mmcfg_virt[i].virt + (pci_mmcfg_virt[i].cfg->start_bus_number << 20)); + pci_mmcfg_virt[i].virt = NULL; + pci_mmcfg_virt[i].cfg = NULL; } } - raw_pci_ops = &pci_mmcfg; - pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + kfree(pci_mmcfg_virt); + pci_mmcfg_virt = NULL; } diff -r 1db1bb63824b -r 02e13e85fa55 arch/i386/pci/mmconfig-shared.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/arch/i386/pci/mmconfig-shared.c Mon Nov 23 23:48:31 2009 +0100 @@ -0,0 +1,439 @@ +/* + * mmconfig-shared.c - Low-level direct PCI config space access via + * MMCONFIG - common code between i386 and x86-64. + * + * This code does: + * - known chipset handling + * - ACPI decoding and validation + * + * Per-architecture code takes care of the mappings and accesses + * themselves. + */ + +#include +#include +#include +#include +#include +#include + +#include "pci.h" + +/* Indicate if the mmcfg resources have been placed into the resource table. */ +static int __initdata pci_mmcfg_resources_inserted; + +static __init int extend_mmcfg(int num) +{ + struct acpi_table_mcfg_config *new; + int new_num = pci_mmcfg_config_num + num; + + new = kzalloc(sizeof(pci_mmcfg_config[0]) * new_num, GFP_KERNEL); + if (!new) + return -1; + + if (pci_mmcfg_config) { + memcpy(new, pci_mmcfg_config, + sizeof(pci_mmcfg_config[0]) * new_num); + kfree(pci_mmcfg_config); + } + pci_mmcfg_config = new; + + return 0; +} + +static __init void fill_one_mmcfg(u64 addr, int segment, int start, int end) +{ + int i = pci_mmcfg_config_num; + + pci_mmcfg_config_num++; + pci_mmcfg_config[i].base_address = addr; + pci_mmcfg_config[i].pci_segment_group_number = segment; + pci_mmcfg_config[i].start_bus_number = start; + pci_mmcfg_config[i].end_bus_number = end; +} + +static const char __init *pci_mmcfg_e7520(void) +{ + u32 win; + raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); + + win = win & 0xf000; + if (win == 0x0000 || win == 0xf000) + return NULL; + + if (extend_mmcfg(1) == -1) + return NULL; + + fill_one_mmcfg(win << 16, 0, 0, 255); + + return "Intel Corporation E7520 Memory Controller Hub"; +} + +static const char __init *pci_mmcfg_intel_945(void) +{ + u32 pciexbar, mask = 0, len = 0; + + raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); + + /* Enable bit */ + if (!(pciexbar & 1)) + return NULL; + + /* Size bits */ + switch ((pciexbar >> 1) & 3) { + case 0: + mask = 0xf0000000U; + len = 0x10000000U; + break; + case 1: + mask = 0xf8000000U; + len = 0x08000000U; + break; + case 2: + mask = 0xfc000000U; + len = 0x04000000U; + break; + default: + return NULL; + } + + /* Errata #2, things break when not aligned on a 256Mb boundary */ + /* Can only happen in 64M/128M mode */ + + if ((pciexbar & mask) & 0x0fffffffU) + return NULL; + + /* Don't hit the APIC registers and their friends */ + if ((pciexbar & mask) >= 0xf0000000U) + return NULL; + + if (extend_mmcfg(1) == -1) + return NULL; + + fill_one_mmcfg(pciexbar & mask, 0, 0, (len >> 20) - 1); + + return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; +} + +static int __initdata mcp55_checked; +static const char __init *pci_mmcfg_nvidia_mcp55(void) +{ + int bus; + int mcp55_mmconf_found = 0; + + static const u32 extcfg_regnum = 0x90; + static const u32 extcfg_regsize = 4; + static const u32 extcfg_enable_mask = 1<<31; + static const u32 extcfg_start_mask = 0xff<<16; + static const int extcfg_start_shift = 16; + static const u32 extcfg_size_mask = 0x3<<28; + static const int extcfg_size_shift = 28; + static const int extcfg_sizebus[] = {0x100, 0x80, 0x40, 0x20}; + static const u32 extcfg_base_mask[] = {0x7ff8, 0x7ffc, 0x7ffe, 0x7fff}; + static const int extcfg_base_lshift = 25; + + /* + * do check if amd fam10h already took over + */ + if (!acpi_disabled || pci_mmcfg_config_num || mcp55_checked) + return NULL; + + mcp55_checked = 1; + for (bus = 0; bus < 256; bus++) { + u64 base; + u32 l, extcfg; + u16 vendor, device; + int start, size_index, end; + + raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l); + vendor = l & 0xffff; + device = (l >> 16) & 0xffff; + + if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device) + continue; + + raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum, + extcfg_regsize, &extcfg); + + if (!(extcfg & extcfg_enable_mask)) + continue; + + if (extend_mmcfg(1) == -1) + continue; + + size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift; + base = extcfg & extcfg_base_mask[size_index]; + /* base could > 4G */ + base <<= extcfg_base_lshift; + start = (extcfg & extcfg_start_mask) >> extcfg_start_shift; + end = start + extcfg_sizebus[size_index] - 1; + fill_one_mmcfg(base, 0, start, end); + mcp55_mmconf_found++; + } + + if (!mcp55_mmconf_found) + return NULL; + + return "nVidia MCP55"; +} + +struct pci_mmcfg_hostbridge_probe { + u32 bus; + u32 devfn; + u32 vendor; + u32 device; + const char *(*probe)(void); +}; + +static struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initdata = { + { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 }, + { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 }, + { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA, + 0x0369, pci_mmcfg_nvidia_mcp55 }, +}; + +static int __init cmp_mmcfg(const void *x1, const void *x2) +{ + const typeof(pci_mmcfg_config[0]) *m1 = x1; + const typeof(pci_mmcfg_config[0]) *m2 = x2; + int start1, start2; + + start1 = m1->start_bus_number; + start2 = m2->start_bus_number; + + return start1 - start2; +} + +static void __init pci_mmcfg_check_end_bus_number(void) +{ + int i; + typeof(pci_mmcfg_config[0]) *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]; + if (cfg->end_bus_number < cfg->start_bus_number) + cfg->end_bus_number = 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_number < cfg->start_bus_number) + cfg->end_bus_number = 255; + + if (cfg->end_bus_number >= cfgx->start_bus_number) + cfg->end_bus_number = cfgx->start_bus_number - 1; + } +} + +static int __init pci_mmcfg_check_hostbridge(void) +{ + u32 l; + u32 bus, devfn; + u16 vendor, device; + int i; + const char *name; + + if (!raw_pci_ops) + return 0; + + pci_mmcfg_config_num = 0; + pci_mmcfg_config = NULL; + + for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) { + bus = pci_mmcfg_probes[i].bus; + devfn = pci_mmcfg_probes[i].devfn; + raw_pci_ops->read(0, bus, devfn, 0, 4, &l); + vendor = l & 0xffff; + device = (l >> 16) & 0xffff; + + name = NULL; + if (pci_mmcfg_probes[i].vendor == vendor && + pci_mmcfg_probes[i].device == device) + name = pci_mmcfg_probes[i].probe(); + + if (name) + printk(KERN_INFO "PCI: Found %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; +} + +static void __init pci_mmcfg_insert_resources(void) +{ +#define PCI_MMCFG_RESOURCE_NAME_LEN 24 + int i; + struct resource *res; + char *names; + unsigned num_buses; + + res = kcalloc(PCI_MMCFG_RESOURCE_NAME_LEN + sizeof(*res), + pci_mmcfg_config_num, GFP_KERNEL); + if (!res) { + printk(KERN_ERR "PCI: Unable to allocate MMCONFIG resources\n"); + return; + } + + names = (void *)&res[pci_mmcfg_config_num]; + for (i = 0; i < pci_mmcfg_config_num; i++, res++) { + struct acpi_table_mcfg_config *cfg = &pci_mmcfg_config[i]; + num_buses = cfg->end_bus_number - cfg->start_bus_number + 1; + res->name = names; + snprintf(names, PCI_MMCFG_RESOURCE_NAME_LEN, + "PCI MMCONFIG %u [%02x-%02x]", cfg->pci_segment_group_number, + cfg->start_bus_number, cfg->end_bus_number); + res->start = cfg->base_address + (cfg->start_bus_number << 20); + res->end = res->start + (num_buses << 20) - 1; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + insert_resource(&iomem_resource, res); + names += PCI_MMCFG_RESOURCE_NAME_LEN; + } + + /* Mark that the resources have been inserted. */ + pci_mmcfg_resources_inserted = 1; +} + +typedef int (*check_reserved_t)(unsigned long start, unsigned long end, unsigned type); + +static int __init is_mmconf_reserved(check_reserved_t is_reserved, + u64 addr, u64 size, int i, + typeof(pci_mmcfg_config[0]) *cfg, int with_e820) +{ + u64 old_size = size; + int valid = 0; + + while (!is_reserved(addr, addr + size, E820_RESERVED)) { + size >>= 1; + if (size < (16UL<<20)) + break; + } + + 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"); + valid = 1; + + if (old_size != size) { + /* update end_bus_number */ + cfg->end_bus_number = cfg->start_bus_number + ((size>>20) - 1); + printk(KERN_NOTICE "PCI: updated MCFG configuration %d: base %lx " + "segment %hu buses %u - %u\n", + i, (unsigned long)cfg->base_address, cfg->pci_segment_group_number, + (unsigned int)cfg->start_bus_number, + (unsigned int)cfg->end_bus_number); + } + } + + return valid; +} + +static void __init pci_mmcfg_reject_broken(int type) +{ + typeof(pci_mmcfg_config[0]) *cfg; + int i; + + if ((pci_mmcfg_config_num == 0) || + (pci_mmcfg_config == NULL) || + (pci_mmcfg_config[0].base_address == 0)) + return; + + for (i = 0; i < pci_mmcfg_config_num; i++) { + u64 addr, size; + + cfg = &pci_mmcfg_config[i]; + addr = cfg->start_bus_number; + addr <<= 20; + addr += cfg->base_address; + size = cfg->end_bus_number + 1 - cfg->start_bus_number; + size <<= 20; + printk(KERN_NOTICE "PCI: MCFG configuration %d: base %lx " + "segment %hu buses %u - %u\n", + i, (unsigned long)cfg->base_address, cfg->pci_segment_group_number, + (unsigned int)cfg->start_bus_number, + (unsigned int)cfg->end_bus_number); + + if (type == 1 && !is_mmconf_reserved(e820_all_mapped, addr, size, i, cfg, 1)) + goto reject; + } + + return; + +reject: + printk(KERN_INFO "PCI: Not using MMCONFIG.\n"); + pci_mmcfg_arch_free(); + kfree(pci_mmcfg_config); + pci_mmcfg_config = NULL; + pci_mmcfg_config_num = 0; +} + +void __init pci_mmcfg_init(int type) +{ + /* MMCONFIG disabled */ + if ((pci_probe & PCI_PROBE_MMCONF) == 0) + return; + + if (type != 1 || !pci_mmcfg_check_hostbridge()) { + acpi_table_parse(ACPI_MCFG, acpi_parse_mcfg); + pci_mmcfg_reject_broken(type); + } + + if ((pci_mmcfg_config_num == 0) || + (pci_mmcfg_config == NULL) || + (pci_mmcfg_config[0].base_address == 0)) + return; + + if (pci_mmcfg_arch_init()) + pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + else { + /* + * Signal not to attempt to insert mmcfg resources because + * the architecture mmcfg setup could not initialize. + */ + pci_mmcfg_resources_inserted = 1; + } +} + +static int __init pci_mmcfg_late_insert_resources(void) +{ + /* + * If resources are already inserted or we are not using MMCONFIG, + * don't insert the resources. + */ + if ((pci_mmcfg_resources_inserted == 1) || + (pci_probe & PCI_PROBE_MMCONF) == 0 || + (pci_mmcfg_config_num == 0) || + (pci_mmcfg_config == NULL) || + (pci_mmcfg_config[0].base_address == 0)) + return 1; + + /* + * Attempt to insert the mmcfg resources but not with the busy flag + * marked so it won't cause request errors when __request_region is + * called. + */ + pci_mmcfg_insert_resources(); + + return 0; +} + +/* + * Perform MMCONFIG resource insertion after PCI initialization to allow for + * misprogrammed MCFG tables that state larger sizes but actually conflict + * with other system resources. + */ +late_initcall(pci_mmcfg_late_insert_resources);