aboutsummaryrefslogtreecommitdiffstats
path: root/pci
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@suse.de>2005-12-06 16:42:57 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2005-12-06 16:42:57 -0800
commitdc4d53eedb18d77f3782c1c8109ebca0424cf976 (patch)
treeb75a3eb28e376a62dd4838478108f2188562d0a0 /pci
parentaf0f449c757c32b2e6dc05d2a50a020fbbdc74e5 (diff)
downloadpatches-dc4d53eedb18d77f3782c1c8109ebca0424cf976.tar.gz
more pci patches
Diffstat (limited to 'pci')
-rw-r--r--pci/i386-x86-64-implement-fallback-for-pci-mmconfig-to-type1.patch201
-rw-r--r--pci/x86-pci-domain-support-a-humble-fix.patch40
-rw-r--r--pci/x86-pci-domain-support-struct-pci_sysdata.patch150
-rw-r--r--pci/x86-pci-domain-support-the-meat.patch116
-rw-r--r--pci/x86_64-i386-correct-for-broken-mcfg-tables-on-k8-systems.patch193
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;
+ }