WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH 7 of 10] New/Updated drivers: MMCONFIG PCI config spa

To: xen-devel@xxxxxxxxxxxxxxxxxxx
Subject: [Xen-devel] [PATCH 7 of 10] New/Updated drivers: MMCONFIG PCI config space access driver backported from Linux Kernel Ver. 2.6.31.5
From: Daniel Kiper <dkiper@xxxxxxxxxxxx>
Date: Tue, 24 Nov 2009 12:58:41 +0100
Delivery-date: Tue, 24 Nov 2009 04:00:12 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.3.28i
# 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

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH 7 of 10] New/Updated drivers: MMCONFIG PCI config space access driver backported from Linux Kernel Ver. 2.6.31.5, Daniel Kiper <=