# HG changeset patch
# User root@xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 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 <dkiper@xxxxxxxxxxxx>
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 <linux/init.h>
#include <linux/acpi.h>
#include <asm/e820.h>
+
#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 <linux/pci.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/bitmap.h>
+#include <linux/sort.h>
+#include <asm/e820.h>
+
+#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);
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|