
From: "Andi Kleen" <ak@suse.de>

AMD dual core support for i386

Run HT initialization on AMD dual core CPUs on i386.  They fake being HT CPUs.
 This patch makes the HT detection run on AMD CPUs too.  I moved the HT
detection code into a common file from intel.c for that.

It would be actually better to run HT detection always on all CPUs but this
would need a second callback afterwards to AMD code, which I avoided for now.

It adds a cpuinfo->x86_num_cores field.

This sets up the phys_proc_id[] array.  This overloads this array with HT, but
when smp_num_siblings is 1

It is currently only used for /proc/cpuinfo printing.  The reason we want to
behave this like SMT is that there are some license managers in user space
that license according to number of physical CPUs, and when they handle HT
they should handle CMP with this hack too.  Another reason we need this is
that the powernow k8 driver needs this information to properly manage dual
core CPUs.  

When there are ever dual core HT CPUs this will need small changes in
smpboot.c.  I didn't do this for now to keep the patch simple.

Then we set smp_num_siblings to 1 on these systems again to prevent the
scheduler from setting up HT scheduling (which is not a very good match for
dual core).  

This is a port of the CMP support code from x86-64 (minus the NUMA bits) 

Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/arch/i386/kernel/cpu/amd.c    |   17 ++++++++++++
 25-akpm/arch/i386/kernel/cpu/common.c |   48 ++++++++++++++++++++++++++++++++++
 25-akpm/arch/i386/kernel/cpu/intel.c  |   43 ------------------------------
 25-akpm/include/asm-i386/processor.h  |    3 ++
 4 files changed, 69 insertions(+), 42 deletions(-)

diff -puN arch/i386/kernel/cpu/amd.c~i386-amd-dual-core-support-for-i386 arch/i386/kernel/cpu/amd.c
--- 25/arch/i386/kernel/cpu/amd.c~i386-amd-dual-core-support-for-i386	2005-01-10 19:17:25.000000000 -0800
+++ 25-akpm/arch/i386/kernel/cpu/amd.c	2005-01-10 19:28:37.000000000 -0800
@@ -188,6 +188,23 @@ static void __init init_amd(struct cpuin
 	}
 
 	display_cacheinfo(c);
+	detect_ht(c);
+
+#ifdef CONFIG_X86_HT
+	/* AMD dual core looks like HT but isn't really. Hide it from the
+	   scheduler. This works around problems with the domain scheduler.
+	   Also probably gives slightly better scheduling and disables
+	   SMT nice which is harmful on dual core.
+	   TBD tune the domain scheduler for dual core. */
+	if (cpu_has(c, X86_FEATURE_CMP_LEGACY))
+		smp_num_siblings = 1;
+#endif
+
+	if (cpuid_eax(0x80000000) >= 0x80000008) {
+		c->x86_num_cores = (cpuid_ecx(0x80000008) & 0xff) + 1;
+		if (c->x86_num_cores & (c->x86_num_cores - 1))
+			c->x86_num_cores = 1;
+	}
 }
 
 static unsigned int amd_size_cache(struct cpuinfo_x86 * c, unsigned int size)
diff -puN arch/i386/kernel/cpu/common.c~i386-amd-dual-core-support-for-i386 arch/i386/kernel/cpu/common.c
--- 25/arch/i386/kernel/cpu/common.c~i386-amd-dual-core-support-for-i386	2005-01-10 19:17:25.000000000 -0800
+++ 25-akpm/arch/i386/kernel/cpu/common.c	2005-01-10 19:45:28.737009672 -0800
@@ -10,6 +10,11 @@
 #include <asm/msr.h>
 #include <asm/io.h>
 #include <asm/mmu_context.h>
+#ifdef CONFIG_X86_LOCAL_APIC
+#include <asm/mpspec.h>
+#include <asm/apic.h>
+#include <mach_apic.h>
+#endif
 
 #include "cpu.h"
 
@@ -323,6 +328,7 @@ void __init identify_cpu(struct cpuinfo_
 	c->x86_model = c->x86_mask = 0;	/* So far unknown... */
 	c->x86_vendor_id[0] = '\0'; /* Unset */
 	c->x86_model_id[0] = '\0';  /* Unset */
+	c->x86_num_cores = 1;
 	memset(&c->x86_capability, 0, sizeof c->x86_capability);
 
 	if (!have_cpuid_p()) {
@@ -431,6 +437,48 @@ void __init dodgy_tsc(void)
 		cpu_devs[X86_VENDOR_CYRIX]->c_init(&boot_cpu_data);
 }
 
+void __init detect_ht(struct cpuinfo_x86 *c)
+{
+	u32 	eax, ebx, ecx, edx;
+	int 	index_lsb, index_msb, tmp;
+	int 	cpu = smp_processor_id();
+
+	if (!cpu_has(c, X86_FEATURE_HT))
+		return;
+
+	cpuid(1, &eax, &ebx, &ecx, &edx);
+	smp_num_siblings = (ebx & 0xff0000) >> 16;
+
+	if (smp_num_siblings == 1) {
+		printk(KERN_INFO  "CPU: Hyper-Threading is disabled\n");
+	} else if (smp_num_siblings > 1 ) {
+		index_lsb = 0;
+		index_msb = 31;
+
+		if (smp_num_siblings > NR_CPUS) {
+			printk(KERN_WARNING "CPU: Unsupported number of the siblings %d", smp_num_siblings);
+			smp_num_siblings = 1;
+			return;
+		}
+		tmp = smp_num_siblings;
+		while ((tmp & 1) == 0) {
+			tmp >>=1 ;
+			index_lsb++;
+		}
+		tmp = smp_num_siblings;
+		while ((tmp & 0x80000000 ) == 0) {
+			tmp <<=1 ;
+			index_msb--;
+		}
+		if (index_lsb != index_msb )
+			index_msb++;
+		phys_proc_id[cpu] = phys_pkg_id((ebx >> 24) & 0xFF, index_msb);
+
+		printk(KERN_INFO  "CPU: Physical Processor ID: %d\n",
+		       phys_proc_id[cpu]);
+	}
+}
+
 void __init print_cpu_info(struct cpuinfo_x86 *c)
 {
 	char *vendor = NULL;
diff -puN arch/i386/kernel/cpu/intel.c~i386-amd-dual-core-support-for-i386 arch/i386/kernel/cpu/intel.c
--- 25/arch/i386/kernel/cpu/intel.c~i386-amd-dual-core-support-for-i386	2005-01-10 19:17:25.000000000 -0800
+++ 25-akpm/arch/i386/kernel/cpu/intel.c	2005-01-10 19:45:28.737009672 -0800
@@ -140,48 +140,7 @@ static void __init init_intel(struct cpu
 		strcpy(c->x86_model_id, p);
 	
 #ifdef CONFIG_X86_HT
-	if (cpu_has(c, X86_FEATURE_HT)) {
-		extern	int phys_proc_id[NR_CPUS];
-		
-		u32 	eax, ebx, ecx, edx;
-		int 	index_lsb, index_msb, tmp;
-		int 	cpu = smp_processor_id();
-
-		cpuid(1, &eax, &ebx, &ecx, &edx);
-		smp_num_siblings = (ebx & 0xff0000) >> 16;
-
-		if (smp_num_siblings == 1) {
-			printk(KERN_INFO  "CPU: Hyper-Threading is disabled\n");
-		} else if (smp_num_siblings > 1 ) {
-			index_lsb = 0;
-			index_msb = 31;
-
-			if (smp_num_siblings > NR_CPUS) {
-				printk(KERN_WARNING "CPU: Unsupported number of the siblings %d", smp_num_siblings);
-				smp_num_siblings = 1;
-				goto too_many_siblings;
-			}
-			tmp = smp_num_siblings;
-			while ((tmp & 1) == 0) {
-				tmp >>=1 ;
-				index_lsb++;
-			}
-			tmp = smp_num_siblings;
-			while ((tmp & 0x80000000 ) == 0) {
-				tmp <<=1 ;
-				index_msb--;
-			}
-			if (index_lsb != index_msb )
-				index_msb++;
-			phys_proc_id[cpu] = phys_pkg_id((ebx >> 24) & 0xFF, index_msb);
-
-			printk(KERN_INFO  "CPU: Physical Processor ID: %d\n",
-                               phys_proc_id[cpu]);
-		}
-
-	}
-too_many_siblings:
-
+	detect_ht(c);
 #endif
 
 	/* Work around errata */
diff -puN include/asm-i386/processor.h~i386-amd-dual-core-support-for-i386 include/asm-i386/processor.h
--- 25/include/asm-i386/processor.h~i386-amd-dual-core-support-for-i386	2005-01-10 19:17:25.000000000 -0800
+++ 25-akpm/include/asm-i386/processor.h	2005-01-10 19:45:28.738009520 -0800
@@ -65,6 +65,7 @@ struct cpuinfo_x86 {
 	int	f00f_bug;
 	int	coma_bug;
 	unsigned long loops_per_jiffy;
+	unsigned char x86_num_cores;
 } __attribute__((__aligned__(SMP_CACHE_BYTES)));
 
 #define X86_VENDOR_INTEL 0
@@ -96,12 +97,14 @@ extern struct cpuinfo_x86 cpu_data[];
 #define current_cpu_data boot_cpu_data
 #endif
 
+extern	int phys_proc_id[NR_CPUS];
 extern char ignore_fpu_irq;
 
 extern void identify_cpu(struct cpuinfo_x86 *);
 extern void print_cpu_info(struct cpuinfo_x86 *);
 extern unsigned int init_intel_cacheinfo(struct cpuinfo_x86 *c);
 extern void dodgy_tsc(void);
+extern void detect_ht(struct cpuinfo_x86 *c);
 
 /*
  * EFLAGS bits
_
