Merge master.kernel.org:/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
[cascardo/linux.git] / arch / x86_64 / pci / mmconfig.c
index b693c23..a0838c4 100644 (file)
@@ -7,25 +7,50 @@
 
 #include <linux/pci.h>
 #include <linux/init.h>
+#include <linux/acpi.h>
 #include "pci.h"
 
 #define MMCONFIG_APER_SIZE (256*1024*1024)
 
-/* The physical address of the MMCONFIG aperture.  Set from ACPI tables. */
-u32 pci_mmcfg_base_addr;
-
 /* Static virtual mapping of the MMCONFIG aperture */
-char *pci_mmcfg_virt;
+struct mmcfg_virt {
+       struct acpi_table_mcfg_config *cfg;
+       char *virt;
+};
+static struct mmcfg_virt *pci_mmcfg_virt;
+
+static char *get_virt(unsigned int seg, int bus)
+{
+       int cfg_num = -1;
+       struct acpi_table_mcfg_config *cfg;
+
+       while (1) {
+               ++cfg_num;
+               if (cfg_num >= pci_mmcfg_config_num) {
+                       /* something bad is going on, no cfg table is found. */
+                       /* so we fall back to the old way we used to do this */
+                       /* and just rely on the first entry to be correct. */
+                       return pci_mmcfg_virt[0].virt;
+               }
+               cfg = pci_mmcfg_virt[cfg_num].cfg;
+               if (cfg->pci_segment_group_number != seg)
+                       continue;
+               if ((cfg->start_bus_number <= bus) &&
+                   (cfg->end_bus_number >= bus))
+                       return pci_mmcfg_virt[cfg_num].virt;
+       }
+}
 
-static inline char *pci_dev_base(unsigned int bus, unsigned int devfn)
+static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
 {
-       return pci_mmcfg_virt + ((bus << 20) | (devfn << 12));
+
+       return get_virt(seg, bus) + ((bus << 20) | (devfn << 12));
 }
 
 static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
                          unsigned int devfn, int reg, int len, u32 *value)
 {
-       char *addr = pci_dev_base(bus, devfn); 
+       char *addr = pci_dev_base(seg, bus, devfn);
 
        if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095)))
                return -EINVAL;
@@ -48,7 +73,7 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
 static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
                           unsigned int devfn, int reg, int len, u32 value)
 {
-       char *addr = pci_dev_base(bus,devfn);
+       char *addr = pci_dev_base(seg, bus, devfn);
 
        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
                return -EINVAL;
@@ -75,26 +100,34 @@ static struct pci_raw_ops pci_mmcfg = {
 
 static int __init pci_mmcfg_init(void)
 {
+       int i;
+
        if ((pci_probe & PCI_PROBE_MMCONF) == 0)
                return 0;
-       if (!pci_mmcfg_base_addr)
-               return 0;
 
-       /* Kludge for now. Don't use mmconfig on AMD systems because
-          those have some busses where mmconfig doesn't work,
-          and we don't parse ACPI MCFG well enough to handle that. 
-          Remove when proper handling is added. */
-       if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD)
-               return 0; 
+       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 0;
 
        /* RED-PEN i386 doesn't do _nocache right now */
-       pci_mmcfg_virt = ioremap_nocache(pci_mmcfg_base_addr, MMCONFIG_APER_SIZE);
-       if (!pci_mmcfg_virt) { 
-               printk("PCI: Cannot map mmconfig aperture\n");
+       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 0;
-       }       
+       }
+       for (i = 0; i < pci_mmcfg_config_num; ++i) {
+               pci_mmcfg_virt[i].cfg = &pci_mmcfg_config[i];
+               pci_mmcfg_virt[i].virt = ioremap_nocache(pci_mmcfg_config[i].base_address, MMCONFIG_APER_SIZE);
+               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 0;
+               }
+               printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address);
+       }
 
-       printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_base_addr);
        raw_pci_ops = &pci_mmcfg;
        pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;