diff options
Diffstat (limited to 'arch/x86/kernel/cpu/amd.c')
| -rw-r--r-- | arch/x86/kernel/cpu/amd.c | 80 | 
1 files changed, 77 insertions, 3 deletions
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 83b217c7225..22a47c82f3c 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -253,6 +253,64 @@ static int __cpuinit nearby_node(int apicid)  #endif  /* + * Fixup core topology information for AMD multi-node processors. + * Assumption 1: Number of cores in each internal node is the same. + * Assumption 2: Mixed systems with both single-node and dual-node + *               processors are not supported. + */ +#ifdef CONFIG_X86_HT +static void __cpuinit amd_fixup_dcm(struct cpuinfo_x86 *c) +{ +#ifdef CONFIG_PCI +	u32 t, cpn; +	u8 n, n_id; +	int cpu = smp_processor_id(); + +	/* fixup topology information only once for a core */ +	if (cpu_has(c, X86_FEATURE_AMD_DCM)) +		return; + +	/* check for multi-node processor on boot cpu */ +	t = read_pci_config(0, 24, 3, 0xe8); +	if (!(t & (1 << 29))) +		return; + +	set_cpu_cap(c, X86_FEATURE_AMD_DCM); + +	/* cores per node: each internal node has half the number of cores */ +	cpn = c->x86_max_cores >> 1; + +	/* even-numbered NB_id of this dual-node processor */ +	n = c->phys_proc_id << 1; + +	/* +	 * determine internal node id and assign cores fifty-fifty to +	 * each node of the dual-node processor +	 */ +	t = read_pci_config(0, 24 + n, 3, 0xe8); +	n = (t>>30) & 0x3; +	if (n == 0) { +		if (c->cpu_core_id < cpn) +			n_id = 0; +		else +			n_id = 1; +	} else { +		if (c->cpu_core_id < cpn) +			n_id = 1; +		else +			n_id = 0; +	} + +	/* compute entire NodeID, use llc_shared_map to store sibling info */ +	per_cpu(cpu_llc_id, cpu) = (c->phys_proc_id << 1) + n_id; + +	/* fixup core id to be in range from 0 to cpn */ +	c->cpu_core_id = c->cpu_core_id % cpn; +#endif +} +#endif + +/*   * On a AMD dual core setup the lower bits of the APIC id distingush the cores.   * Assumes number of cores is a power of two.   */ @@ -269,6 +327,9 @@ static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c)  	c->phys_proc_id = c->initial_apicid >> bits;  	/* use socket ID also for last level cache */  	per_cpu(cpu_llc_id, cpu) = c->phys_proc_id; +	/* fixup topology information on multi-node processors */ +	if ((c->x86 == 0x10) && (c->x86_model == 9)) +		amd_fixup_dcm(c);  #endif  } @@ -277,9 +338,10 @@ static void __cpuinit srat_detect_node(struct cpuinfo_x86 *c)  #if defined(CONFIG_NUMA) && defined(CONFIG_X86_64)  	int cpu = smp_processor_id();  	int node; -	unsigned apicid = cpu_has_apic ? hard_smp_processor_id() : c->apicid; +	unsigned apicid = c->apicid; + +	node = per_cpu(cpu_llc_id, cpu); -	node = c->phys_proc_id;  	if (apicid_to_node[apicid] != NUMA_NO_NODE)  		node = apicid_to_node[apicid];  	if (!node_online(node)) { @@ -406,12 +468,24 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c)  		/*  		 * Some BIOSes incorrectly force this feature, but only K8  		 * revision D (model = 0x14) and later actually support it. +		 * (AMD Erratum #110, docId: 25759).  		 */ -		if (c->x86_model < 0x14) +		if (c->x86_model < 0x14 && cpu_has(c, X86_FEATURE_LAHF_LM)) { +			u64 val; +  			clear_cpu_cap(c, X86_FEATURE_LAHF_LM); +			if (!rdmsrl_amd_safe(0xc001100d, &val)) { +				val &= ~(1ULL << 32); +				wrmsrl_amd_safe(0xc001100d, val); +			} +		} +  	}  	if (c->x86 == 0x10 || c->x86 == 0x11)  		set_cpu_cap(c, X86_FEATURE_REP_GOOD); + +	/* get apicid instead of initial apic id from cpuid */ +	c->apicid = hard_smp_processor_id();  #else  	/*  |