[patch 3/5] x86: Add PCI extended config space access for AMD Barcelona

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch implements PCI extended configuration space access for
AMD's Barcelona CPUs. It extends the method using CF8/CFC IO
addresses. An x86 capability bit has been introduced that is set for
CPUs supporting PCI extended config space accesses.

Signed-off-by: Robert Richter <[email protected]>

---
 arch/i386/kernel/cpu/amd.c    |   11 +++++-
 arch/i386/pci/direct.c        |   67 +++++++++++++++++++++++++++++++++++-------
 arch/x86_64/kernel/setup.c    |   12 ++++++-
 include/asm-i386/cpufeature.h |    2 +
 4 files changed, 77 insertions(+), 15 deletions(-)

Index: linux-2.6/arch/i386/pci/direct.c
===================================================================
--- linux-2.6.orig/arch/i386/pci/direct.c
+++ linux-2.6/arch/i386/pci/direct.c
@@ -8,18 +8,22 @@
 #include "pci.h"
 
 /*
- * Functions for accessing PCI configuration space with type 1 accesses
+ * Functions for accessing PCI base (first 256 bytes) and extended
+ * (4096 bytes per PCI function) configuration space with type 1
+ * accesses.
  */
 
 #define PCI_CONF1_ADDRESS(bus, devfn, reg) \
-	(0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3))
+	(0x80000000 | ((reg & 0xF00) << 16) | (bus << 16) \
+	| (devfn << 8) | (reg & 0xFC))
 
-int pci_conf1_read(unsigned int seg, unsigned int bus,
-			  unsigned int devfn, int reg, int len, u32 *value)
+static inline int
+_pci_conf1ext_read(unsigned int seg, unsigned int bus,
+		   unsigned int devfn, int reg, int len, u32 *value)
 {
 	unsigned long flags;
 
-	if ((bus > 255) || (devfn > 255) || (reg > 255)) {
+	if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
 		*value = -1;
 		return -EINVAL;
 	}
@@ -45,12 +49,29 @@ int pci_conf1_read(unsigned int seg, uns
 	return 0;
 }
 
-int pci_conf1_write(unsigned int seg, unsigned int bus,
-			   unsigned int devfn, int reg, int len, u32 value)
+int pci_conf1ext_read(unsigned int seg, unsigned int bus,
+		      unsigned int devfn, int reg, int len, u32 *value)
+{
+	return _pci_conf1ext_read(seg, bus, devfn, reg, len, value);
+}
+
+int pci_conf1_read(unsigned int seg, unsigned int bus,
+			  unsigned int devfn, int reg, int len, u32 *value)
+{
+	if (reg > 255) {
+		*value = -1;
+		return -EINVAL;
+	}
+	return _pci_conf1ext_read(seg, bus, devfn, reg, len, value);
+}
+
+static inline int
+_pci_conf1ext_write(unsigned int seg, unsigned int bus,
+		    unsigned int devfn, int reg, int len, u32 value)
 {
 	unsigned long flags;
 
-	if ((bus > 255) || (devfn > 255) || (reg > 255)) 
+	if ((bus > 255) || (devfn > 255) || (reg > 4095))
 		return -EINVAL;
 
 	spin_lock_irqsave(&pci_config_lock, flags);
@@ -74,6 +95,20 @@ int pci_conf1_write(unsigned int seg, un
 	return 0;
 }
 
+int pci_conf1ext_write(unsigned int seg, unsigned int bus,
+		       unsigned int devfn, int reg, int len, u32 value)
+{
+	return _pci_conf1ext_write(seg, bus, devfn, reg, len, value);
+}
+
+int pci_conf1_write(unsigned int seg, unsigned int bus,
+		 unsigned int devfn, int reg, int len, u32 value)
+{
+	if (reg > 255)
+		return -EINVAL;
+	return _pci_conf1ext_write(seg, bus, devfn, reg, len, value);
+}
+
 #undef PCI_CONF1_ADDRESS
 
 struct pci_raw_ops pci_direct_conf1 = {
@@ -81,6 +116,11 @@ struct pci_raw_ops pci_direct_conf1 = {
 	.write =	pci_conf1_write,
 };
 
+struct pci_raw_ops pci_direct_conf1ext = {
+	.read =		pci_conf1ext_read,
+	.write =	pci_conf1ext_write,
+};
+
 
 /*
  * Functions for accessing PCI configuration space with type 2 accesses
@@ -259,10 +299,15 @@ 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
+	if (type != 1) {
 		raw_pci_ops = &pci_direct_conf2;
+	} else if (cpu_has_pci_ext_cfg) {
+		printk(KERN_INFO
+		       "PCI: Extended configuration space enabled\n");
+		raw_pci_ops = &pci_direct_conf1ext;
+	} else {
+		raw_pci_ops = &pci_direct_conf1;
+	}
 }
 
 int __init pci_direct_probe(void)
Index: linux-2.6/arch/x86_64/kernel/setup.c
===================================================================
--- linux-2.6.orig/arch/x86_64/kernel/setup.c
+++ linux-2.6/arch/x86_64/kernel/setup.c
@@ -65,6 +65,8 @@
 #include <asm/sections.h>
 #include <asm/dmi.h>
 
+#define ENABLE_CF8_EXT_CFG		(1ULL << 46)
+
 /*
  * Machine setup..
  */
@@ -549,10 +551,9 @@ static void __init amd_detect_cmp(struct
 static void __cpuinit init_amd(struct cpuinfo_x86 *c)
 {
 	unsigned level;
-
-#ifdef CONFIG_SMP
 	unsigned long value;
 
+#ifdef CONFIG_SMP
 	/*
 	 * Disable TLB flush filter by setting HWCR.FFDIS on K8
 	 * bit 6 of msr C001_0015
@@ -617,6 +618,13 @@ static void __cpuinit init_amd(struct cp
 	/* Family 10 doesn't support C states in MWAIT so don't use it */
 	if (c->x86 == 0x10 && !force_mwait)
 		clear_bit(X86_FEATURE_MWAIT, &c->x86_capability);
+
+	/* Access to PCI extended config space? */
+	if (c->x86 == 0x10) {
+		rdmsrl(MSR_AMD64_NB_CFG, value);
+		if (value & ENABLE_CF8_EXT_CFG)
+			set_bit(X86_FEATURE_PCI_EXT_CFG, &c->x86_capability);
+	}
 }
 
 static void __cpuinit detect_ht(struct cpuinfo_x86 *c)
Index: linux-2.6/include/asm-i386/cpufeature.h
===================================================================
--- linux-2.6.orig/include/asm-i386/cpufeature.h
+++ linux-2.6/include/asm-i386/cpufeature.h
@@ -82,6 +82,7 @@
 /* 14 free */
 #define X86_FEATURE_SYNC_RDTSC	(3*32+15)  /* RDTSC synchronizes the CPU */
 #define X86_FEATURE_REP_GOOD   (3*32+16) /* rep microcode works well on this CPU */
+#define X86_FEATURE_PCI_EXT_CFG	(3*32+17) /* PCI extended cfg access */
 
 /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
 #define X86_FEATURE_XMM3	(4*32+ 0) /* Streaming SIMD Extensions-3 */
@@ -164,6 +165,7 @@
 #define cpu_has_pebs 		boot_cpu_has(X86_FEATURE_PEBS)
 #define cpu_has_clflush		boot_cpu_has(X86_FEATURE_CLFLSH)
 #define cpu_has_bts 		boot_cpu_has(X86_FEATURE_BTS)
+#define cpu_has_pci_ext_cfg	boot_cpu_has(X86_FEATURE_PCI_EXT_CFG)
 
 #endif /* __ASM_I386_CPUFEATURE_H */
 
Index: linux-2.6/arch/i386/kernel/cpu/amd.c
===================================================================
--- linux-2.6.orig/arch/i386/kernel/cpu/amd.c
+++ linux-2.6/arch/i386/kernel/cpu/amd.c
@@ -32,6 +32,7 @@ __asm__(".align 4\nvide: ret");
 #define CPUID_XFAM_11H          0x00200000
 #define CPUID_XMOD              0x000f0000
 #define CPUID_XMOD_REV_F        0x00040000
+#define ENABLE_CF8_EXT_CFG      (1ULL << 46)
 
 /* AMD systems with C1E don't have a working lAPIC timer. Check for that. */
 static __cpuinit int amd_apic_timer_broken(void)
@@ -63,10 +64,9 @@ static void __cpuinit init_amd(struct cp
 	u32 l, h;
 	int mbytes = num_physpages >> (20-PAGE_SHIFT);
 	int r;
-
-#ifdef CONFIG_SMP
 	unsigned long long value;
 
+#ifdef CONFIG_SMP
 	/* Disable TLB flush filter by setting HWCR.FFDIS on K8
 	 * bit 6 of msr C001_0015
 	 *
@@ -296,6 +296,13 @@ static void __cpuinit init_amd(struct cp
 	/* K6s reports MCEs but don't actually have all the MSRs */
 	if (c->x86 < 6)
 		clear_bit(X86_FEATURE_MCE, c->x86_capability);
+
+	/* Access to PCI extended config space? */
+	if (c->x86 == 0x10) {
+		rdmsrl(MSR_AMD64_NB_CFG, value);
+		if (value & ENABLE_CF8_EXT_CFG)
+			set_bit(X86_FEATURE_PCI_EXT_CFG, c->x86_capability);
+	}
 }
 
 static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 * c, unsigned int size)

-- 
AMD Saxony, Dresden, Germany
Operating System Research Center
email: [email protected]



-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux