diff options
author | Andi Kleen <ak@suse.de> | 2007-07-21 11:10:03 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-07-21 21:37:08 -0400 |
commit | 67cddd947992b02f01ad093ec814738c5827d17c (patch) | |
tree | 5c10c3a1f645c119e0cc23ecdfc7c3c4dd7eacad /arch/i386 | |
parent | 2aae950b21e4bc789d1fc6668faf67e8748300b7 (diff) |
i386: Add L3 cache support to AMD CPUID4 emulation
With that an L3 cache is correctly reported in the cache information in /sys
With fixes from Andreas Herrmann and Dean Gaudet and Joachim Deguara
Signed-off-by: Andi Kleen <ak@suse.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386')
-rw-r--r-- | arch/i386/kernel/cpu/amd.c | 8 | ||||
-rw-r--r-- | arch/i386/kernel/cpu/intel_cacheinfo.c | 74 |
2 files changed, 60 insertions, 22 deletions
diff --git a/arch/i386/kernel/cpu/amd.c b/arch/i386/kernel/cpu/amd.c index 6f47eeeb93ea..815a5f0aa474 100644 --- a/arch/i386/kernel/cpu/amd.c +++ b/arch/i386/kernel/cpu/amd.c | |||
@@ -272,8 +272,12 @@ static void __cpuinit init_amd(struct cpuinfo_x86 *c) | |||
272 | } | 272 | } |
273 | #endif | 273 | #endif |
274 | 274 | ||
275 | if (cpuid_eax(0x80000000) >= 0x80000006) | 275 | if (cpuid_eax(0x80000000) >= 0x80000006) { |
276 | num_cache_leaves = 3; | 276 | if ((c->x86 == 0x10) && (cpuid_edx(0x80000006) & 0xf000)) |
277 | num_cache_leaves = 4; | ||
278 | else | ||
279 | num_cache_leaves = 3; | ||
280 | } | ||
277 | 281 | ||
278 | if (amd_apic_timer_broken()) | 282 | if (amd_apic_timer_broken()) |
279 | set_bit(X86_FEATURE_LAPIC_TIMER_BROKEN, c->x86_capability); | 283 | set_bit(X86_FEATURE_LAPIC_TIMER_BROKEN, c->x86_capability); |
diff --git a/arch/i386/kernel/cpu/intel_cacheinfo.c b/arch/i386/kernel/cpu/intel_cacheinfo.c index 25b4b7d89cae..43db806b9609 100644 --- a/arch/i386/kernel/cpu/intel_cacheinfo.c +++ b/arch/i386/kernel/cpu/intel_cacheinfo.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * Changes: | 4 | * Changes: |
5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) | 5 | * Venkatesh Pallipadi : Adding cache identification through cpuid(4) |
6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. | 6 | * Ashok Raj <ashok.raj@intel.com>: Work with CPU hotplug infrastructure. |
7 | * Andi Kleen : CPUID4 emulation on AMD. | 7 | * Andi Kleen / Andreas Herrmann : CPUID4 emulation on AMD. |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/init.h> | 10 | #include <linux/init.h> |
@@ -135,7 +135,7 @@ unsigned short num_cache_leaves; | |||
135 | 135 | ||
136 | /* AMD doesn't have CPUID4. Emulate it here to report the same | 136 | /* AMD doesn't have CPUID4. Emulate it here to report the same |
137 | information to the user. This makes some assumptions about the machine: | 137 | information to the user. This makes some assumptions about the machine: |
138 | No L3, L2 not shared, no SMT etc. that is currently true on AMD CPUs. | 138 | L2 not shared, no SMT etc. that is currently true on AMD CPUs. |
139 | 139 | ||
140 | In theory the TLBs could be reported as fake type (they are in "dummy"). | 140 | In theory the TLBs could be reported as fake type (they are in "dummy"). |
141 | Maybe later */ | 141 | Maybe later */ |
@@ -159,13 +159,26 @@ union l2_cache { | |||
159 | unsigned val; | 159 | unsigned val; |
160 | }; | 160 | }; |
161 | 161 | ||
162 | union l3_cache { | ||
163 | struct { | ||
164 | unsigned line_size : 8; | ||
165 | unsigned lines_per_tag : 4; | ||
166 | unsigned assoc : 4; | ||
167 | unsigned res : 2; | ||
168 | unsigned size_encoded : 14; | ||
169 | }; | ||
170 | unsigned val; | ||
171 | }; | ||
172 | |||
162 | static const unsigned short assocs[] = { | 173 | static const unsigned short assocs[] = { |
163 | [1] = 1, [2] = 2, [4] = 4, [6] = 8, | 174 | [1] = 1, [2] = 2, [4] = 4, [6] = 8, |
164 | [8] = 16, | 175 | [8] = 16, [0xa] = 32, [0xb] = 48, |
176 | [0xc] = 64, | ||
165 | [0xf] = 0xffff // ?? | 177 | [0xf] = 0xffff // ?? |
166 | }; | 178 | }; |
167 | static const unsigned char levels[] = { 1, 1, 2 }; | 179 | |
168 | static const unsigned char types[] = { 1, 2, 3 }; | 180 | static const unsigned char levels[] = { 1, 1, 2, 3 }; |
181 | static const unsigned char types[] = { 1, 2, 3, 3 }; | ||
169 | 182 | ||
170 | static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, | 183 | static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, |
171 | union _cpuid4_leaf_ebx *ebx, | 184 | union _cpuid4_leaf_ebx *ebx, |
@@ -175,37 +188,58 @@ static void __cpuinit amd_cpuid4(int leaf, union _cpuid4_leaf_eax *eax, | |||
175 | unsigned line_size, lines_per_tag, assoc, size_in_kb; | 188 | unsigned line_size, lines_per_tag, assoc, size_in_kb; |
176 | union l1_cache l1i, l1d; | 189 | union l1_cache l1i, l1d; |
177 | union l2_cache l2; | 190 | union l2_cache l2; |
191 | union l3_cache l3; | ||
192 | union l1_cache *l1 = &l1d; | ||
178 | 193 | ||
179 | eax->full = 0; | 194 | eax->full = 0; |
180 | ebx->full = 0; | 195 | ebx->full = 0; |
181 | ecx->full = 0; | 196 | ecx->full = 0; |
182 | 197 | ||
183 | cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val); | 198 | cpuid(0x80000005, &dummy, &dummy, &l1d.val, &l1i.val); |
184 | cpuid(0x80000006, &dummy, &dummy, &l2.val, &dummy); | 199 | cpuid(0x80000006, &dummy, &dummy, &l2.val, &l3.val); |
185 | 200 | ||
186 | if (leaf > 2 || !l1d.val || !l1i.val || !l2.val) | 201 | switch (leaf) { |
187 | return; | 202 | case 1: |
188 | 203 | l1 = &l1i; | |
189 | eax->split.is_self_initializing = 1; | 204 | case 0: |
190 | eax->split.type = types[leaf]; | 205 | if (!l1->val) |
191 | eax->split.level = levels[leaf]; | 206 | return; |
192 | eax->split.num_threads_sharing = 0; | ||
193 | eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1; | ||
194 | |||
195 | if (leaf <= 1) { | ||
196 | union l1_cache *l1 = leaf == 0 ? &l1d : &l1i; | ||
197 | assoc = l1->assoc; | 207 | assoc = l1->assoc; |
198 | line_size = l1->line_size; | 208 | line_size = l1->line_size; |
199 | lines_per_tag = l1->lines_per_tag; | 209 | lines_per_tag = l1->lines_per_tag; |
200 | size_in_kb = l1->size_in_kb; | 210 | size_in_kb = l1->size_in_kb; |
201 | } else { | 211 | break; |
212 | case 2: | ||
213 | if (!l2.val) | ||
214 | return; | ||
202 | assoc = l2.assoc; | 215 | assoc = l2.assoc; |
203 | line_size = l2.line_size; | 216 | line_size = l2.line_size; |
204 | lines_per_tag = l2.lines_per_tag; | 217 | lines_per_tag = l2.lines_per_tag; |
205 | /* cpu_data has errata corrections for K7 applied */ | 218 | /* cpu_data has errata corrections for K7 applied */ |
206 | size_in_kb = current_cpu_data.x86_cache_size; | 219 | size_in_kb = current_cpu_data.x86_cache_size; |
220 | break; | ||
221 | case 3: | ||
222 | if (!l3.val) | ||
223 | return; | ||
224 | assoc = l3.assoc; | ||
225 | line_size = l3.line_size; | ||
226 | lines_per_tag = l3.lines_per_tag; | ||
227 | size_in_kb = l3.size_encoded * 512; | ||
228 | break; | ||
229 | default: | ||
230 | return; | ||
207 | } | 231 | } |
208 | 232 | ||
233 | eax->split.is_self_initializing = 1; | ||
234 | eax->split.type = types[leaf]; | ||
235 | eax->split.level = levels[leaf]; | ||
236 | if (leaf == 3) | ||
237 | eax->split.num_threads_sharing = current_cpu_data.x86_max_cores - 1; | ||
238 | else | ||
239 | eax->split.num_threads_sharing = 0; | ||
240 | eax->split.num_cores_on_die = current_cpu_data.x86_max_cores - 1; | ||
241 | |||
242 | |||
209 | if (assoc == 0xf) | 243 | if (assoc == 0xf) |
210 | eax->split.is_fully_associative = 1; | 244 | eax->split.is_fully_associative = 1; |
211 | ebx->split.coherency_line_size = line_size - 1; | 245 | ebx->split.coherency_line_size = line_size - 1; |