aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/amd64_edac.c
diff options
context:
space:
mode:
authorDoug Thompson <dougthompson@xmission.com>2009-05-04 14:46:50 -0400
committerBorislav Petkov <borislav.petkov@amd.com>2009-06-10 06:18:51 -0400
commit93c2df58b5b1a434cca8f60067e0e12d1942b7f1 (patch)
tree54b28a1bb6eae8cf5cf5d1ad7e98624f86576ced /drivers/edac/amd64_edac.c
parente2ce7255e84db656853e91536e6023f92ff89f97 (diff)
amd64_edac: add DRAM address type conversion facilities
Borislav: - cleanup/fix comments, add BKDG refs - fix function return value patterns - cleanup dbg 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>
Diffstat (limited to 'drivers/edac/amd64_edac.c')
-rw-r--r--drivers/edac/amd64_edac.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 4716fb561e6e..28f85c9e3afc 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -434,4 +434,298 @@ int amd64_get_dram_hole_info(struct mem_ctl_info *mci, u64 *hole_base,
434} 434}
435EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info); 435EXPORT_SYMBOL_GPL(amd64_get_dram_hole_info);
436 436
437/*
438 * Return the DramAddr that the SysAddr given by @sys_addr maps to. It is
439 * assumed that sys_addr maps to the node given by mci.
440 *
441 * The first part of section 3.4.4 (p. 70) shows how the DRAM Base (section
442 * 3.4.4.1) and DRAM Limit (section 3.4.4.2) registers are used to translate a
443 * SysAddr to a DramAddr. If the DRAM Hole Address Register (DHAR) is enabled,
444 * then it is also involved in translating a SysAddr to a DramAddr. Sections
445 * 3.4.8 and 3.5.8.2 describe the DHAR and how it is used for memory hoisting.
446 * These parts of the documentation are unclear. I interpret them as follows:
447 *
448 * When node n receives a SysAddr, it processes the SysAddr as follows:
449 *
450 * 1. It extracts the DRAMBase and DRAMLimit values from the DRAM Base and DRAM
451 * Limit registers for node n. If the SysAddr is not within the range
452 * specified by the base and limit values, then node n ignores the Sysaddr
453 * (since it does not map to node n). Otherwise continue to step 2 below.
454 *
455 * 2. If the DramHoleValid bit of the DHAR for node n is clear, the DHAR is
456 * disabled so skip to step 3 below. Otherwise see if the SysAddr is within
457 * the range of relocated addresses (starting at 0x100000000) from the DRAM
458 * hole. If not, skip to step 3 below. Else get the value of the
459 * DramHoleOffset field from the DHAR. To obtain the DramAddr, subtract the
460 * offset defined by this value from the SysAddr.
461 *
462 * 3. Obtain the base address for node n from the DRAMBase field of the DRAM
463 * Base register for node n. To obtain the DramAddr, subtract the base
464 * address from the SysAddr, as shown near the start of section 3.4.4 (p.70).
465 */
466static u64 sys_addr_to_dram_addr(struct mem_ctl_info *mci, u64 sys_addr)
467{
468 u64 dram_base, hole_base, hole_offset, hole_size, dram_addr;
469 int ret = 0;
470
471 dram_base = get_dram_base(mci);
472
473 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
474 &hole_size);
475 if (!ret) {
476 if ((sys_addr >= (1ull << 32)) &&
477 (sys_addr < ((1ull << 32) + hole_size))) {
478 /* use DHAR to translate SysAddr to DramAddr */
479 dram_addr = sys_addr - hole_offset;
480
481 debugf2("using DHAR to translate SysAddr 0x%lx to "
482 "DramAddr 0x%lx\n",
483 (unsigned long)sys_addr,
484 (unsigned long)dram_addr);
485
486 return dram_addr;
487 }
488 }
489
490 /*
491 * Translate the SysAddr to a DramAddr as shown near the start of
492 * section 3.4.4 (p. 70). Although sys_addr is a 64-bit value, the k8
493 * only deals with 40-bit values. Therefore we discard bits 63-40 of
494 * sys_addr below. If bit 39 of sys_addr is 1 then the bits we
495 * discard are all 1s. Otherwise the bits we discard are all 0s. See
496 * section 3.4.2 of AMD publication 24592: AMD x86-64 Architecture
497 * Programmer's Manual Volume 1 Application Programming.
498 */
499 dram_addr = (sys_addr & 0xffffffffffull) - dram_base;
500
501 debugf2("using DRAM Base register to translate SysAddr 0x%lx to "
502 "DramAddr 0x%lx\n", (unsigned long)sys_addr,
503 (unsigned long)dram_addr);
504 return dram_addr;
505}
506
507/*
508 * @intlv_en is the value of the IntlvEn field from a DRAM Base register
509 * (section 3.4.4.1). Return the number of bits from a SysAddr that are used
510 * for node interleaving.
511 */
512static int num_node_interleave_bits(unsigned intlv_en)
513{
514 static const int intlv_shift_table[] = { 0, 1, 0, 2, 0, 0, 0, 3 };
515 int n;
516
517 BUG_ON(intlv_en > 7);
518 n = intlv_shift_table[intlv_en];
519 return n;
520}
521
522/* Translate the DramAddr given by @dram_addr to an InputAddr. */
523static u64 dram_addr_to_input_addr(struct mem_ctl_info *mci, u64 dram_addr)
524{
525 struct amd64_pvt *pvt;
526 int intlv_shift;
527 u64 input_addr;
528
529 pvt = mci->pvt_info;
530
531 /*
532 * See the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
533 * concerning translating a DramAddr to an InputAddr.
534 */
535 intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
536 input_addr = ((dram_addr >> intlv_shift) & 0xffffff000ull) +
537 (dram_addr & 0xfff);
538
539 debugf2(" Intlv Shift=%d DramAddr=0x%lx maps to InputAddr=0x%lx\n",
540 intlv_shift, (unsigned long)dram_addr,
541 (unsigned long)input_addr);
542
543 return input_addr;
544}
545
546/*
547 * Translate the SysAddr represented by @sys_addr to an InputAddr. It is
548 * assumed that @sys_addr maps to the node given by mci.
549 */
550static u64 sys_addr_to_input_addr(struct mem_ctl_info *mci, u64 sys_addr)
551{
552 u64 input_addr;
553
554 input_addr =
555 dram_addr_to_input_addr(mci, sys_addr_to_dram_addr(mci, sys_addr));
556
557 debugf2("SysAdddr 0x%lx translates to InputAddr 0x%lx\n",
558 (unsigned long)sys_addr, (unsigned long)input_addr);
559
560 return input_addr;
561}
562
563
564/*
565 * @input_addr is an InputAddr associated with the node represented by mci.
566 * Translate @input_addr to a DramAddr and return the result.
567 */
568static u64 input_addr_to_dram_addr(struct mem_ctl_info *mci, u64 input_addr)
569{
570 struct amd64_pvt *pvt;
571 int node_id, intlv_shift;
572 u64 bits, dram_addr;
573 u32 intlv_sel;
574
575 /*
576 * Near the start of section 3.4.4 (p. 70, BKDG #26094, K8, revA-E)
577 * shows how to translate a DramAddr to an InputAddr. Here we reverse
578 * this procedure. When translating from a DramAddr to an InputAddr, the
579 * bits used for node interleaving are discarded. Here we recover these
580 * bits from the IntlvSel field of the DRAM Limit register (section
581 * 3.4.4.2) for the node that input_addr is associated with.
582 */
583 pvt = mci->pvt_info;
584 node_id = pvt->mc_node_id;
585 BUG_ON((node_id < 0) || (node_id > 7));
586
587 intlv_shift = num_node_interleave_bits(pvt->dram_IntlvEn[0]);
588
589 if (intlv_shift == 0) {
590 debugf1(" InputAddr 0x%lx translates to DramAddr of "
591 "same value\n", (unsigned long)input_addr);
592
593 return input_addr;
594 }
595
596 bits = ((input_addr & 0xffffff000ull) << intlv_shift) +
597 (input_addr & 0xfff);
598
599 intlv_sel = pvt->dram_IntlvSel[node_id] & ((1 << intlv_shift) - 1);
600 dram_addr = bits + (intlv_sel << 12);
601
602 debugf1("InputAddr 0x%lx translates to DramAddr 0x%lx "
603 "(%d node interleave bits)\n", (unsigned long)input_addr,
604 (unsigned long)dram_addr, intlv_shift);
605
606 return dram_addr;
607}
608
609/*
610 * @dram_addr is a DramAddr that maps to the node represented by mci. Convert
611 * @dram_addr to a SysAddr.
612 */
613static u64 dram_addr_to_sys_addr(struct mem_ctl_info *mci, u64 dram_addr)
614{
615 struct amd64_pvt *pvt = mci->pvt_info;
616 u64 hole_base, hole_offset, hole_size, base, limit, sys_addr;
617 int ret = 0;
618
619 ret = amd64_get_dram_hole_info(mci, &hole_base, &hole_offset,
620 &hole_size);
621 if (!ret) {
622 if ((dram_addr >= hole_base) &&
623 (dram_addr < (hole_base + hole_size))) {
624 sys_addr = dram_addr + hole_offset;
625
626 debugf1("using DHAR to translate DramAddr 0x%lx to "
627 "SysAddr 0x%lx\n", (unsigned long)dram_addr,
628 (unsigned long)sys_addr);
629
630 return sys_addr;
631 }
632 }
633
634 amd64_get_base_and_limit(pvt, pvt->mc_node_id, &base, &limit);
635 sys_addr = dram_addr + base;
636
637 /*
638 * The sys_addr we have computed up to this point is a 40-bit value
639 * because the k8 deals with 40-bit values. However, the value we are
640 * supposed to return is a full 64-bit physical address. The AMD
641 * x86-64 architecture specifies that the most significant implemented
642 * address bit through bit 63 of a physical address must be either all
643 * 0s or all 1s. Therefore we sign-extend the 40-bit sys_addr to a
644 * 64-bit value below. See section 3.4.2 of AMD publication 24592:
645 * AMD x86-64 Architecture Programmer's Manual Volume 1 Application
646 * Programming.
647 */
648 sys_addr |= ~((sys_addr & (1ull << 39)) - 1);
649
650 debugf1(" Node %d, DramAddr 0x%lx to SysAddr 0x%lx\n",
651 pvt->mc_node_id, (unsigned long)dram_addr,
652 (unsigned long)sys_addr);
653
654 return sys_addr;
655}
656
657/*
658 * @input_addr is an InputAddr associated with the node given by mci. Translate
659 * @input_addr to a SysAddr.
660 */
661static inline u64 input_addr_to_sys_addr(struct mem_ctl_info *mci,
662 u64 input_addr)
663{
664 return dram_addr_to_sys_addr(mci,
665 input_addr_to_dram_addr(mci, input_addr));
666}
667
668/*
669 * Find the minimum and maximum InputAddr values that map to the given @csrow.
670 * Pass back these values in *input_addr_min and *input_addr_max.
671 */
672static void find_csrow_limits(struct mem_ctl_info *mci, int csrow,
673 u64 *input_addr_min, u64 *input_addr_max)
674{
675 struct amd64_pvt *pvt;
676 u64 base, mask;
677
678 pvt = mci->pvt_info;
679 BUG_ON((csrow < 0) || (csrow >= CHIPSELECT_COUNT));
680
681 base = base_from_dct_base(pvt, csrow);
682 mask = mask_from_dct_mask(pvt, csrow);
683
684 *input_addr_min = base & ~mask;
685 *input_addr_max = base | mask | pvt->dcs_mask_notused;
686}
687
688/*
689 * Extract error address from MCA NB Address Low (section 3.6.4.5) and MCA NB
690 * Address High (section 3.6.4.6) register values and return the result. Address
691 * is located in the info structure (nbeah and nbeal), the encoding is device
692 * specific.
693 */
694static u64 extract_error_address(struct mem_ctl_info *mci,
695 struct amd64_error_info_regs *info)
696{
697 struct amd64_pvt *pvt = mci->pvt_info;
698
699 return pvt->ops->get_error_address(mci, info);
700}
701
702
703/* Map the Error address to a PAGE and PAGE OFFSET. */
704static inline void error_address_to_page_and_offset(u64 error_address,
705 u32 *page, u32 *offset)
706{
707 *page = (u32) (error_address >> PAGE_SHIFT);
708 *offset = ((u32) error_address) & ~PAGE_MASK;
709}
710
711/*
712 * @sys_addr is an error address (a SysAddr) extracted from the MCA NB Address
713 * Low (section 3.6.4.5) and MCA NB Address High (section 3.6.4.6) registers
714 * of a node that detected an ECC memory error. mci represents the node that
715 * the error address maps to (possibly different from the node that detected
716 * the error). Return the number of the csrow that sys_addr maps to, or -1 on
717 * error.
718 */
719static int sys_addr_to_csrow(struct mem_ctl_info *mci, u64 sys_addr)
720{
721 int csrow;
722
723 csrow = input_addr_to_csrow(mci, sys_addr_to_input_addr(mci, sys_addr));
724
725 if (csrow == -1)
726 amd64_mc_printk(mci, KERN_ERR,
727 "Failed to translate InputAddr to csrow for "
728 "address 0x%lx\n", (unsigned long)sys_addr);
729 return csrow;
730}
437 731