diff options
author | Doug Thompson <dougthompson@xmission.com> | 2009-04-27 09:57:12 -0400 |
---|---|---|
committer | Borislav Petkov <borislav.petkov@amd.com> | 2009-06-10 06:18:50 -0400 |
commit | e2ce7255e84db656853e91536e6023f92ff89f97 (patch) | |
tree | dbc960bb1c4e9a536c899fdc7e3517007bcb26e2 | |
parent | 6775763a2377e1ea865299b6daadc875d622de3f (diff) |
amd64_edac: add functionality to compute the DRAM hole
Borislav:
- cleanup/fix comments, add BKDG refs
- cleanup debug calls
Reviewed-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Doug Thompson <dougthompson@xmission.com>
Signed-off-by: Borislav Petkov <borislav.petkov@amd.com>
-rw-r--r-- | drivers/edac/amd64_edac.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index a774f34d508..4716fb561e6 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c | |||
@@ -269,3 +269,169 @@ err_no_match: | |||
269 | 269 | ||
270 | return NULL; | 270 | return NULL; |
271 | } | 271 | } |
272 | |||
273 | /* | ||
274 | * Extract the DRAM CS base address from selected csrow register. | ||
275 | */ | ||
276 | static u64 base_from_dct_base(struct amd64_pvt *pvt, int csrow) | ||
277 | { | ||
278 | return ((u64) (amd64_get_dct_base(pvt, 0, csrow) & pvt->dcsb_base)) << | ||
279 | pvt->dcs_shift; | ||
280 | } | ||
281 | |||
282 | /* | ||
283 | * Extract the mask from the dcsb0[csrow] entry in a CPU revision-specific way. | ||
284 | */ | ||
285 | static u64 mask_from_dct_mask(struct amd64_pvt *pvt, int csrow) | ||
286 | { | ||
287 | u64 dcsm_bits, other_bits; | ||
288 | u64 mask; | ||
289 | |||
290 | /* Extract bits from DRAM CS Mask. */ | ||
291 | dcsm_bits = amd64_get_dct_mask(pvt, 0, csrow) & pvt->dcsm_mask; | ||
292 | |||
293 | other_bits = pvt->dcsm_mask; | ||
294 | other_bits = ~(other_bits << pvt->dcs_shift); | ||
295 | |||
296 | /* | ||
297 | * The extracted bits from DCSM belong in the spaces represented by | ||
298 | * the cleared bits in other_bits. | ||
299 | */ | ||
300 | mask = (dcsm_bits << pvt->dcs_shift) | other_bits; | ||
301 | |||
302 | return mask; | ||
303 | } | ||
304 | |||
305 | /* | ||
306 | * @input_addr is an InputAddr associated with the node given by mci. Return the | ||
307 | * csrow that input_addr maps to, or -1 on failure (no csrow claims input_addr). | ||
308 | */ | ||
309 | static int input_addr_to_csrow(struct mem_ctl_info *mci, u64 input_addr) | ||
310 | { | ||
311 | struct amd64_pvt *pvt; | ||
312 | int csrow; | ||
313 | u64 base, mask; | ||
314 | |||
315 | pvt = mci->pvt_info; | ||
316 | |||
317 | /* | ||
318 | * Here we use the DRAM CS Base and DRAM CS Mask registers. For each CS | ||
319 | * base/mask register pair, test the condition shown near the start of | ||
320 | * section 3.5.4 (p. 84, BKDG #26094, K8, revA-E). | ||
321 | */ | ||
322 | for (csrow = 0; csrow < CHIPSELECT_COUNT; csrow++) { | ||
323 | |||
324 | /* This DRAM chip select is disabled on this node */ | ||
325 | if ((pvt->dcsb0[csrow] & K8_DCSB_CS_ENABLE) == 0) | ||
326 | continue; | ||
327 | |||
328 | base = base_from_dct_base(pvt, csrow); | ||
329 | mask = ~mask_from_dct_mask(pvt, csrow); | ||
330 | |||
331 | if ((input_addr & mask) == (base & mask)) { | ||
332 | debugf2("InputAddr 0x%lx matches csrow %d (node %d)\n", | ||
333 | (unsigned long)input_addr, csrow, | ||
334 | pvt->mc_node_id); | ||
335 | |||
336 | return csrow; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | debugf2("no matching csrow for InputAddr 0x%lx (MC node %d)\n", | ||
341 | (unsigned long)input_addr, pvt->mc_node_id); | ||
342 | |||
343 | return -1; | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Return the base value defined by the DRAM Base register for the node | ||
348 | * represented by mci. This function returns the full 40-bit value despite the | ||
349 | * fact that the register only stores bits 39-24 of the value. See section | ||
350 | * 3.4.4.1 (BKDG #26094, K8, revA-E) | ||
351 | */ | ||
352 | static inline u64 get_dram_base(struct mem_ctl_info *mci) | ||
353 | { | ||
354 | struct amd64_pvt *pvt = mci->pvt_info; | ||
355 | |||
356 | return pvt->dram_base[pvt->mc_node_id]; | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Obtain info from the DRAM Hole Address Register (section 3.4.8, pub #26094) | ||
361 | * for the node represented by mci. Info is passed back in *hole_base, | ||
362 | * *hole_offset, and *hole_size. Function returns 0 if info is valid or 1 if | ||
363 | * info is invalid. Info may be invalid for either of the following reasons: | ||
364 | * | ||
365 | * - The revision of the node is not E or greater. In this case, the DRAM Hole | ||
366 | * Address Register does not exist. | ||
367 | * | ||
368 | * - The DramHoleValid bit is cleared in the DRAM Hole Address Register, | ||
369 | * indicating that its contents are not valid. | ||
370 | * | ||
371 | * The values passed back in *hole_base, *hole_offset, and *hole_size are | ||
372 | * complete 32-bit values despite the fact that the bitfields in the DHAR | ||
373 | * only represent bits 31-24 of the base and offset values. | ||
374 | */ | ||
375 | int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base, | ||
376 | u64 *hole_offset, u64 *hole_size) | ||
377 | { | ||
378 | struct amd64_pvt *pvt = mci->pvt_info; | ||
379 | u64 base; | ||
380 | |||
381 | /* only revE and later have the DRAM Hole Address Register */ | ||
382 | if (boot_cpu_data.x86 == 0xf && pvt->ext_model < OPTERON_CPU_REV_E) { | ||
383 | debugf1(" revision %d for node %d does not support DHAR\n", | ||
384 | pvt->ext_model, pvt->mc_node_id); | ||
385 | return 1; | ||
386 | } | ||
387 | |||
388 | /* only valid for Fam10h */ | ||
389 | if (boot_cpu_data.x86 == 0x10 && | ||
390 | (pvt->dhar & F10_DRAM_MEM_HOIST_VALID) == 0) { | ||
391 | debugf1(" Dram Memory Hoisting is DISABLED on this system\n"); | ||
392 | return 1; | ||
393 | } | ||
394 | |||
395 | if ((pvt->dhar & DHAR_VALID) == 0) { | ||
396 | debugf1(" Dram Memory Hoisting is DISABLED on this node %d\n", | ||
397 | pvt->mc_node_id); | ||
398 | return 1; | ||
399 | } | ||
400 | |||
401 | /* This node has Memory Hoisting */ | ||
402 | |||
403 | /* +------------------+--------------------+--------------------+----- | ||
404 | * | memory | DRAM hole | relocated | | ||
405 | * | [0, (x - 1)] | [x, 0xffffffff] | addresses from | | ||
406 | * | | | DRAM hole | | ||
407 | * | | | [0x100000000, | | ||
408 | * | | | (0x100000000+ | | ||
409 | * | | | (0xffffffff-x))] | | ||
410 | * +------------------+--------------------+--------------------+----- | ||
411 | * | ||
412 | * Above is a diagram of physical memory showing the DRAM hole and the | ||
413 | * relocated addresses from the DRAM hole. As shown, the DRAM hole | ||
414 | * starts at address x (the base address) and extends through address | ||
415 | * 0xffffffff. The DRAM Hole Address Register (DHAR) relocates the | ||
416 | * addresses in the hole so that they start at 0x100000000. | ||
417 | */ | ||
418 | |||
419 | base = dhar_base(pvt->dhar); | ||
420 | |||
421 | *hole_base = base; | ||
422 | *hole_size = (0x1ull << 32) - base; | ||
423 | |||
424 | if (boot_cpu_data.x86 > 0xf) | ||
425 | *hole_offset = f10_dhar_offset(pvt->dhar); | ||
426 | else | ||
427 | *hole_offset = k8_dhar_offset(pvt->dhar); | ||
428 | |||
429 | debugf1(" DHAR info for node %d base 0x%lx offset 0x%lx size 0x%lx\n", | ||
430 | pvt->mc_node_id, (unsigned long)*hole_base, | ||
431 | (unsigned long)*hole_offset, (unsigned long)*hole_size); | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); | ||
436 | |||
437 | |||