[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 7 of 10] New/Updated drivers: MMCONFIG PCI config space access driver backported from Linux Kernel Ver. 2.6.31.5



# 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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.