diff options
author | Greg Kroah-Hartman <gregkh@suse.de> | 2005-12-06 16:42:57 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-12-06 16:42:57 -0800 |
commit | dc4d53eedb18d77f3782c1c8109ebca0424cf976 (patch) | |
tree | b75a3eb28e376a62dd4838478108f2188562d0a0 /pci | |
parent | af0f449c757c32b2e6dc05d2a50a020fbbdc74e5 (diff) | |
download | patches-dc4d53eedb18d77f3782c1c8109ebca0424cf976.tar.gz |
more pci patches
Diffstat (limited to 'pci')
-rw-r--r-- | pci/i386-x86-64-implement-fallback-for-pci-mmconfig-to-type1.patch | 201 | ||||
-rw-r--r-- | pci/x86-pci-domain-support-a-humble-fix.patch | 40 | ||||
-rw-r--r-- | pci/x86-pci-domain-support-struct-pci_sysdata.patch | 150 | ||||
-rw-r--r-- | pci/x86-pci-domain-support-the-meat.patch | 116 | ||||
-rw-r--r-- | pci/x86_64-i386-correct-for-broken-mcfg-tables-on-k8-systems.patch | 193 |
5 files changed, 700 insertions, 0 deletions
diff --git a/pci/i386-x86-64-implement-fallback-for-pci-mmconfig-to-type1.patch b/pci/i386-x86-64-implement-fallback-for-pci-mmconfig-to-type1.patch new file mode 100644 index 0000000000000..8267a9382c7c5 --- /dev/null +++ b/pci/i386-x86-64-implement-fallback-for-pci-mmconfig-to-type1.patch @@ -0,0 +1,201 @@ +From ak@muc.de Sun Dec 4 15:36:28 2005 +Date: 4 Dec 2005 19:23:59 +0100 +Date: Sun, 4 Dec 2005 19:23:59 +0100 +From: Andi Kleen <ak@muc.de> +To: <torvalds@osdl.org>, <akpm@osdl.org> +Cc: gregkh@suse.de +Subject: i386/x86-64: Implement fallback for PCI mmconfig to type1 +Message-ID: <20051204182359.GA84932@muc.de> +Content-Disposition: inline + + +When there is no entry for a bus in MCFG fall back to type1. This is +especially important on K8 systems where always some devices can't be +accessed using mmconfig (in particular the builtin northbridge doesn't +support it for its own devices) + +To be complete needs also Jeff's _SEG fixes, but that is something for +after 2.6.15 + +Signed-off-by: Andi Kleen <ak@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/i386/pci/direct.c | 4 ++-- + arch/i386/pci/mmconfig.c | 24 ++++++++++++++++-------- + arch/i386/pci/pci.h | 7 +++++++ + arch/x86_64/pci/mmconfig.c | 29 ++++++++++++++++++++--------- + 4 files changed, 45 insertions(+), 19 deletions(-) + +--- gregkh-2.6.orig/arch/i386/pci/direct.c ++++ gregkh-2.6/arch/i386/pci/direct.c +@@ -13,7 +13,7 @@ + #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ + (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) + +-static int pci_conf1_read(unsigned int seg, unsigned int bus, ++int pci_conf1_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value) + { + unsigned long flags; +@@ -42,7 +42,7 @@ static int pci_conf1_read(unsigned int s + return 0; + } + +-static int pci_conf1_write(unsigned int seg, unsigned int bus, ++int pci_conf1_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value) + { + unsigned long flags; +--- gregkh-2.6.orig/arch/i386/pci/pci.h ++++ gregkh-2.6/arch/i386/pci/pci.h +@@ -74,3 +74,10 @@ extern spinlock_t pci_config_lock; + + extern int (*pcibios_enable_irq)(struct pci_dev *dev); + extern void (*pcibios_disable_irq)(struct pci_dev *dev); ++ ++extern int pci_conf1_write(unsigned int seg, unsigned int bus, ++ unsigned int devfn, int reg, int len, u32 value); ++extern int pci_conf1_read(unsigned int seg, unsigned int bus, ++ unsigned int devfn, int reg, int len, u32 *value); ++ ++ +--- gregkh-2.6.orig/arch/i386/pci/mmconfig.c ++++ gregkh-2.6/arch/i386/pci/mmconfig.c +@@ -30,10 +30,8 @@ static u32 get_base_addr(unsigned int se + 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_config[0].base_address; ++ /* Not found - fallback to type 1 */ ++ return 0; + } + cfg = &pci_mmcfg_config[cfg_num]; + if (cfg->pci_segment_group_number != seg) +@@ -44,9 +42,9 @@ static u32 get_base_addr(unsigned int se + } + } + +-static inline void pci_exp_set_dev_base(unsigned int seg, int bus, int devfn) ++static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) + { +- u32 dev_base = get_base_addr(seg, bus) | (bus << 20) | (devfn << 12); ++ u32 dev_base = base | (bus << 20) | (devfn << 12); + if (dev_base != mmcfg_last_accessed_device) { + mmcfg_last_accessed_device = dev_base; + set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); +@@ -57,13 +55,18 @@ static int pci_mmcfg_read(unsigned int s + unsigned int devfn, int reg, int len, u32 *value) + { + unsigned long flags; ++ u32 base; + + if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + ++ base = get_base_addr(seg, bus); ++ if (!base) ++ return pci_conf1_read(seg,bus,devfn,reg,len,value); ++ + spin_lock_irqsave(&pci_config_lock, flags); + +- pci_exp_set_dev_base(seg, bus, devfn); ++ pci_exp_set_dev_base(base, bus, devfn); + + switch (len) { + case 1: +@@ -86,13 +89,18 @@ static int pci_mmcfg_write(unsigned int + unsigned int devfn, int reg, int len, u32 value) + { + unsigned long flags; ++ u32 base; + + if ((bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + ++ base = get_base_addr(seg, bus); ++ if (!base) ++ return pci_conf1_write(seg,bus,devfn,reg,len,value); ++ + spin_lock_irqsave(&pci_config_lock, flags); + +- pci_exp_set_dev_base(seg, bus, devfn); ++ pci_exp_set_dev_base(base, bus, devfn); + + switch (len) { + case 1: +--- gregkh-2.6.orig/arch/x86_64/pci/mmconfig.c ++++ gregkh-2.6/arch/x86_64/pci/mmconfig.c +@@ -19,7 +19,7 @@ struct mmcfg_virt { + }; + static struct mmcfg_virt *pci_mmcfg_virt; + +-static char *get_virt(unsigned int seg, int bus) ++static char *get_virt(unsigned int seg, unsigned bus) + { + int cfg_num = -1; + struct acpi_table_mcfg_config *cfg; +@@ -27,10 +27,9 @@ static char *get_virt(unsigned int seg, + 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) +@@ -43,18 +42,25 @@ static char *get_virt(unsigned int seg, + + static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) + { +- +- return get_virt(seg, bus) + ((bus << 20) | (devfn << 12)); ++ char *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 *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 +79,16 @@ static int pci_mmcfg_read(unsigned int s + 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 *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); diff --git a/pci/x86-pci-domain-support-a-humble-fix.patch b/pci/x86-pci-domain-support-a-humble-fix.patch new file mode 100644 index 0000000000000..b6712113f14d0 --- /dev/null +++ b/pci/x86-pci-domain-support-a-humble-fix.patch @@ -0,0 +1,40 @@ +From garzik@havoc.gtf.org Fri Dec 2 19:24:13 2005 +Date: Fri, 2 Dec 2005 20:39:41 -0500 +From: Jeff Garzik <jgarzik@pobox.com> +Cc: <gregkh@suse.de>, <ak@suse.de> +Subject: [PATCH 1/3] x86 PCI domain support: a humble fix +Message-ID: <20051203013941.GA2663@havoc.gtf.org> +Content-Disposition: inline + +From: Jeff Garzik <jgarzik@pobox.com> + +[x86, PCI] pass PCI domain number to PCI config read/write hooks + +Don't hardcode zero, since modern x86 (with special ACPI sauce) +can support multiple "PCI segments", aka PCI domains. + +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/i386/pci/common.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +--- gregkh-2.6.orig/arch/i386/pci/common.c ++++ gregkh-2.6/arch/i386/pci/common.c +@@ -31,12 +31,14 @@ struct pci_raw_ops *raw_pci_ops; + + static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value) + { +- return raw_pci_ops->read(0, bus->number, devfn, where, size, value); ++ return raw_pci_ops->read(pci_domain_nr(bus), bus->number, ++ devfn, where, size, value); + } + + static int pci_write(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 value) + { +- return raw_pci_ops->write(0, bus->number, devfn, where, size, value); ++ return raw_pci_ops->write(pci_domain_nr(bus), bus->number, ++ devfn, where, size, value); + } + + struct pci_ops pci_root_ops = { diff --git a/pci/x86-pci-domain-support-struct-pci_sysdata.patch b/pci/x86-pci-domain-support-struct-pci_sysdata.patch new file mode 100644 index 0000000000000..a1224ee9a961b --- /dev/null +++ b/pci/x86-pci-domain-support-struct-pci_sysdata.patch @@ -0,0 +1,150 @@ +From garzik@havoc.gtf.org Fri Dec 2 19:24:13 2005 +Date: Fri, 2 Dec 2005 20:40:06 -0500 +From: Jeff Garzik <jgarzik@pobox.com> +Cc: <gregkh@suse.de>, <ak@suse.de> +Subject: [PATCH 2/3] x86 PCI domain support: struct pci_sysdata +Message-ID: <20051203014006.GB2663@havoc.gtf.org> +Content-Disposition: inline + + +[x86, PCI] Switch pci_bus::sysdata from NUMA node integer to a pointer + +On x86[-64], struct pci_bus::sysdata is only used on NUMA platforms, +to store the associated NUMA node. + +Preparing for the future when we'll want to do other things with +sysdata, struct pci_sysdata was created. An allocated structure +replaces the cast-pointer-to-int NUMA usage. Updated all NUMA users. + +From: Jeff Garzik <jgarzik@pobox.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + + +--- + arch/i386/pci/acpi.c | 7 ++++--- + arch/i386/pci/common.c | 13 ++++++++++++- + arch/x86_64/pci/k8-bus.c | 6 +++++- + include/asm-i386/pci.h | 5 +++++ + include/asm-i386/topology.h | 2 +- + include/asm-x86_64/pci.h | 4 ++++ + include/asm-x86_64/topology.h | 2 +- + 7 files changed, 32 insertions(+), 7 deletions(-) + +--- gregkh-2.6.orig/arch/i386/pci/acpi.c ++++ gregkh-2.6/arch/i386/pci/acpi.c +@@ -19,9 +19,10 @@ struct pci_bus * __devinit pci_acpi_scan + if (bus != NULL) { + int pxm = acpi_get_pxm(device->handle); + if (pxm >= 0) { +- bus->sysdata = (void *)(unsigned long)pxm_to_node(pxm); +- printk("bus %d -> pxm %d -> node %ld\n", +- busnum, pxm, (long)(bus->sysdata)); ++ struct pci_sysdata *sd = bus->sysdata; ++ sd->node = pxm_to_node(pxm); ++ printk("bus %d -> pxm %d -> node %d\n", ++ busnum, pxm, sd->node); + } + } + #endif +--- gregkh-2.6.orig/arch/i386/pci/common.c ++++ gregkh-2.6/arch/i386/pci/common.c +@@ -126,6 +126,7 @@ void __devinit pcibios_fixup_bus(struct + struct pci_bus * __devinit pcibios_scan_root(int busnum) + { + struct pci_bus *bus = NULL; ++ struct pci_sysdata *sd; + + while ((bus = pci_find_next_bus(bus)) != NULL) { + if (bus->number == busnum) { +@@ -134,9 +135,19 @@ struct pci_bus * __devinit pcibios_scan_ + } + } + ++ /* Allocate per-root-bus (not per bus) arch-specific data. ++ * TODO: leak; this memory is never freed. ++ * It's arguable whether it's worth the trouble to care. ++ */ ++ sd = kzalloc(sizeof(*sd), GFP_KERNEL); ++ if (!sd) { ++ printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum); ++ return NULL; ++ } ++ + printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum); + +- return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, NULL); ++ return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); + } + + extern u8 pci_cache_line_size; +--- gregkh-2.6.orig/arch/x86_64/pci/k8-bus.c ++++ gregkh-2.6/arch/x86_64/pci/k8-bus.c +@@ -59,6 +59,8 @@ fill_mp_bus_to_cpumask(void) + j <= SUBORDINATE_LDT_BUS_NUMBER(ldtbus); + j++) { + struct pci_bus *bus; ++ struct pci_sysdata *sd; ++ + long node = NODE_ID(nid); + /* Algorithm a bit dumb, but + it shouldn't matter here */ +@@ -67,7 +69,9 @@ fill_mp_bus_to_cpumask(void) + continue; + if (!node_online(node)) + node = 0; +- bus->sysdata = (void *)node; ++ ++ sd = bus->sysdata; ++ sd->node = node; + } + } + } +--- gregkh-2.6.orig/include/asm-i386/pci.h ++++ gregkh-2.6/include/asm-i386/pci.h +@@ -4,6 +4,11 @@ + #include <linux/config.h> + + #ifdef __KERNEL__ ++ ++struct pci_sysdata { ++ int node; /* NUMA node */ ++}; ++ + #include <linux/mm.h> /* for struct page */ + + /* Can be used to override the logic in pci_scan_bus for skipping +--- gregkh-2.6.orig/include/asm-i386/topology.h ++++ gregkh-2.6/include/asm-i386/topology.h +@@ -60,7 +60,7 @@ static inline int node_to_first_cpu(int + return first_cpu(mask); + } + +-#define pcibus_to_node(bus) ((long) (bus)->sysdata) ++#define pcibus_to_node(bus) ((struct pci_sysdata *)((bus)->sysdata))->node + #define pcibus_to_cpumask(bus) node_to_cpumask(pcibus_to_node(bus)) + + /* sched_domains SD_NODE_INIT for NUMAQ machines */ +--- gregkh-2.6.orig/include/asm-x86_64/pci.h ++++ gregkh-2.6/include/asm-x86_64/pci.h +@@ -6,6 +6,10 @@ + + #ifdef __KERNEL__ + ++struct pci_sysdata { ++ int node; /* NUMA node */ ++}; ++ + #include <linux/mm.h> /* for struct page */ + + /* Can be used to override the logic in pci_scan_bus for skipping +--- gregkh-2.6.orig/include/asm-x86_64/topology.h ++++ gregkh-2.6/include/asm-x86_64/topology.h +@@ -25,7 +25,7 @@ extern int __node_distance(int, int); + #define parent_node(node) (node) + #define node_to_first_cpu(node) (__ffs(node_to_cpumask[node])) + #define node_to_cpumask(node) (node_to_cpumask[node]) +-#define pcibus_to_node(bus) ((long)(bus->sysdata)) ++#define pcibus_to_node(bus) ((struct pci_sysdata *)((bus)->sysdata))->node + #define pcibus_to_cpumask(bus) node_to_cpumask(pcibus_to_node(bus)); + + #define numa_node_id() read_pda(nodenumber) diff --git a/pci/x86-pci-domain-support-the-meat.patch b/pci/x86-pci-domain-support-the-meat.patch new file mode 100644 index 0000000000000..0101d2b5dc03a --- /dev/null +++ b/pci/x86-pci-domain-support-the-meat.patch @@ -0,0 +1,116 @@ +From garzik@havoc.gtf.org Fri Dec 2 19:24:13 2005 +Date: Fri, 2 Dec 2005 20:40:33 -0500 +From: Jeff Garzik <jgarzik@pobox.com> +To: linux-kernel@vger.kernel.org +Cc: <gregkh@suse.de>, <ak@suse.de> +Subject: [PATCH 3/3] x86 PCI domain support: the meat +Message-ID: <20051203014033.GC2663@havoc.gtf.org> +Content-Disposition: inline + + +[x86, PCI] add PCI domain support + +* Store PCI domain in struct pci_sysdata +* Add magic inlines to pass PCI domain to read/write config hooks +* Support ACPI's notion of PCI domains (PCI segments) + +From: Jeff Garzik <jgarzik@pobox.com> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/i386/pci/acpi.c | 20 ++++++++++++++++++-- + include/asm-i386/pci.h | 14 ++++++++++++++ + include/asm-x86_64/pci.h | 14 ++++++++++++++ + 3 files changed, 46 insertions(+), 2 deletions(-) + +--- gregkh-2.6.orig/arch/i386/pci/acpi.c ++++ gregkh-2.6/arch/i386/pci/acpi.c +@@ -8,18 +8,34 @@ + struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum) + { + struct pci_bus *bus; ++ struct pci_sysdata *sd; + ++ /* Allocate per-root-bus (not per bus) arch-specific data. ++ * TODO: leak; this memory is never freed. ++ * It's arguable whether it's worth the trouble to care. ++ */ ++ sd = kzalloc(sizeof(*sd), GFP_KERNEL); ++ if (!sd) { ++ printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum); ++ return NULL; ++ } ++ ++#ifdef CONFIG_PCI_DOMAINS ++ sd->domain = domain; ++#else + if (domain != 0) { + printk(KERN_WARNING "PCI: Multiple domains not supported\n"); + return NULL; + } ++#endif /* CONFIG_PCI_DOMAINS */ + +- bus = pcibios_scan_root(busnum); ++ bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); ++ if (!bus) ++ kfree(sd); + #ifdef CONFIG_ACPI_NUMA + if (bus != NULL) { + int pxm = acpi_get_pxm(device->handle); + if (pxm >= 0) { +- struct pci_sysdata *sd = bus->sysdata; + sd->node = pxm_to_node(pxm); + printk("bus %d -> pxm %d -> node %d\n", + busnum, pxm, sd->node); +--- gregkh-2.6.orig/include/asm-i386/pci.h ++++ gregkh-2.6/include/asm-i386/pci.h +@@ -6,9 +6,23 @@ + #ifdef __KERNEL__ + + struct pci_sysdata { ++ int domain; /* PCI domain */ + int node; /* NUMA node */ + }; + ++#ifdef CONFIG_PCI_DOMAINS ++static inline int pci_domain_nr(struct pci_bus *bus) ++{ ++ struct pci_sysdata *sd = bus->sysdata; ++ return sd->domain; ++} ++ ++static inline int pci_proc_domain(struct pci_bus *bus) ++{ ++ return pci_domain_nr(bus); ++} ++#endif /* CONFIG_PCI_DOMAINS */ ++ + #include <linux/mm.h> /* for struct page */ + + /* Can be used to override the logic in pci_scan_bus for skipping +--- gregkh-2.6.orig/include/asm-x86_64/pci.h ++++ gregkh-2.6/include/asm-x86_64/pci.h +@@ -7,9 +7,23 @@ + #ifdef __KERNEL__ + + struct pci_sysdata { ++ int domain; /* PCI domain */ + int node; /* NUMA node */ + }; + ++#ifdef CONFIG_PCI_DOMAINS ++static inline int pci_domain_nr(struct pci_bus *bus) ++{ ++ struct pci_sysdata *sd = bus->sysdata; ++ return sd->domain; ++} ++ ++static inline int pci_proc_domain(struct pci_bus *bus) ++{ ++ return pci_domain_nr(bus); ++} ++#endif /* CONFIG_PCI_DOMAINS */ ++ + #include <linux/mm.h> /* for struct page */ + + /* Can be used to override the logic in pci_scan_bus for skipping diff --git a/pci/x86_64-i386-correct-for-broken-mcfg-tables-on-k8-systems.patch b/pci/x86_64-i386-correct-for-broken-mcfg-tables-on-k8-systems.patch new file mode 100644 index 0000000000000..a6de90923a87c --- /dev/null +++ b/pci/x86_64-i386-correct-for-broken-mcfg-tables-on-k8-systems.patch @@ -0,0 +1,193 @@ +From ak@muc.de Sun Dec 4 15:36:28 2005 +Date: 4 Dec 2005 19:26:18 +0100 +Date: Sun, 4 Dec 2005 19:26:18 +0100 +From: Andi Kleen <ak@muc.de> +To: <torvalds@osdl.org>, <akpm@osdl.org>, <gregkh@suse.de> +Subject: x86_64/i386: Correct for broken MCFG tables on K8 systems +Message-ID: <20051204182618.GB84932@muc.de> +Content-Disposition: inline + +They report all busses as MMCONFIG capable, but it never works for the +internal devices in the CPU's builtin northbridge. + +This causes problems - some devices are not visible and on x86-64 the +IOMMU is not detected, causing boot failures on systems with >3GB of +RAM. It also breaks AGP. + +Workaround just probes all func 0 devices on bus 0 (the internal +northbridge is currently always on bus 0) and if they are not accessible +using MCFG they are put into a special fallback bitmap. + +On systems where it isn't we assume the BIOS vendor supplied correct +MCFG. + +Requires the earlier patch for mmconfig type1 fallback + +Signed-off-by: Andi Kleen <ak@suse.de> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> + +--- + arch/i386/pci/mmconfig.c | 45 ++++++++++++++++++++++++++++++++++++++++++--- + arch/x86_64/pci/mmconfig.c | 36 ++++++++++++++++++++++++++++++++++-- + 2 files changed, 76 insertions(+), 5 deletions(-) + +--- gregkh-2.6.orig/arch/x86_64/pci/mmconfig.c ++++ gregkh-2.6/arch/x86_64/pci/mmconfig.c +@@ -8,10 +8,13 @@ + #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; +@@ -40,9 +43,12 @@ static char *get_virt(unsigned int seg, + } + } + +-static inline char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) ++static char *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) + { +- char *addr = get_virt(seg, bus); ++ char *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)); +@@ -109,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 *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; +@@ -139,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; + +--- gregkh-2.6.orig/arch/i386/pci/mmconfig.c ++++ gregkh-2.6/arch/i386/pci/mmconfig.c +@@ -19,14 +19,20 @@ + /* The base address of the last MMCONFIG device accessed */ + static u32 mmcfg_last_accessed_device; + ++static DECLARE_BITMAP(fallback_slots, 32); ++ + /* + * Functions for accessing PCI configuration space with MMCONFIG accesses + */ +-static u32 get_base_addr(unsigned int seg, int bus) ++static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) + { + int cfg_num = -1; + struct acpi_table_mcfg_config *cfg; + ++ if (seg == 0 && bus == 0 && ++ test_bit(PCI_SLOT(devfn), fallback_slots)) ++ return 0; ++ + while (1) { + ++cfg_num; + if (cfg_num >= pci_mmcfg_config_num) { +@@ -60,7 +66,7 @@ static int pci_mmcfg_read(unsigned int s + if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + +- base = get_base_addr(seg, bus); ++ base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_read(seg,bus,devfn,reg,len,value); + +@@ -94,7 +100,7 @@ static int pci_mmcfg_write(unsigned int + if ((bus > 255) || (devfn > 255) || (reg > 4095)) + return -EINVAL; + +- base = get_base_addr(seg, bus); ++ base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_write(seg,bus,devfn,reg,len,value); + +@@ -124,6 +130,37 @@ 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; ++ unsigned long flags; ++ ++ for (i = 0; i < 32; i++) { ++ u32 val1; ++ u32 addr; ++ ++ pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1); ++ if (val1 == 0xffffffff) ++ continue; ++ ++ /* Locking probably not needed, but safer */ ++ spin_lock_irqsave(&pci_config_lock, flags); ++ addr = get_base_addr(0, 0, PCI_DEVFN(i, 0)); ++ if (addr != 0) ++ pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0)); ++ if (addr == 0 || readl((u32 *)addr) != val1) ++ set_bit(i, fallback_slots); ++ spin_unlock_irqrestore(&pci_config_lock, flags); ++ } ++} ++ + static int __init pci_mmcfg_init(void) + { + if ((pci_probe & PCI_PROBE_MMCONF) == 0) +@@ -139,6 +176,8 @@ static int __init pci_mmcfg_init(void) + raw_pci_ops = &pci_mmcfg; + pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + ++ unreachable_devices(); ++ + out: + return 0; + } |