]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/x86_64/pci/mmconfig.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sam/kbuild
[linux-2.6-omap-h63xx.git] / arch / x86_64 / pci / mmconfig.c
index 657e88aa09022ba67df0fcc8dca7d20e44ecef53..f16c0d57c552f1fade440fb7a683fa649bc7099f 100644 (file)
@@ -8,18 +8,21 @@
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/acpi.h>
+#include <linux/bitmap.h>
 #include "pci.h"
 
 #define MMCONFIG_APER_SIZE (256*1024*1024)
 
+static DECLARE_BITMAP(fallback_slots, 32);
+
 /* Static virtual mapping of the MMCONFIG aperture */
 struct mmcfg_virt {
        struct acpi_table_mcfg_config *cfg;
-       char *virt;
+       char __iomem *virt;
 };
 static struct mmcfg_virt *pci_mmcfg_virt;
 
-static char *get_virt(unsigned int seg, int bus)
+static char __iomem *get_virt(unsigned int seg, unsigned bus)
 {
        int cfg_num = -1;
        struct acpi_table_mcfg_config *cfg;
@@ -27,10 +30,9 @@ static char *get_virt(unsigned int seg, int bus)
        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;
+                       /* Not found - fall back to type 1. This happens
+                          e.g. on the internal devices of a K8 northbridge. */
+                       return NULL;
                }
                cfg = pci_mmcfg_virt[cfg_num].cfg;
                if (cfg->pci_segment_group_number != seg)
@@ -41,20 +43,30 @@ static char *get_virt(unsigned int seg, int bus)
        }
 }
 
-static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
+static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
 {
-
-       return get_virt(seg, bus) + ((bus << 20) | (devfn << 12));
+       char __iomem *addr;
+       if (seg == 0 && bus == 0 && test_bit(PCI_SLOT(devfn), &fallback_slots))
+               return NULL;
+       addr = get_virt(seg, bus);
+       if (!addr)
+               return NULL;
+       return addr + ((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(seg, bus, devfn);
+       char __iomem *addr;
 
+       /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
        if (unlikely(!value || (bus > 255) || (devfn > 255) || (reg > 4095)))
                return -EINVAL;
 
+       addr = pci_dev_base(seg, bus, devfn);
+       if (!addr)
+               return pci_conf1_read(seg,bus,devfn,reg,len,value);
+
        switch (len) {
        case 1:
                *value = readb(addr + reg);
@@ -73,11 +85,16 @@ 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(seg, bus, devfn);
+       char __iomem *addr;
 
+       /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
                return -EINVAL;
 
+       addr = pci_dev_base(seg, bus, devfn);
+       if (!addr)
+               return pci_conf1_write(seg,bus,devfn,reg,len,value);
+
        switch (len) {
        case 1:
                writeb(value, addr + reg);
@@ -98,6 +115,30 @@ static struct pci_raw_ops pci_mmcfg = {
        .write =        pci_mmcfg_write,
 };
 
+/* K8 systems have some devices (typically in the builtin northbridge)
+   that are only accessible using type1
+   Normally this can be expressed in the MCFG by not listing them
+   and assigning suitable _SEGs, but this isn't implemented in some BIOS.
+   Instead try to discover all devices on bus 0 that are unreachable using MM
+   and fallback for them.
+   We only do this for bus 0/seg 0 */
+static __init void unreachable_devices(void)
+{
+       int i;
+       for (i = 0; i < 32; i++) {
+               u32 val1;
+               char __iomem *addr;
+
+               pci_conf1_read(0, 0, PCI_DEVFN(i,0), 0, 4, &val1);
+               if (val1 == 0xffffffff)
+                       continue;
+               addr = pci_dev_base(0, 0, PCI_DEVFN(i, 0));
+               if (addr == NULL|| readl(addr) != val1) {
+                       set_bit(i, &fallback_slots);
+               }
+       }
+}
+
 static int __init pci_mmcfg_init(void)
 {
        int i;
@@ -111,13 +152,6 @@ static int __init pci_mmcfg_init(void)
            (pci_mmcfg_config[0].base_address == 0))
                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; 
-
        /* 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) {
@@ -135,6 +169,8 @@ static int __init pci_mmcfg_init(void)
                printk(KERN_INFO "PCI: Using MMCONFIG at %x\n", pci_mmcfg_config[i].base_address);
        }
 
+       unreachable_devices();
+
        raw_pci_ops = &pci_mmcfg;
        pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;