diff options
Diffstat (limited to 'arch/x86/kernel/cpu/amd_64.c')
-rw-r--r-- | arch/x86/kernel/cpu/amd_64.c | 222 |
1 files changed, 222 insertions, 0 deletions
diff --git a/arch/x86/kernel/cpu/amd_64.c b/arch/x86/kernel/cpu/amd_64.c new file mode 100644 index 000000000000..7c36fb8a28d4 --- /dev/null +++ b/arch/x86/kernel/cpu/amd_64.c | |||
@@ -0,0 +1,222 @@ | |||
1 | #include <linux/init.h> | ||
2 | #include <linux/mm.h> | ||
3 | |||
4 | #include <asm/numa_64.h> | ||
5 | #include <asm/mmconfig.h> | ||
6 | #include <asm/cacheflush.h> | ||
7 | |||
8 | #include <mach_apic.h> | ||
9 | |||
10 | #include "cpu.h" | ||
11 | |||
12 | int force_mwait __cpuinitdata; | ||
13 | |||
14 | #ifdef CONFIG_NUMA | ||
15 | static int __cpuinit nearby_node(int apicid) | ||
16 | { | ||
17 | int i, node; | ||
18 | |||
19 | for (i = apicid - 1; i >= 0; i--) { | ||
20 | node = apicid_to_node[i]; | ||
21 | if (node != NUMA_NO_NODE && node_online(node)) | ||
22 | return node; | ||
23 | } | ||
24 | for (i = apicid + 1; i < MAX_LOCAL_APIC; i++) { | ||
25 | node = apicid_to_node[i]; | ||
26 | if (node != NUMA_NO_NODE && node_online(node)) | ||
27 | return node; | ||
28 | } | ||
29 | return first_node(node_online_map); /* Shouldn't happen */ | ||
30 | } | ||
31 | #endif | ||
32 | |||
33 | /* | ||
34 | * On a AMD dual core setup the lower bits of the APIC id distingush the cores. | ||
35 | * Assumes number of cores is a power of two. | ||
36 | */ | ||
37 | static void __cpuinit amd_detect_cmp(struct cpuinfo_x86 *c) | ||
38 | { | ||
39 | #ifdef CONFIG_SMP | ||
40 | unsigned bits; | ||
41 | #ifdef CONFIG_NUMA | ||
42 | int cpu = smp_processor_id(); | ||
43 | int node = 0; | ||
44 | unsigned apicid = hard_smp_processor_id(); | ||
45 | #endif | ||
46 | bits = c->x86_coreid_bits; | ||
47 | |||
48 | /* Low order bits define the core id (index of core in socket) */ | ||
49 | c->cpu_core_id = c->initial_apicid & ((1 << bits)-1); | ||
50 | /* Convert the initial APIC ID into the socket ID */ | ||
51 | c->phys_proc_id = c->initial_apicid >> bits; | ||
52 | |||
53 | #ifdef CONFIG_NUMA | ||
54 | node = c->phys_proc_id; | ||
55 | if (apicid_to_node[apicid] != NUMA_NO_NODE) | ||
56 | node = apicid_to_node[apicid]; | ||
57 | if (!node_online(node)) { | ||
58 | /* Two possibilities here: | ||
59 | - The CPU is missing memory and no node was created. | ||
60 | In that case try picking one from a nearby CPU | ||
61 | - The APIC IDs differ from the HyperTransport node IDs | ||
62 | which the K8 northbridge parsing fills in. | ||
63 | Assume they are all increased by a constant offset, | ||
64 | but in the same order as the HT nodeids. | ||
65 | If that doesn't result in a usable node fall back to the | ||
66 | path for the previous case. */ | ||
67 | |||
68 | int ht_nodeid = c->initial_apicid; | ||
69 | |||
70 | if (ht_nodeid >= 0 && | ||
71 | apicid_to_node[ht_nodeid] != NUMA_NO_NODE) | ||
72 | node = apicid_to_node[ht_nodeid]; | ||
73 | /* Pick a nearby node */ | ||
74 | if (!node_online(node)) | ||
75 | node = nearby_node(apicid); | ||
76 | } | ||
77 | numa_set_node(cpu, node); | ||
78 | |||
79 | printk(KERN_INFO "CPU %d/%x -> Node %d\n", cpu, apicid, node); | ||
80 | #endif | ||
81 | #endif | ||
82 | } | ||
83 | |||
84 | static void __cpuinit early_init_amd_mc(struct cpuinfo_x86 *c) | ||
85 | { | ||
86 | #ifdef CONFIG_SMP | ||
87 | unsigned bits, ecx; | ||
88 | |||
89 | /* Multi core CPU? */ | ||
90 | if (c->extended_cpuid_level < 0x80000008) | ||
91 | return; | ||
92 | |||
93 | ecx = cpuid_ecx(0x80000008); | ||
94 | |||
95 | c->x86_max_cores = (ecx & 0xff) + 1; | ||
96 | |||
97 | /* CPU telling us the core id bits shift? */ | ||
98 | bits = (ecx >> 12) & 0xF; | ||
99 | |||
100 | /* Otherwise recompute */ | ||
101 | if (bits == 0) { | ||
102 | while ((1 << bits) < c->x86_max_cores) | ||
103 | bits++; | ||
104 | } | ||
105 | |||
106 | c->x86_coreid_bits = bits; | ||
107 | |||
108 | #endif | ||
109 | } | ||
110 | |||
111 | static void __cpuinit early_init_amd(struct cpuinfo_x86 *c) | ||
112 | { | ||
113 | early_init_amd_mc(c); | ||
114 | |||
115 | /* c->x86_power is 8000_0007 edx. Bit 8 is constant TSC */ | ||
116 | if (c->x86_power & (1<<8)) | ||
117 | set_cpu_cap(c, X86_FEATURE_CONSTANT_TSC); | ||
118 | } | ||
119 | |||
120 | static void __cpuinit init_amd(struct cpuinfo_x86 *c) | ||
121 | { | ||
122 | unsigned level; | ||
123 | |||
124 | #ifdef CONFIG_SMP | ||
125 | unsigned long value; | ||
126 | |||
127 | /* | ||
128 | * Disable TLB flush filter by setting HWCR.FFDIS on K8 | ||
129 | * bit 6 of msr C001_0015 | ||
130 | * | ||
131 | * Errata 63 for SH-B3 steppings | ||
132 | * Errata 122 for all steppings (F+ have it disabled by default) | ||
133 | */ | ||
134 | if (c->x86 == 0xf) { | ||
135 | rdmsrl(MSR_K8_HWCR, value); | ||
136 | value |= 1 << 6; | ||
137 | wrmsrl(MSR_K8_HWCR, value); | ||
138 | } | ||
139 | #endif | ||
140 | |||
141 | /* Bit 31 in normal CPUID used for nonstandard 3DNow ID; | ||
142 | 3DNow is IDd by bit 31 in extended CPUID (1*32+31) anyway */ | ||
143 | clear_cpu_cap(c, 0*32+31); | ||
144 | |||
145 | /* On C+ stepping K8 rep microcode works well for copy/memset */ | ||
146 | if (c->x86 == 0xf) { | ||
147 | level = cpuid_eax(1); | ||
148 | if((level >= 0x0f48 && level < 0x0f50) || level >= 0x0f58) | ||
149 | set_cpu_cap(c, X86_FEATURE_REP_GOOD); | ||
150 | } | ||
151 | if (c->x86 == 0x10 || c->x86 == 0x11) | ||
152 | set_cpu_cap(c, X86_FEATURE_REP_GOOD); | ||
153 | |||
154 | /* Enable workaround for FXSAVE leak */ | ||
155 | if (c->x86 >= 6) | ||
156 | set_cpu_cap(c, X86_FEATURE_FXSAVE_LEAK); | ||
157 | |||
158 | level = get_model_name(c); | ||
159 | if (!level) { | ||
160 | switch (c->x86) { | ||
161 | case 0xf: | ||
162 | /* Should distinguish Models here, but this is only | ||
163 | a fallback anyways. */ | ||
164 | strcpy(c->x86_model_id, "Hammer"); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | display_cacheinfo(c); | ||
169 | |||
170 | /* Multi core CPU? */ | ||
171 | if (c->extended_cpuid_level >= 0x80000008) | ||
172 | amd_detect_cmp(c); | ||
173 | |||
174 | if (c->extended_cpuid_level >= 0x80000006 && | ||
175 | (cpuid_edx(0x80000006) & 0xf000)) | ||
176 | num_cache_leaves = 4; | ||
177 | else | ||
178 | num_cache_leaves = 3; | ||
179 | |||
180 | if (c->x86 >= 0xf && c->x86 <= 0x11) | ||
181 | set_cpu_cap(c, X86_FEATURE_K8); | ||
182 | |||
183 | /* MFENCE stops RDTSC speculation */ | ||
184 | set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC); | ||
185 | |||
186 | if (c->x86 == 0x10) { | ||
187 | /* do this for boot cpu */ | ||
188 | if (c == &boot_cpu_data) | ||
189 | check_enable_amd_mmconf_dmi(); | ||
190 | |||
191 | fam10h_check_enable_mmcfg(); | ||
192 | } | ||
193 | |||
194 | if (c == &boot_cpu_data && c->x86 >= 0xf && c->x86 <= 0x11) { | ||
195 | unsigned long long tseg; | ||
196 | |||
197 | /* | ||
198 | * Split up direct mapping around the TSEG SMM area. | ||
199 | * Don't do it for gbpages because there seems very little | ||
200 | * benefit in doing so. | ||
201 | */ | ||
202 | if (!rdmsrl_safe(MSR_K8_TSEG_ADDR, &tseg)) { | ||
203 | printk(KERN_DEBUG "tseg: %010llx\n", tseg); | ||
204 | if ((tseg>>PMD_SHIFT) < | ||
205 | (max_low_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) || | ||
206 | ((tseg>>PMD_SHIFT) < | ||
207 | (max_pfn_mapped>>(PMD_SHIFT-PAGE_SHIFT)) && | ||
208 | (tseg>>PMD_SHIFT) >= (1ULL<<(32 - PMD_SHIFT)))) | ||
209 | set_memory_4k((unsigned long)__va(tseg), 1); | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | |||
214 | static struct cpu_dev amd_cpu_dev __cpuinitdata = { | ||
215 | .c_vendor = "AMD", | ||
216 | .c_ident = { "AuthenticAMD" }, | ||
217 | .c_early_init = early_init_amd, | ||
218 | .c_init = init_amd, | ||
219 | }; | ||
220 | |||
221 | cpu_vendor_dev_register(X86_VENDOR_AMD, &amd_cpu_dev); | ||
222 | |||