diff options
author | Francis Rowe <info@gluglug.org.uk> | 2015-10-19 00:12:53 +0100 |
---|---|---|
committer | Francis Rowe <info@gluglug.org.uk> | 2015-10-19 02:32:36 +0100 |
commit | 0622df6194dbb1b2120743c0fd1cc5e72c380128 (patch) | |
tree | 4c858b8c5667fe001a9907ae0578b4ec28a8f513 /resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch | |
parent | 5999dba5f71f1c05040a551d2420ab8c7f3a9da4 (diff) | |
download | librebootfr-0622df6194dbb1b2120743c0fd1cc5e72c380128.tar.gz librebootfr-0622df6194dbb1b2120743c0fd1cc5e72c380128.zip |
KGPE-D16: update patch set (also update coreboot and vboot)
Also contains other fixes from coreboot, like:
* 551cff0 Derive lvds_dual_channel from EDID timings.
^ makes single/dual channel LVDS selection on GM45 automatic
* 26fc544 lenovo/t60: Enable native intel gfx init.
^ was being maintained in libreboot, now upstreamed so not needed
Framebuffer mode was disabled for the KGPE-D16, because only
text-mode works at the moment.
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch')
-rw-r--r-- | resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch | 16206 |
1 files changed, 16206 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch b/resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch new file mode 100644 index 00000000..75aa1952 --- /dev/null +++ b/resources/libreboot/patch/kgpe-d16/0033-cpu-amd-Add-initial-AMD-Family-15h-support.patch @@ -0,0 +1,16206 @@ +From db769f9a54ca4b8a1872c031f29aae31f412e2a2 Mon Sep 17 00:00:00 2001 +From: Timothy Pearson <tpearson@raptorengineeringinc.com> +Date: Fri, 16 Oct 2015 13:51:51 -0500 +Subject: [PATCH 033/139] cpu/amd: Add initial AMD Family 15h support + +TEST: Booted ASUS KGPE-D16 with single Opteron 6380 + * Unbuffered DDR3 DIMMs tested and working + * Suspend to RAM (S3) tested and working + +Conflicts: + + src/cpu/amd/car/disable_cache_as_ram.c + +Change-Id: Idffd2ce36ce183fbfa087e5ba69a9148f084b45e +Signed-off-by: Timothy Pearson <tpearson@raptorengineeringinc.com> +--- + src/cpu/amd/car/cache_as_ram.inc | 130 +- + src/cpu/amd/car/disable_cache_as_ram.c | 77 +- + src/cpu/amd/family_10h-family_15h/defaults.h | 266 +- + src/cpu/amd/family_10h-family_15h/fidvid.c | 235 +- + src/cpu/amd/family_10h-family_15h/init_cpus.c | 232 +- + .../amd/family_10h-family_15h/model_10xxx_init.c | 92 +- + src/cpu/amd/family_10h-family_15h/powernow_acpi.c | 50 +- + src/cpu/amd/family_10h-family_15h/processor_name.c | 194 +- + .../amd/family_10h-family_15h/update_microcode.c | 6 + + src/cpu/amd/quadcore/quadcore.c | 109 +- + src/cpu/amd/quadcore/quadcore_id.c | 43 +- + src/include/cpu/amd/model_10xxx_msr.h | 7 + + src/mainboard/advansus/a785e-i/romstage.c | 2 +- + src/mainboard/amd/bimini_fam10/romstage.c | 2 +- + src/mainboard/amd/mahogany_fam10/romstage.c | 2 +- + .../amd/serengeti_cheetah_fam10/romstage.c | 2 +- + src/mainboard/amd/tilapia_fam10/romstage.c | 2 +- + src/mainboard/asus/kfsn4-dre/romstage.c | 2 +- + src/mainboard/asus/m4a78-em/romstage.c | 2 +- + src/mainboard/asus/m4a785-m/romstage.c | 2 +- + src/mainboard/asus/m5a88-v/romstage.c | 2 +- + src/mainboard/avalue/eax-785e/romstage.c | 2 +- + src/mainboard/gigabyte/ma785gm/romstage.c | 2 +- + src/mainboard/gigabyte/ma785gmt/romstage.c | 2 +- + src/mainboard/gigabyte/ma78gm/romstage.c | 2 +- + src/mainboard/hp/dl165_g6_fam10/romstage.c | 2 +- + src/mainboard/iei/kino-780am2-fam10/romstage.c | 2 +- + src/mainboard/jetway/pa78vm5/romstage.c | 2 +- + src/mainboard/msi/ms9652_fam10/romstage.c | 2 +- + src/mainboard/supermicro/h8dmr_fam10/romstage.c | 2 +- + src/mainboard/supermicro/h8qme_fam10/romstage.c | 2 +- + src/mainboard/supermicro/h8scm_fam10/romstage.c | 2 +- + src/mainboard/tyan/s2912_fam10/romstage.c | 2 +- + src/northbridge/amd/amdfam10/Kconfig | 2 +- + src/northbridge/amd/amdfam10/Makefile.inc | 2 + + src/northbridge/amd/amdfam10/amdfam10.h | 6 +- + src/northbridge/amd/amdfam10/amdfam10_util.c | 13 +- + src/northbridge/amd/amdfam10/link_control.c | 86 + + src/northbridge/amd/amdfam10/misc_control.c | 7 + + src/northbridge/amd/amdfam10/nb_control.c | 85 + + src/northbridge/amd/amdfam10/northbridge.c | 233 +- + src/northbridge/amd/amdfam10/raminit_amdmct.c | 304 +- + src/northbridge/amd/amdht/h3ncmn.c | 171 +- + src/northbridge/amd/amdht/ht_wrapper.c | 43 +- + src/northbridge/amd/amdmct/amddefs.h | 78 +- + src/northbridge/amd/amdmct/mct/mct_d.c | 4 +- + src/northbridge/amd/amdmct/mct/mct_d.h | 20 +- + src/northbridge/amd/amdmct/mct/mctpro_d.c | 21 +- + src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 3187 ++++++++++++++++---- + src/northbridge/amd/amdmct/mct_ddr3/mct_d.h | 124 +- + src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h | 9 + + src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c | 21 +- + src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c | 27 +- + src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c | 1087 ++++++- + src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c | 55 +- + src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c | 7 +- + src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c | 105 +- + src/northbridge/amd/amdmct/mct_ddr3/mctproc.c | 2 +- + src/northbridge/amd/amdmct/mct_ddr3/mctrci.c | 24 +- + src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c | 585 +++- + src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c | 1342 ++++++++- + src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c | 10 +- + src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c | 20 +- + src/northbridge/amd/amdmct/mct_ddr3/mctwl.c | 255 +- + src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c | 1007 +++++-- + src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c | 69 +- + src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h | 46 +- + src/northbridge/amd/amdmct/mct_ddr3/s3utils.c | 652 +++- + src/northbridge/amd/amdmct/wrappers/mcti.h | 14 +- + src/northbridge/amd/amdmct/wrappers/mcti_d.c | 42 +- + 70 files changed, 9184 insertions(+), 2064 deletions(-) + create mode 100644 src/northbridge/amd/amdfam10/link_control.c + create mode 100644 src/northbridge/amd/amdfam10/nb_control.c + +diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc +index 0b2bc60..6542906 100644 +--- a/src/cpu/amd/car/cache_as_ram.inc ++++ b/src/cpu/amd/car/cache_as_ram.inc +@@ -32,18 +32,23 @@ + #define CacheSizeAPStack CONFIG_DCACHE_AP_STACK_SIZE + + #define MSR_MCFG_BASE 0xC0010058 +-#define MSR_FAM10 0xC001102A ++#define MSR_BU_CFG2 0xC001102A + + #define jmp_if_k8(x) comisd %xmm2, %xmm1; jb x ++#define jmp_if_not_fam15h(x) comisd %xmm3, %xmm1; jb x ++#define jmp_if_fam15h(x) comisd %xmm3, %xmm1; jae x + + #define CPUID_MASK 0x0ff00f00 + #define CPUID_VAL_FAM10_ROTATED 0x0f000010 ++#define CPUID_VAL_FAM15_ROTATED 0x0f000060 + + /* + * XMM map: + * xmm1: CPU family + * xmm2: Fam10h comparison value +- * xmm3: Backup EBX ++ * xmm3: Fam15h comparison value ++ * xmm4: Backup EBX ++ * xmm5: Coreboot init detect + */ + + /* Save the BIST result. */ +@@ -63,7 +68,7 @@ cache_as_ram_setup: + movl %eax, %cr4 + + /* Figure out the CPU family. */ +- cvtsi2sd %ebx, %xmm3 ++ cvtsi2sd %ebx, %xmm4 + movl $0x01, %eax + cpuid + /* Base family is bits 8..11, extended family is bits 20..27. */ +@@ -73,13 +78,16 @@ cache_as_ram_setup: + cvtsi2sd %eax, %xmm1 + movl $CPUID_VAL_FAM10_ROTATED, %eax + cvtsi2sd %eax, %xmm2 +- cvtsd2si %xmm3, %ebx ++ movl $CPUID_VAL_FAM15_ROTATED, %eax ++ cvtsi2sd %eax, %xmm3 ++ cvtsd2si %xmm4, %ebx + + /* Check if cpu_init_detected. */ + movl $MTRR_DEF_TYPE_MSR, %ecx + rdmsr + andl $MTRR_DEF_TYPE_EN, %eax + movl %eax, %ebx /* We store the status. */ ++ cvtsi2sd %ebx, %xmm5 + + jmp_if_k8(CAR_FAM10_out_post_errata) + +@@ -120,21 +128,24 @@ cache_as_ram_setup: + + CAR_FAM10_out: + ++ jmp_if_fam15h(CAR_FAM10_errata_applied) + /* + * Errata 193: Disable clean copybacks to L3 cache to allow cached ROM. + * Re-enable it in after RAM is initialized and before CAR is disabled. + */ +- movl $MSR_FAM10, %ecx ++ movl $MSR_BU_CFG2, %ecx + rdmsr +- bts $15, %eax ++ bts $15, %eax /* Set bit 15 in EDX:EAX (bit 15 in EAX). */ + wrmsr + + /* Erratum 343, RevGuide for Fam10h, Pub#41322 Rev. 3.33 */ +- movl $MSR_FAM10, %ecx ++ movl $MSR_BU_CFG2, %ecx + rdmsr + bts $35-32, %edx /* Set bit 35 in EDX:EAX (bit 3 in EDX). */ + wrmsr + ++CAR_FAM10_errata_applied: ++ + #if CONFIG_MMCONF_SUPPORT + #if (CONFIG_MMCONF_BASE_ADDRESS > 0xFFFFFFFF) + #error "MMCONF_BASE_ADDRESS too big" +@@ -169,6 +180,63 @@ CAR_FAM10_out: + + CAR_FAM10_out_post_errata: + ++ /* Fam15h APIC IDs do not depend on NB config bit 54 */ ++ jmp_if_not_fam15h(skip_nb54_set) ++ movl $0xc001001f, %ecx /* NB_CFG_MSR */ ++ rdmsr ++ bts $(54 - 32), %edx /* Set NB config bit 54 */ ++ wrmsr ++ ++skip_nb54_set: ++ /* On Fam15h CPUs each compute unit's MTRRs are shared between two cores */ ++ jmp_if_not_fam15h(skip_cu_check) ++ ++ /* Get the initial APIC ID. */ ++ movl $1, %eax ++ cpuid ++ movl %ebx, %eax ++ ++ /* Restore init detect */ ++ cvtsd2si %xmm5, %ebx ++ ++ /* Determine if this is the second core to start in a compute unit; if so, wait for first core start, clear init detect and skip MTRR init */ ++ bt $24, %eax ++ jnc skip_cu_check /* First core in the compute unit jumps to skip_cu_check */ ++ ++ /* Determine if this is the second core to start in a compute unit; if so, clear init detect and skip MTRR init */ ++ /* Busywait until the first core sets up the MTRRs */ ++check_init_detect_1: ++ /* Check if cpu_init_detected. */ ++ movl $MTRR_DEF_TYPE_MSR, %ecx ++ rdmsr ++ andl $MTRR_DEF_TYPE_EN, %eax ++ cmp $0x00000000, %eax ++ je check_init_detect_1 /* First core has not yet started */ ++ ++check_init_detect_2: ++ movl $SYSCFG_MSR, %ecx ++ rdmsr ++ andl $(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrVarDramEn), %eax ++ cmp $0x00000000, %eax ++ je check_init_detect_2 /* First core has not yet started */ ++ ++ /* First core has now started */ ++ movl $0x00000000, %ebx /* Clear init detect flag */ ++ cvtsi2sd %ebx, %xmm5 ++ jmp fam10_mtrr_setup_complete ++ ++skip_cu_check: ++ ++ jmp_if_not_fam15h(CAR_FAM15_errata_applied) ++ ++ /* Erratum 714, RevGuide for Fam15h, Pub#48063 Rev. 3.24 */ ++ movl $MSR_BU_CFG2, %ecx ++ rdmsr ++ bts $8, %eax /* Set bit 8 in EDX:EAX (bit 8 in EAX). */ ++ wrmsr ++ ++CAR_FAM15_errata_applied: ++ + /* Set MtrrFixDramModEn for clear fixed MTRR. */ + enable_fixed_mtrr_dram_modify: + movl $SYSCFG_MSR, %ecx +@@ -337,8 +405,42 @@ wbcache_post_fam10_setup: + orl $(SYSCFG_MSR_MtrrVarDramEn | SYSCFG_MSR_MtrrFixDramEn), %eax + wrmsr + ++fam10_mtrr_setup_complete: + post_code(0xa1) + ++ /* Disable conversion of INVD to WBINVD (INVDWBINVD = 0) */ ++ mov $0xc0010015, %ecx ++ rdmsr ++ btr $4, %eax ++ wrmsr ++ ++jmp_if_not_fam15h(fam15_car_msr_setup_complete) ++ /* Disable streaming store (DisSS = 1) */ ++ mov $0xc0011020, %ecx ++ rdmsr ++ bts $28, %eax ++ wrmsr ++ ++ /* Disable speculative ITLB reloads (DisSpecTlbRld = 1) */ ++ mov $0xc0011021, %ecx ++ rdmsr ++ bts $9, %eax ++ wrmsr ++ ++ /* Disable speculative DTLB reloads (DisSpecTlbRld = 1) and set DisHwPf = 1 */ ++ mov $0xc0011022, %ecx ++ rdmsr ++ bts $4, %eax ++ bts $13, %eax ++ wrmsr ++ ++ /* Disable CR0 combining (CombineCr0Cd = 0) */ ++ mov $0xc001102b, %ecx ++ rdmsr ++ btr $49-32, %edx ++ wrmsr ++fam15_car_msr_setup_complete: ++ + /* Enable cache. */ + movl %cr0, %eax + andl $(~(CR0_CacheDisable | CR0_NoWriteThrough)), %eax +@@ -393,9 +495,6 @@ CAR_FAM10_ap: + * to reverse it. + */ + +- /* Store our init detected. */ +- movl %ebx, %esi +- + /* Get the coreid bits at first. */ + movl $0x80000008, %eax + cpuid +@@ -414,6 +513,8 @@ CAR_FAM10_ap: + movl %edi, %ecx /* CoreID bits */ + bt $(54 - 32), %edx + jc roll_cfg ++ ++ /* Fam10h NB config bit 54 was not set */ + rolb %cl, %bl + roll_cfg: + +@@ -423,8 +524,8 @@ roll_cfg: + movl $(CacheBase + (CacheSize - (CacheSizeBSPStack + CacheSizeBSPSlush))), %esp + subl %eax, %esp + +- /* Retrive init detected. */ +- movl %esi, %ebx ++ /* Restore init detect */ ++ cvtsd2si %xmm5, %ebx + + post_code(0xa4) + +@@ -437,6 +538,8 @@ CAR_FAM10_ap_out: + andl $~(3 << 9), %eax + movl %eax, %cr4 + ++ post_code(0xa6) ++ + /* Restore the BIST result. */ + movl %ebp, %eax + +@@ -444,6 +547,9 @@ CAR_FAM10_ap_out: + movl %esp, %ebp + pushl %ebx /* Init detected. */ + pushl %eax /* BIST */ ++ ++ post_code(0xa7) ++ + call cache_as_ram_main + + /* We will not go back. */ +diff --git a/src/cpu/amd/car/disable_cache_as_ram.c b/src/cpu/amd/car/disable_cache_as_ram.c +index 5eccf79..5cab544 100644 +--- a/src/cpu/amd/car/disable_cache_as_ram.c ++++ b/src/cpu/amd/car/disable_cache_as_ram.c +@@ -19,7 +19,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc. + * +- * be warned, this file will be used other cores and core 0 / node 0 ++ * WARNING: this file will be used by both any AP cores and core 0 / node 0 + */ + + #include <cpu/x86/cache.h> +@@ -34,41 +34,78 @@ static inline __attribute__((always_inline)) uint32_t amd_fam1x_cpu_family(void) + return family; + } + +-static inline __attribute__((always_inline)) void disable_cache_as_ram(void) ++static inline __attribute__((always_inline)) void disable_cache_as_ram(uint8_t skip_sharedc_config) + { + msr_t msr; ++ uint32_t family; + +- /* disable cache */ +- write_cr0(read_cr0() | CR0_CacheDisable); ++ if (!skip_sharedc_config) { ++ /* disable cache */ ++ write_cr0(read_cr0() | CR0_CacheDisable); + +- msr.lo = 0; +- msr.hi = 0; +- wrmsr(MTRR_FIX_4K_C8000, msr); ++ msr.lo = 0; ++ msr.hi = 0; ++ wrmsr(MTRR_FIX_4K_C8000, msr); + #if CONFIG_DCACHE_RAM_SIZE > 0x8000 +- wrmsr(MTRR_FIX_4K_C0000, msr); ++ wrmsr(MTRR_FIX_4K_C0000, msr); + #endif + #if CONFIG_DCACHE_RAM_SIZE > 0x10000 +- wrmsr(MTRR_FIX_4K_D0000, msr); ++ wrmsr(MTRR_FIX_4K_D0000, msr); + #endif + #if CONFIG_DCACHE_RAM_SIZE > 0x18000 +- wrmsr(MTRR_FIX_4K_D8000, msr); ++ wrmsr(MTRR_FIX_4K_D8000, msr); + #endif +- /* disable fixed mtrr from now on, it will be enabled by ramstage again*/ ++ /* disable fixed mtrr from now on, it will be enabled by ramstage again */ ++ msr = rdmsr(SYSCFG_MSR); ++ msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn); ++ wrmsr(SYSCFG_MSR, msr); ++ ++ /* Set the default memory type and disable fixed and enable variable MTRRs */ ++ msr.hi = 0; ++ msr.lo = (1 << 11); ++ ++ wrmsr(MTRR_DEF_TYPE_MSR, msr); ++ ++ enable_cache(); ++ } ++ ++ /* INVDWBINVD = 1 */ ++ msr = rdmsr(0xc0010015); ++ msr.lo |= (0x1 << 4); ++ wrmsr(0xc0010015, msr); ++ ++ family = amd_fam1x_cpu_family(); ++ ++ if (family >= 0x6f) { ++ /* Family 15h or later */ + +- msr = rdmsr(SYSCFG_MSR); +- msr.lo &= ~(SYSCFG_MSR_MtrrFixDramEn | SYSCFG_MSR_MtrrFixDramModEn); +- wrmsr(SYSCFG_MSR, msr); ++ /* DisSS = 0 */ ++ msr = rdmsr(0xc0011020); ++ msr.lo &= ~(0x1 << 28); ++ wrmsr(0xc0011020, msr); + +- /* Set the default memory type and disable fixed and enable variable MTRRs */ +- msr.hi = 0; +- msr.lo = (1 << 11); ++ if (!skip_sharedc_config) { ++ /* DisSpecTlbRld = 0 */ ++ msr = rdmsr(0xc0011021); ++ msr.lo &= ~(0x1 << 9); ++ wrmsr(0xc0011021, msr); + +- wrmsr(MTRR_DEF_TYPE_MSR, msr); ++ /* Erratum 714: SpecNbReqDis = 0 */ ++ msr = rdmsr(BU_CFG2_MSR); ++ msr.lo &= ~(0x1 << 8); ++ wrmsr(BU_CFG2_MSR, msr); ++ } + +- enable_cache(); ++ /* DisSpecTlbRld = 0 */ ++ /* DisHwPf = 0 */ ++ msr = rdmsr(0xc0011022); ++ msr.lo &= ~(0x1 << 4); ++ msr.lo &= ~(0x1 << 13); ++ wrmsr(0xc0011022, msr); ++ } + } + + static void disable_cache_as_ram_bsp(void) + { +- disable_cache_as_ram(); ++ disable_cache_as_ram(0); + } +diff --git a/src/cpu/amd/family_10h-family_15h/defaults.h b/src/cpu/amd/family_10h-family_15h/defaults.h +index 6fd1a7e..24f87ba 100644 +--- a/src/cpu/amd/family_10h-family_15h/defaults.h ++++ b/src/cpu/amd/family_10h-family_15h/defaults.h +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2008 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -25,41 +26,65 @@ + */ + static const struct { + u32 msr; +- u32 revision; ++ uint64_t revision; + u32 platform; + u32 data_lo; + u32 data_hi; + u32 mask_lo; + u32 mask_hi; + } fam10_msr_default[] = { +- { TOP_MEM2, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { TOP_MEM2, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF }, + +- { SYSCFG, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { SYSCFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 3 << 21, 0x00000000, + 3 << 21, 0x00000000 }, /* [MtrrTom2En]=1,[TOM2EnWB] = 1*/ + +- { HWCR, AMD_FAM10_ALL, AMD_PTYPE_ALL, +- 1 << 4, 0x00000000, +- 1 << 4, 0x00000000 }, /* [INVD_WBINVD]=1 */ ++ { MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL, ++ 1 << 18, 0x00000000, ++ 1 << 18, 0x00000000 }, /* Erratum 586: [DEIBP]=1 */ + +- { MC4_CTL_MASK, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { MC1_CTL_MASK, AMD_OR_B2, AMD_PTYPE_ALL, ++ 1 << 15, 0x00000000, ++ 1 << 15, 0x00000000 }, /* Erratum 593: [BSRP]=1 */ ++ ++ { MC1_CTL_MASK, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 15, 0x00000000, ++ 1 << 15, 0x00000000 }, /* Erratum 739: [BSRP]=1 */ ++ ++ { 0xc0011000, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 1 << 16, 0x00000000, ++ 1 << 16, 0x00000000 }, /* Erratum 608: [bit 16]=1 */ ++ ++ { 0xc0011000, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 15, 0x00000000, ++ 1 << 15, 0x00000000 }, /* Erratum 727: [bit 15]=1 */ ++ ++ { MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0xF << 19, 0x00000000, + 0xF << 19, 0x00000000 }, /* [RtryHt[0..3]]=1 */ + ++ { MC4_CTL_MASK, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 1 << 10, 0x00000000, ++ 1 << 10, 0x00000000 }, /* [GartTblWkEn]=1 */ ++ + { DC_CFG, AMD_FAM10_ALL, AMD_PTYPE_SVR, + 0x00000000, 0x00000004, +- 0x00000000, 0x0000000C }, /* [REQ_CTR] = 1 for Server */ ++ 0x00000000, 0x0000000C }, /* Family 10h: [REQ_CTR] = 1 for Server */ + + { DC_CFG, AMD_DR_Bx, AMD_PTYPE_SVR, + 0x00000000, 0x00000000, + 0x00000000, 0x00000C00 }, /* Erratum 326 */ + +- { NB_CFG, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC, ++ { NB_CFG, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC, + 0x00000000, 1 << 22, + 0x00000000, 1 << 22 }, /* [ApicInitIDLo]=1 */ + ++ { NB_CFG, AMD_FAM15_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC, ++ 1 << 23, 0x00000000, ++ 1 << 23, 0x00000000 }, /* Erratum 663: [bit 23]=1 */ ++ + { BU_CFG2, AMD_DR_Bx, AMD_PTYPE_ALL, + 1 << 29, 0x00000000, + 1 << 29, 0x00000000 }, /* For Bx Smash1GPages=1 */ +@@ -72,6 +97,14 @@ static const struct { + 0 << 1, 0x00000000, + 1 << 1, 0x00000000 }, /* IDX_MATCH_ALL=0 */ + ++ { IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL, ++ 0x00000000, 1 << (39-32), ++ 0x00000000, 1 << (39-32)}, /* C0 or above [DisLoopPredictor]=1 */ ++ ++ { IC_CFG, AMD_OR_C0, AMD_PTYPE_ALL, ++ 0xf << 1, 0x00000000, ++ 0xf << 1, 0x00000000}, /* C0 or above [DisIcWayFilter]=0xf */ ++ + { BU_CFG, AMD_DR_LT_B3, AMD_PTYPE_ALL, + 1 << 21, 0x00000000, + 1 << 21, 0x00000000 }, /* Erratum #254 DR B1 BU_CFG[21]=1 */ +@@ -80,19 +113,51 @@ static const struct { + 1 << 23, 0x00000000, + 1 << 23, 0x00000000 }, /* Erratum #309 BU_CFG[23]=1 */ + ++ { BU_CFG, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0 << 10, 0x00000000, ++ 1 << 10, 0x00000000 }, /* [DcacheAgressivePriority]=0 */ ++ + /* CPUID_EXT_FEATURES */ +- { CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC | AMD_PTYPE_MC, ++ { CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC | AMD_PTYPE_MC, + 1 << 28, 0x00000000, + 1 << 28, 0x00000000 }, /* [HyperThreadFeatEn]=1 */ + +- { CPUIDFEATURES, AMD_FAM10_ALL, AMD_PTYPE_DC, ++ { CPUIDFEATURES, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_DC, + 0x00000000, 1 << (33-32), + 0x00000000, 1 << (33-32) }, /* [ExtendedFeatEn]=1 */ + ++ { DE_CFG, AMD_OR_B2, AMD_PTYPE_ALL, ++ 1 << 10, 0x00000000, ++ 1 << 10, 0x00000000 }, /* Bx [ResyncPredSingleDispDis]=1 */ ++ + { BU_CFG2, AMD_DRBH_Cx, AMD_PTYPE_ALL, + 0x00000000, 1 << (35-32), + 0x00000000, 1 << (35-32) }, /* Erratum 343 (set to 0 after CAR, in post_cache_as_ram()/model_10xxx_init() ) */ + ++ { BU_CFG3, AMD_OR_B2, AMD_PTYPE_ALL, ++ 0x00000000, 1 << (42-32), ++ 0x00000000, 1 << (42-32)}, /* Bx [PwcDisableWalkerSharing]=1 */ ++ ++ { BU_CFG3, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 22, 0x00000000, ++ 1 << 22, 0x00000000}, /* C0 or above [PfcDoubleStride]=1 */ ++ ++ { EX_CFG, AMD_OR_C0, AMD_PTYPE_ALL, ++ 0x00000000, 1 << (54-32), ++ 0x00000000, 1 << (54-32)}, /* C0 or above [LateSbzResync]=1 */ ++ ++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 23, 0x00000000, ++ 1 << 23, 0x00000000}, /* C0 or above [DisScbThreshold]=1 */ ++ ++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 14, 0x00000000, ++ 1 << 14, 0x00000000}, /* C0 or above [ForceSmcCheckFlowStDis]=1 */ ++ ++ { LS_CFG2, AMD_OR_C0, AMD_PTYPE_ALL, ++ 1 << 12, 0x00000000, ++ 1 << 12, 0x00000000}, /* C0 or above [ForceBusLockDis]=1 */ ++ + { OSVW_ID_Length, AMD_DR_Bx | AMD_DR_Cx | AMD_DR_Dx, AMD_PTYPE_ALL, + 0x00000004, 0x00000000, + 0x00000004, 0x00000000}, /* B0 or Above, OSVW_ID_Length is 0004h */ +@@ -105,9 +170,45 @@ static const struct { + 0x00000000, 1 << (50-32), + 0x00000000, 1 << (50-32)}, /* D0 or Above, RdMmExtCfgQwEn*/ + ++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 0x0 << (36-32), ++ 0x00000000, 0x3 << (36-32)}, /* [ThrottleNbInterface]=0 */ ++ ++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 1 << 10, 0x00000000, ++ 1 << 10, 0x00000000}, /* [VicResyncChkEn]=1 */ ++ ++ { BU_CFG2, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 1 << 11, 0x00000000, ++ 1 << 11, 0x00000000}, /* Erratum 503: [bit 11]=1 */ ++ + { CPU_ID_EXT_FEATURES_MSR, AMD_DR_Dx, AMD_PTYPE_ALL, + 0x00000000, 1 << (51 - 32), + 0x00000000, 1 << (51 - 32)}, /* G34_PKG | C32_PKG | S1G4_PKG | ASB2_PKG */ ++ ++ { CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 1 << (56 - 32), ++ 0x00000000, 1 << (56 - 32)}, /* [PerfCtrExtNB]=1 */ ++ ++ { CPU_ID_EXT_FEATURES_MSR, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 1 << (55 - 32), ++ 0x00000000, 1 << (55 - 32)}, /* [PerfCtrExtCore]=1 */ ++ ++ { IBS_OP_DATA3, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0 << 16, 0x00000000, ++ 1 << 16, 0x00000000}, /* [IbsDcMabHit]=0 */ ++ ++ { MC4_MISC0, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 0x1 << (52-32), ++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */ ++ ++ { MC4_MISC1, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 0x1 << (52-32), ++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */ ++ ++ { MC4_MISC2, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000000, 0x1 << (52-32), ++ 0x00000000, 0xf << (52-32)}, /* [LvtOffset]=1 */ + }; + + +@@ -117,37 +218,46 @@ static const struct { + static const struct { + u8 function; + u16 offset; +- u32 revision; ++ uint64_t revision; + u32 platform; + u32 data; + u32 mask; + } fam10_pci_default[] = { + + /* Function 0 - HT Config */ ++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 0x000e0000, 0x000e0000 }, /* [19:17] for 8bit APIC config */ ++ ++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 0x00400000, 0x00600000 }, /* [22:21] DsNpReqLmt = 10b */ + +- { 0, 0x68, AMD_FAM10_ALL, AMD_PTYPE_ALL, +- 0x004E4800, 0x006E6800 }, /* [19:17] for 8bit APIC config, +- [14:13] BufPriRel = 2h [11] RspPassPW set, +- [22:21] DsNpReqLmt = 10b */ ++ { 0, 0x68, AMD_FAM10_LT_D, AMD_PTYPE_ALL, ++ 0x00004000, 0x00006000 }, /* [14:13] BufRelPri = 2h */ ++ ++ { 0, 0x68, (AMD_FAM10_REV_D | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 0x00002000, 0x00006000 }, /* [14:13] BufRelPri = 1h */ ++ ++ { 0, 0x68, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 0x00000800, 0x00000800 }, /* [11] RspPassPW = 1 */ + + /* Errata 281 Workaround */ + { 0, 0x68, (AMD_DR_B0 | AMD_DR_B1), + AMD_PTYPE_SVR, 0x00200000, 0x00600000 }, /* [22:21] DsNpReqLmt0 = 01b */ + +- { 0, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 0, 0x84, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */ + +- { 0, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 0, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */ + +- { 0, 0xC4, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 0, 0xC4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */ + +- { 0, 0xE4, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 0, 0xE4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */ + + /* Link Global Retry Control Register */ +- { 0, 0x150, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00073900, 0x00073F00 }, + + /* Errata 351 +@@ -172,13 +282,39 @@ static const struct { + 0x00000000, 0x00000100 }, + { 0, 0x18C, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0x00000000, 0x00000100 }, +- { 0, 0x170, AMD_FAM10_ALL, AMD_PTYPE_ALL, +- 0x00000000, 0x00000100 }, + + /* Link Global Extended Control Register */ + { 0, 0x16C, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0x00000014, 0x0000003F }, /* [15:13] ForceFullT0 = 0b, +- * Set T0Time 14h per BKDG */ ++ * Set T0Time 14h per BKDG */ ++ ++ { 0, 0x170, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x174, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x178, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x17C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x180, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x184, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x188, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ { 0, 0x18C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000100, 0x00000100 }, ++ ++ /* Link Global Extended Control Register */ ++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000014, 0x0000003F }, /* [15:13] ForceFullT0 = 111b, ++ * Set T0Time 26h per BKDG */ ++ ++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x7 << 13, 0x7 << 13 }, /* [15:13] ForceFullT0 = 7h */ ++ ++ { 0, 0x16C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x26, 0x3f }, /* [5:0] T0Time = 26h */ + + + /* Function 1 - Map Init */ +@@ -205,10 +341,10 @@ static const struct { + /* Function 2 - DRAM Controller */ + + /* Function 3 - Misc. Control */ +- { 3, 0x40, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0x40, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00000100, 0x00000100 }, /* [8] MstrAbrtEn */ + +- { 3, 0x44, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0x44, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x4A30005C, 0x4A30005C }, /* [30] SyncOnDramAdrParErrEn = 1, + [27] NbMcaToMstCpuEn = 1, + [25] DisPciCfgCpuErrRsp = 1, +@@ -220,8 +356,12 @@ static const struct { + [2] SyncOnUcEccEn = 1 */ + + /* XBAR buffer settings */ +- { 3, 0x6C, AMD_FAM10_ALL, AMD_PTYPE_ALL, +- 0x00018052, 0x700780F7 }, ++ { 3, 0x6c, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ 0x00018052, 0x700780f7 }, ++ ++ /* XBAR buffer settings */ ++ { 3, 0x6c, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x10010052, 0x700700f7 }, + + /* Errata 281 Workaround */ + { 3, 0x6C, ( AMD_DR_B0 | AMD_DR_B1), +@@ -233,12 +373,18 @@ static const struct { + { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0x00041153, 0x777777F7 }, + ++ { 3, 0x70, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x10171155, 0x777777f7 }, ++ + { 3, 0x70, AMD_FAM10_ALL, AMD_PTYPE_UMA, + 0x61221151, 0x777777F7 }, + + { 3, 0x74, AMD_FAM10_ALL, AMD_PTYPE_UMA, + 0x00080101, 0x000F7777 }, + ++ { 3, 0x74, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00172111, 0x77ff7777 }, ++ + { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0x00090914, 0x707FFF1F }, + +@@ -246,12 +392,18 @@ static const struct { + { 3, 0x7C, ( AMD_DR_B0 | AMD_DR_B1), + AMD_PTYPE_SVR, 0x00144514, 0x707FFF1F }, + ++ { 3, 0x7C, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x040d0f16, 0x07ffff1f }, ++ + { 3, 0x7C, AMD_FAM10_ALL, AMD_PTYPE_UMA, + 0x00070814, 0x007FFF1F }, + + { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0x00800756, 0x00F3FFFF }, + ++ { 3, 0x140, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00a11755, 0x00f3ffff }, ++ + { 3, 0x140, AMD_FAM10_ALL, AMD_PTYPE_UMA, + 0x00C37756, 0x00F3FFFF }, + +@@ -263,6 +415,9 @@ static const struct { + AMD_PTYPE_SVR, 0x00000001, 0x0000000F }, + /* [3:0] RspTok = 0001b */ + ++ { 3, 0x144, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x00000028, 0x000000ff }, ++ + { 3, 0x148, AMD_FAM10_ALL, AMD_PTYPE_UMA, + 0x8000052A, 0xD5FFFFFF }, + +@@ -270,41 +425,53 @@ static const struct { + { 3, 0x80, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0xE6002200, 0xFFFFFFFF }, + ++ /* ACPI Power State Control Reg1 */ ++ { 3, 0x80, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0xe20be200, 0xefefef00 }, ++ + /* ACPI Power State Control Reg2 */ + { 3, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL, + 0xA0E641E6, 0xFFFFFFFF }, + ++ /* ACPI Power State Control Reg2 */ ++ { 3, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL, ++ 0x01e200e2, 0xefef00ef }, ++ + { 3, 0xA0, AMD_FAM10_ALL, AMD_PTYPE_MOB | AMD_PTYPE_DSK, + 0x00000080, 0x00000080 }, /* [7] PSIVidEnable */ + + { 3, 0xA0, AMD_DR_Bx, AMD_PTYPE_ALL, + 0x00002800, 0x000003800 }, /* [13:11] PllLockTime = 5 */ + +- { 3, 0xA0, (AMD_FAM10_ALL & ~(AMD_DR_Bx)), AMD_PTYPE_ALL, ++ { 3, 0xA0, ((AMD_FAM10_ALL | AMD_FAM15_ALL) & ~(AMD_DR_Bx)), AMD_PTYPE_ALL, + 0x00000800, 0x000003800 }, /* [13:11] PllLockTime = 1 */ + + /* Reported Temp Control Register */ +- { 3, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00000080, 0x00000080 }, /* [7] TempSlewDnEn = 1 */ + + /* Clock Power/Timing Control 0 Register */ +- { 3, 0xD4, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0xD4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0xC0000F00, 0xF0000F00 }, /* [31] NbClkDivApplyAll = 1, + [30:28] NbClkDiv = 100b,[11:8] ClkRampHystSel = 1111b */ + + /* Clock Power/Timing Control 1 Register */ ++ { 3, 0xD8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, ++ 0x03000010, 0x0F000070 }, /* [6:4] VSRampTime = 1, ++ * [27:24] ReConDel = 3 */ ++ ++ /* Clock Power/Timing Control 1 Register */ + { 3, 0xD8, AMD_FAM10_ALL, AMD_PTYPE_ALL, +- 0x03000016, 0x0F000077 }, /* [6:4] VSRampTime = 1, +- [2:0] VSSlamTime = 6, [27:24] ReConDel = 3 */ ++ 0x00000006, 0x00000007 }, /* [2:0] VSSlamTime = 6 */ + + + /* Clock Power/Timing Control 2 Register */ +- { 3, 0xDC, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0xDC, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00005000, 0x00007000 }, /* [14:12] NbsynPtrAdj = 5 */ + + + /* Extended NB MCA Config Register */ +- { 3, 0x180, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0x180, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x007003E2, 0x007003E2 }, /* [22:20] = SyncFloodOn_Err = 7, + [9] SyncOnUncNbAryEn = 1 , + [8] SyncOnProtEn = 1, +@@ -319,12 +486,17 @@ static const struct { + 0x00400000, 0x00400000 }, + + /* L3 Control Register */ +- { 3, 0x1B8, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0x1b8, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00001000, 0x00001000 }, /* [12] = L3PrivReplEn */ + + /* IBS Control Register */ +- { 3, 0x1CC, AMD_FAM10_ALL, AMD_PTYPE_ALL, ++ { 3, 0x1cc, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, + 0x00000100, 0x00000100 }, /* [8] = LvtOffsetVal */ ++ ++ /* Erratum 619 - Family 15h Bx ++ * System software should set F5x88[14] to 1b. */ ++ { 5, 0x88, AMD_OR_B2, AMD_PTYPE_ALL, ++ 1 << 14, 1 << 14 }, + }; + + +@@ -333,7 +505,7 @@ static const struct { + */ + static const struct { + u16 htreg; /* HT Phy Register index */ +- u32 revision; ++ uint64_t revision; + u32 platform; + u32 linktype; + u32 data; +@@ -442,38 +614,38 @@ static const struct { + { 0x530A, AMD_DR_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL, + 0x00004400, 0x00006400 }, /* HT_PHY_DLL_REG */ + +- { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, ++ { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, + 0x00000000, 0x000000FF }, /* Provide clear setting for logical + completeness */ + +- { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, ++ { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, + 0x00000000, 0x000000FF }, /* Provide clear setting for logical + completeness */ + +- { 0xCF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, ++ { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, + 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */ + +- { 0xDF, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, ++ { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, + 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */ + + /* Link Phy Receiver Loop Filter Registers */ +- { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, ++ { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, + 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h, + [21:14] LfcMin = 10h */ + +- { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, ++ { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3, + 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h, + [21:14] LfcMin = 10h */ + +- { 0xD1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, ++ { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, + 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h, + [21:14] LfcMin = 08h */ + +- { 0xC1, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, ++ { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1, + 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h, + [21:14] LfcMin = 08h */ + +- { 0xC0, AMD_FAM10_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL, ++ { 0xC0, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL, + 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h, + [20:16] RttIndex = 04h */ + }; +diff --git a/src/cpu/amd/family_10h-family_15h/fidvid.c b/src/cpu/amd/family_10h-family_15h/fidvid.c +index 99ffcc8..2e26645 100644 +--- a/src/cpu/amd/family_10h-family_15h/fidvid.c ++++ b/src/cpu/amd/family_10h-family_15h/fidvid.c +@@ -44,7 +44,7 @@ Fam10 Bios and Kernel Development Guide #31116, rev 3.48, April 22, 2010 + + 3.- 2.4.2.7 dualPlaneOnly(dev) + +-4.- 2.4.2.8 applyBoostFIDOffset(dev) ++4.- 2.4.2.8 applyBoostFIDOffset(dev, nodeid) + + 5.- enableNbPState1(dev) + +@@ -143,25 +143,33 @@ static void enable_fid_change(u8 fid) + } + } + +-static void applyBoostFIDOffset( device_t dev ) { +- // BKDG 2.4.2.8 +- // revision E only, but E is apparently not supported yet, therefore untested +- if ((cpuid_edx(0x80000007) & CPB_MASK) +- && ((cpuid_ecx(0x80000008) & NC_MASK) ==5) ) { +- u32 core = get_node_core_id_x().coreid; +- u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3; +- msr_t msr = rdmsr(PS_REG_BASE); +- u32 cpuFid = msr.lo & PS_CPU_FID_MASK; +- cpuFid = cpuFid + asymetricBoostThisCore; +- msr.lo &= ~PS_CPU_FID_MASK; +- msr.lo |= cpuFid ; +- wrmsr(PS_REG_BASE , msr); +- +- } ++static void applyBoostFIDOffset(device_t dev, uint32_t nodeid) { ++ // BKDG 2.4.2.8 ++ // Fam10h revision E only, but E is apparently not supported yet, therefore untested ++ if ((cpuid_edx(0x80000007) & CPB_MASK) ++ && ((cpuid_ecx(0x80000008) & NC_MASK) == 5) ) { ++ u32 core = get_node_core_id_x().coreid; ++ u32 asymetricBoostThisCore = ((pci_read_config32(dev, 0x10C) >> (core*2))) & 3; ++ msr_t msr = rdmsr(PS_REG_BASE); ++ u32 cpuFid = msr.lo & PS_CPU_FID_MASK; ++ cpuFid = cpuFid + asymetricBoostThisCore; ++ msr.lo &= ~PS_CPU_FID_MASK; ++ msr.lo |= cpuFid ; ++ wrmsr(PS_REG_BASE , msr); ++ } else if (is_fam15h()) { ++ uint32_t dword = pci_read_config32(NODE_PCI(nodeid, 4), 0x15c); ++ uint8_t boost_count = (dword >> 2) & 0x7; ++ if (boost_count > 0) { ++ /* Enable boost */ ++ dword &= ~0x3; ++ dword |= 0x1; ++ pci_write_config32(NODE_PCI(nodeid, 4), 0x15c, dword); ++ } ++ } + } + + static void enableNbPState1( device_t dev ) { +- u32 cpuRev = mctGetLogicalCPUID(0xFF); ++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF); + if (cpuRev & AMD_FAM10_C3) { + u32 nbPState = (pci_read_config32(dev, 0x1F0) & NB_PSTATE_MASK); + if ( nbPState){ +@@ -203,7 +211,7 @@ static u8 setPStateMaxVal( device_t dev ) { + static void dualPlaneOnly( device_t dev ) { + // BKDG 2.4.2.7 + +- u32 cpuRev = mctGetLogicalCPUID(0xFF); ++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF); + if ((mctGetProcessorPackageType() == AMD_PKGTYPE_AM3_2r2) + && (cpuRev & AMD_DR_Cx)) { // should be rev C or rev E but there's no constant for E + if ( (pci_read_config32(dev, 0x1FC) & DUAL_PLANE_ONLY_MASK) +@@ -283,12 +291,16 @@ static void recalculateVsSlamTimeSettingOnCorePre(device_t dev) + */ + + /* Determine if this is a PVI or SVI system */ +- dtemp = pci_read_config32(dev, 0xA0); +- +- if (dtemp & PVI_MODE) +- pviModeFlag = 1; +- else ++ if (is_fam15h()) { + pviModeFlag = 0; ++ } else { ++ dtemp = pci_read_config32(dev, 0xa0); ++ ++ if (dtemp & PVI_MODE) ++ pviModeFlag = 1; ++ else ++ pviModeFlag = 0; ++ } + + /* Get P0's voltage */ + /* MSRC001_00[68:64] are not programmed yet when called from +@@ -515,59 +527,67 @@ static void config_nb_syn_ptr_adj(device_t dev, u32 cpuRev) { + } + + static void config_acpi_pwr_state_ctrl_regs(device_t dev, u32 cpuRev, u8 procPkg) { +- /* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */ +- u32 dword; +- u32 c1= 1; +- if (cpuRev & (AMD_DR_Bx)) { +- // will coreboot ever enable cache scrubbing ? +- // if it does, will it be enough to check the current state +- // or should we configure for what we'll set up later ? +- dword = pci_read_config32(dev, 0x58); +- u32 scrubbingCache = dword & +- ( (0x1F << 16) // DCacheScrub +- | (0x1F << 8) ); // L2Scrub +- if (scrubbingCache) { +- c1 = 0x80; +- } else { +- c1 = 0xA0; +- } +- } else { // rev C or later +- // same doubt as cache scrubbing: ok to check current state ? +- dword = pci_read_config32(dev, 0xDC); +- u32 cacheFlushOnHalt = dword & (7 << 16); +- if (!cacheFlushOnHalt) { +- c1 = 0x80; +- } +- } +- dword = (c1 << 24) | (0xE641E6); +- pci_write_config32(dev, 0x84, dword); +- +- +- /* FIXME: BKDG Table 100 says if the link is at a Gen1 +-frequency and the chipset does not support a 10us minimum LDTSTOP +-assertion time, then { If ASB2 && SVI then smaf001 = F6h else +-smaf001=87h. } else ... I hardly know what it means or how to check +-it from here, so I bluntly assume it is false and code here the else, +-which is easier */ +- +- u32 smaf001 = 0xE6; +- if (cpuRev & AMD_DR_Bx ) { +- smaf001 = 0xA6; +- } else { +- #if CONFIG_SVI_HIGH_FREQ +- if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) { +- smaf001 = 0xF6; +- } +- #endif +- } +- u32 fidvidChange = 0; +- if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX)) +- || (cpuRev & AMD_RB_C3) ) { +- fidvidChange=0x0B; +- } +- dword = (0xE6 << 24) | (fidvidChange << 16) +- | (smaf001 << 8) | 0x81; +- pci_write_config32(dev, 0x80, dword); ++ if (is_fam15h()) { ++ /* Family 15h BKDG Rev. 3.14 D18F3x80 recommended settings */ ++ pci_write_config32(dev, 0x80, 0xe20be281); ++ ++ /* Family 15h BKDG Rev. 3.14 D18F3x84 recommended settings */ ++ pci_write_config32(dev, 0x84, 0x01e200e2); ++ } else { ++ /* step 1, chapter 2.4.2.6 of AMD Fam 10 BKDG #31116 Rev 3.48 22.4.2010 */ ++ u32 dword; ++ u32 c1= 1; ++ if (cpuRev & (AMD_DR_Bx)) { ++ // will coreboot ever enable cache scrubbing ? ++ // if it does, will it be enough to check the current state ++ // or should we configure for what we'll set up later ? ++ dword = pci_read_config32(dev, 0x58); ++ u32 scrubbingCache = dword & ++ ( (0x1F << 16) // DCacheScrub ++ | (0x1F << 8) ); // L2Scrub ++ if (scrubbingCache) { ++ c1 = 0x80; ++ } else { ++ c1 = 0xA0; ++ } ++ } else { // rev C or later ++ // same doubt as cache scrubbing: ok to check current state ? ++ dword = pci_read_config32(dev, 0xDC); ++ u32 cacheFlushOnHalt = dword & (7 << 16); ++ if (!cacheFlushOnHalt) { ++ c1 = 0x80; ++ } ++ } ++ dword = (c1 << 24) | (0xE641E6); ++ pci_write_config32(dev, 0x84, dword); ++ ++ /* FIXME: BKDG Table 100 says if the link is at a Gen1 ++ * frequency and the chipset does not support a 10us minimum LDTSTOP ++ * assertion time, then { If ASB2 && SVI then smaf001 = F6h else ++ * smaf001=87h. } else ... I hardly know what it means or how to check ++ * it from here, so I bluntly assume it is false and code here the else, ++ * which is easier ++ */ ++ ++ u32 smaf001 = 0xE6; ++ if (cpuRev & AMD_DR_Bx ) { ++ smaf001 = 0xA6; ++ } else { ++ #if CONFIG_SVI_HIGH_FREQ ++ if (cpuRev & (AMD_RB_C3 | AMD_DA_C3)) { ++ smaf001 = 0xF6; ++ } ++ #endif ++ } ++ u32 fidvidChange = 0; ++ if (((cpuRev & AMD_DA_Cx) && (procPkg & AMD_PKGTYPE_S1gX)) ++ || (cpuRev & AMD_RB_C3) ) { ++ fidvidChange=0x0B; ++ } ++ dword = (0xE6 << 24) | (fidvidChange << 16) ++ | (smaf001 << 8) | 0x81; ++ pci_write_config32(dev, 0x80, dword); ++ } + } + + static void prep_fid_change(void) +@@ -584,7 +604,7 @@ static void prep_fid_change(void) + for (i = 0; i < nodes; i++) { + printk(BIOS_DEBUG, "Prep FID/VID Node:%02x\n", i); + dev = NODE_PCI(i, 3); +- u32 cpuRev = mctGetLogicalCPUID(0xFF) ; ++ uint64_t cpuRev = mctGetLogicalCPUID(0xFF) ; + u8 procPkg = mctGetProcessorPackageType(); + + setVSRamp(dev); +@@ -612,7 +632,7 @@ static void prep_fid_change(void) + } + } + +-static void waitCurrentPstate(u32 target_pstate){ ++static void waitCurrentPstate(u32 target_pstate) { + msr_t initial_msr = rdmsr(TSC_MSR); + msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR); + msr_t tsc_msr; +@@ -645,7 +665,7 @@ static void waitCurrentPstate(u32 target_pstate){ + + if (pstate_msr.lo != target_pstate) { + msr_t limit_msr = rdmsr(0xc0010061); +- printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%02x\n", target_pstate, pstate_msr.lo, limit_msr.lo); ++ printk(BIOS_ERR, "*** Time out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n", target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo); + + do { // should we just go on instead ? + pstate_msr = rdmsr(CUR_PSTATE_MSR); +@@ -655,6 +675,7 @@ static void waitCurrentPstate(u32 target_pstate){ + + static void set_pstate(u32 nonBoostedPState) { + msr_t msr; ++ uint8_t skip_wait; + + // Transition P0 for calling core. + msr = rdmsr(0xC0010062); +@@ -662,12 +683,21 @@ static void set_pstate(u32 nonBoostedPState) { + msr.lo = nonBoostedPState; + wrmsr(0xC0010062, msr); + +- /* Wait for P0 to set. */ +- waitCurrentPstate(nonBoostedPState); +-} +- +- ++ if (is_fam15h()) { ++ /* Do not wait for the first (even) set of cores to transition on Family 15h systems */ ++ if ((cpuid_ebx(0x00000001) & 0x01000000)) ++ skip_wait = 0; ++ else ++ skip_wait = 1; ++ } else { ++ skip_wait = 0; ++ } + ++ if (!skip_wait) { ++ /* Wait for core to transition to P0 */ ++ waitCurrentPstate(nonBoostedPState); ++ } ++} + + static void UpdateSinglePlaneNbVid(void) + { +@@ -757,11 +787,14 @@ static u32 needs_NB_COF_VID_update(void) + u8 nodes; + u8 i; + ++ if (is_fam15h()) ++ return 0; ++ + /* If any node has nb_cof_vid_update set all nodes need an update. */ + nodes = get_nodes(); + nb_cof_vid_update = 0; + for (i = 0; i < nodes; i++) { +- u32 cpuRev = mctGetLogicalCPUID(i) ; ++ uint64_t cpuRev = mctGetLogicalCPUID(i); + u32 nbCofVidUpdateDefined = (cpuRev & (AMD_FAM10_LT_D)); + if (nbCofVidUpdateDefined + && (pci_read_config32(NODE_PCI(i, 3), 0x1FC) +@@ -785,9 +818,11 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid) + /* Steps 1-6 of BIOS NB COF and VID Configuration + * for SVI and Single-Plane PVI Systems. BKDG 2.4.2.9 #31116 rev 3.48 + */ +- + dev = NODE_PCI(nodeid, 3); +- pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE; ++ if (is_fam15h()) ++ pvimode = 0; ++ else ++ pvimode = pci_read_config32(dev, PW_CTL_MISC) & PVI_MODE; + reg1fc = pci_read_config32(dev, 0x1FC); + + if (nb_cof_vid_update) { +@@ -799,7 +834,7 @@ static u32 init_fidvid_core(u32 nodeid, u32 coreid) + fid_max = fid_max + ((reg1fc & DUAL_PLANE_NB_FID_OFF_MASK ) >> DUAL_PLANE_NB_FID_SHIFT ); + } + /* write newNbVid to P-state Reg's NbVid always if NbVidUpdatedAll=1 */ +- fixPsNbVidBeforeWR(vid_max, coreid,dev,pvimode); ++ fixPsNbVidBeforeWR(vid_max, coreid, dev, pvimode); + + /* fid setup is handled by the BSP at the end. */ + +@@ -819,7 +854,7 @@ static void init_fidvid_ap(u32 apicid, u32 nodeid, u32 coreid) + + printk(BIOS_DEBUG, "FIDVID on AP: %02x\n", apicid); + +- send = init_fidvid_core(nodeid,coreid); ++ send = init_fidvid_core(nodeid, coreid); + send |= (apicid << 24); // ap apicid + + // Send signal to BSP about this AP max fid +@@ -861,7 +896,7 @@ static void init_fidvid_bsp_stage1(u32 ap_apicid, void *gp) + while (--loop > 0) { + if (lapic_remote_read(ap_apicid, LAPIC_MSG_REG, &readback) != 0) + continue; +- if ((readback & 0x3f) == 1) { ++ if (((readback & 0x3f) == 1) || ((readback & 0x3f) == F10_APSTATE_ASLEEP)) { + timeout = 0; + break; /* target ap is in stage 1 */ + } +@@ -949,7 +984,10 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid) + /* If any node has nb_cof_vid_update set all nodes need an update. */ + + dev = NODE_PCI(nodeid, 3); +- pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1; ++ if (is_fam15h()) ++ pvimode = 0; ++ else ++ pvimode = (pci_read_config32(dev, 0xA0) >> 8) & 1; + reg1fc = pci_read_config32(dev, 0x1FC); + nbvid = (reg1fc >> 7) & 0x7F; + NbVidUpdateAll = (reg1fc >> 1) & 1; +@@ -970,15 +1008,17 @@ static void init_fidvid_stage2(u32 apicid, u32 nodeid) + pci_write_config32(dev, 0xA0, dtemp); + + dualPlaneOnly(dev); +- applyBoostFIDOffset(dev); ++ applyBoostFIDOffset(dev, nodeid); + enableNbPState1(dev); + + finalPstateChange(); + +- /* Set TSC to tick at the P0 ndfid rate */ +- msr = rdmsr(HWCR); +- msr.lo |= 1 << 24; +- wrmsr(HWCR, msr); ++ if (!is_fam15h()) { ++ /* Set TSC to tick at the P0 ndfid rate */ ++ msr = rdmsr(HWCR); ++ msr.lo |= 1 << 24; ++ wrmsr(HWCR, msr); ++ } + } + + +@@ -1012,8 +1052,7 @@ static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes) + /* Steps 1-6 of BIOS NB COF and VID Configuration + * for SVI and Single-Plane PVI Systems. + */ +- +- fv.common_fid = init_fidvid_core(0,0); ++ fv.common_fid = init_fidvid_core(0, 0); + + print_debug_fv("BSP fid = ", fv.common_fid); + +diff --git a/src/cpu/amd/family_10h-family_15h/init_cpus.c b/src/cpu/amd/family_10h-family_15h/init_cpus.c +index 8de6d25..aced850 100644 +--- a/src/cpu/amd/family_10h-family_15h/init_cpus.c ++++ b/src/cpu/amd/family_10h-family_15h/init_cpus.c +@@ -30,9 +30,12 @@ + #include <northbridge/amd/amdfam10/raminit_amdmct.c> + #include <reset.h> + ++#if IS_ENABLED(CONFIG_SET_FIDVID) + static void prep_fid_change(void); + static void init_fidvid_stage2(u32 apicid, u32 nodeid); +-void cpuSetAMDMSR(void); ++#endif ++ ++void cpuSetAMDMSR(uint8_t node_id); + + #if CONFIG_PCI_IO_CFG_EXT + static void set_EnableCf8ExtCfg(void) +@@ -51,43 +54,38 @@ static void set_EnableCf8ExtCfg(void) { } + + typedef void (*process_ap_t) (u32 apicid, void *gp); + +-//core_range = 0 : all cores +-//core range = 1 : core 0 only +-//core range = 2 : cores other than core0 ++uint32_t get_boot_apic_id(uint8_t node, uint32_t core) { ++ uint32_t ap_apicid; + +-static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap, +- void *gp) +-{ +- // here assume the OS don't change our apicid +- u32 ap_apicid; ++ uint32_t nb_cfg_54; ++ uint32_t siblings; ++ uint32_t cores_found; + +- u32 nodes; +- u32 siblings; +- u32 disable_siblings; +- u32 cores_found; +- u32 nb_cfg_54; +- int i, j; +- u32 ApicIdCoreIdSize; ++ uint8_t fam15h = 0; + uint8_t rev_gte_d = 0; + uint8_t dual_node = 0; + uint32_t f3xe8; ++ uint32_t family; ++ uint32_t model; + +- /* get_nodes define in ht_wrapper.c */ +- nodes = get_nodes(); +- +- if (!CONFIG_LOGICAL_CPUS || +- read_option(multi_core, 0) != 0) { // 0 means multi core +- disable_siblings = 1; +- } else { +- disable_siblings = 0; +- } ++ uint32_t ApicIdCoreIdSize; + + /* Assume that all node are same stepping, otherwise we can use use + nb_cfg_54 from bsp for all nodes */ + nb_cfg_54 = read_nb_cfg_54(); + f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8); + +- if (cpuid_eax(0x80000001) >= 0x8) ++ family = model = cpuid_eax(0x80000001); ++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) { ++ /* Family 15h or later */ ++ fam15h = 1; ++ nb_cfg_54 = 1; ++ } ++ ++ if ((model >= 0x8) || fam15h) + /* Revision D or later */ + rev_gte_d = 1; + +@@ -103,10 +101,63 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap, + siblings = 3; //quad core + } + ++ cores_found = get_core_num_in_bsp(node); ++ if (siblings > cores_found) ++ siblings = cores_found; ++ ++ if (dual_node) { ++ ap_apicid = 0; ++ if (fam15h) { ++ ap_apicid |= ((node >> 1) & 0x3) << 5; /* Node ID */ ++ ap_apicid |= ((node & 0x1) * (siblings + 1)) + core; /* Core ID */ ++ } else { ++ if (nb_cfg_54) { ++ ap_apicid |= ((node >> 1) & 0x3) << 4; /* Node ID */ ++ ap_apicid |= ((node & 0x1) * (siblings + 1)) + core; /* Core ID */ ++ } else { ++ ap_apicid |= node & 0x3; /* Node ID */ ++ ap_apicid |= (((node & 0x1) * (siblings + 1)) + core) << 4; /* Core ID */ ++ } ++ } ++ } else { ++ if (fam15h) { ++ ap_apicid = (node * (siblings + 1)) + core; ++ } else { ++ ap_apicid = node * (nb_cfg_54 ? (siblings + 1) : 1) + ++ core * (nb_cfg_54 ? 1 : 64); ++ } ++ } ++ ++ return ap_apicid; ++} ++ ++//core_range = 0 : all cores ++//core range = 1 : core 0 only ++//core range = 2 : cores other than core0 ++ ++static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap, ++ void *gp) ++{ ++ // here assume the OS don't change our apicid ++ u32 ap_apicid; ++ ++ u32 nodes; ++ u32 disable_siblings; ++ u32 cores_found; ++ int i, j; ++ ++ /* get_nodes define in ht_wrapper.c */ ++ nodes = get_nodes(); ++ ++ if (!CONFIG_LOGICAL_CPUS || ++ read_option(multi_core, 0) != 0) { // 0 means multi core ++ disable_siblings = 1; ++ } else { ++ disable_siblings = 0; ++ } ++ + for (i = 0; i < nodes; i++) { + cores_found = get_core_num_in_bsp(i); +- if (siblings > cores_found) +- siblings = cores_found; + + u32 jstart, jend; + +@@ -123,21 +174,7 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap, + } + + for (j = jstart; j <= jend; j++) { +- if (dual_node) { +- ap_apicid = 0; +- if (nb_cfg_54) { +- ap_apicid |= ((i >> 1) & 0x3) << 4; /* Node ID */ +- ap_apicid |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */ +- } else { +- ap_apicid |= i & 0x3; /* Node ID */ +- ap_apicid |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */ +- } +- } else { +- ap_apicid = +- i * (nb_cfg_54 ? (siblings + 1) : 1) + +- j * (nb_cfg_54 ? 1 : 64); +- } +- ++ ap_apicid = get_boot_apic_id(i, j); + + #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET > 0) + #if !CONFIG_LIFT_BSP_APIC_ID +@@ -197,7 +234,7 @@ void print_apicid_nodeid_coreid(u32 apicid, struct node_core_id id, + apicid, id.nodeid, id.coreid); + } + +-static u32 wait_cpu_state(u32 apicid, u32 state) ++uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2) + { + u32 readback = 0; + u32 timeout = 1; +@@ -205,7 +242,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state) + while (--loop > 0) { + if (lapic_remote_read(apicid, LAPIC_MSG_REG, &readback) != 0) + continue; +- if ((readback & 0x3f) == state || (readback & 0x3f) == F10_APSTATE_RESET) { ++ if ((readback & 0x3f) == state || (readback & 0x3f) == state2 || (readback & 0x3f) == F10_APSTATE_RESET) { + timeout = 0; + break; //target cpu is in stage started + } +@@ -222,7 +259,7 @@ static u32 wait_cpu_state(u32 apicid, u32 state) + static void wait_ap_started(u32 ap_apicid, void *gp) + { + u32 timeout; +- timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED); ++ timeout = wait_cpu_state(ap_apicid, F10_APSTATE_STARTED, F10_APSTATE_ASLEEP); + printk(BIOS_DEBUG, "* AP %02x", ap_apicid); + if (timeout) { + printk(BIOS_DEBUG, " timed out:%08x\n", timeout); +@@ -258,16 +295,27 @@ static void enable_apic_ext_id(u32 node) + pci_write_config32(NODE_HT(node), 0x68, val); + } + +-static void STOP_CAR_AND_CPU(void) ++static void STOP_CAR_AND_CPU(uint8_t skip_sharedc_config, uint32_t apicid) + { + msr_t msr; ++ uint32_t family; ++ ++ family = amd_fam1x_cpu_family(); // inline ++ ++ if (family < 0x6f) { ++ /* Family 10h or earlier */ ++ ++ /* Disable L2 IC to L3 connection (Only for CAR) */ ++ msr = rdmsr(BU_CFG2); ++ msr.lo &= ~(1 << ClLinesToNbDis); ++ wrmsr(BU_CFG2, msr); ++ } + +- /* Disable L2 IC to L3 connection (Only for CAR) */ +- msr = rdmsr(BU_CFG2); +- msr.lo &= ~(1 << ClLinesToNbDis); +- wrmsr(BU_CFG2, msr); ++ disable_cache_as_ram(skip_sharedc_config); // inline ++ ++ /* Mark the core as sleeping */ ++ lapic_write(LAPIC_MSG_REG, (apicid << 24) | F10_APSTATE_ASLEEP); + +- disable_cache_as_ram(); // inline + /* stop all cores except node0/core0 the bsp .... */ + stop_this_cpu(); + } +@@ -276,6 +324,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo) + { + u32 bsp_apicid = 0; + u32 apicid; ++ uint8_t set_mtrrs; + struct node_core_id id; + + /* Please refer to the calculations and explaination in cache_as_ram.inc before modifying these values */ +@@ -362,7 +411,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo) + */ + update_microcode(cpuid_eax(1)); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(id.nodeid); + + #if CONFIG_SET_FIDVID + #if CONFIG_LOGICAL_CPUS && CONFIG_SET_FIDVID_CORE0_ONLY +@@ -385,10 +434,29 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo) + } + #endif + ++ if (is_fam15h()) { ++ /* core 1 on node 0 is special; to avoid corrupting the ++ * BSP do not alter MTRRs on that core */ ++ if (apicid == 1) ++ set_mtrrs = 0; ++ else ++ set_mtrrs = !!(apicid & 0x1); ++ } else { ++ set_mtrrs = 1; ++ } ++ + /* AP is ready, configure MTRRs and go to sleep */ +- set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK); ++ if (set_mtrrs) ++ set_var_mtrr(0, 0x00000000, CONFIG_RAMTOP, MTRR_TYPE_WRBACK); + +- STOP_CAR_AND_CPU(); ++ printk(BIOS_DEBUG, "Disabling CAR on AP %02x\n", apicid); ++ if (is_fam15h()) { ++ /* Only modify the MSRs on the odd cores (the last cores to finish booting) */ ++ STOP_CAR_AND_CPU(!set_mtrrs, apicid); ++ } else { ++ /* Modify MSRs on all cores */ ++ STOP_CAR_AND_CPU(0, apicid); ++ } + + printk(BIOS_DEBUG, + "\nAP %02x should be halted but you are reading this....\n", +@@ -496,7 +564,7 @@ static void setup_remote_node(u8 node) + } + #endif /* CONFIG_MAX_PHYSICAL_CPUS > 1 */ + +-static void AMD_Errata281(u8 node, u32 revision, u32 platform) ++static void AMD_Errata281(u8 node, uint64_t revision, u32 platform) + { + /* Workaround for Transaction Scheduling Conflict in + * Northbridge Cross Bar. Implement XCS Token adjustment +@@ -794,7 +862,7 @@ static void AMD_SetHtPhyRegister(u8 node, u8 link, u8 entry) + } while (!(val & HTPHY_IS_COMPLETE_MASK)); + } + +-void cpuSetAMDMSR(void) ++void cpuSetAMDMSR(uint8_t node_id) + { + /* This routine loads the CPU with default settings in fam10_msr_default + * table . It must be run after Cache-As-RAM has been enabled, and +@@ -804,7 +872,8 @@ void cpuSetAMDMSR(void) + */ + msr_t msr; + u8 i; +- u32 revision, platform; ++ u32 platform; ++ uint64_t revision; + + printk(BIOS_DEBUG, "cpuSetAMDMSR "); + +@@ -824,6 +893,49 @@ void cpuSetAMDMSR(void) + } + AMD_Errata298(); + ++ if (revision & AMD_FAM15_ALL) { ++ uint32_t f5x80; ++ uint8_t enabled; ++ uint8_t compute_unit_count = 0; ++ f5x80 = pci_read_config32(NODE_PCI(node_id, 5), 0x80); ++ enabled = f5x80 & 0xf; ++ if (enabled == 0x1) ++ compute_unit_count = 1; ++ if (enabled == 0x3) ++ compute_unit_count = 2; ++ if (enabled == 0x7) ++ compute_unit_count = 3; ++ if (enabled == 0xf) ++ compute_unit_count = 4; ++ msr = rdmsr(BU_CFG2); ++ msr.lo &= ~(0x3 << 6); /* ThrottleNbInterface[1:0] */ ++ msr.lo |= (((compute_unit_count - 1) & 0x3) << 6); ++ wrmsr(BU_CFG2, msr); ++ } ++ ++ /* Revision C0 and above */ ++ if (revision & AMD_OR_C0) { ++ uint32_t f3x1fc = pci_read_config32(NODE_PCI(node_id, 3), 0x1fc); ++ msr = rdmsr(FP_CFG); ++ msr.hi &= ~(0x7 << (42-32)); /* DiDtCfg4 */ ++ msr.hi |= (((f3x1fc >> 17) & 0x7) << (42-32)); ++ msr.hi &= ~(0x1 << (41-32)); /* DiDtCfg5 */ ++ msr.hi |= (((f3x1fc >> 22) & 0x1) << (41-32)); ++ msr.hi &= ~(0x1 << (40-32)); /* DiDtCfg3 */ ++ msr.hi |= (((f3x1fc >> 16) & 0x1) << (40-32)); ++ msr.hi &= ~(0x7 << (32-32)); /* DiDtCfg1 (1) */ ++ msr.hi |= (((f3x1fc >> 11) & 0x7) << (32-32)); ++ msr.lo &= ~(0x1f << 27); /* DiDtCfg1 (2) */ ++ msr.lo |= (((f3x1fc >> 6) & 0x1f) << 27); ++ msr.lo &= ~(0x3 << 25); /* DiDtCfg2 */ ++ msr.lo |= (((f3x1fc >> 14) & 0x3) << 25); ++ msr.lo &= ~(0x1f << 18); /* DiDtCfg0 */ ++ msr.lo |= (((f3x1fc >> 1) & 0x1f) << 18); ++ msr.lo &= ~(0x1 << 16); /* DiDtMode */ ++ msr.lo |= ((f3x1fc & 0x1) << 16); ++ wrmsr(FP_CFG, msr); ++ } ++ + printk(BIOS_DEBUG, " done\n"); + } + +@@ -835,9 +947,10 @@ static void cpuSetAMDPCI(u8 node) + * that it is run for the first core on each node + */ + u8 i, j; +- u32 revision, platform; ++ u32 platform; + u32 val; + u8 offset; ++ uint64_t revision; + + printk(BIOS_DEBUG, "cpuSetAMDPCI %02d", node); + +@@ -899,6 +1012,7 @@ static void cpuSetAMDPCI(u8 node) + } + + #ifdef UNUSED_CODE ++/* Clearing the MCA registers is apparently handled in the ramstage CPU Function 3 driver */ + static void cpuInitializeMCA(void) + { + /* Clears Machine Check Architecture (MCA) registers, which power on +diff --git a/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c b/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c +index b942c1a..8a61f13 100644 +--- a/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c ++++ b/src/cpu/amd/family_10h-family_15h/model_10xxx_init.c +@@ -39,6 +39,23 @@ + + #define MCI_STATUS 0x401 + ++static inline uint8_t is_fam15h(void) ++{ ++ uint8_t fam15h = 0; ++ uint32_t family; ++ ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ return fam15h; ++} ++ ++static volatile uint8_t fam15h_startup_flags[MAX_NODES_SUPPORTED][MAX_CORES_SUPPORTED] = {{ 0 }}; ++ + static void model_10xxx_init(device_t dev) + { + u8 i; +@@ -47,13 +64,44 @@ static void model_10xxx_init(device_t dev) + #if CONFIG_LOGICAL_CPUS + u32 siblings; + #endif ++ uint8_t delay_start; + + id = get_node_core_id(read_nb_cfg_54()); /* nb_cfg_54 can not be set */ + printk(BIOS_DEBUG, "nodeid = %02d, coreid = %02d\n", id.nodeid, id.coreid); + ++ if (is_fam15h()) ++ delay_start = !!(id.coreid & 0x1); ++ else ++ delay_start = 0; ++ + /* Turn on caching if we haven't already */ + x86_enable_cache(); +- amd_setup_mtrrs(); ++ ++ if (!delay_start) { ++ /* Initialize all variable MTRRs except the first pair. ++ * This prevents Linux from having to correct an inconsistent ++ * MTRR setup, which would crash Family 15h CPUs due to the ++ * compute unit structure sharing MTRR MSRs between AP cores. ++ */ ++ msr.hi = 0x00000000; ++ msr.lo = 0x00000000; ++ ++ disable_cache(); ++ ++ for (i = 0x2; i < 0x10; i++) { ++ wrmsr(0x00000200 | i, msr); ++ } ++ ++ enable_cache(); ++ ++ /* Set up other MTRRs */ ++ amd_setup_mtrrs(); ++ } else { ++ while (!fam15h_startup_flags[id.nodeid][id.coreid - 1]) { ++ /* Wait for CU first core startup */ ++ } ++ } ++ + x86_mtrr_check(); + + disable_cache(); +@@ -88,17 +136,24 @@ static void model_10xxx_init(device_t dev) + printk(BIOS_DEBUG, "siblings = %02d, ", siblings); + #endif + +- /* DisableCf8ExtCfg */ ++ /* Disable Cf8ExtCfg */ + msr = rdmsr(NB_CFG_MSR); + msr.hi &= ~(1 << (46 - 32)); + wrmsr(NB_CFG_MSR, msr); + +- msr = rdmsr(BU_CFG2_MSR); +- /* Clear ClLinesToNbDis */ +- msr.lo &= ~(1 << 15); +- /* Clear bit 35 as per Erratum 343 */ +- msr.hi &= ~(1 << (35-32)); +- wrmsr(BU_CFG2_MSR, msr); ++ if (is_fam15h()) { ++ msr = rdmsr(BU_CFG3_MSR); ++ /* Set CombineCr0Cd */ ++ msr.hi |= (1 << (49-32)); ++ wrmsr(BU_CFG3_MSR, msr); ++ } else { ++ msr = rdmsr(BU_CFG2_MSR); ++ /* Clear ClLinesToNbDis */ ++ msr.lo &= ~(1 << 15); ++ /* Clear bit 35 as per Erratum 343 */ ++ msr.hi &= ~(1 << (35-32)); ++ wrmsr(BU_CFG2_MSR, msr); ++ } + + if (IS_ENABLED(CONFIG_HAVE_SMI_HANDLER)) { + printk(BIOS_DEBUG, "Initializing SMM ASeg memory\n"); +@@ -131,6 +186,7 @@ static void model_10xxx_init(device_t dev) + msr.lo |= (1 << 0); + wrmsr(HWCR_MSR, msr); + ++ fam15h_startup_flags[id.nodeid][id.coreid] = 1; + } + + static struct device_operations cpu_dev_ops = { +@@ -147,15 +203,17 @@ static struct cpu_device_id cpu_table[] = { + { X86_VENDOR_AMD, 0x100f22 }, + { X86_VENDOR_AMD, 0x100f23 }, + { X86_VENDOR_AMD, 0x100f40 }, /* RB-C0 */ +- { X86_VENDOR_AMD, 0x100F42 }, /* RB-C2 */ +- { X86_VENDOR_AMD, 0x100F43 }, /* RB-C3 */ +- { X86_VENDOR_AMD, 0x100F52 }, /* BL-C2 */ +- { X86_VENDOR_AMD, 0x100F62 }, /* DA-C2 */ +- { X86_VENDOR_AMD, 0x100F63 }, /* DA-C3 */ +- { X86_VENDOR_AMD, 0x100F80 }, /* HY-D0 */ +- { X86_VENDOR_AMD, 0x100F81 }, /* HY-D1 */ +- { X86_VENDOR_AMD, 0x100F91 }, /* HY-D1 */ +- { X86_VENDOR_AMD, 0x100FA0 }, /* PH-E0 */ ++ { X86_VENDOR_AMD, 0x100f42 }, /* RB-C2 */ ++ { X86_VENDOR_AMD, 0x100f43 }, /* RB-C3 */ ++ { X86_VENDOR_AMD, 0x100f52 }, /* BL-C2 */ ++ { X86_VENDOR_AMD, 0x100f62 }, /* DA-C2 */ ++ { X86_VENDOR_AMD, 0x100f63 }, /* DA-C3 */ ++ { X86_VENDOR_AMD, 0x100f80 }, /* HY-D0 */ ++ { X86_VENDOR_AMD, 0x100f81 }, /* HY-D1 */ ++ { X86_VENDOR_AMD, 0x100f91 }, /* HY-D1 */ ++ { X86_VENDOR_AMD, 0x100fa0 }, /* PH-E0 */ ++ { X86_VENDOR_AMD, 0x600f12 }, /* OR-B2 */ ++ { X86_VENDOR_AMD, 0x600f20 }, /* OR-C0 */ + { 0, 0 }, + }; + +diff --git a/src/cpu/amd/family_10h-family_15h/powernow_acpi.c b/src/cpu/amd/family_10h-family_15h/powernow_acpi.c +index 98ef08a..84e5514 100644 +--- a/src/cpu/amd/family_10h-family_15h/powernow_acpi.c ++++ b/src/cpu/amd/family_10h-family_15h/powernow_acpi.c +@@ -74,8 +74,7 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p + /* Revision C or greater single-link processor */ + cpuid1 = cpuid(0x80000008); + acpigen_write_PSD_package(0, (cpuid1.ecx & 0xff) + 1, SW_ALL); +- } +- else { ++ } else { + /* Find the local APIC ID for the specified core ID */ + struct device* cpu; + int cpu_index = 0; +@@ -99,7 +98,9 @@ static void write_pstates_for_core(u8 pstate_num, u16 *pstate_feq, u32 *pstate_p + } + + /* +-* For details of this algorithm, please refer to the BDKG 3.62 page 69 ++* For details of this algorithm, please refer to: ++* Family 10h BDKG 3.62 page 69 ++* Family 15h BDKG 3.14 page 74 + * + * WARNING: The core count algorithm below assumes that all processors + * are identical, with the same number of active cores. While the BKDG +@@ -149,6 +150,13 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + uint8_t node_count; + uint8_t cores_per_node; + uint8_t total_core_count; ++ uint8_t fam15h; ++ uint8_t fam10h_rev_e = 0; ++ ++ /* Detect Revision E processors via method used in fidvid.c */ ++ if ((cpuid_edx(0x80000007) & CPB_MASK) ++ && ((cpuid_ecx(0x80000008) & NC_MASK) == 5)) ++ fam10h_rev_e = 1; + + /* + * Based on the CPU socket type,cmp_cap and pwr_lmt , get the power limit. +@@ -156,11 +164,17 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + * cmp_cap : 0x0 SingleCore ; 0x1 DualCore ; 0x2 TripleCore ; 0x3 QuadCore ; 0x4 QuintupleCore ; 0x5 HexCore + */ + printk(BIOS_INFO, "Pstates algorithm ...\n"); ++ fam15h = !!(mctGetLogicalCPUID(0) & AMD_FAM15_ALL); + /* Get number of cores */ +- dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xE8); +- cmp_cap = (dtemp & 0x3000) >> 12; +- if (mctGetLogicalCPUID(0) & AMD_FAM10_REV_D) /* revision D */ +- cmp_cap |= (dtemp & 0x8000) >> 13; ++ if (fam15h) { ++ cmp_cap = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 5)), 0x84) & 0xff; ++ } else { ++ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 3)), 0xe8); ++ cmp_cap = (dtemp & 0x3000) >> 12; ++ if (mctGetLogicalCPUID(0) & (AMD_FAM10_REV_D | AMD_FAM15_ALL)) /* revision D or higher */ ++ cmp_cap |= (dtemp & 0x8000) >> 13; ++ } ++ + /* Get number of nodes */ + dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 0)), 0x60); + node_count = ((dtemp & 0x70) >> 4) + 1; +@@ -169,6 +183,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + /* Compute total number of cores installed in system */ + total_core_count = cores_per_node * node_count; + ++ /* Get number of boost states */ ++ uint8_t boost_count = 0; ++ dtemp = pci_read_config32(dev_find_slot(0, PCI_DEVFN(0x18, 4)), 0x15c); ++ if (fam10h_rev_e) ++ boost_count = (dtemp >> 2) & 0x1; ++ else if (mctGetLogicalCPUID(0) & AMD_FAM15_ALL) ++ boost_count = (dtemp >> 2) & 0x7; ++ + Pstate_num = 0; + + /* See if the CPUID(0x80000007) returned EDX[7]==1b */ +@@ -205,7 +227,7 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + + /* Get PSmax's index */ + msr = rdmsr(0xC0010061); +- Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & BIT_MASK_3); ++ Pstate_max = (uint8_t) ((msr.lo >> PS_MAX_VAL_SHFT) & ((fam15h)?BIT_MASK_7:BIT_MASK_3)); + + /* Determine if all enabled Pstates have the same fidvid */ + uint8_t i; +@@ -219,10 +241,14 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + } + } + ++ /* Family 15h uses slightly different PSmax numbering */ ++ if (fam15h) ++ Pstate_max++; ++ + /* Populate tables with all Pstate information */ + for (Pstate_num = 0; Pstate_num < Pstate_max; Pstate_num++) { + /* Get power state information */ +- msr = rdmsr(0xC0010064 + Pstate_num); ++ msr = rdmsr(0xC0010064 + Pstate_num + boost_count); + cpufid = (msr.lo & 0x3f); + cpudid = (msr.lo & 0x1c0) >> 6; + cpuvid = (msr.lo & 0xfe00) >> 9; +@@ -232,12 +258,10 @@ void amd_generate_powernow(u32 pcontrol_blk, u8 plen, u8 onlyBSP) + if (pviModeFlag) { + if (cpuvid >= 0x20) { + core_voltage = 7625 - (((cpuvid - 0x20) * 10000) / 80); +- } +- else { ++ } else { + core_voltage = 15500 - ((cpuvid * 10000) / 40); + } +- } +- else { ++ } else { + cpuvid = cpuvid & 0x7f; + if (cpuvid >= 0x7c) + core_voltage = 0; +diff --git a/src/cpu/amd/family_10h-family_15h/processor_name.c b/src/cpu/amd/family_10h-family_15h/processor_name.c +index 12c45c9..fbd0452 100644 +--- a/src/cpu/amd/family_10h-family_15h/processor_name.c ++++ b/src/cpu/amd/family_10h-family_15h/processor_name.c +@@ -33,6 +33,10 @@ + #include <cpu/amd/mtrr.h> + #include <cpu/cpu.h> + #include <cpu/amd/model_10xxx_rev.h> ++#include <device/device.h> ++#include <device/pci.h> ++#include <device/pnp.h> ++#include <device/pci_ops.h> + + /* The maximum length of CPU names is 48 bytes, including the final NULL byte. + * If you change these names your BIOS will _NOT_ pass the AMD validation and +@@ -212,104 +216,138 @@ static int strcpymax(char *dst, const char *src, int buflen) + return i; + } + ++#define NAME_STRING_MAXLEN 48 + + int init_processor_name(void) + { +- /* variable names taken from fam10 revision guide for clarity */ +- u32 BrandId; /* CPUID Fn8000_0001_EBX */ +- u8 String1; /* BrandID[14:11] */ +- u8 String2; /* BrandID[3:0] */ +- u8 Model; /* BrandID[10:4] */ +- u8 Pg; /* BrandID[15] */ +- u8 PkgTyp; /* BrandID[31:28] */ +- u8 NC; /* CPUID Fn8000_0008_ECX */ +- const char *processor_name_string = unknown; +- char program_string[48]; +- u32 *p_program_string = (u32 *)program_string; + msr_t msr; +- int i, j = 0, str2_checkNC = 1; +- const struct str_s *str, *str2; ++ ssize_t i; ++ char program_string[NAME_STRING_MAXLEN]; ++ u32 *p_program_string = (u32 *)program_string; ++ uint8_t fam15h = 0; ++ uint32_t family; + ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); + +- /* Find out which CPU brand it is */ +- BrandId = cpuid_ebx(0x80000001); +- String1 = (u8)((BrandId >> 11) & 0x0F); +- String2 = (u8)((BrandId >> 0) & 0x0F); +- Model = (u8)((BrandId >> 4) & 0x7F); +- Pg = (u8)((BrandId >> 15) & 0x01); +- PkgTyp = (u8)((BrandId >> 28) & 0x0F); +- NC = (u8)(cpuid_ecx(0x80000008) & 0xFF); ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; + + /* null the string */ + memset(program_string, 0, sizeof(program_string)); + +- if (!Model) { +- processor_name_string = Pg ? thermal : sample; +- goto done; +- } +- +- switch (PkgTyp) { +- case 0: /* F1207 */ +- str = String1_socket_F; +- str2 = String2_socket_F; +- str2_checkNC = 0; +- break; +- case 1: /* AM2 */ +- str = String1_socket_AM2; +- str2 = String2_socket_AM2; +- break; +- case 3: /* G34 */ +- str = String1_socket_G34; +- str2 = String2_socket_G34; +- str2_checkNC = 0; +- break; +- case 5: /* C32 */ +- str = String1_socket_C32; +- str2 = String2_socket_C32; +- break; +- default: +- goto done; +- } ++ if (fam15h) { ++ /* Family 15h or later */ ++ uint32_t dword; ++ device_t cpu_fn5_dev = dev_find_slot(0, PCI_DEVFN(0x18, 5)); ++ pci_write_config32(cpu_fn5_dev, 0x194, 0); ++ dword = pci_read_config32(cpu_fn5_dev, 0x198); ++ if (dword == 0) { ++ strcpymax(program_string, sample, sizeof(program_string)); ++ } else { ++ /* Assemble the string from PCI configuration register contents */ ++ for (i = 0; i < 12; i++) { ++ pci_write_config32(cpu_fn5_dev, 0x194, i); ++ p_program_string[i] = pci_read_config32(cpu_fn5_dev, 0x198); ++ } ++ ++ /* Correctly place the null terminator */ ++ for (i = (NAME_STRING_MAXLEN - 2); i > 0; i--) { ++ if (program_string[i] != 0x20) ++ break; ++ } ++ program_string[i + 1] = 0; ++ } ++ } else { ++ /* variable names taken from fam10 revision guide for clarity */ ++ u32 BrandId; /* CPUID Fn8000_0001_EBX */ ++ u8 String1; /* BrandID[14:11] */ ++ u8 String2; /* BrandID[3:0] */ ++ u8 Model; /* BrandID[10:4] */ ++ u8 Pg; /* BrandID[15] */ ++ u8 PkgTyp; /* BrandID[31:28] */ ++ u8 NC; /* CPUID Fn8000_0008_ECX */ ++ const char *processor_name_string = unknown; ++ int j = 0, str2_checkNC = 1; ++ const struct str_s *str, *str2; ++ ++ /* Find out which CPU brand it is */ ++ BrandId = cpuid_ebx(0x80000001); ++ String1 = (u8)((BrandId >> 11) & 0x0F); ++ String2 = (u8)((BrandId >> 0) & 0x0F); ++ Model = (u8)((BrandId >> 4) & 0x7F); ++ Pg = (u8)((BrandId >> 15) & 0x01); ++ PkgTyp = (u8)((BrandId >> 28) & 0x0F); ++ NC = (u8)(cpuid_ecx(0x80000008) & 0xFF); ++ ++ if (!Model) { ++ processor_name_string = Pg ? thermal : sample; ++ goto done; ++ } + +- /* String1 */ +- for (i = 0; str[i].value; i++) { +- if ((str[i].Pg == Pg) && +- (str[i].NC == NC) && +- (str[i].String == String1)) { +- processor_name_string = str[i].value; ++ switch (PkgTyp) { ++ case 0: /* F1207 */ ++ str = String1_socket_F; ++ str2 = String2_socket_F; ++ str2_checkNC = 0; ++ break; ++ case 1: /* AM2 */ ++ str = String1_socket_AM2; ++ str2 = String2_socket_AM2; ++ break; ++ case 3: /* G34 */ ++ str = String1_socket_G34; ++ str2 = String2_socket_G34; ++ str2_checkNC = 0; ++ break; ++ case 5: /* C32 */ ++ str = String1_socket_C32; ++ str2 = String2_socket_C32; + break; ++ default: ++ goto done; + } +- } + +- if (!str[i].value) +- goto done; ++ /* String1 */ ++ for (i = 0; str[i].value; i++) { ++ if ((str[i].Pg == Pg) && ++ (str[i].NC == NC) && ++ (str[i].String == String1)) { ++ processor_name_string = str[i].value; ++ break; ++ } ++ } + +- j = strcpymax(program_string, processor_name_string, +- sizeof(program_string)); ++ if (!str[i].value) ++ goto done; + +- /* Translate Model from 01-99 to ASCII and put it on the end. +- * Numbers less than 10 should include a leading zero, e.g., 09.*/ +- if (Model < 100 && j < sizeof(program_string) - 2) { +- program_string[j++] = (Model / 10) + '0'; +- program_string[j++] = (Model % 10) + '0'; +- } ++ j = strcpymax(program_string, processor_name_string, ++ sizeof(program_string)); + +- processor_name_string = unknown2; +- +- /* String 2 */ +- for(i = 0; str2[i].value; i++) { +- if ((str2[i].Pg == Pg) && +- ((str2[i].NC == NC) || !str2_checkNC) && +- (str2[i].String == String2)) { +- processor_name_string = str2[i].value; +- break; ++ /* Translate Model from 01-99 to ASCII and put it on the end. ++ * Numbers less than 10 should include a leading zero, e.g., 09.*/ ++ if (Model < 100 && j < sizeof(program_string) - 2) { ++ program_string[j++] = (Model / 10) + '0'; ++ program_string[j++] = (Model % 10) + '0'; + } +- } + ++ processor_name_string = unknown2; ++ ++ /* String 2 */ ++ for(i = 0; str2[i].value; i++) { ++ if ((str2[i].Pg == Pg) && ++ ((str2[i].NC == NC) || !str2_checkNC) && ++ (str2[i].String == String2)) { ++ processor_name_string = str2[i].value; ++ break; ++ } ++ } + +-done: +- strcpymax(&program_string[j], processor_name_string, +- sizeof(program_string) - j); ++ done: ++ strcpymax(&program_string[j], processor_name_string, ++ sizeof(program_string) - j); ++ } + + printk(BIOS_DEBUG, "CPU model: %s\n", program_string); + +diff --git a/src/cpu/amd/family_10h-family_15h/update_microcode.c b/src/cpu/amd/family_10h-family_15h/update_microcode.c +index 51aca35..3b2f5dd 100644 +--- a/src/cpu/amd/family_10h-family_15h/update_microcode.c ++++ b/src/cpu/amd/family_10h-family_15h/update_microcode.c +@@ -28,6 +28,7 @@ struct id_mapping { + + static u16 get_equivalent_processor_rev_id(u32 orig_id) { + static const struct id_mapping id_mapping_table[] = { ++ /* Family 10h */ + { 0x100f00, 0x1000 }, + { 0x100f01, 0x1000 }, + { 0x100f02, 0x1000 }, +@@ -42,8 +43,13 @@ static u16 get_equivalent_processor_rev_id(u32 orig_id) { + { 0x100f62, 0x1062 }, /* DA-C2 */ + { 0x100f63, 0x1043 }, /* DA-C3 */ + { 0x100f81, 0x1081 }, /* HY-D1 */ ++ { 0x100f91, 0x1081 }, /* HY-D1 */ + { 0x100fa0, 0x10A0 }, /* PH-E0 */ + ++ /* Family 15h */ ++ { 0x600f12, 0x6012 }, /* OR-B2 */ ++ { 0x600f20, 0x6020 }, /* OR-C0 */ ++ + /* Array terminator */ + { 0xffffff, 0x0000 }, + }; +diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c +index 9c21e94..8a9b5ed 100644 +--- a/src/cpu/amd/quadcore/quadcore.c ++++ b/src/cpu/amd/quadcore/quadcore.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -26,16 +27,41 @@ + + #include "cpu/amd/quadcore/quadcore_id.c" + ++/* get_boot_apic_id and wait_cpu_state located in init_cpus.c */ ++uint32_t get_boot_apic_id(uint8_t node, uint32_t core); ++uint32_t wait_cpu_state(uint32_t apicid, uint32_t state, uint32_t state2); ++ ++static inline uint8_t is_fam15h(void) ++{ ++ uint8_t fam15h = 0; ++ uint32_t family; ++ ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ return fam15h; ++} ++ + static u32 get_core_num_in_bsp(u32 nodeid) + { + u32 dword; +- dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8); +- dword >>= 12; +- /* Bit 15 is CmpCap[2] since Revision D. */ +- if ((cpuid_ecx(0x80000008) & 0xff) > 3) +- dword = ((dword & 8) >> 1) | (dword & 3); +- else +- dword &= 3; ++ if (is_fam15h()) { ++ /* Family 15h moved CmpCap to F5x84 [7:0] */ ++ dword = pci_read_config32(NODE_PCI(nodeid, 5), 0x84); ++ dword &= 0xff; ++ } else { ++ dword = pci_read_config32(NODE_PCI(nodeid, 3), 0xe8); ++ dword >>= 12; ++ /* Bit 15 is CmpCap[2] since Revision D. */ ++ if ((cpuid_ecx(0x80000008) & 0xff) > 3) ++ dword = ((dword & 8) >> 1) | (dword & 3); ++ else ++ dword &= 3; ++ } + return dword; + } + +@@ -50,28 +76,68 @@ static u8 set_apicid_cpuid_lo(void) + return 1; + } + +-static void real_start_other_core(u32 nodeid, u32 cores) ++static void real_start_other_core(uint32_t nodeid, uint32_t cores) + { +- u32 dword, i; ++ ssize_t i; ++ uint32_t dword; + + printk(BIOS_DEBUG, "Start other core - nodeid: %02x cores: %02x\n", nodeid, cores); + + /* set PCI_DEV(0, 0x18+nodeid, 3), 0x44 bit 27 to redirect all MC4 + accesses and error logging to core0 */ + dword = pci_read_config32(NODE_PCI(nodeid, 3), 0x44); +- dword |= 1 << 27; // NbMcaToMstCpuEn bit ++ dword |= 1 << 30; /* SyncFloodOnDramAdrParErr=1 */ ++ dword |= 1 << 27; /* NbMcaToMstCpuEn=1 */ ++ dword |= 1 << 21; /* SyncFloodOnAnyUcErr=1 */ ++ dword |= 1 << 20; /* SyncFloodOnWDT=1 */ ++ dword |= 1 << 2; /* SyncFloodOnDramUcEcc=1 */ + pci_write_config32(NODE_PCI(nodeid, 3), 0x44, dword); +- // set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1 +- dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68); +- dword |= 1 << 5; +- pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword); +- +- if(cores > 1) { +- dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168); +- for (i = 0; i < cores - 1; i++) { +- dword |= 1 << i; ++ if (is_fam15h()) { ++ uint32_t core_activation_flags = 0; ++ uint32_t active_cores = 0; ++ ++ /* Set PCI_DEV(0, 0x18+nodeid, 0), 0x1dc bits 7:1 to start cores */ ++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x1dc); ++ for (i = 1; i < cores + 1; i++) { ++ core_activation_flags |= 1 << i; ++ } ++ ++ /* Start the first core of each compute unit */ ++ active_cores |= core_activation_flags & 0x55; ++ pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores); ++ ++ /* Each core shares a single set of MTRR registers with ++ * another core in the same compute unit, therefore, it ++ * is important that one core in each CU starts in advance ++ * of the other in order to avoid one core stomping all over ++ * the other core's settings. ++ */ ++ ++ /* Wait for the first core of each compute unit to start... */ ++ uint32_t timeout; ++ for (i = 1; i < cores + 1; i++) { ++ if (!(i & 0x1)) { ++ uint32_t ap_apicid = get_boot_apic_id(nodeid, i); ++ timeout = wait_cpu_state(ap_apicid, F10_APSTATE_ASLEEP, F10_APSTATE_ASLEEP); ++ } ++ } ++ ++ /* Start the second core of each compute unit */ ++ active_cores |= core_activation_flags & 0xaa; ++ pci_write_config32(NODE_PCI(nodeid, 0), 0x1dc, dword | active_cores); ++ } else { ++ // set PCI_DEV(0, 0x18+nodeid, 0), 0x68 bit 5 to start core1 ++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x68); ++ dword |= 1 << 5; ++ pci_write_config32(NODE_PCI(nodeid, 0), 0x68, dword); ++ ++ if (cores > 1) { ++ dword = pci_read_config32(NODE_PCI(nodeid, 0), 0x168); ++ for (i = 0; i < cores - 1; i++) { ++ dword |= 1 << i; ++ } ++ pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword); + } +- pci_write_config32(NODE_PCI(nodeid, 0), 0x168, dword); + } + } + +@@ -91,10 +157,9 @@ static void start_other_cores(void) + + for (nodeid = 0; nodeid < nodes; nodeid++) { + u32 cores = get_core_num_in_bsp(nodeid); +- printk(BIOS_DEBUG, "init node: %02x cores: %02x \n", nodeid, cores); ++ printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1 \n", nodeid, cores); + if (cores > 0) { + real_start_other_core(nodeid, cores); + } + } +- + } +diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c +index c5921de..c0537b3 100644 +--- a/src/cpu/amd/quadcore/quadcore_id.c ++++ b/src/cpu/amd/quadcore/quadcore_id.c +@@ -43,9 +43,12 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54) + { + struct node_core_id id; + uint8_t apicid; ++ uint8_t fam15h = 0; + uint8_t rev_gte_d = 0; + uint8_t dual_node = 0; + uint32_t f3xe8; ++ uint32_t family; ++ uint32_t model; + + #ifdef __PRE_RAM__ + f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8); +@@ -53,7 +56,17 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54) + f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8); + #endif + +- if (cpuid_eax(0x80000001) >= 0x8) ++ family = model = cpuid_eax(0x80000001); ++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) { ++ /* Family 15h or later */ ++ fam15h = 1; ++ nb_cfg_54 = 1; ++ } ++ ++ if ((model >= 0x8) || fam15h) + /* Revision D or later */ + rev_gte_d = 1; + +@@ -67,7 +80,13 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54) + */ + apicid = (cpuid_ebx(1) >> 24) & 0xff; + if( nb_cfg_54) { +- if (rev_gte_d && dual_node) { ++ if (fam15h && dual_node) { ++ id.coreid = apicid & 0x1f; ++ id.nodeid = (apicid & 0x60) >> 5; ++ } else if (fam15h && !dual_node) { ++ id.coreid = apicid & 0xf; ++ id.nodeid = (apicid & 0x70) >> 4; ++ } else if (rev_gte_d && dual_node) { + id.coreid = apicid & 0xf; + id.nodeid = (apicid & 0x30) >> 4; + } else if (rev_gte_d && !dual_node) { +@@ -90,7 +109,25 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54) + } + } + +- if (rev_gte_d && dual_node) { ++ if (fam15h && dual_node) { ++ /* Coreboot expects each separate processor die to be on a different nodeid. ++ * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed... ++ */ ++ uint32_t f5x84; ++ uint8_t core_count; ++ ++#ifdef __PRE_RAM__ ++ f5x84 = pci_read_config32(NODE_PCI(0, 5), 0x84); ++#else ++ f5x84 = pci_read_config32(get_node_pci(0, 5), 0x84); ++#endif ++ core_count = (f5x84 & 0xff) + 1; ++ id.nodeid = id.nodeid * 2; ++ if (id.coreid >= core_count) { ++ id.nodeid += 1; ++ id.coreid = id.coreid - core_count; ++ } ++ } else if (rev_gte_d && dual_node) { + /* Coreboot expects each separate processor die to be on a different nodeid. + * Since the code above returns nodeid 0 even on internal node 1 some fixup is needed... + */ +diff --git a/src/include/cpu/amd/model_10xxx_msr.h b/src/include/cpu/amd/model_10xxx_msr.h +index 6c7dece..7d78e2d 100644 +--- a/src/include/cpu/amd/model_10xxx_msr.h ++++ b/src/include/cpu/amd/model_10xxx_msr.h +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -32,7 +33,13 @@ + #define IC_CFG_MSR 0xC0011021 + #define DC_CFG_MSR 0xC0011022 + #define BU_CFG_MSR 0xC0011023 ++#define FP_CFG_MSR 0xC0011028 ++#define DE_CFG_MSR 0xC0011029 + #define BU_CFG2_MSR 0xC001102A ++#define BU_CFG3_MSR 0xC001102B ++#define EX_CFG_MSR 0xC001102C ++#define LS_CFG2_MSR 0xC001102D ++#define IBS_OP_DATA3_MSR 0xC0011037 + + #define CPU_ID_FEATURES_MSR 0xC0011004 + #define CPU_ID_HYPER_EXT_FEATURES 0xC001100d +diff --git a/src/mainboard/advansus/a785e-i/romstage.c b/src/mainboard/advansus/a785e-i/romstage.c +index 4c2b38a..ab717fd 100644 +--- a/src/mainboard/advansus/a785e-i/romstage.c ++++ b/src/mainboard/advansus/a785e-i/romstage.c +@@ -131,7 +131,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/amd/bimini_fam10/romstage.c b/src/mainboard/amd/bimini_fam10/romstage.c +index e2bd351..5e2cf82 100644 +--- a/src/mainboard/amd/bimini_fam10/romstage.c ++++ b/src/mainboard/amd/bimini_fam10/romstage.c +@@ -123,7 +123,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/amd/mahogany_fam10/romstage.c b/src/mainboard/amd/mahogany_fam10/romstage.c +index 74bc9d5..025a8bb 100644 +--- a/src/mainboard/amd/mahogany_fam10/romstage.c ++++ b/src/mainboard/amd/mahogany_fam10/romstage.c +@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c b/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c +index 20d46e6..5063439 100644 +--- a/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c ++++ b/src/mainboard/amd/serengeti_cheetah_fam10/romstage.c +@@ -231,7 +231,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/amd/tilapia_fam10/romstage.c b/src/mainboard/amd/tilapia_fam10/romstage.c +index 89100b1..e37bc08 100644 +--- a/src/mainboard/amd/tilapia_fam10/romstage.c ++++ b/src/mainboard/amd/tilapia_fam10/romstage.c +@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/asus/kfsn4-dre/romstage.c b/src/mainboard/asus/kfsn4-dre/romstage.c +index 5d1f5a6..dd5c7dc 100644 +--- a/src/mainboard/asus/kfsn4-dre/romstage.c ++++ b/src/mainboard/asus/kfsn4-dre/romstage.c +@@ -245,7 +245,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/asus/m4a78-em/romstage.c b/src/mainboard/asus/m4a78-em/romstage.c +index 82f30d9..82b96bf 100644 +--- a/src/mainboard/asus/m4a78-em/romstage.c ++++ b/src/mainboard/asus/m4a78-em/romstage.c +@@ -127,7 +127,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/asus/m4a785-m/romstage.c b/src/mainboard/asus/m4a785-m/romstage.c +index 780bf81..30975fa 100644 +--- a/src/mainboard/asus/m4a785-m/romstage.c ++++ b/src/mainboard/asus/m4a785-m/romstage.c +@@ -127,7 +127,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/asus/m5a88-v/romstage.c b/src/mainboard/asus/m5a88-v/romstage.c +index 38761a6..4edaba2 100644 +--- a/src/mainboard/asus/m5a88-v/romstage.c ++++ b/src/mainboard/asus/m5a88-v/romstage.c +@@ -128,7 +128,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/avalue/eax-785e/romstage.c b/src/mainboard/avalue/eax-785e/romstage.c +index 764a5c6..447012b 100644 +--- a/src/mainboard/avalue/eax-785e/romstage.c ++++ b/src/mainboard/avalue/eax-785e/romstage.c +@@ -132,7 +132,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/gigabyte/ma785gm/romstage.c b/src/mainboard/gigabyte/ma785gm/romstage.c +index db4e449..444e59d 100644 +--- a/src/mainboard/gigabyte/ma785gm/romstage.c ++++ b/src/mainboard/gigabyte/ma785gm/romstage.c +@@ -122,7 +122,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/gigabyte/ma785gmt/romstage.c b/src/mainboard/gigabyte/ma785gmt/romstage.c +index 4ce7c58..705d7c5 100644 +--- a/src/mainboard/gigabyte/ma785gmt/romstage.c ++++ b/src/mainboard/gigabyte/ma785gmt/romstage.c +@@ -122,7 +122,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/gigabyte/ma78gm/romstage.c b/src/mainboard/gigabyte/ma78gm/romstage.c +index d2a0b95..5d21801 100644 +--- a/src/mainboard/gigabyte/ma78gm/romstage.c ++++ b/src/mainboard/gigabyte/ma78gm/romstage.c +@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/hp/dl165_g6_fam10/romstage.c b/src/mainboard/hp/dl165_g6_fam10/romstage.c +index 97e60d5..26c0bb9 100644 +--- a/src/mainboard/hp/dl165_g6_fam10/romstage.c ++++ b/src/mainboard/hp/dl165_g6_fam10/romstage.c +@@ -137,7 +137,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/iei/kino-780am2-fam10/romstage.c b/src/mainboard/iei/kino-780am2-fam10/romstage.c +index edbae3a..321eea6 100644 +--- a/src/mainboard/iei/kino-780am2-fam10/romstage.c ++++ b/src/mainboard/iei/kino-780am2-fam10/romstage.c +@@ -125,7 +125,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/jetway/pa78vm5/romstage.c b/src/mainboard/jetway/pa78vm5/romstage.c +index 16bb089..93dd2ce 100644 +--- a/src/mainboard/jetway/pa78vm5/romstage.c ++++ b/src/mainboard/jetway/pa78vm5/romstage.c +@@ -130,7 +130,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/msi/ms9652_fam10/romstage.c b/src/mainboard/msi/ms9652_fam10/romstage.c +index 4ea3306..5da971f 100644 +--- a/src/mainboard/msi/ms9652_fam10/romstage.c ++++ b/src/mainboard/msi/ms9652_fam10/romstage.c +@@ -150,7 +150,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/supermicro/h8dmr_fam10/romstage.c b/src/mainboard/supermicro/h8dmr_fam10/romstage.c +index c224dbc..1425546 100644 +--- a/src/mainboard/supermicro/h8dmr_fam10/romstage.c ++++ b/src/mainboard/supermicro/h8dmr_fam10/romstage.c +@@ -146,7 +146,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/supermicro/h8qme_fam10/romstage.c b/src/mainboard/supermicro/h8qme_fam10/romstage.c +index 0f9445b..4721eba 100644 +--- a/src/mainboard/supermicro/h8qme_fam10/romstage.c ++++ b/src/mainboard/supermicro/h8qme_fam10/romstage.c +@@ -214,7 +214,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/mainboard/supermicro/h8scm_fam10/romstage.c b/src/mainboard/supermicro/h8scm_fam10/romstage.c +index 4ea14fe..858aca0 100644 +--- a/src/mainboard/supermicro/h8scm_fam10/romstage.c ++++ b/src/mainboard/supermicro/h8scm_fam10/romstage.c +@@ -136,7 +136,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + /* TODO: The Kernel must support 12 processor, otherwise the interrupt +diff --git a/src/mainboard/tyan/s2912_fam10/romstage.c b/src/mainboard/tyan/s2912_fam10/romstage.c +index 0030619..cdf51b1 100644 +--- a/src/mainboard/tyan/s2912_fam10/romstage.c ++++ b/src/mainboard/tyan/s2912_fam10/romstage.c +@@ -149,7 +149,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx) + + post_code(0x33); + +- cpuSetAMDMSR(); ++ cpuSetAMDMSR(0); + post_code(0x34); + + amd_ht_init(sysinfo); +diff --git a/src/northbridge/amd/amdfam10/Kconfig b/src/northbridge/amd/amdfam10/Kconfig +index ada5b9f..cb0d109 100644 +--- a/src/northbridge/amd/amdfam10/Kconfig ++++ b/src/northbridge/amd/amdfam10/Kconfig +@@ -96,7 +96,7 @@ endif + if HAVE_ACPI_RESUME + config S3_DATA_SIZE + int +- default 16384 ++ default 32768 + endif + + if DIMM_DDR2 +diff --git a/src/northbridge/amd/amdfam10/Makefile.inc b/src/northbridge/amd/amdfam10/Makefile.inc +index b4097b4..4098dce 100644 +--- a/src/northbridge/amd/amdfam10/Makefile.inc ++++ b/src/northbridge/amd/amdfam10/Makefile.inc +@@ -2,6 +2,8 @@ ifeq ($(CONFIG_NORTHBRIDGE_AMD_AMDFAM10),y) + + ramstage-y += northbridge.c + ramstage-y += misc_control.c ++ramstage-y += link_control.c ++ramstage-y += nb_control.c + romstage-y += amdfam10_util.c + ramstage-y += amdfam10_util.c + +diff --git a/src/northbridge/amd/amdfam10/amdfam10.h b/src/northbridge/amd/amdfam10/amdfam10.h +index a1e08a0..b724394 100644 +--- a/src/northbridge/amd/amdfam10/amdfam10.h ++++ b/src/northbridge/amd/amdfam10/amdfam10.h +@@ -962,9 +962,12 @@ that are corresponding to 0x01, 0x02, 0x03, 0x05, 0x06, 0x07 + + #define LAPIC_MSG_REG 0x380 + #define F10_APSTATE_STARTED 0x13 // start of AP execution +-#define F10_APSTATE_STOPPED 0x14 // allow AP to stop ++#define F10_APSTATE_ASLEEP 0x14 // AP sleeping ++#define F10_APSTATE_STOPPED 0x15 // allow AP to stop + #define F10_APSTATE_RESET 0x01 // waiting for warm reset + ++#define MAX_CORES_SUPPORTED 128 ++ + #include "nums.h" + + #ifdef __PRE_RAM__ +@@ -1038,7 +1041,6 @@ struct sys_info { + + struct MCTStatStruc MCTstat; + struct DCTStatStruc DCTstatA[NODE_NUMS]; +- + } __attribute__((packed)); + + #ifdef __PRE_RAM__ +diff --git a/src/northbridge/amd/amdfam10/amdfam10_util.c b/src/northbridge/amd/amdfam10/amdfam10_util.c +index 423bb73..a4045bd 100644 +--- a/src/northbridge/amd/amdfam10/amdfam10_util.c ++++ b/src/northbridge/amd/amdfam10/amdfam10_util.c +@@ -34,14 +34,14 @@ u32 Get_NB32(u32 dev, u32 reg) + } + #endif + +-u32 mctGetLogicalCPUID(u32 Node) ++uint64_t mctGetLogicalCPUID(u32 Node) + { + /* Converts the CPUID to a logical ID MASK that is used to check + CPU version support versions */ + u32 dev; + u32 val, valx; + u32 family, model, stepping; +- u32 ret; ++ uint64_t ret; + + if (Node == 0xFF) { /* current node */ + val = cpuid_eax(0x80000001); +@@ -100,9 +100,16 @@ u32 mctGetLogicalCPUID(u32 Node) + case 0x100a0: + ret = AMD_PH_E0; + break; ++ case 0x15012: ++ case 0x1501f: ++ ret = AMD_OR_B2; ++ break; ++ case 0x15020: ++ ret = AMD_OR_C0; ++ break; + default: + /* FIXME: mabe we should die() here. */ +- printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! \n"); ++ printk(BIOS_ERR, "FIXME! CPU Version unknown or not supported! %08x\n", valx); + ret = 0; + } + +diff --git a/src/northbridge/amd/amdfam10/link_control.c b/src/northbridge/amd/amdfam10/link_control.c +new file mode 100644 +index 0000000..1091ef4 +--- /dev/null ++++ b/src/northbridge/amd/amdfam10/link_control.c +@@ -0,0 +1,86 @@ ++/* ++ * This file is part of the coreboot project. ++ * ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* Configure various power control registers, including processor ++ * boost support. ++ */ ++ ++#include <console/console.h> ++#include <device/device.h> ++#include <device/pci.h> ++#include <device/pci_ids.h> ++#include <device/pci_ops.h> ++#include <pc80/mc146818rtc.h> ++#include <lib.h> ++#include <cpu/amd/model_10xxx_rev.h> ++ ++#include "amdfam10.h" ++ ++static inline uint8_t is_fam15h(void) ++{ ++ uint8_t fam15h = 0; ++ uint32_t family; ++ ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ return fam15h; ++} ++ ++static void nb_control_init(struct device *dev) ++{ ++ uint32_t dword; ++ ++ printk(BIOS_DEBUG, "NB: Function 4 Link Control.. "); ++ ++ if (is_fam15h()) { ++ /* Enable APM */ ++ dword = pci_read_config32(dev, 0x15c); ++ dword |= (0x1 << 7); /* ApmMasterEn = 1 */ ++ pci_write_config32(dev, 0x15c, dword); ++ } ++ ++ printk(BIOS_DEBUG, "done.\n"); ++} ++ ++ ++static struct device_operations mcf4_ops = { ++ .read_resources = pci_dev_read_resources, ++ .set_resources = pci_dev_set_resources, ++ .enable_resources = pci_dev_enable_resources, ++ .init = nb_control_init, ++ .scan_bus = 0, ++ .ops_pci = 0, ++}; ++ ++static const struct pci_driver mcf4_driver_fam10 __pci_driver = { ++ .ops = &mcf4_ops, ++ .vendor = PCI_VENDOR_ID_AMD, ++ .device = 0x1204, ++}; ++ ++static const struct pci_driver mcf4_driver_fam15 __pci_driver = { ++ .ops = &mcf4_ops, ++ .vendor = PCI_VENDOR_ID_AMD, ++ .device = 0x1604, ++}; +\ No newline at end of file +diff --git a/src/northbridge/amd/amdfam10/misc_control.c b/src/northbridge/amd/amdfam10/misc_control.c +index 90a4db1..8777e8f 100644 +--- a/src/northbridge/amd/amdfam10/misc_control.c ++++ b/src/northbridge/amd/amdfam10/misc_control.c +@@ -4,6 +4,7 @@ + * Copyright (C) 2003 by Eric Biederman + * Copyright (C) Stefan Reinauer + * Copyright (C) 2007 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -152,3 +153,9 @@ static const struct pci_driver mcf3_driver __pci_driver = { + .vendor = PCI_VENDOR_ID_AMD, + .device = 0x1203, + }; ++ ++static const struct pci_driver mcf3_driver_fam15 __pci_driver = { ++ .ops = &mcf3_ops, ++ .vendor = PCI_VENDOR_ID_AMD, ++ .device = 0x1603, ++}; +diff --git a/src/northbridge/amd/amdfam10/nb_control.c b/src/northbridge/amd/amdfam10/nb_control.c +new file mode 100644 +index 0000000..f95b6f8 +--- /dev/null ++++ b/src/northbridge/amd/amdfam10/nb_control.c +@@ -0,0 +1,85 @@ ++/* ++ * This file is part of the coreboot project. ++ * ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; version 2 of the License. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* Configure various power control registers, including processor boost ++ * and TDP monitoring support. ++ */ ++ ++#include <console/console.h> ++#include <device/device.h> ++#include <device/pci.h> ++#include <device/pci_ids.h> ++#include <device/pci_ops.h> ++#include <pc80/mc146818rtc.h> ++#include <lib.h> ++#include <cpu/amd/model_10xxx_rev.h> ++ ++#include "amdfam10.h" ++ ++static void nb_control_init(struct device *dev) ++{ ++ uint32_t dword; ++ uint32_t f5x80; ++ uint8_t cu_enabled; ++ uint8_t compute_unit_count = 0; ++ ++ printk(BIOS_DEBUG, "NB: Function 5 Northbridge Control.. "); ++ ++ /* Determine the number of active compute units on this node */ ++ f5x80 = pci_read_config32(dev, 0x80); ++ cu_enabled = f5x80 & 0xf; ++ if (cu_enabled == 0x1) ++ compute_unit_count = 1; ++ if (cu_enabled == 0x3) ++ compute_unit_count = 2; ++ if (cu_enabled == 0x7) ++ compute_unit_count = 3; ++ if (cu_enabled == 0xf) ++ compute_unit_count = 4; ++ ++ /* Configure Processor TDP Running Average */ ++ dword = pci_read_config32(dev, 0xe0); ++ dword &= ~0xf; /* RunAvgRange = 0x9 */ ++ dword |= 0x9; ++ pci_write_config32(dev, 0xe0, dword); ++ ++ /* Configure northbridge P-states */ ++ dword = pci_read_config32(dev, 0xe0); ++ dword &= ~(0x7 << 9); /* NbPstateThreshold = compute_unit_count */ ++ dword |= (compute_unit_count & 0x7) << 9; ++ pci_write_config32(dev, 0xe0, dword); ++ ++ printk(BIOS_DEBUG, "done.\n"); ++} ++ ++ ++static struct device_operations mcf5_ops = { ++ .read_resources = pci_dev_read_resources, ++ .set_resources = pci_dev_set_resources, ++ .enable_resources = pci_dev_enable_resources, ++ .init = nb_control_init, ++ .scan_bus = 0, ++ .ops_pci = 0, ++}; ++ ++static const struct pci_driver mcf5_driver_fam15 __pci_driver = { ++ .ops = &mcf5_ops, ++ .vendor = PCI_VENDOR_ID_AMD, ++ .device = 0x1605, ++}; +\ No newline at end of file +diff --git a/src/northbridge/amd/amdfam10/northbridge.c b/src/northbridge/amd/amdfam10/northbridge.c +index adcfdf0..baf77d6 100644 +--- a/src/northbridge/amd/amdfam10/northbridge.c ++++ b/src/northbridge/amd/amdfam10/northbridge.c +@@ -81,6 +81,21 @@ device_t get_node_pci(u32 nodeid, u32 fn) + #endif + } + ++static inline uint8_t is_fam15h(void) ++{ ++ uint8_t fam15h = 0; ++ uint32_t family; ++ ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ return fam15h; ++} ++ + static void get_fx_devs(void) + { + int i; +@@ -202,7 +217,7 @@ static void amd_g34_fixup(struct bus *link, device_t dev) + /* Revision D or later */ + rev_gte_d = 1; + +- if (rev_gte_d) { ++ if (rev_gte_d || is_fam15h()) { + f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8); + + /* Check for dual node capability */ +@@ -215,6 +230,15 @@ static void amd_g34_fixup(struct bus *link, device_t dev) + */ + f3xe8 = pci_read_config32(get_node_pci(nodeid, 3), 0xe8); + uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30); ++ uint8_t defective_link_number_1; ++ uint8_t defective_link_number_2; ++ if (is_fam15h()) { ++ defective_link_number_1 = 4; /* Link 0 Sublink 1 */ ++ defective_link_number_2 = 7; /* Link 3 Sublink 1 */ ++ } else { ++ defective_link_number_1 = 6; /* Link 2 Sublink 1 */ ++ defective_link_number_2 = 5; /* Link 1 Sublink 1 */ ++ } + if (internal_node_number == 0) { + /* Node 0 */ + if (link->link_num == 6) /* Link 2 Sublink 1 */ +@@ -314,6 +338,46 @@ static void amdfam10_scan_chains(device_t dev) + { + struct bus *link; + ++#if CONFIG_CPU_AMD_SOCKET_G34_NON_AGESA ++ if (is_fam15h()) { ++ uint8_t current_link_number = 0; ++ ++ for (link = dev->link_list; link; link = link->next) { ++ /* The following links have changed position in Fam15h G34 processors: ++ * Fam10 Fam15 ++ * Node 0 ++ * L3 --> L1 ++ * L0 --> L3 ++ * L1 --> L2 ++ * L2 --> L0 ++ * Node 1 ++ * L0 --> L0 ++ * L1 --> L3 ++ * L2 --> L1 ++ * L3 --> L2 ++ */ ++ if (link->link_num == 0) ++ link->link_num = 3; ++ else if (link->link_num == 1) ++ link->link_num = 2; ++ else if (link->link_num == 2) ++ link->link_num = 0; ++ else if (link->link_num == 3) ++ link->link_num = 1; ++ else if (link->link_num == 5) ++ link->link_num = 7; ++ else if (link->link_num == 6) ++ link->link_num = 5; ++ else if (link->link_num == 7) ++ link->link_num = 6; ++ ++ current_link_number++; ++ if (current_link_number > 3) ++ current_link_number = 0; ++ } ++ } ++#endif ++ + /* Do sb ht chain at first, in case s2885 put sb chain (8131/8111) on link2, but put 8151 on link0 */ + trim_ht_chain(dev); + +@@ -620,13 +684,21 @@ static const struct pci_driver mcf0_driver __pci_driver = { + .device = 0x1200, + }; + ++ + static void amdfam10_nb_init(void *chip_info) + { + relocate_sb_ht_chain(); + } + ++static const struct pci_driver mcf0_driver_fam15 __pci_driver = { ++ .ops = &northbridge_operations, ++ .vendor = PCI_VENDOR_ID_AMD, ++ .device = 0x1600, ++}; ++ ++ + struct chip_operations northbridge_amd_amdfam10_ops = { +- CHIP_NAME("AMD FAM10 Northbridge") ++ CHIP_NAME("AMD Family 10h/15h Northbridge") + .enable_dev = 0, + .init = amdfam10_nb_init, + }; +@@ -950,38 +1022,61 @@ static int amdfam10_get_smbios_data16(int* count, int handle, unsigned long *cur + + static uint16_t amdmct_mct_speed_enum_to_mhz(uint8_t speed) + { +- if (IS_ENABLED(CONFIG_DIMM_DDR2)) { +- switch (speed) { +- case 1: +- return 200; +- case 2: +- return 266; +- case 3: +- return 333; +- case 4: +- return 400; +- case 5: +- return 533; +- default: +- return 0; +- } +- } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) { +- switch (speed) { +- case 3: +- return 333; +- case 4: +- return 400; +- case 5: +- return 533; +- case 6: +- return 667; +- case 7: +- return 800; +- default: +- return 0; ++ if (is_fam15h()) { ++ if (IS_ENABLED(CONFIG_DIMM_DDR3)) { ++ switch (speed) { ++ case 0x4: ++ return 333; ++ case 0x6: ++ return 400; ++ case 0xa: ++ return 533; ++ case 0xe: ++ return 667; ++ case 0x12: ++ return 800; ++ case 0x16: ++ return 933; ++ default: ++ return 0; ++ } ++ } else { ++ return 0; + } + } else { +- return 0; ++ if (IS_ENABLED(CONFIG_DIMM_DDR2)) { ++ switch (speed) { ++ case 1: ++ return 200; ++ case 2: ++ return 266; ++ case 3: ++ return 333; ++ case 4: ++ return 400; ++ case 5: ++ return 533; ++ default: ++ return 0; ++ } ++ } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) { ++ switch (speed) { ++ case 3: ++ return 333; ++ case 4: ++ return 400; ++ case 5: ++ return 533; ++ case 6: ++ return 667; ++ case 7: ++ return 800; ++ default: ++ return 0; ++ } ++ } else { ++ return 0; ++ } + } + } + +@@ -1076,6 +1171,8 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle, + #if IS_ENABLED(CONFIG_DIMM_DDR3) + /* Find the maximum and minimum supported voltages */ + uint8_t supported_voltages = mem_info->dct_stat[node].DimmSupportedVoltages[slot]; ++ uint8_t configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot]; ++ + if (supported_voltages & 0x8) + t->minimum_voltage = 1150; + else if (supported_voltages & 0x4) +@@ -1094,7 +1191,14 @@ static int amdfam10_get_smbios_data17(int* count, int handle, int parent_handle, + else if (supported_voltages & 0x8) + t->maximum_voltage = 1150; + +- t->configured_voltage = mem_info->dct_stat[node].DimmConfiguredVoltage[slot]; ++ if (configured_voltage & 0x8) ++ t->configured_voltage = 1150; ++ else if (configured_voltage & 0x4) ++ t->configured_voltage = 1250; ++ else if (configured_voltage & 0x2) ++ t->configured_voltage = 1350; ++ else if (configured_voltage & 0x1) ++ t->configured_voltage = 1500; + #endif + } + t->memory_error_information_handle = 0xFFFE; /* no error information handle available */ +@@ -1233,12 +1337,14 @@ static void cpu_bus_scan(device_t dev) + #if CONFIG_CBB + device_t pci_domain; + #endif ++ int nvram = 0; + int i,j; + int nodes; + unsigned nb_cfg_54; + unsigned siblings; + int cores_found; + int disable_siblings; ++ uint8_t disable_cu_siblings = 0; + unsigned ApicIdCoreIdSize; + + nb_cfg_54 = 0; +@@ -1325,14 +1431,23 @@ static void cpu_bus_scan(device_t dev) + /* Always use the devicetree node with lapic_id 0 for BSP. */ + remap_bsp_lapic(cpu_bus); + ++ if (get_option(&nvram, "compute_unit_siblings") == CB_SUCCESS) ++ disable_cu_siblings = !!nvram; ++ ++ if (disable_cu_siblings) ++ printk(BIOS_DEBUG, "Disabling siblings on each compute unit as requested\n"); ++ + for(i = 0; i < nodes; i++) { + device_t cdb_dev; + unsigned busn, devn; + struct bus *pbus; + ++ uint8_t fam15h = 0; + uint8_t rev_gte_d = 0; + uint8_t dual_node = 0; + uint32_t f3xe8; ++ uint32_t family; ++ uint32_t model; + + busn = CONFIG_CBB; + devn = CONFIG_CDB+i; +@@ -1372,7 +1487,16 @@ static void cpu_bus_scan(device_t dev) + + f3xe8 = pci_read_config32(get_node_pci(0, 3), 0xe8); + +- if (cpuid_eax(0x80000001) >= 0x8) ++ family = model = cpuid_eax(0x80000001); ++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4); ++ ++ if (is_fam15h()) { ++ /* Family 15h or later */ ++ fam15h = 1; ++ nb_cfg_54 = 1; ++ } ++ ++ if ((model >= 0x8) || fam15h) + /* Revision D or later */ + rev_gte_d = 1; + +@@ -1382,13 +1506,20 @@ static void cpu_bus_scan(device_t dev) + dual_node = 1; + + cores_found = 0; // one core +- cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3)); ++ if (fam15h) ++ cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 5)); ++ else ++ cdb_dev = dev_find_slot(busn, PCI_DEVFN(devn, 3)); + int enable_node = cdb_dev && cdb_dev->enabled; + if (enable_node) { +- j = pci_read_config32(cdb_dev, 0xe8); +- cores_found = (j >> 12) & 3; // dev is func 3 +- if (siblings > 3) +- cores_found |= (j >> 13) & 4; ++ if (fam15h) { ++ cores_found = pci_read_config32(cdb_dev, 0x84) & 0xff; ++ } else { ++ j = pci_read_config32(cdb_dev, 0xe8); ++ cores_found = (j >> 12) & 3; // dev is func 3 ++ if (siblings > 3) ++ cores_found |= (j >> 13) & 4; ++ } + printk(BIOS_DEBUG, " %s siblings=%d\n", dev_path(cdb_dev), cores_found); + } + +@@ -1408,15 +1539,24 @@ static void cpu_bus_scan(device_t dev) + + if (dual_node) { + apic_id = 0; +- if (nb_cfg_54) { +- apic_id |= ((i >> 1) & 0x3) << 4; /* Node ID */ ++ if (fam15h) { ++ apic_id |= ((i >> 1) & 0x3) << 5; /* Node ID */ + apic_id |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */ + } else { +- apic_id |= i & 0x3; /* Node ID */ +- apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */ ++ if (nb_cfg_54) { ++ apic_id |= ((i >> 1) & 0x3) << 4; /* Node ID */ ++ apic_id |= ((i & 0x1) * (siblings + 1)) + j; /* Core ID */ ++ } else { ++ apic_id |= i & 0x3; /* Node ID */ ++ apic_id |= (((i & 0x1) * (siblings + 1)) + j) << 4; /* Core ID */ ++ } + } + } else { +- apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ? ++ if (fam15h) { ++ apic_id = (i * (siblings + 1)) + j; ++ } else { ++ apic_id = i * (nb_cfg_54?(siblings+1):1) + j * (nb_cfg_54?1:64); // ? ++ } + } + + #if CONFIG_ENABLE_APIC_EXT_ID && (CONFIG_APIC_ID_OFFSET>0) +@@ -1426,6 +1566,9 @@ static void cpu_bus_scan(device_t dev) + } + } + #endif ++ if (disable_cu_siblings && (j & 0x1)) ++ continue; ++ + device_t cpu = add_cpu_device(cpu_bus, apic_id, enable_node); + if (cpu) + amd_cpu_topology(cpu, i, j); +@@ -1484,6 +1627,6 @@ static void root_complex_enable_dev(struct device *dev) + } + + struct chip_operations northbridge_amd_amdfam10_root_complex_ops = { +- CHIP_NAME("AMD FAM10 Root Complex") ++ CHIP_NAME("AMD Family 10h/15h Root Complex") + .enable_dev = root_complex_enable_dev, + }; +diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c +index 5068e7a..cae228f 100644 +--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c ++++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c +@@ -44,8 +44,120 @@ static void print_tf(const char *func, const char *strval) + #endif + } + +-static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq) ++static inline void fam15h_switch_dct(uint32_t dev, uint8_t dct) + { ++ uint32_t dword; ++ ++ dword = Get_NB32(dev, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ Set_NB32(dev, 0x10c, dword); ++} ++ ++static inline void fam15h_switch_nb_pstate_config_reg(uint32_t dev, uint8_t nb_pstate) ++{ ++ uint32_t dword; ++ ++ dword = Get_NB32(dev, 0x10c); ++ dword &= ~(0x3 << 4); ++ dword |= (nb_pstate & 0x3) << 4; ++ Set_NB32(dev, 0x10c, dword); ++} ++ ++static inline uint32_t Get_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ return Get_NB32(dev, reg); ++ } else { ++ return Get_NB32(dev, (0x100 * dct) + reg); ++ } ++} ++ ++static inline void Set_NB32_DCT(uint32_t dev, uint8_t dct, uint32_t reg, uint32_t val) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ Set_NB32(dev, reg, val); ++ } else { ++ Set_NB32(dev, (0x100 * dct) + reg, val); ++ } ++} ++ ++static inline uint32_t Get_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate); ++ return Get_NB32(dev, reg); ++ } else { ++ return Get_NB32(dev, (0x100 * dct) + reg); ++ } ++} ++ ++static inline void Set_NB32_DCT_NBPstate(uint32_t dev, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t val) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ fam15h_switch_nb_pstate_config_reg(dev_map, nb_pstate); ++ Set_NB32(dev, reg, val); ++ } else { ++ Set_NB32(dev, (0x100 * dct) + reg, val); ++ } ++} ++ ++static inline uint32_t Get_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ return Get_NB32_index_wait(dev, index_reg, index); ++ } else { ++ return Get_NB32_index_wait(dev, (0x100 * dct) + index_reg, index); ++ } ++} ++ ++static inline void Set_NB32_index_wait_DCT(uint32_t dev, uint8_t dct, uint32_t index_reg, uint32_t index, uint32_t data) ++{ ++ if (is_fam15h()) { ++ /* Obtain address of function 0x1 */ ++ uint32_t dev_map = (dev & (~(0x7 << 12))) | (0x1 << 12); ++ fam15h_switch_dct(dev_map, dct); ++ Set_NB32_index_wait(dev, index_reg, index, data); ++ } else { ++ Set_NB32_index_wait(dev, (0x100 * dct) + index_reg, index, data); ++ } ++} ++ ++static uint16_t voltage_index_to_mv(uint8_t index) ++{ ++ if (index & 0x8) ++ return 1150; ++ if (index & 0x4) ++ return 1250; ++ else if (index & 0x2) ++ return 1350; ++ else ++ return 1500; ++} ++ ++static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t highest_rank_count, uint8_t registered, uint8_t voltage, uint16_t freq) ++{ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ + /* Return limited maximum RAM frequency */ + if (IS_ENABLED(CONFIG_DIMM_DDR2)) { + if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) { +@@ -68,34 +180,178 @@ static uint16_t mct_MaxLoadFreq(uint8_t count, uint8_t registered, uint16_t freq + } + } + } else if (IS_ENABLED(CONFIG_DIMM_DDR3)) { +- if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) { +- /* K10 BKDG Rev. 3.62 Table 34 */ +- if (count > 2) { +- /* Limit to DDR3-800 */ +- if (freq > 400) { +- freq = 400; +- print_tf(__func__, ": More than 2 registered DIMMs on channel; limiting to DDR3-800\n"); ++ if (voltage == 0) { ++ printk(BIOS_DEBUG, "%s: WARNING: Mainboard DDR3 voltage unknown, assuming 1.5V!\n", __func__); ++ voltage = 0x1; ++ } ++ ++ if (is_fam15h()) { ++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) { ++ /* Fam15h BKDG Rev. 3.14 Table 27 */ ++ if (voltage & 0x4) { ++ /* 1.25V */ ++ if (count > 1) { ++ if (highest_rank_count > 1) { ++ /* Limit to DDR3-1066 */ ++ if (freq > 533) { ++ freq = 533; ++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else if (voltage & 0x2) { ++ /* 1.35V */ ++ if (count > 1) { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else if (voltage & 0x1) { ++ /* 1.50V */ ++ if (count > 1) { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: More than 1 registered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1866 */ ++ if (freq > 933) { ++ freq = 933; ++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } ++ } else { ++ /* Fam15h BKDG Rev. 3.14 Table 26 */ ++ if (voltage & 0x4) { ++ /* 1.25V */ ++ if (count > 1) { ++ if (highest_rank_count > 1) { ++ /* Limit to DDR3-1066 */ ++ if (freq > 533) { ++ freq = 533; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else if (voltage & 0x2) { ++ /* 1.35V */ ++ if (MaxDimmsInstallable > 1) { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else if (voltage & 0x1) { ++ if (MaxDimmsInstallable == 1) { ++ if (count > 1) { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1866 */ ++ if (freq > 933) { ++ freq = 933; ++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1866\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else { ++ if (count > 1) { ++ if (highest_rank_count > 1) { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: More than 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } else { ++ /* Limit to DDR3-1600 */ ++ if (freq > 800) { ++ freq = 800; ++ printk(BIOS_DEBUG, "%s: 1 unbuffered DIMM on %dmV channel; limiting to DDR3-1600\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } ++ } + } +- } else if (count == 2) { +- /* Limit to DDR3-1066 */ +- if (freq > 533) { +- freq = 533; +- print_tf(__func__, ": 2 registered DIMMs on channel; limiting to DDR3-1066\n"); ++ } ++ } else { ++ if (IS_ENABLED(CONFIG_DIMM_REGISTERED) && registered) { ++ /* K10 BKDG Rev. 3.62 Table 34 */ ++ if (count > 2) { ++ /* Limit to DDR3-800 */ ++ if (freq > 400) { ++ freq = 400; ++ printk(BIOS_DEBUG, "%s: More than 2 registered DIMMs on %dmV channel; limiting to DDR3-800\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else if (count == 2) { ++ /* Limit to DDR3-1066 */ ++ if (freq > 533) { ++ freq = 533; ++ printk(BIOS_DEBUG, "%s: 2 registered DIMMs on %dmV channel; limiting to DDR3-1066\n", __func__, voltage_index_to_mv(voltage)); ++ } ++ } else { ++ /* Limit to DDR3-1333 */ ++ if (freq > 666) { ++ freq = 666; ++ printk(BIOS_DEBUG, "%s: 1 registered DIMM on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); ++ } + } + } else { ++ /* K10 BKDG Rev. 3.62 Table 33 */ + /* Limit to DDR3-1333 */ + if (freq > 666) { + freq = 666; +- print_tf(__func__, ": 1 registered DIMM on channel; limiting to DDR3-1333\n"); ++ printk(BIOS_DEBUG, "%s: unbuffered DIMMs on %dmV channel; limiting to DDR3-1333\n", __func__, voltage_index_to_mv(voltage)); + } + } +- } else { +- /* K10 BKDG Rev. 3.62 Table 33 */ +- /* Limit to DDR3-1333 */ +- if (freq > 666) { +- freq = 666; +- print_tf(__func__, ": unbuffered DIMMs on channel; limiting to DDR3-1333\n"); +- } + } + } + +@@ -225,11 +481,13 @@ void mctGet_DIMMAddr(struct DCTStatStruc *pDCTstat, u32 node) + + } + ++#if IS_ENABLED(CONFIG_SET_FIDVID) + static u8 mctGetProcessorPackageType(void) { + /* FIXME: I guess this belongs wherever mctGetLogicalCPUID ends up ? */ +- u32 BrandId = cpuid_ebx(0x80000001); +- return (u8)((BrandId >> 28) & 0x0F); ++ u32 BrandId = cpuid_ebx(0x80000001); ++ return (u8)((BrandId >> 28) & 0x0F); + } ++#endif + + static void raminit_amdmct(struct sys_info *sysinfo) + { +diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c +index 97f9db8..8f9177f 100644 +--- a/src/northbridge/amd/amdht/h3ncmn.c ++++ b/src/northbridge/amd/amdht/h3ncmn.c +@@ -43,6 +43,7 @@ + #define CPU_HTNB_FUNC_04 4 + #define CPU_ADDR_FUNC_01 1 + #define CPU_NB_FUNC_03 3 ++#define CPU_NB_FUNC_05 5 + + /* Function 0 registers */ + #define REG_ROUTE0_0X40 0x40 +@@ -70,6 +71,7 @@ + #define REG_NB_CPUID_3XFC 0xFC + #define REG_NB_LINK_XCS_TOKEN0_3X148 0x148 + #define REG_NB_DOWNCORE_3X190 0x190 ++#define REG_NB_CAPABILITY_5X84 0x84 + + /* Function 4 registers */ + +@@ -555,9 +557,10 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb) + 15, 12, &temp); + + /* bits[15,13,12] specify the cores */ +- /* Support Downcoring */ + temp = ((temp & 8) >> 1) + (temp & 3); + cores = temp + 1; ++ ++ /* Support Downcoring */ + AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node), + makePCIBusFromNode(node), + makePCIDeviceFromNode(node), +@@ -576,6 +579,56 @@ static u8 fam10GetNumCoresOnNode(u8 node, cNorthBridge *nb) + + /***************************************************************************//** + * ++ * static u8 ++ * fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb) ++ * ++ * Description: ++ * Return the number of cores (1 based count) on node. ++ * ++ * Parameters: ++ * @param[in] node = the node that will be examined ++ * @param[in] *nb = this northbridge ++ * @return = the number of cores ++ * ++ * ++ */ ++static u8 fam15GetNumCoresOnNode(u8 node, cNorthBridge *nb) ++{ ++ u32 temp, leveling, cores; ++ u8 i; ++ ++ ASSERT((node < nb->maxNodes)); ++ /* Read CmpCap [7:0] */ ++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), ++ makePCIBusFromNode(node), ++ makePCIDeviceFromNode(node), ++ CPU_NB_FUNC_05, ++ REG_NB_CAPABILITY_5X84), ++ 7, 0, &temp); ++ ++ /* bits[7:0] specify the cores */ ++ temp = temp & 0xff; ++ cores = temp + 1; ++ ++ /* Support Downcoring */ ++ AmdPCIReadBits (MAKE_SBDFO(makePCISegmentFromNode(node), ++ makePCIBusFromNode(node), ++ makePCIDeviceFromNode(node), ++ CPU_NB_FUNC_03, ++ REG_NB_DOWNCORE_3X190), ++ 31, 0, &leveling); ++ for (i=0; i<cores; i++) ++ { ++ if (leveling & ((u32) 1 << i)) ++ { ++ temp--; ++ } ++ } ++ return (u8)(temp+1); ++} ++ ++/***************************************************************************//** ++ * + * static void + * setTotalNodesAndCores(u8 node, u8 totalNodes, u8 totalCores, cNorthBridge *nb) + * +@@ -854,6 +907,69 @@ static BOOL fam10IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) + + /***************************************************************************//** + * ++ * static BOOL ++ * fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) ++ * ++ * Description: ++ * Get node capability and update the minimum supported system capability. ++ * Return whether the current configuration exceeds the capability. ++ * ++ * Parameters: ++ * @param[in] node = the node ++ * @param[in,out] *pDat = sysMpCap (updated) and NodesDiscovered ++ * @param[in] *nb = this northbridge ++ * @return true: system is capable of current config. ++ * false: system is not capable of current config. ++ * ++ * --------------------------------------------------------------------------------------- ++ */ ++static BOOL fam15IsCapable(u8 node, sMainData *pDat, cNorthBridge *nb) ++{ ++#ifndef HT_BUILD_NC_ONLY ++ u32 temp; ++ u8 maxNodes; ++ ++ ASSERT(node < nb->maxNodes); ++ ++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), ++ makePCIBusFromNode(node), ++ makePCIDeviceFromNode(node), ++ CPU_NB_FUNC_03, ++ REG_NB_CAPABILITY_3XE8), ++ 18, 16, &temp); ++ ++ if (temp != 0) ++ { ++ maxNodes = (1 << (~temp & 0x3)); /* That is, 1, 2, 4, or 8 */ ++ } ++ else ++ { ++ /* Check if CPU package is dual node */ ++ AmdPCIReadBits(MAKE_SBDFO(makePCISegmentFromNode(node), ++ makePCIBusFromNode(node), ++ makePCIDeviceFromNode(node), ++ CPU_NB_FUNC_03, ++ REG_NB_CAPABILITY_3XE8), ++ 29, 29, &temp); ++ if (temp) ++ maxNodes = 4; ++ else ++ maxNodes = 8; ++ } ++ ++ if (pDat->sysMpCap > maxNodes) ++ { ++ pDat->sysMpCap = maxNodes; ++ } ++ /* Note since sysMpCap is one based and NodesDiscovered is zero based, equal is false */ ++ return (pDat->sysMpCap > pDat->NodesDiscovered); ++#else ++ return 1; ++#endif ++} ++ ++/***************************************************************************//** ++ * + * static void + * fam0fStopLink(u8 currentNode, u8 currentLink, cNorthBridge *nb) + * +@@ -2068,6 +2184,49 @@ void newNorthBridge(u8 node, cNorthBridge *nb) + u32 match; + u32 extFam, baseFam, model; + ++ cNorthBridge fam15 = ++ { ++#ifdef HT_BUILD_NC_ONLY ++ 8, ++ 1, ++ 12, ++#else ++ 8, ++ 8, ++ 64, ++#endif /* HT_BUILD_NC_ONLY*/ ++ writeRoutingTable, ++ writeNodeID, ++ readDefLnk, ++ enableRoutingTables, ++ verifyLinkIsCoherent, ++ readTrueLinkFailStatus, ++ readToken, ++ writeToken, ++ fam15GetNumCoresOnNode, ++ setTotalNodesAndCores, ++ limitNodes, ++ writeFullRoutingTable, ++ isCompatible, ++ fam15IsCapable, ++ (void (*)(u8, u8, cNorthBridge*))commonVoid, ++ (BOOL (*)(u8, u8, sMainData*, cNorthBridge*))commonReturnFalse, ++ readSbLink, ++ verifyLinkIsNonCoherent, ++ ht3SetCFGAddrMap, ++ convertBitsToWidth, ++ convertWidthToBits, ++ fam10NorthBridgeFreqMask, ++ gatherLinkData, ++ setLinkData, ++ ht3WriteTrafficDistribution, ++ fam10BufferOptimizations, ++ 0x00000001, ++ 0x00000200, ++ 18, ++ 0x00000f06 ++ }; ++ + cNorthBridge fam10 = + { + #ifdef HT_BUILD_NC_ONLY +@@ -2175,8 +2334,14 @@ void newNorthBridge(u8 node, cNorthBridge *nb) + 7, 4, &model); + match = (u32)((baseFam << 8) | extFam); + +- /* Test each in turn looking for a match. Init the struct if found */ +- if (match == fam10.compatibleKey) ++ /* Test each in turn looking for a match. ++ * Initialize the struct if found. ++ */ ++ if (match == fam15.compatibleKey) ++ { ++ Amdmemcpy((void *)nb, (const void *)&fam15, (u32) sizeof(cNorthBridge)); ++ } ++ else if (match == fam10.compatibleKey) + { + Amdmemcpy((void *)nb, (const void *)&fam10, (u32) sizeof(cNorthBridge)); + } +diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c +index 389b1b1..c0ccc69 100644 +--- a/src/northbridge/amd/amdht/ht_wrapper.c ++++ b/src/northbridge/amd/amdht/ht_wrapper.c +@@ -174,16 +174,22 @@ void amd_ht_fixup(struct sys_info *sysinfo) { + printk(BIOS_DEBUG, "amd_ht_fixup()\n"); + if (IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX)) { + uint8_t rev_gte_d = 0; ++ uint8_t fam15h = 0; + uint8_t dual_node = 0; + uint32_t f3xe8; + uint32_t family; + uint32_t model; + + family = model = cpuid_eax(0x80000001); +- model = ((model & 0xf0000) >> 16) | ((model & 0xf0) >> 4); ++ model = ((model & 0xf0000) >> 12) | ((model & 0xf0) >> 4); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); + +- if (model >= 0x8) +- /* Revision D or later */ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ if ((model >= 0x8) || fam15h) ++ /* Family 10h Revision D or later */ + rev_gte_d = 1; + + if (rev_gte_d) { +@@ -195,7 +201,8 @@ void amd_ht_fixup(struct sys_info *sysinfo) { + + if (dual_node) { + /* Each G34 processor contains a defective HT link. +- * See the BKDG Rev 3.62 section 2.7.1.5 for details. ++ * See the Family 10h BKDG Rev 3.62 section 2.7.1.5 for details ++ * For Family 15h see the BKDG Rev. 3.14 section 2.12.1.5 for details. + */ + uint8_t node; + uint8_t node_count = get_nodes(); +@@ -205,46 +212,46 @@ void amd_ht_fixup(struct sys_info *sysinfo) { + uint8_t internal_node_number = ((f3xe8 & 0xc0000000) >> 30); + printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link\n", node, internal_node_number); + if (internal_node_number == 0) { +- uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xd8) & 0x1; ++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x98:0xd8) & 0x1; + if (package_link_3_connected) { + /* Set WidthIn and WidthOut to 0 */ +- dword = pci_read_config32(NODE_PCI(node, 0), 0xc4); ++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4); + dword &= ~0x77000000; +- pci_write_config32(NODE_PCI(node, 0), 0xc4, dword); ++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4, dword); + /* Set Ganged to 1 */ +- dword = pci_read_config32(NODE_PCI(node, 0), 0x178); ++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178); + dword |= 0x00000001; +- pci_write_config32(NODE_PCI(node, 0), 0x178, dword); ++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x170:0x178, dword); + } else { + /* Set ConnDly to 1 */ + dword = pci_read_config32(NODE_PCI(node, 0), 0x16c); + dword |= 0x00000100; + pci_write_config32(NODE_PCI(node, 0), 0x16c, dword); + /* Set TransOff and EndOfChain to 1 */ +- dword = pci_read_config32(NODE_PCI(node, 4), 0xc4); ++ dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4); + dword |= 0x000000c0; +- pci_write_config32(NODE_PCI(node, 4), 0xc4, dword); ++ pci_write_config32(NODE_PCI(node, 4), (fam15h)?0x84:0xc4, dword); + } + } else if (internal_node_number == 1) { +- uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), 0xb8) & 0x1; ++ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xf8:0xb8) & 0x1; + if (package_link_3_connected) { + /* Set WidthIn and WidthOut to 0 */ +- dword = pci_read_config32(NODE_PCI(node, 0), 0xa4); ++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4); + dword &= ~0x77000000; +- pci_write_config32(NODE_PCI(node, 0), 0xa4, dword); ++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4, dword); + /* Set Ganged to 1 */ +- dword = pci_read_config32(NODE_PCI(node, 0), 0x174); ++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174); + dword |= 0x00000001; +- pci_write_config32(NODE_PCI(node, 0), 0x174, dword); ++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174, dword); + } else { + /* Set ConnDly to 1 */ + dword = pci_read_config32(NODE_PCI(node, 0), 0x16c); + dword |= 0x00000100; + pci_write_config32(NODE_PCI(node, 0), 0x16c, dword); + /* Set TransOff and EndOfChain to 1 */ +- dword = pci_read_config32(NODE_PCI(node, 4), 0xa4); ++ dword = pci_read_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4); + dword |= 0x000000c0; +- pci_write_config32(NODE_PCI(node, 4), 0xa4, dword); ++ pci_write_config32(NODE_PCI(node, 4), (fam15h)?0xe4:0xa4, dword); + } + } + } +diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h +index 117fea5..20a77d3 100644 +--- a/src/northbridge/amd/amdmct/amddefs.h ++++ b/src/northbridge/amd/amdmct/amddefs.h +@@ -20,33 +20,35 @@ + /* FIXME: this file should be moved to include/cpu/amd/amddefs.h */ + + /* Public Revisions - USE THESE VERSIONS TO MAKE COMPARE WITH CPULOGICALID RETURN VALUE*/ +-#define AMD_SAFEMODE 0x80000000 /* Unknown future revision - SAFE MODE */ +-#define AMD_NPT_F0 0x00000001 /* F0 stepping */ +-#define AMD_NPT_F1 0x00000002 /* F1 stepping */ +-#define AMD_NPT_F2C 0x00000004 +-#define AMD_NPT_F2D 0x00000008 +-#define AMD_NPT_F2E 0x00000010 /* F2 stepping E */ +-#define AMD_NPT_F2G 0x00000020 /* F2 stepping G */ +-#define AMD_NPT_F2J 0x00000040 +-#define AMD_NPT_F2K 0x00000080 +-#define AMD_NPT_F3L 0x00000100 /* F3 Stepping */ +-#define AMD_NPT_G0A 0x00000200 /* G0 stepping */ +-#define AMD_NPT_G1B 0x00000400 /* G1 stepping */ +-#define AMD_DR_A0A 0x00010000 /* Barcelona A0 */ +-#define AMD_DR_A1B 0x00020000 /* Barcelona A1 */ +-#define AMD_DR_A2 0x00040000 /* Barcelona A2 */ +-#define AMD_DR_B0 0x00080000 /* Barcelona B0 */ +-#define AMD_DR_B1 0x00100000 /* Barcelona B1 */ +-#define AMD_DR_B2 0x00200000 /* Barcelona B2 */ +-#define AMD_DR_BA 0x00400000 /* Barcelona BA */ +-#define AMD_DR_B3 0x00800000 /* Barcelona B3 */ +-#define AMD_RB_C2 0x01000000 /* Shanghai C2 */ +-#define AMD_DA_C2 0x02000000 /* XXXX C2 */ +-#define AMD_HY_D0 0x04000000 /* Istanbul D0 */ +-#define AMD_RB_C3 0x08000000 /* ??? C3 */ +-#define AMD_DA_C3 0x10000000 /* XXXX C3 */ +-#define AMD_HY_D1 0x20000000 /* Istanbul D1 */ +-#define AMD_PH_E0 0x40000000 /* Phenom II X4 X6 */ ++#define AMD_SAFEMODE 0x8000000000000000 /* Unknown future revision - SAFE MODE */ ++#define AMD_NPT_F0 0x0000000000000001 /* F0 stepping */ ++#define AMD_NPT_F1 0x0000000000000002 /* F1 stepping */ ++#define AMD_NPT_F2C 0x0000000000000004 ++#define AMD_NPT_F2D 0x0000000000000008 ++#define AMD_NPT_F2E 0x0000000000000010 /* F2 stepping E */ ++#define AMD_NPT_F2G 0x0000000000000020 /* F2 stepping G */ ++#define AMD_NPT_F2J 0x0000000000000040 ++#define AMD_NPT_F2K 0x0000000000000080 ++#define AMD_NPT_F3L 0x0000000000000100 /* F3 Stepping */ ++#define AMD_NPT_G0A 0x0000000000000200 /* G0 stepping */ ++#define AMD_NPT_G1B 0x0000000000000400 /* G1 stepping */ ++#define AMD_DR_A0A 0x0000000000010000 /* Barcelona A0 */ ++#define AMD_DR_A1B 0x0000000000020000 /* Barcelona A1 */ ++#define AMD_DR_A2 0x0000000000040000 /* Barcelona A2 */ ++#define AMD_DR_B0 0x0000000000080000 /* Barcelona B0 */ ++#define AMD_DR_B1 0x0000000000100000 /* Barcelona B1 */ ++#define AMD_DR_B2 0x0000000000200000 /* Barcelona B2 */ ++#define AMD_DR_BA 0x0000000000400000 /* Barcelona BA */ ++#define AMD_DR_B3 0x0000000000800000 /* Barcelona B3 */ ++#define AMD_RB_C2 0x0000000001000000 /* Shanghai C2 */ ++#define AMD_DA_C2 0x0000000002000000 /* XXXX C2 */ ++#define AMD_HY_D0 0x0000000004000000 /* Istanbul D0 */ ++#define AMD_RB_C3 0x0000000008000000 /* ??? C3 */ ++#define AMD_DA_C3 0x0000000010000000 /* XXXX C3 */ ++#define AMD_HY_D1 0x0000000020000000 /* Istanbul D1 */ ++#define AMD_PH_E0 0x0000000040000000 /* Phenom II X4 X6 */ ++#define AMD_OR_B2 0x0000000080000000 /* Interlagos */ ++#define AMD_OR_C0 0x0000000100000000 /* Abu Dhabi */ + + /* + * Groups - Create as many as you wish, from the above public values +@@ -76,6 +78,7 @@ + #define AMD_DRBH_Cx (AMD_DR_Cx | AMD_HY_D0 ) + #define AMD_DRBA23_RBC2 (AMD_DR_BA | AMD_DR_B2 | AMD_DR_B3 | AMD_RB_C2 ) + #define AMD_DR_DAC2_OR_C3 (AMD_DA_C2 | AMD_DA_C3 | AMD_RB_C3) ++#define AMD_FAM15_ALL (AMD_OR_B2 | AMD_OR_C0) + + /* + * Public Platforms - USE THESE VERSIONS TO MAKE COMPARE WITH CPUPLATFORMTYPE RETURN VALUE +@@ -122,23 +125,34 @@ + */ + #define CPUID_EXT_PM 0x80000007 + #define CPUID_MODEL 1 +-#define MCG_CAP 0x00000179 ++#define MCG_CAP 0x00000179 + #define MCG_CTL_P 8 +-#define MC0_CTL 0x00000400 +-#define MC0_STA MC0_CTL + 1 +-#define FS_Base 0xC0000100 ++#define MC0_CTL 0x00000400 ++#define MC0_STA (MC0_CTL + 1) ++#define MC4_MISC0 0x00000413 ++#define MC4_MISC1 0xC0000408 ++#define MC4_MISC2 0xC0000409 ++#define FS_Base 0xC0000100 + #define SYSCFG 0xC0010010 + #define HWCR 0xC0010015 + #define NB_CFG 0xC001001F + #define FidVidStatus 0xC0010042 ++#define MC1_CTL_MASK 0xC0010045 + #define MC4_CTL_MASK 0xC0010048 + #define OSVW_ID_Length 0xC0010140 + #define OSVW_Status 0xC0010141 + #define CPUIDFEATURES 0xC0011004 + #define LS_CFG 0xC0011020 ++#define IC_CFG 0xC0011021 + #define DC_CFG 0xC0011022 + #define BU_CFG 0xC0011023 +-#define BU_CFG2 0xC001102A ++#define FP_CFG 0xC0011028 ++#define DE_CFG 0xC0011029 ++#define BU_CFG2 0xC001102A ++#define BU_CFG3 0xC001102B ++#define EX_CFG 0xC001102C ++#define LS_CFG2 0xC001102D ++#define IBS_OP_DATA3 0xC0011037 + + /* + * Processor package types +diff --git a/src/northbridge/amd/amdmct/mct/mct_d.c b/src/northbridge/amd/amdmct/mct/mct_d.c +index 88910e2..be0af65 100644 +--- a/src/northbridge/amd/amdmct/mct/mct_d.c ++++ b/src/northbridge/amd/amdmct/mct/mct_d.c +@@ -2189,6 +2189,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8); + for (k = 0; k < SPD_PARTN_LENGTH; k++) + pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k); ++ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0; + pDCTstat->DimmRevisionNumber[i] = 0; + for (k = 0; k < 2; k++) + pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8); +@@ -2206,8 +2207,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + if (byte & JED_REGADCMSK) { + RegDIMMPresent |= 1 << i; + pDCTstat->DimmRegistered[i] = 1; +- } +- else { ++ } else { + pDCTstat->DimmRegistered[i] = 0; + } + /* Check ECC capable */ +diff --git a/src/northbridge/amd/amdmct/mct/mct_d.h b/src/northbridge/amd/amdmct/mct/mct_d.h +index 132bdc9..6b6194d 100644 +--- a/src/northbridge/amd/amdmct/mct/mct_d.h ++++ b/src/northbridge/amd/amdmct/mct/mct_d.h +@@ -434,7 +434,7 @@ struct DCTStatStruc { /* A per Node structure*/ + /* CH A byte lane 0 - 7 maximum filtered window passing DQS delay value*/ + /* CH B byte lane 0 - 7 minimum filtered window passing DQS delay value*/ + /* CH B byte lane 0 - 7 maximum filtered window passing DQS delay value*/ +- u32 LogicalCPUID; /* The logical CPUID of the node*/ ++ uint64_t LogicalCPUID; /* The logical CPUID of the node*/ + u16 HostBiosSrvc1; /* Word sized general purpose field for use by host BIOS. Scratch space.*/ + u32 HostBiosSrvc2; /* Dword sized general purpose field for use by host BIOS. Scratch space.*/ + u16 DimmQRPresent; /* QuadRank DIMM present?*/ +@@ -529,7 +529,7 @@ struct DCTStatStruc { /* A per Node structure*/ + uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED]; + + uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED]; +- char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH]; ++ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1]; + uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED]; + uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED]; + } __attribute__((packed)); +@@ -598,17 +598,18 @@ struct DCTStatStruc { /* A per Node structure*/ + 266=266MHz (DDR533) + 333=333MHz (DDR667) + 400=400MHz (DDR800)*/ +-#define NV_ECC_CAP 4 /* Bus ECC capable (1-bits) ++#define NV_MIN_MEMCLK 4 /* Minimum platform demonstrated Memclock (10-bits) */ ++#define NV_ECC_CAP 5 /* Bus ECC capable (1-bits) + 0=Platform not capable + 1=Platform is capable*/ +-#define NV_4RANKType 5 /* Quad Rank DIMM slot type (2-bits) ++#define NV_4RANKType 6 /* Quad Rank DIMM slot type (2-bits) + 0=Normal + 1=R4 (4-Rank Registered DIMMs in AMD server configuration) + 2=S4 (Unbuffered SO-DIMMs)*/ +-#define NV_BYPMAX 6 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition). ++#define NV_BYPMAX 7 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition). + 4=4 times bypass (normal for non-UMA systems) + 7=7 times bypass (normal for UMA systems)*/ +-#define NV_RDWRQBYP 7 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition). ++#define NV_RDWRQBYP 8 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition). + 2=8 times (normal for non-UMA systems) + 3=16 times (normal for UMA systems)*/ + +@@ -671,8 +672,9 @@ struct DCTStatStruc { /* A per Node structure*/ + #define NV_ECCRedir 54 /* Dram ECC Redirection enable*/ + #define NV_DramBKScrub 55 /* Dram ECC Background Scrubber CTL*/ + #define NV_L2BKScrub 56 /* L2 ECC Background Scrubber CTL*/ +-#define NV_DCBKScrub 57 /* DCache ECC Background Scrubber CTL*/ +-#define NV_CS_SpareCTL 58 /* Chip Select Spare Control bit 0: ++#define NV_L3BKScrub 57 /* L3 ECC Background Scrubber CTL*/ ++#define NV_DCBKScrub 58 /* DCache ECC Background Scrubber CTL*/ ++#define NV_CS_SpareCTL 59 /* Chip Select Spare Control bit 0: + 0=disable Spare + 1=enable Spare */ + /* Chip Select Spare Control bit 1-4: +@@ -712,7 +714,7 @@ u8 mct_Get_Start_RcvrEnDly_1Pass(u8 Pass); + u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 RcvrEnDlyLimit, u8 Channel, u8 Receiver, u8 Pass); + void CPUMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); + void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); +-u32 mctGetLogicalCPUID(u32 Node); ++uint64_t mctGetLogicalCPUID(u32 Node); + u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); + void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u8 Pass); + void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); +diff --git a/src/northbridge/amd/amdmct/mct/mctpro_d.c b/src/northbridge/amd/amdmct/mct/mctpro_d.c +index c332357..fe56201 100644 +--- a/src/northbridge/amd/amdmct/mct/mctpro_d.c ++++ b/src/northbridge/amd/amdmct/mct/mctpro_d.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -23,7 +24,7 @@ void EarlySampleSupport_D(void) + + u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val) + { +- u32 tmp; ++ uint64_t tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + val &= 0x0FFFFFFF; +@@ -42,7 +43,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val) + * ( F2x[1, 0]8C[1:0] > 00b). Silicon Status: Fixed in Rev B + * FIXME: check if this is still required. + */ +- u32 tmp; ++ uint64_t tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + if(!(val & (3<<12) )) +@@ -54,7 +55,7 @@ u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val) + + void mct_ForceAutoPrecharge_D(struct DCTStatStruc *pDCTstat, u32 dct) + { +- u32 tmp; ++ uint64_t tmp; + u32 reg; + u32 reg_off; + u32 dev; +@@ -96,7 +97,7 @@ void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat, + * FIXME: check this. + */ + +- u32 tmp; ++ uint64_t tmp; + u32 dev; + u32 reg; + u32 val; +@@ -143,10 +144,9 @@ void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat, + u32 index; + u32 reg; + u32 val; +- u32 tmp; ++ uint64_t tmp; + u32 Channel; + +- + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + +@@ -206,7 +206,7 @@ u32 Modify_D3CMP(struct DCTStatStruc *pDCTstat, u32 dct, u32 value) + u32 index_reg; + u32 index; + u32 val; +- u32 tmp; ++ uint64_t tmp; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { +@@ -237,7 +237,7 @@ void SyncSetting(struct DCTStatStruc *pDCTstat) + * Silicon Status: Fix TBD + */ + +- u32 tmp; ++ uint64_t tmp; + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + pDCTstat->CH_ODC_CTL[1] = pDCTstat->CH_ODC_CTL[0]; +@@ -278,7 +278,7 @@ u32 CheckNBCOFAutoPrechg(struct DCTStatStruc *pDCTstat, u32 dct) + + void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct) + { +- u32 tmp; ++ uint64_t tmp; + u32 Speed; + u32 ch, ch_start, ch_end; + u32 index_reg; +@@ -286,7 +286,6 @@ void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct) + u32 dev; + u32 val; + +- + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { + Speed = pDCTstat->Speed; +@@ -331,7 +330,7 @@ static u8 mct_checkFenceHoleAdjust_D(struct MCTStatStruc *pMCTstat, + u8 ChipSel, u8 *result) + { + u8 ByteLane; +- u32 tmp; ++ uint64_t tmp; + + tmp = pDCTstat->LogicalCPUID; + if ((tmp == AMD_DR_A0A) || (tmp == AMD_DR_A1B) || (tmp == AMD_DR_A2)) { +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +index 12dfff1..74066b1 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c +@@ -75,6 +75,8 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static u16 Get_Fk_D(u8 k); + static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i); ++static void mct_preInitDCT(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat); + static void mct_initDCT(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); + static void mct_DramInit(struct MCTStatStruc *pMCTstat, +@@ -105,11 +107,11 @@ static void Get_TrwtTO(struct MCTStatStruc *pMCTstat, + static void Get_TrwtWB(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); + static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, +- u32 dev, u32 index_reg); ++ u32 dev, uint8_t dct, u32 index_reg); + static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, u8 dct, + u32 dev, u32 index_reg); + static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat, +- u32 dev, u32 index_reg, u32 index); ++ u32 dev, uint8_t dct, u32 index_reg, u32 index); + static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); + static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct, +@@ -128,6 +130,8 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static void SetODTTriState(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); ++static void InitDDRPhy(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct); + static void InitPhyCompensation(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static u32 mct_NodePresent_D(void); +@@ -138,7 +142,9 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat, + static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstat); ++ struct DCTStatStruc *pDCTstat, u8 dct); ++static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct); + void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); + static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat, +@@ -158,6 +164,10 @@ static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct); + static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); ++static void ChangeMemClk(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat); ++void SetTargetFreq(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat); + + static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel); +@@ -165,7 +175,8 @@ static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dimm); + static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2); + static void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat); +-static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); ++static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstatA, uint8_t Pass); + static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static void SyncSetting(struct DCTStatStruc *pDCTstat); +@@ -173,6 +184,12 @@ static u8 crcCheck(u8 smbaddr); + static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat); + static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat); + ++static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, ++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg); ++ ++static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, ++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg); ++ + /*See mctAutoInitMCT header for index relationships to CL and T*/ + static const u16 Table_F_k[] = {00,200,266,333,400,533 }; + static const u8 Tab_BankAddr[] = {0x3F,0x01,0x09,0x3F,0x3F,0x11,0x0A,0x19,0x12,0x1A,0x21,0x22,0x23}; +@@ -223,6 +240,936 @@ static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF}; + static const u8 Table_Comp_Fall_Slew_20x[] = {7, 5, 3, 2, 0xFF}; + static const u8 Table_Comp_Fall_Slew_15x[] = {7, 7, 5, 3, 0xFF}; + ++static uint8_t dct_ddr_voltage_index(struct DCTStatStruc *pDCTstat, uint8_t dct) ++{ ++ uint8_t dimm; ++ uint8_t ddr_voltage_index = 0; ++ ++ /* Find current DDR supply voltage for this DCT */ ++ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm++) { ++ if (pDCTstat->DIMMValidDCT[dct] & (1 << dimm)) ++ ddr_voltage_index |= pDCTstat->DimmConfiguredVoltage[dimm]; ++ } ++ if (ddr_voltage_index > 0x7) { ++ printk(BIOS_DEBUG, "%s: Insufficient DDR supply voltage indicated! Configuring processor for 1.25V operation, but this attempt may fail...\n", __func__); ++ ddr_voltage_index = 0x4; ++ } ++ if (ddr_voltage_index == 0x0) { ++ printk(BIOS_DEBUG, "%s: No DDR supply voltage indicated! Configuring processor for 1.5V operation, but this attempt may fail...\n", __func__); ++ ddr_voltage_index = 0x1; ++ } ++ ++ return ddr_voltage_index; ++} ++ ++static uint16_t fam15h_mhz_to_memclk_config(uint16_t freq) ++{ ++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; ++ uint16_t iter; ++ ++ /* Compute the index value for the given frequency */ ++ for (iter = 0; iter <= 0x16; iter++) { ++ if (fam15h_freq_tab[iter] == freq) ++ break; ++ } ++ if (fam15h_freq_tab[iter] == freq) ++ freq = iter; ++ if (freq == 0) ++ freq = 0x4; ++ ++ return freq; ++} ++ ++static uint16_t fam10h_mhz_to_memclk_config(uint16_t freq) ++{ ++ uint16_t fam10h_freq_tab[] = {0, 0, 0, 400, 533, 667, 800}; ++ uint16_t iter; ++ ++ /* Compute the index value for the given frequency */ ++ for (iter = 0; iter <= 0x6; iter++) { ++ if (fam10h_freq_tab[iter] == freq) ++ break; ++ } ++ if (fam10h_freq_tab[iter] == freq) ++ freq = iter; ++ if (freq == 0) ++ freq = 0x3; ++ ++ return freq; ++} ++ ++static uint16_t mhz_to_memclk_config(uint16_t freq) ++{ ++ if (is_fam15h()) ++ return fam15h_mhz_to_memclk_config(freq); ++ else ++ return fam10h_mhz_to_memclk_config(freq) + 1; ++} ++ ++static uint32_t fam15h_phy_predriver_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength) ++{ ++ uint8_t lrdimm = 0; ++ uint8_t package_type; ++ uint8_t ddr_voltage_index; ++ uint32_t calibration_code = 0; ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct); ++ package_type = mctGet_NVbits(NV_PACK_TYPE); ++ ++ if (!lrdimm) { ++ /* Not an LRDIMM */ ++ if ((package_type == PT_M2) || (package_type == PT_GR)) { ++ /* Socket AM3 or G34 */ ++ if (ddr_voltage_index & 0x4) { ++ /* 1.25V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 43 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xdb6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xfff; ++ } ++ } ++ else if (ddr_voltage_index & 0x2) { ++ /* 1.35V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 42 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xdb6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xbd6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xdb6; ++ } ++ } ++ else if (ddr_voltage_index & 0x1) { ++ /* 1.5V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 41 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xb6d; ++ } ++ } ++ } ++ else if (package_type == PT_C3) { ++ /* Socket C32 */ ++ if (ddr_voltage_index & 0x4) { ++ /* 1.25V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 46 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xdb6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xfff; ++ } ++ } ++ else if (ddr_voltage_index & 0x2) { ++ /* 1.35V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 45 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xdb6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xdb6; ++ } ++ } ++ else if (ddr_voltage_index & 0x1) { ++ /* 1.5V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 44 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xfff; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xb6d; ++ } ++ } ++ } ++ } else { ++ /* LRDIMM */ ++ ++ /* TODO ++ * Implement LRDIMM support ++ * See Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Tables 47 - 49 ++ */ ++ } ++ ++ return calibration_code; ++} ++ ++static uint32_t fam15h_phy_predriver_cmd_addr_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength) ++{ ++ uint8_t ddr_voltage_index; ++ uint32_t calibration_code = 0; ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct); ++ ++ if (ddr_voltage_index & 0x4) { ++ /* 1.25V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 52 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xb64; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xb64; ++ } ++ } ++ else if (ddr_voltage_index & 0x2) { ++ /* 1.35V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 51 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } ++ } ++ else if (ddr_voltage_index & 0x1) { ++ /* 1.5V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 50 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x492; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x492; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x6db; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x6db; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xb6d; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xb6d; ++ } ++ } ++ ++ return calibration_code; ++} ++ ++static uint32_t fam15h_phy_predriver_clk_calibration_code(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t drive_strength) ++{ ++ uint8_t ddr_voltage_index; ++ uint32_t calibration_code = 0; ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct); ++ ++ if (ddr_voltage_index & 0x4) { ++ /* 1.25V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 55 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xff6; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xff6; ++ } ++ } ++ else if (ddr_voltage_index & 0x2) { ++ /* 1.35V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 54 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xdad; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xdad; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xdad; ++ } ++ } ++ else if (ddr_voltage_index & 0x1) { ++ /* 1.5V */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 53 */ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6)) { ++ /* DDR3-667 - DDR3-800 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x1) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x2) ++ calibration_code = 0x924; ++ else if (drive_strength == 0x3) ++ calibration_code = 0x924; ++ } else if ((MemClkFreq == 0xa) || (MemClkFreq == 0xe)) { ++ /* DDR3-1066 - DDR3-1333 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xb6d; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (drive_strength == 0x0) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x1) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x2) ++ calibration_code = 0xff6; ++ else if (drive_strength == 0x3) ++ calibration_code = 0xff6; ++ } ++ } ++ ++ return calibration_code; ++} ++ ++static uint32_t fam15h_output_driver_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct) ++{ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ uint8_t package_type; ++ uint32_t calibration_code = 0; ++ ++ package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ /* Obtain number of DIMMs on channel */ ++ uint8_t dimm_count = pDCTstat->MAdimms[dct]; ++ uint8_t rank_count_dimm0; ++ uint8_t rank_count_dimm1; ++ ++ if (package_type == PT_GR) { ++ /* Socket G34 */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */ ++ if (MaxDimmsInstallable == 1) { ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ calibration_code = 0x00112222; ++ } ++ else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ calibration_code = 0x10112222; ++ } ++ else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x20112222; ++ } ++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ /* DDR3-1333 - DDR3-1600 */ ++ calibration_code = 0x30112222; ++ } ++ else if (MemClkFreq == 0x16) { ++ /* DDR3-1866 */ ++ calibration_code = 0x30332222; ++ } ++ } else if (MaxDimmsInstallable == 2) { ++ if (dimm_count == 1) { ++ /* 1 DIMM detected */ ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ calibration_code = 0x00112222; ++ } ++ else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ calibration_code = 0x10112222; ++ } ++ else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x20112222; ++ } ++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ /* DDR3-1333 - DDR3-1600 */ ++ calibration_code = 0x30112222; ++ } ++ } else if (dimm_count == 2) { ++ /* 2 DIMMs detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0]; ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ calibration_code = 0x10222222; ++ } ++ else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ calibration_code = 0x20222222; ++ } ++ else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x30222222; ++ } ++ else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ calibration_code = 0x30222222; ++ } ++ else if (MemClkFreq == 0x12) { ++ /* DDR3-1600 */ ++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1)) ++ calibration_code = 0x30222222; ++ else ++ calibration_code = 0x30112222; ++ } ++ } ++ } else if (MaxDimmsInstallable == 3) { ++ /* TODO ++ * 3 DIMM/channel support unimplemented ++ */ ++ } ++ } else { ++ /* TODO ++ * Other socket support unimplemented ++ */ ++ } ++ ++ return calibration_code; ++} ++ ++static uint32_t fam15h_address_timing_compensation_code(struct DCTStatStruc *pDCTstat, uint8_t dct) ++{ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ uint8_t package_type; ++ uint32_t calibration_code = 0; ++ ++ package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ /* Obtain number of DIMMs on channel */ ++ uint8_t dimm_count = pDCTstat->MAdimms[dct]; ++ uint8_t rank_count_dimm0; ++ uint8_t rank_count_dimm1; ++ ++ if (package_type == PT_GR) { ++ /* Socket G34 */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */ ++ if (MaxDimmsInstallable == 1) { ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00000000; ++ else ++ calibration_code = 0x003b0000; ++ } else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00000000; ++ else ++ calibration_code = 0x003b0000; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x00383837; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ calibration_code = 0x00363635; ++ } else if (MemClkFreq == 0x12) { ++ /* DDR3-1600 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00353533; ++ else ++ calibration_code = 0x00003533; ++ } else if (MemClkFreq == 0x16) { ++ /* DDR3-1866 */ ++ calibration_code = 0x00333330; ++ } ++ } else if (MaxDimmsInstallable == 2) { ++ if (dimm_count == 1) { ++ /* 1 DIMM detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00000000; ++ else ++ calibration_code = 0x003b0000; ++ } else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00000000; ++ else ++ calibration_code = 0x003b0000; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x00383837; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ calibration_code = 0x00363635; ++ } else if (MemClkFreq == 0x12) { ++ /* DDR3-1600 */ ++ if (rank_count_dimm0 == 1) ++ calibration_code = 0x00353533; ++ else ++ calibration_code = 0x00003533; ++ } ++ } else if (dimm_count == 2) { ++ /* 2 DIMMs detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0]; ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if (MemClkFreq == 0x4) { ++ /* DDR3-667 */ ++ calibration_code = 0x00390039; ++ } else if (MemClkFreq == 0x6) { ++ /* DDR3-800 */ ++ calibration_code = 0x00390039; ++ } else if (MemClkFreq == 0xa) { ++ /* DDR3-1066 */ ++ calibration_code = 0x003a3a3a; ++ } else if (MemClkFreq == 0xe) { ++ /* DDR3-1333 */ ++ calibration_code = 0x00003939; ++ } else if (MemClkFreq == 0x12) { ++ /* DDR3-1600 */ ++ if ((rank_count_dimm0 == 1) && (rank_count_dimm1 == 1)) ++ calibration_code = 0x00003738; ++ } ++ } ++ } else if (MaxDimmsInstallable == 3) { ++ /* TODO ++ * 3 DIMM/channel support unimplemented ++ */ ++ } ++ } else { ++ /* TODO ++ * Other socket support unimplemented ++ */ ++ } ++ ++ return calibration_code; ++} ++ ++static uint8_t fam15h_slow_access_mode(struct DCTStatStruc *pDCTstat, uint8_t dct) ++{ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ uint8_t package_type; ++ uint32_t slow_access = 0; ++ ++ package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint16_t MemClkFreq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ /* Obtain number of DIMMs on channel */ ++ uint8_t dimm_count = pDCTstat->MAdimms[dct]; ++ uint8_t rank_count_dimm0; ++ uint8_t rank_count_dimm1; ++ ++ if (package_type == PT_GR) { ++ /* Socket G34 */ ++ /* Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 Table 73 */ ++ if (MaxDimmsInstallable == 1) { ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6) ++ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) { ++ /* DDR3-667 - DDR3-1333 */ ++ slow_access = 0; ++ } else if ((MemClkFreq == 0x12) || (MemClkFreq == 0x16)) { ++ /* DDR3-1600 - DDR3-1866 */ ++ if (rank_count_dimm0 == 1) ++ slow_access = 0; ++ else ++ slow_access = 1; ++ } ++ } else if (MaxDimmsInstallable == 2) { ++ if (dimm_count == 1) { ++ /* 1 DIMM detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6) ++ || (MemClkFreq == 0xa) | (MemClkFreq == 0xe)) { ++ /* DDR3-667 - DDR3-1333 */ ++ slow_access = 0; ++ } ++ else if (MemClkFreq == 0x12) { ++ /* DDR3-1600 */ ++ if (rank_count_dimm0 == 1) ++ slow_access = 0; ++ else ++ slow_access = 1; ++ } ++ } else if (dimm_count == 2) { ++ /* 2 DIMMs detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0]; ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ ++ if ((MemClkFreq == 0x4) || (MemClkFreq == 0x6) ++ || (MemClkFreq == 0xa)) { ++ /* DDR3-667 - DDR3-1066 */ ++ slow_access = 0; ++ } ++ else if ((MemClkFreq == 0xe) || (MemClkFreq == 0x12)) { ++ /* DDR3-1333 - DDR3-1600 */ ++ slow_access = 1; ++ } ++ } ++ } else if (MaxDimmsInstallable == 3) { ++ /* TODO ++ * 3 DIMM/channel support unimplemented ++ */ ++ } ++ } else { ++ /* TODO ++ * Other socket support unimplemented ++ */ ++ } ++ ++ return slow_access; ++} ++ ++static void set_2t_configuration(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct) ++{ ++ uint32_t dev; ++ uint32_t reg; ++ uint32_t dword; ++ ++ uint8_t enable_slow_access_mode = 0; ++ dev = pDCTstat->dev_dct; ++ ++ if (is_fam15h()) { ++ if (pDCTstat->_2Tmode) ++ enable_slow_access_mode = 1; ++ } else { ++ if (pDCTstat->_2Tmode == 2) ++ enable_slow_access_mode = 1; ++ } ++ ++ reg = 0x94; /* DRAM Configuration High */ ++ dword = Get_NB32_DCT(dev, dct, reg); ++ if (enable_slow_access_mode) ++ dword |= (0x1 << 20); /* Set 2T CMD mode */ ++ else ++ dword &= ~(0x1 << 20); /* Clear 2T CMD mode */ ++ Set_NB32_DCT(dev, dct, reg, dword); ++} ++ ++static void precise_ndelay_fam15(struct MCTStatStruc *pMCTstat, uint32_t nanoseconds) { ++ msr_t tsc_msr; ++ uint64_t cycle_count = (((uint64_t)pMCTstat->TSCFreq) * nanoseconds) / 1000; ++ uint64_t start_timestamp; ++ uint64_t current_timestamp; ++ ++ tsc_msr = rdmsr(0x00000010); ++ start_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo; ++ do { ++ tsc_msr = rdmsr(0x00000010); ++ current_timestamp = (((uint64_t)tsc_msr.hi) << 32) | tsc_msr.lo; ++ } while ((current_timestamp - start_timestamp) < cycle_count); ++} ++ ++static void precise_memclk_delay_fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t clocks) { ++ uint16_t memclk_freq; ++ uint32_t delay_ns; ++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; ++ ++ memclk_freq = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ ++ delay_ns = (((uint64_t)clocks * 1000) / fam15h_freq_tab[memclk_freq]); ++ precise_ndelay_fam15(pMCTstat, delay_ns); ++} ++ + static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) + { +@@ -277,10 +1224,26 @@ static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, + restartinit: + mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/ + if (s3resume) { ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_En_Fam15\n"); ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat); ++ } ++ + #if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME) + printk(BIOS_DEBUG, "mctAutoInitMCT_D: Restoring DCT configuration from NVRAM\n"); + restore_mct_information_from_nvram(); + #endif ++ ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n"); ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat); ++ } + } else { + NodesWmem = 0; + node_sys_base = 0; +@@ -297,15 +1260,15 @@ restartinit: + pDCTstat->dev_map = PA_MAP(Node); + pDCTstat->dev_dct = PA_DCT(Node); + pDCTstat->dev_nbmisc = PA_NBMISC(Node); ++ pDCTstat->dev_link = PA_LINK(Node); ++ pDCTstat->dev_nbctl = PA_NBCTL(Node); + pDCTstat->NodeSysBase = node_sys_base; + + printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_init Node %d\n", Node); + mct_init(pMCTstat, pDCTstat); + mctNodeIDDebugPort_D(); + pDCTstat->NodePresent = NodePresent_D(Node); +- if (pDCTstat->NodePresent) { /* See if Node is there*/ +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: clear_legacy_Mode\n"); +- clear_legacy_Mode(pMCTstat, pDCTstat); ++ if (pDCTstat->NodePresent) { + pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node); + + printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_InitialMCT_D\n"); +@@ -314,6 +1277,26 @@ restartinit: + printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n"); + mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ + ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_preInitDCT\n"); ++ mct_preInitDCT(pMCTstat, pDCTstat); ++ } ++ node_sys_base = pDCTstat->NodeSysBase; ++ node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; ++ } ++ ++#if IS_ENABLED(DIMM_VOLTAGE_SET_SUPPORT) ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n"); ++ DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */ ++#endif ++ ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ if (pDCTstat->NodePresent) { ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mctSMBhub_Init\n"); ++ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ ++ + printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_initDCT\n"); + mct_initDCT(pMCTstat, pDCTstat); + if (pDCTstat->ErrCode == SC_FatalErr) { +@@ -321,20 +1304,13 @@ restartinit: + } else if (pDCTstat->ErrCode < SC_StopError) { + NodesWmem++; + } +- } /* if Node present */ +- node_sys_base = pDCTstat->NodeSysBase; +- node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; ++ } + } + if (NodesWmem == 0) { + printk(BIOS_DEBUG, "No Nodes?!\n"); + goto fatalexit; + } + +-#if IS_ENABLED(DIMM_VOLTAGE_SET_SUPPORT) +- printk(BIOS_DEBUG, "mctAutoInitMCT_D: DIMMSetVoltage\n"); +- DIMMSetVoltages(pMCTstat, pDCTstatA); /* Set the DIMM voltages (mainboard specific) */ +-#endif +- + printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n"); + SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/ + +@@ -355,7 +1331,6 @@ restartinit: + printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); + mct_OtherTiming(pMCTstat, pDCTstatA); + +- + if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/ + goto restartinit; + } +@@ -369,6 +1344,14 @@ restartinit: + MCTMemClr_D(pMCTstat,pDCTstatA); + } + ++ printk(BIOS_DEBUG, "mctAutoInitMCT_D: mct_ForceNBPState0_Dis_Fam15\n"); ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ ++ mct_ForceNBPState0_Dis_Fam15(pMCTstat, pDCTstat); ++ } ++ + mct_FinalMCT_D(pMCTstat, pDCTstatA); + printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus); + } +@@ -408,6 +1391,425 @@ static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat, + return ret; + } + ++/* Enable or disable phy-assisted training mode ++ * Phy-assisted training mode applies to the follow DRAM training procedures: ++ * Write Levelization Training (2.10.5.8.1) ++ * DQS Receiver Enable Training (2.10.5.8.2) ++ */ ++static void fam15EnableTrainingMode(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t enable) ++{ ++ uint8_t index; ++ uint32_t dword; ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ if (enable) { ++ /* Enable training mode */ ++ dword = Get_NB32_DCT(dev, dct, 0x78); /* DRAM Control */ ++ dword &= ~(0x1 << 17); /* AddrCmdTriEn = 0 */ ++ Set_NB32_DCT(dev, dct, 0x78, dword); /* DRAM Control */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */ ++ dword |= (0x1 << 18); /* DisAutoRefresh = 1 */ ++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x94); /* DRAM Configuration High */ ++ dword &= ~(0xf << 24); /* DcqBypassMax = 0 */ ++ dword &= ~(0x1 << 22); /* BankSwizzleMode = 0 */ ++ dword &= ~(0x1 << 15); /* PowerDownEn = 0 */ ++ dword &= ~(0x3 << 10); /* ZqcsInterval = 0 */ ++ Set_NB32_DCT(dev, dct, 0x94, dword); /* DRAM Configuration High */ ++ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d); ++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 0 */ ++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 0 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword); ++ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8)); ++ dword &= ~(0x1 << 12); /* EnRxPadStandby = 0 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword); ++ } ++ ++ dword = Get_NB32_DCT(dev, dct, 0xa4); /* DRAM Controller Temperature Throttle */ ++ dword &= ~(0x1 << 11); /* BwCapEn = 0 */ ++ dword &= ~(0x1 << 8); /* ODTSEn = 0 */ ++ Set_NB32_DCT(dev, dct, 0xa4, dword); /* DRAM Controller Temperature Throttle */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x110); /* DRAM Controller Select Low */ ++ dword &= ~(0x1 << 2); /* DctSelIntLvEn = 0 */ ++ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */ ++ dword &= ~(0x1f << 24); /* L3Scrub = 0 */ ++ dword &= ~(0x1f); /* DramScrub = 0 */ ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */ ++ dword &= ~(0x1); /* ScrubReDirEn = 0 */ ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */ ++ dword |= (0x1 << 4); /* L3ScrbRedirDis = 1 */ ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.1 */ ++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */ ++ dword &= ~(0xf << 24); /* TrdrdSdSc = 0xb */ ++ dword |= (0xb << 24); ++ dword &= ~(0xf << 16); /* TrdrdSdDc = 0xb */ ++ dword |= (0xb << 16); ++ dword &= ~(0xf); /* TrdrdDd = 0xb */ ++ dword |= 0xb; ++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.2 */ ++ dword = Get_NB32_DCT(dev, dct, 0x214); /* DRAM Timing 4 */ ++ dword &= ~(0xf << 16); /* TwrwrSdSc = 0xb */ ++ dword |= (0xb << 16); ++ dword &= ~(0xf << 8); /* TwrwrSdDc = 0xb */ ++ dword |= (0xb << 8); ++ dword &= ~(0xf); /* TwrwrDd = 0xb */ ++ dword |= 0xb; ++ Set_NB32_DCT(dev, dct, 0x214, dword); /* DRAM Timing 4 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.3 */ ++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */ ++ dword &= ~(0xf << 8); /* Twrrd = 0xb */ ++ dword |= (0xb << 8); ++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.4 */ ++ dword = Get_NB32_DCT(dev, dct, 0x21c); /* DRAM Timing 6 */ ++ dword &= ~(0x1f << 8); /* TrwtTO = 0x16 */ ++ dword |= (0x16 << 8); ++ dword &= ~(0x1f << 16); /* TrwtWB = TrwtTO + 1 */ ++ dword |= ((((dword >> 8) & 0x1f) + 1) << 16); ++ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */ ++ } else { ++ /* Disable training mode */ ++ uint8_t lane; ++ uint8_t dimm; ++ uint8_t receiver; ++ uint8_t max_lane; ++ uint8_t ecc_enabled; ++ uint8_t x4_present = 0; ++ uint8_t x8_present = 0; ++ uint8_t memclk_index; ++ uint8_t interleave_channels = 0; ++ uint8_t redirect_ecc_scrub = 0; ++ uint16_t trdrdsddc; ++ uint16_t trdrddd; ++ uint16_t cdd_trdrddd; ++ uint16_t twrwrsddc; ++ uint16_t twrwrdd; ++ uint16_t cdd_twrwrdd; ++ uint16_t twrrd; ++ uint16_t trwtto; ++ uint8_t first_dimm; ++ uint16_t delay; ++ uint16_t delay2; ++ uint8_t read_odt_delay; ++ uint8_t write_odt_delay; ++ uint16_t difference; ++ uint16_t current_total_delay_1[MAX_BYTE_LANES]; ++ uint16_t current_total_delay_2[MAX_BYTE_LANES]; ++ ++ /* FIXME ++ * This should be platform configurable ++ */ ++ uint8_t dimm_event_l_pin_support = 0; ++ ++ ecc_enabled = !!(pMCTstat->GStatus & 1 << GSB_ECCDIMMs); ++ if (ecc_enabled) ++ max_lane = 9; ++ else ++ max_lane = 8; ++ ++ if (pDCTstat->Dimmx4Present & ((dct)?0xaa:0x55)) ++ x4_present = 1; ++ if (pDCTstat->Dimmx8Present & ((dct)?0xaa:0x55)) ++ x8_present = 1; ++ memclk_index = Get_NB32_DCT(dev, dct, 0x94) & 0x1f; ++ ++ if (pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1] && mctGet_NVbits(NV_Unganged)) ++ interleave_channels = 1; ++ ++ if ((pMCTstat->GStatus & 1 << GSB_ECCDIMMs) && mctGet_NVbits(NV_ECCRedir)) ++ redirect_ecc_scrub = 1; ++ ++ dword = (Get_NB32_DCT(dev, dct, 0x240) >> 4) & 0xf; ++ if (dword > 6) ++ read_odt_delay = dword - 6; ++ else ++ read_odt_delay = 0; ++ ++ dword = Get_NB32_DCT(dev, dct, 0x240); ++ delay = (dword >> 4) & 0xf; ++ if (delay > 6) ++ read_odt_delay = delay - 6; ++ else ++ read_odt_delay = 0; ++ delay = (dword >> 12) & 0x7; ++ if (delay > 6) ++ write_odt_delay = delay - 6; ++ else ++ write_odt_delay = 0; ++ ++ /* TODO: ++ * Adjust trdrdsddc if four-rank DIMMs are installed per ++ * section 2.10.5.5.1 of the Family 15h BKDG. ++ * cdd_trdrdsddc will also need to be calculated in that process. ++ */ ++ trdrdsddc = 3; ++ ++ /* Calculate the Critical Delay Difference for TrdrdDd */ ++ cdd_trdrddd = 0; ++ first_dimm = 1; ++ for (receiver = 0; receiver < 8; receiver += 2) { ++ dimm = (receiver >> 1); ++ ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver)) ++ continue; ++ ++ read_dqs_receiver_enable_control_registers(current_total_delay_2, dev, dct, dimm, index_reg); ++ ++ if (first_dimm) { ++ memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1)); ++ first_dimm = 0; ++ } ++ ++ for (lane = 0; lane < max_lane; lane++) { ++ if (current_total_delay_1[lane] > current_total_delay_2[lane]) ++ difference = current_total_delay_1[lane] - current_total_delay_2[lane]; ++ else ++ difference = current_total_delay_2[lane] - current_total_delay_1[lane]; ++ ++ if (difference > cdd_trdrddd) ++ cdd_trdrddd = difference; ++ } ++ } ++ ++ /* Convert the difference to MEMCLKs */ ++ cdd_trdrddd = (((cdd_trdrddd >> 5) & 0x1f) + 1) / 2; ++ ++ /* Calculate Trdrddd */ ++ delay = (read_odt_delay + 3) * 2; ++ delay2 = cdd_trdrddd + 7; ++ if (delay2 > delay) ++ delay = delay2; ++ trdrddd = (delay + 1) / 2; /* + 1 is equivalent to ceiling function here */ ++ if (trdrdsddc > trdrddd) ++ trdrddd = trdrdsddc; ++ ++ /* TODO: ++ * Adjust twrwrsddc if four-rank DIMMs are installed per ++ * section 2.10.5.5.1 of the Family 15h BKDG. ++ * cdd_twrwrsddc will also need to be calculated in that process. ++ */ ++ twrwrsddc = 4; ++ ++ /* Calculate the Critical Delay Difference for TwrwrDd */ ++ cdd_twrwrdd = 0; ++ first_dimm = 1; ++ for (receiver = 0; receiver < 8; receiver += 2) { ++ dimm = (receiver >> 1); ++ ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, receiver)) ++ continue; ++ ++ read_dqs_write_timing_control_registers(current_total_delay_2, dev, dct, dimm, index_reg); ++ ++ if (first_dimm) { ++ memcpy(current_total_delay_1, current_total_delay_2, sizeof(current_total_delay_1)); ++ first_dimm = 0; ++ } ++ ++ for (lane = 0; lane < max_lane; lane++) { ++ if (current_total_delay_1[lane] > current_total_delay_2[lane]) ++ difference = current_total_delay_1[lane] - current_total_delay_2[lane]; ++ else ++ difference = current_total_delay_2[lane] - current_total_delay_1[lane]; ++ ++ if (difference > cdd_twrwrdd) ++ cdd_twrwrdd = difference; ++ } ++ } ++ ++ /* Convert the difference to MEMCLKs */ ++ cdd_twrwrdd = (((cdd_twrwrdd >> 5) & 0x1f) + 1) / 2; ++ ++ /* Calculate Twrwrdd */ ++ delay = (write_odt_delay + 3) * 2; ++ delay2 = cdd_twrwrdd + 7; ++ if (delay2 > delay) ++ delay = delay2; ++ twrwrdd = (delay + 1) / 2; /* + 1 is equivalent to ceiling function here */ ++ if (twrwrsddc > twrwrdd) ++ twrwrdd = twrwrsddc; ++ ++ dword = Get_NB32_DCT(dev, dct, 0x78); /* DRAM Control */ ++ dword |= (0x1 << 17); /* AddrCmdTriEn = 1 */ ++ Set_NB32_DCT(dev, dct, 0x78, dword); /* DRAM Control */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */ ++ dword &= ~(0x1 << 18); /* DisAutoRefresh = 0 */ ++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x94); /* DRAM Configuration High */ ++ dword |= (0xf << 24); /* DcqBypassMax = 0xf */ ++ dword |= (0x1 << 22); /* BankSwizzleMode = 1 */ ++ dword |= (0x1 << 15); /* PowerDownEn = 1 */ ++ dword &= ~(0x3 << 10); /* ZqcsInterval = 0x2 */ ++ dword |= (0x2 << 10); ++ Set_NB32_DCT(dev, dct, 0x94, dword); /* DRAM Configuration High */ ++ ++ if (x4_present && x8_present) { ++ /* Mixed channel of 4x and 8x DIMMs */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d); ++ dword &= ~(0x3 << 24); /* RxDLLWakeupTime = 0 */ ++ dword &= ~(0x7 << 20); /* RxCPUpdPeriod = 0 */ ++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 0 */ ++ dword &= ~(0x3 << 8); /* TxDLLWakeupTime = 0 */ ++ dword &= ~(0x7 << 4); /* TxCPUpdPeriod = 0 */ ++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 0 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword); ++ } else { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d); ++ dword &= ~(0x3 << 24); /* RxDLLWakeupTime = 3 */ ++ dword |= (0x3 << 24); ++ dword &= ~(0x7 << 20); /* RxCPUpdPeriod = 3 */ ++ dword |= (0x3 << 20); ++ dword &= ~(0xf << 16); /* RxMaxDurDllNoLock = 7 */ ++ dword |= (0x7 << 16); ++ dword &= ~(0x3 << 8); /* TxDLLWakeupTime = 3 */ ++ dword |= (0x3 << 8); ++ dword &= ~(0x7 << 4); /* TxCPUpdPeriod = 3 */ ++ dword |= (0x3 << 4); ++ dword &= ~(0xf); /* TxMaxDurDllNoLock = 7 */ ++ dword |= 0x7; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000d, dword); ++ } ++ ++ if ((memclk_index <= 0x12) && (x4_present != x8_present)) { ++ /* MemClkFreq <= 800MHz ++ * Not a mixed channel of x4 and x8 DIMMs ++ */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8)); ++ dword |= (0x1 << 12); /* EnRxPadStandby = 1 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword); ++ } ++ } else { ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8)); ++ dword &= ~(0x1 << 12); /* EnRxPadStandby = 0 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0010 | (index << 8), dword); ++ } ++ } ++ ++ /* TODO ++ * Calculate Twrrd per section 2.10.5.5.3 of the Family 15h BKDG ++ */ ++ twrrd = 0xb; ++ ++ /* TODO ++ * Calculate TrwtTO per section 2.10.5.5.4 of the Family 15h BKDG ++ */ ++ trwtto = 0x16; ++ ++ dword = Get_NB32_DCT(dev, dct, 0xa4); /* DRAM Controller Temperature Throttle */ ++ dword &= ~(0x1 << 11); /* BwCapEn = 0 */ ++ dword &= ~(0x1 << 8); /* ODTSEn = dimm_event_l_pin_support */ ++ dword |= (dimm_event_l_pin_support & 0x1) << 8; ++ Set_NB32_DCT(dev, dct, 0xa4, dword); /* DRAM Controller Temperature Throttle */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x110); /* DRAM Controller Select Low */ ++ dword &= ~(0x1 << 2); /* DctSelIntLvEn = interleave_channels */ ++ dword |= (interleave_channels & 0x1) << 2; ++ Set_NB32_DCT(dev, dct, 0x110, dword); /* DRAM Controller Select Low */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58); /* Scrub Rate Control */ ++ dword &= ~(0x1f << 24); /* L3Scrub = NV_L3BKScrub */ ++ dword |= (mctGet_NVbits(NV_L3BKScrub) & 0x1f) << 24; ++ dword &= ~(0x1f); /* DramScrub = NV_DramBKScrub */ ++ dword |= mctGet_NVbits(NV_DramBKScrub) & 0x1f; ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x58, dword); /* Scrub Rate Control */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c); /* DRAM Scrub Address Low */ ++ dword &= ~(0x1); /* ScrubReDirEn = redirect_ecc_scrub */ ++ dword |= redirect_ecc_scrub & 0x1; ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x5c, dword); /* DRAM Scrub Address Low */ ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8); /* L3 Control 1 */ ++ dword &= ~(0x1 << 4); /* L3ScrbRedirDis = 0 */ ++ Set_NB32_DCT(pDCTstat->dev_nbmisc, dct, 0x1b8, dword); /* L3 Control 1 */ ++ ++ /* FIXME ++ * The BKDG-recommended settings cause memory corruption on the ASUS KGPE-D16. ++ * Investigate and fix... ++ */ ++#if 0 ++ /* Fam15h BKDG section 2.10.5.5.1 */ ++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */ ++ dword &= ~(0xf << 24); /* TrdrdSdSc = 0x1 */ ++ dword |= (0x1 << 24); ++ dword &= ~(0xf << 16); /* TrdrdSdDc = trdrdsddc */ ++ dword |= ((trdrdsddc & 0xf) << 16); ++ dword &= ~(0xf); /* TrdrdDd = trdrddd */ ++ dword |= (trdrddd & 0xf); ++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */ ++#endif ++ ++ /* Fam15h BKDG section 2.10.5.5.2 */ ++ dword = Get_NB32_DCT(dev, dct, 0x214); /* DRAM Timing 4 */ ++ dword &= ~(0xf << 16); /* TwrwrSdSc = 0x1 */ ++ dword |= (0x1 << 16); ++ dword &= ~(0xf << 8); /* TwrwrSdDc = twrwrsddc */ ++ dword |= ((twrwrsddc & 0xf) << 8); ++ dword &= ~(0xf); /* TwrwrDd = twrwrdd */ ++ dword |= (twrwrdd & 0xf); ++ Set_NB32_DCT(dev, dct, 0x214, dword); /* DRAM Timing 4 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.3 */ ++ dword = Get_NB32_DCT(dev, dct, 0x218); /* DRAM Timing 5 */ ++ dword &= ~(0xf << 8); /* Twrrd = twrrd */ ++ dword |= ((twrrd & 0xf) << 8); ++ Set_NB32_DCT(dev, dct, 0x218, dword); /* DRAM Timing 5 */ ++ ++ /* Fam15h BKDG section 2.10.5.5.4 */ ++ dword = Get_NB32_DCT(dev, dct, 0x21c); /* DRAM Timing 6 */ ++ dword &= ~(0x1f << 8); /* TrwtTO = trwtto */ ++ dword |= ((trwtto & 0x1f) << 8); ++ dword &= ~(0x1f << 16); /* TrwtWB = TrwtTO + 1 */ ++ dword |= ((((dword >> 8) & 0x1f) + 1) << 16); ++ Set_NB32_DCT(dev, dct, 0x21c, dword); /* DRAM Timing 6 */ ++ ++ /* Enable prefetchers */ ++ dword = Get_NB32_DCT(dev, dct, 0x110); /* Memory Controller Configuration High */ ++ dword &= ~(0x1 << 13); /* PrefIoDis = 0 */ ++ dword &= ~(0x1 << 12); /* PrefCpuDis = 0 */ ++ Set_NB32_DCT(dev, dct, 0x110, dword); /* Memory Controller Configuration High */ ++ } ++} ++ ++static void exit_training_mode_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstatA) ++{ ++ uint8_t node; ++ uint8_t dct; ++ ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + node; ++ ++ if (pDCTstat->NodePresent) ++ for (dct = 0; dct < 2; dct++) ++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 0); ++ } ++} ++ + static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) + { +@@ -424,6 +1826,20 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA); + phyAssistedMemFnceTraining(pMCTstat, pDCTstatA); + ++ if (is_fam15h()) { ++ uint8_t Node; ++ struct DCTStatStruc *pDCTstat; ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ pDCTstat = pDCTstatA + Node; ++ if (pDCTstat->NodePresent) { ++ if (pDCTstat->DIMMValidDCT[0]) ++ InitPhyCompensation(pMCTstat, pDCTstat, 0); ++ if (pDCTstat->DIMMValidDCT[1]) ++ InitPhyCompensation(pMCTstat, pDCTstat, 1); ++ } ++ } ++ } ++ + if (nv_DQSTrainCTL) { + mctHookBeforeAnyTraining(pMCTstat, pDCTstatA); + /* TODO: should be in mctHookBeforeAnyTraining */ +@@ -431,16 +1847,35 @@ static void DQSTiming_D(struct MCTStatStruc *pMCTstat, + _WRMSR(0x26D, 0x04040404, 0x04040404); + _WRMSR(0x26E, 0x04040404, 0x04040404); + _WRMSR(0x26F, 0x04040404, 0x04040404); +- mct_WriteLevelization_HW(pMCTstat, pDCTstatA); ++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, FirstPass); + +- TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); ++ if (is_fam15h()) { ++ /* Receiver Enable Training Pass 1 */ ++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); ++ } + +- mct_TrainDQSPos_D(pMCTstat, pDCTstatA); ++ mct_WriteLevelization_HW(pMCTstat, pDCTstatA, SecondPass); ++ ++ if (is_fam15h()) { ++ /* Receiver Enable Training Pass 2 */ ++ // TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); ++ ++ /* TODO: ++ * Determine why running TrainReceiverEn_D in SecondPass ++ * mode yields less stable training values than when run ++ * in FirstPass mode as in the HACK below. ++ */ ++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); ++ } else { ++ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); ++ } + +- /* Second Pass never used for Barcelona! */ +- /* TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); */ ++ mct_TrainDQSPos_D(pMCTstat, pDCTstatA); + +- mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA); ++ if (is_fam15h()) ++ exit_training_mode_fam15(pMCTstat, pDCTstatA); ++ else ++ mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA); + + /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */ + mctHookAfterAnyTraining(); +@@ -476,7 +1911,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + for (Channel = 0;Channel < 2; Channel++) { + /* there are four receiver pairs, + loosely associated with chipselects.*/ +- index_reg = 0x98 + Channel * 0x100; ++ index_reg = 0x98; + for (Receiver = 0; Receiver < 8; Receiver += 2) { + /* Set Receiver Enable Values */ + mct_SetRcvrEnDly_D(pDCTstat, +@@ -492,7 +1927,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + txdqs = pDCTstat->CH_D_B_TxDqs[Channel][Receiver >> 1][ByteLane]; + index = Table_DQSRcvEn_Offset[ByteLane >> 1]; + index += (Receiver >> 1) * 3 + 0x10 + 0x20; /* Addl_Index */ +- val = Get_NB32_index_wait(dev, 0x98 + 0x100*Channel, index); ++ val = Get_NB32_index_wait_DCT(dev, Channel, 0x98, index); + if (ByteLane & 1) { /* odd byte lane */ + val &= ~(0xFF << 16); + val |= txdqs << 16; +@@ -500,7 +1935,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + val &= ~0xFF; + val |= txdqs; + } +- Set_NB32_index_wait(dev, 0x98 + 0x100*Channel, index, val); ++ Set_NB32_index_wait_DCT(dev, Channel, 0x98, index, val); + } + } + } +@@ -510,7 +1945,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + + for (Channel = 0; Channel < 2; Channel++) { + u8 *p; +- index_reg = 0x98 + Channel * 0x100; ++ index_reg = 0x98; + + /* NOTE: + * when 400, 533, 667, it will support dimm0/1/2/3, +@@ -525,7 +1960,7 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + if (DIMM == 0) { + index = 0; /* CHA Write Data Timing Low */ + } else { +- if (pDCTstat->Speed >= 4) { ++ if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { + index = 0x100 * DIMM; + } else { + break; +@@ -534,23 +1969,23 @@ static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, + for (Dir = 0; Dir < 2; Dir++) {/* RD/WR */ + p = pDCTstat->CH_D_DIR_B_DQS[Channel][DIMM][Dir]; + val = stream_to_int(p); /* CHA Read Data Timing High */ +- Set_NB32_index_wait(dev, index_reg, index+1, val); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+1, val); + val = stream_to_int(p+4); /* CHA Write Data Timing High */ +- Set_NB32_index_wait(dev, index_reg, index+2, val); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+2, val); + val = *(p+8); /* CHA Write ECC Timing */ +- Set_NB32_index_wait(dev, index_reg, index+3, val); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index+3, val); + index += 4; + } + } + } + + for (Channel = 0; Channel<2; Channel++) { +- reg = 0x78 + Channel * 0x100; +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, Channel, reg); + val &= ~(0x3ff<<22); + val |= ((u32) pDCTstat->CH_MaxRdLat[Channel] << 22); + val &= ~(1<<DqsRcvEnTrain); +- Set_NB32(dev, reg, val); /* program MaxRdLatency to correspond with current delay*/ ++ Set_NB32_DCT(dev, Channel, reg, val); /* program MaxRdLatency to correspond with current delay*/ + } + } + } +@@ -812,49 +2247,70 @@ finish: + return ret; + } + +-static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct) ++static void DCTPreInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct) + { + /* +- * Initialize DRAM on single Athlon 64/Opteron Node. ++ * Run DCT pre-initialization tasks + */ +- u8 stopDCTflag; +- u32 val; ++ uint32_t dword; + ++ /* Reset DCT registers */ + ClearDCT_D(pMCTstat, pDCTstat, dct); +- stopDCTflag = 1; /*preload flag with 'disable' */ +- /* enable DDR3 support */ +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100); +- val |= 1 << Ddr3Mode; +- Set_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100, val); ++ pDCTstat->stopDCT = 1; /*preload flag with 'disable' */ ++ ++ if (!is_fam15h()) { ++ /* Enable DDR3 support */ ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94); ++ dword |= 1 << Ddr3Mode; ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword); ++ } ++ ++ /* Read the SPD information into the data structures */ + if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) { + printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_DIMMPresence Done\n"); +- if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) { +- printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n"); +- if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) { +- printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n"); +- if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) { +- printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n"); +- if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) { +- printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n"); +- stopDCTflag = 0; +- if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) { +- printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n"); +- StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */ +- } ++ } ++} ++ ++static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct) ++{ ++ /* ++ * Initialize DRAM on single Athlon 64/Opteron Node. ++ */ ++ uint32_t dword; ++ ++ if (!is_fam15h()) { ++ /* (Re)-enable DDR3 support */ ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94); ++ dword |= 1 << Ddr3Mode; ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword); ++ } ++ ++ if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) { ++ printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n"); ++ if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) { ++ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n"); ++ if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) { ++ printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n"); ++ if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) { ++ printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n"); ++ pDCTstat->stopDCT = 0; ++ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) { ++ printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n"); ++ StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */ + } + } + } + } + } + +- if (stopDCTflag) { +- u32 reg_off = dct * 0x100; +- val = 1<<DisDramInterface; +- Set_NB32(pDCTstat->dev_dct, reg_off+0x94, val); +- /*To maximize power savings when DisDramInterface=1b, +- all of the MemClkDis bits should also be set.*/ +- val = 0xFF000000; +- Set_NB32(pDCTstat->dev_dct, reg_off+0x88, val); ++ if (pDCTstat->stopDCT) { ++ dword = 1 << DisDramInterface; ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x94, dword); ++ ++ /* To maximize power savings when DisDramInterface=1b, ++ * all of the MemClkDis bits should also be set. ++ */ ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x88, 0xff000000); + } else { + mct_EnDllShutdownSR(pMCTstat, pDCTstat, dct); + } +@@ -876,20 +2332,24 @@ static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat, + pDCTstat = pDCTstatA + Node; + mct_SyncDCTsReady(pDCTstat); + } +- /* v6.1.3 */ +- /* re-enable phy compensation engine when dram init is completed on all nodes. */ +- for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { +- struct DCTStatStruc *pDCTstat; +- pDCTstat = pDCTstatA + Node; +- if (pDCTstat->NodePresent) { +- if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) { +- /* re-enable phy compensation engine when dram init on both DCTs is completed. */ +- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8); +- val &= ~(1 << DisAutoComp); +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val); ++ ++ if (!is_fam15h()) { ++ /* v6.1.3 */ ++ /* re-enable phy compensation engine when dram init is completed on all nodes. */ ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { ++ struct DCTStatStruc *pDCTstat; ++ pDCTstat = pDCTstatA + Node; ++ if (pDCTstat->NodePresent) { ++ if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) { ++ /* re-enable phy compensation engine when dram init on both DCTs is completed. */ ++ val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8); ++ val &= ~(1 << DisAutoComp); ++ Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val); ++ } + } + } + } ++ + /* wait 750us before any memory access can be made. */ + mct_Wait(15000); + } +@@ -911,10 +2371,9 @@ static void StartupDCT_D(struct MCTStatStruc *pMCTstat, + */ + u32 val; + u32 dev; +- u32 reg_off = dct * 0x100; + + dev = pDCTstat->dev_dct; +- val = Get_NB32(dev, 0x94 + reg_off); ++ val = Get_NB32_DCT(dev, dct, 0x94); + if (val & (1<<MemClkFreqVal)) { + mctHookBeforeDramInit(); /* generalized Hook */ + if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) +@@ -929,23 +2388,23 @@ static void ClearDCT_D(struct MCTStatStruc *pMCTstat, + { + u32 reg_end; + u32 dev = pDCTstat->dev_dct; +- u32 reg = 0x40 + 0x100 * dct; ++ u32 reg = 0x40; + u32 val = 0; + + if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { +- reg_end = 0x78 + 0x100 * dct; ++ reg_end = 0x78; + } else { +- reg_end = 0xA4 + 0x100 * dct; ++ reg_end = 0xA4; + } + + while(reg < reg_end) { + if ((reg & 0xFF) == 0x90) { + if (pDCTstat->LogicalCPUID & AMD_DR_Dx) { +- val = Get_NB32(dev, reg); /* get DRAMConfigLow */ ++ val = Get_NB32_DCT(dev, dct, reg); /* get DRAMConfigLow */ + val |= 0x08000000; /* preserve value of DisDllShutdownSR for only Rev.D */ + } + } +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + val = 0; + reg += 4; + } +@@ -964,6 +2423,7 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat, + u16 Trp, Trrd, Trcd, Tras, Trc; + u8 Trfc[4]; + u16 Tfaw; ++ u16 Tcwl; /* Fam15h only */ + u32 DramTimingLo, DramTimingHi; + u8 tCK16x; + u16 Twtr; +@@ -972,10 +2432,11 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat, + u8 byte; + u32 dword; + u32 dev; +- u32 reg_off; + u32 val; + u16 smbaddr; + ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); ++ + /* Gather all DIMM mini-max values for cycle timing data */ + Trp = 0; + Trrd = 0; +@@ -1188,88 +2649,164 @@ static void SPD2ndTiming(struct MCTStatStruc *pMCTstat, + + mctAdjustAutoCycTmg_D(); + ++ if (is_fam15h()) { ++ /* Compute Tcwl (Fam15h BKDG v3.14 Table 203) */ ++ if (pDCTstat->Speed <= 0x6) ++ Tcwl = 0x5; ++ else if (pDCTstat->Speed == 0xa) ++ Tcwl = 0x6; ++ else if (pDCTstat->Speed == 0xe) ++ Tcwl = 0x7; ++ else if (pDCTstat->Speed == 0x12) ++ Tcwl = 0x8; ++ else if (pDCTstat->Speed == 0x16) ++ Tcwl = 0x9; ++ else ++ Tcwl = 0x5; /* Power-on default */ ++ } ++ + /* Program DRAM Timing values */ +- DramTimingLo = 0; /* Dram Timing Low init */ +- val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */ +- DramTimingLo |= val; ++ if (is_fam15h()) { ++ dev = pDCTstat->dev_dct; + +- val = pDCTstat->Trcd - Bias_TrcdT; +- DramTimingLo |= val<<4; ++ dword = Get_NB32_DCT(dev, dct, 0x8c); /* DRAM Timing High */ ++ val = 2; /* Tref = 7.8us */ ++ dword &= ~(0x3 << 16); ++ dword |= (val & 0x3) << 16; ++ Set_NB32_DCT(dev, dct, 0x8c, dword); /* DRAM Timing High */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x200); /* DRAM Timing 0 */ ++ dword &= ~(0x3f1f1f1f); ++ dword |= ((pDCTstat->Tras + 0xf) & 0x3f) << 24; /* Tras */ ++ dword |= ((pDCTstat->Trp + 0x5) & 0x1f) << 16; /* Trp */ ++ dword |= ((pDCTstat->Trcd + 0x5) & 0x1f) << 8; /* Trcd */ ++ dword |= (pDCTstat->CASL & 0x1f); /* Tcl */ ++ Set_NB32_DCT(dev, dct, 0x200, dword); /* DRAM Timing 0 */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x204); /* DRAM Timing 1 */ ++ dword &= ~(0x0f3f0f3f); ++ dword |= ((pDCTstat->Trtp + 0x4) & 0xf) << 24; /* Trtp */ ++ if (pDCTstat->Tfaw != 0) ++ dword |= ((((pDCTstat->Tfaw - 0x1) * 2) + 0x10) & 0x3f) << 16; /* FourActWindow */ ++ dword |= ((pDCTstat->Trrd + 0x4) & 0xf) << 8; /* Trrd */ ++ dword |= ((pDCTstat->Trc + 0xb) & 0x3f); /* Trc */ ++ Set_NB32_DCT(dev, dct, 0x204, dword); /* DRAM Timing 1 */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x208); /* DRAM Timing 2 */ ++ dword &= ~(0x07070707); ++ dword |= (pDCTstat->Trfc[3] & 0x7) << 24; /* Trfc3 */ ++ dword |= (pDCTstat->Trfc[2] & 0x7) << 16; /* Trfc2 */ ++ dword |= (pDCTstat->Trfc[1] & 0x7) << 8; /* Trfc1 */ ++ dword |= (pDCTstat->Trfc[0] & 0x7); /* Trfc0 */ ++ Set_NB32_DCT(dev, dct, 0x208, dword); /* DRAM Timing 2 */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x20c); /* DRAM Timing 3 */ ++ dword &= ~(0x00000f00); ++ dword |= ((pDCTstat->Twtr + 0x4) & 0xf) << 8; /* Twtr */ ++ dword &= ~(0x0000001f); ++ dword |= (Tcwl & 0x1f); /* Tcwl */ ++ Set_NB32_DCT(dev, dct, 0x20c, dword); /* DRAM Timing 3 */ ++ ++ dword = Get_NB32_DCT(dev, dct, 0x22c); /* DRAM Timing 10 */ ++ dword &= ~(0x0000001f); ++ dword |= ((pDCTstat->Twr + 0x4) & 0x1f); /* Twr */ ++ Set_NB32_DCT(dev, dct, 0x22c, dword); /* DRAM Timing 10 */ ++ ++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { ++ /* Enable phy-assisted training mode */ ++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1); ++ } + +- val = pDCTstat->Trp - Bias_TrpT; +- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); +- DramTimingLo |= val<<7; ++ /* Other setup (not training specific) */ ++ dword = Get_NB32_DCT(dev, dct, 0x90); /* DRAM Configuration Low */ ++ dword &= ~(0x1 << 23); /* ForceAutoPchg = 0 */ ++ dword &= ~(0x1 << 20); /* DynPageCloseEn = 0 */ ++ Set_NB32_DCT(dev, dct, 0x90, dword); /* DRAM Configuration Low */ + +- val = pDCTstat->Trtp - Bias_TrtpT; +- DramTimingLo |= val<<10; ++ Set_NB32_DCT(dev, dct, 0x228, 0x14141414); /* DRAM Timing 9 */ ++ } else { ++ DramTimingLo = 0; /* Dram Timing Low init */ ++ val = pDCTstat->CASL - 4; /* pDCTstat.CASL to reg. definition */ ++ DramTimingLo |= val; + +- val = pDCTstat->Tras - Bias_TrasT; +- DramTimingLo |= val<<12; ++ val = pDCTstat->Trcd - Bias_TrcdT; ++ DramTimingLo |= val<<4; + +- val = pDCTstat->Trc - Bias_TrcT; +- DramTimingLo |= val<<16; ++ val = pDCTstat->Trp - Bias_TrpT; ++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); ++ DramTimingLo |= val<<7; + +- val = pDCTstat->Trrd - Bias_TrrdT; +- DramTimingLo |= val<<22; ++ val = pDCTstat->Trtp - Bias_TrtpT; ++ DramTimingLo |= val<<10; + +- DramTimingHi = 0; /* Dram Timing High init */ +- val = pDCTstat->Twtr - Bias_TwtrT; +- DramTimingHi |= val<<8; ++ val = pDCTstat->Tras - Bias_TrasT; ++ DramTimingLo |= val<<12; + +- val = 2; +- DramTimingHi |= val<<16; ++ val = pDCTstat->Trc - Bias_TrcT; ++ DramTimingLo |= val<<16; + +- val = 0; +- for (i=4;i>0;i--) { +- val <<= 3; +- val |= Trfc[i-1]; +- } +- DramTimingHi |= val << 20; ++ val = pDCTstat->Trrd - Bias_TrrdT; ++ DramTimingLo |= val<<22; + +- dev = pDCTstat->dev_dct; +- reg_off = 0x100 * dct; +- /* Twr */ +- val = pDCTstat->Twr; +- if (val == 10) +- val = 9; +- else if (val == 12) +- val = 10; +- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); +- val -= Bias_TwrT; +- val <<= 4; +- dword = Get_NB32(dev, 0x84 + reg_off); +- dword &= ~0x70; +- dword |= val; +- Set_NB32(dev, 0x84 + reg_off, dword); ++ DramTimingHi = 0; /* Dram Timing High init */ ++ val = pDCTstat->Twtr - Bias_TwtrT; ++ DramTimingHi |= val<<8; + +- /* Tfaw */ +- val = pDCTstat->Tfaw; +- val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); +- val -= Bias_TfawT; +- val >>= 1; +- val <<= 28; +- dword = Get_NB32(dev, 0x94 + reg_off); +- dword &= ~0xf0000000; +- dword |= val; +- Set_NB32(dev, 0x94 + reg_off, dword); +- +- /* dev = pDCTstat->dev_dct; */ +- /* reg_off = 0x100 * dct; */ +- +- if (pDCTstat->Speed > 4) { +- val = Get_NB32(dev, 0x88 + reg_off); +- val &= 0xFF000000; +- DramTimingLo |= val; +- } +- Set_NB32(dev, 0x88 + reg_off, DramTimingLo); /*DCT Timing Low*/ ++ val = 2; /* Tref = 7.8us */ ++ DramTimingHi |= val<<16; ++ ++ val = 0; ++ for (i=4;i>0;i--) { ++ val <<= 3; ++ val |= Trfc[i-1]; ++ } ++ DramTimingHi |= val << 20; ++ ++ dev = pDCTstat->dev_dct; ++ /* Twr */ ++ val = pDCTstat->Twr; ++ if (val == 10) ++ val = 9; ++ else if (val == 12) ++ val = 10; ++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); ++ val -= Bias_TwrT; ++ val <<= 4; ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ dword &= ~0x70; ++ dword |= val; ++ Set_NB32_DCT(dev, dct, 0x84, dword); ++ ++ /* Tfaw */ ++ val = pDCTstat->Tfaw; ++ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); ++ val -= Bias_TfawT; ++ val >>= 1; ++ val <<= 28; ++ dword = Get_NB32_DCT(dev, dct, 0x94); ++ dword &= ~0xf0000000; ++ dword |= val; ++ Set_NB32_DCT(dev, dct, 0x94, dword); ++ ++ /* dev = pDCTstat->dev_dct; */ ++ ++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { ++ val = Get_NB32_DCT(dev, dct, 0x88); ++ val &= 0xFF000000; ++ DramTimingLo |= val; ++ } ++ Set_NB32_DCT(dev, dct, 0x88, DramTimingLo); /*DCT Timing Low*/ + +- if (pDCTstat->Speed > 4) { +- DramTimingHi |= 1 << DisAutoRefresh; ++ if (pDCTstat->Speed > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { ++ DramTimingHi |= 1 << DisAutoRefresh; ++ } ++ DramTimingHi |= 0x000018FF; ++ Set_NB32_DCT(dev, dct, 0x8c, DramTimingHi); /*DCT Timing Hi*/ + } +- DramTimingHi |= 0x000018FF; +- Set_NB32(dev, 0x8c + reg_off, DramTimingHi); /*DCT Timing Hi*/ + + /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ ++ ++ printk(BIOS_DEBUG, "%s: Done\n", __func__); + } + + static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat, +@@ -1303,6 +2840,8 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat, + * timing mode is 'Auto'. + */ + ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); ++ + /* Get primary timing (CAS Latency and Cycle Time) */ + if (pDCTstat->Speed == 0) { + mctGet_MaxLoadFreq(pDCTstat); +@@ -1312,6 +2851,7 @@ static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat, + + /* Go get best T and CL as specified by DIMM mfgs. and OEM */ + SPDGetTCL_D(pMCTstat, pDCTstat, dct); ++ + /* skip callback mctForce800to1067_D */ + pDCTstat->Speed = pDCTstat->DIMMAutoSpeed; + pDCTstat->CASL = pDCTstat->DIMMCASL; +@@ -1344,7 +2884,10 @@ static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat, + u16 word; + + /* Get CPU Si Revision defined limit (NPT) */ +- proposedFreq = 800; /* Rev F0 programmable max memclock is */ ++ if (is_fam15h()) ++ proposedFreq = 933; ++ else ++ proposedFreq = 800; /* Rev F0 programmable max memclock is */ + + /*Get User defined limit if "limit" mode */ + if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) { +@@ -1381,6 +2924,7 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, + u16 tCKmin16x; + u16 tCKproposed16x; + u8 CLactual, CLdesired, CLT_Fail; ++ uint16_t min_frequency_tck16x; + + u8 smbaddr, byte = 0, bytex = 0; + +@@ -1390,6 +2934,17 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, + tCKmin16x = 0; + CLT_Fail = 0; + ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); ++ ++ if (is_fam15h()) { ++ uint16_t minimum_frequency_mhz = mctGet_NVbits(NV_MIN_MEMCLK); ++ if (minimum_frequency_mhz == 0) ++ minimum_frequency_mhz = 333; ++ min_frequency_tck16x = 16000 / minimum_frequency_mhz; ++ } else { ++ min_frequency_tck16x = 40; ++ } ++ + for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) { + if (pDCTstat->DIMMValid & (1 << i)) { + smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i)); +@@ -1419,27 +2974,44 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, + tCKmin16x = byte * MTB16x; + } + } +- /* calculate tCKproposed16x */ ++ /* calculate tCKproposed16x (proposed clock period in ns * 16) */ + tCKproposed16x = 16000 / pDCTstat->PresetmaxFreq; + if (tCKmin16x > tCKproposed16x) + tCKproposed16x = tCKmin16x; + +- /* mctHookTwo1333DimmOverride(); */ +- /* For UDIMM, if there are two DDR3-1333 on the same channel, +- downgrade DDR speed to 1066. */ +- + /* TODO: get user manual tCK16x(Freq.) and overwrite current tCKproposed16x if manual. */ +- if (tCKproposed16x == 20) +- pDCTstat->TargetFreq = 7; +- else if (tCKproposed16x <= 24) { +- pDCTstat->TargetFreq = 6; +- tCKproposed16x = 24; +- } else if (tCKproposed16x <= 30) { +- pDCTstat->TargetFreq = 5; +- tCKproposed16x = 30; ++ if (is_fam15h()) { ++ if (tCKproposed16x == 17) ++ pDCTstat->TargetFreq = 0x16; ++ else if (tCKproposed16x <= 20) { ++ pDCTstat->TargetFreq = 0x12; ++ tCKproposed16x = 20; ++ } else if (tCKproposed16x <= 24) { ++ pDCTstat->TargetFreq = 0xe; ++ tCKproposed16x = 24; ++ } else if (tCKproposed16x <= 30) { ++ pDCTstat->TargetFreq = 0xa; ++ tCKproposed16x = 30; ++ } else if (tCKproposed16x <= 40) { ++ pDCTstat->TargetFreq = 0x6; ++ tCKproposed16x = 40; ++ } else { ++ pDCTstat->TargetFreq = 0x4; ++ tCKproposed16x = 48; ++ } + } else { +- pDCTstat->TargetFreq = 4; +- tCKproposed16x = 40; ++ if (tCKproposed16x == 20) ++ pDCTstat->TargetFreq = 7; ++ else if (tCKproposed16x <= 24) { ++ pDCTstat->TargetFreq = 6; ++ tCKproposed16x = 24; ++ } else if (tCKproposed16x <= 30) { ++ pDCTstat->TargetFreq = 5; ++ tCKproposed16x = 30; ++ } else { ++ pDCTstat->TargetFreq = 4; ++ tCKproposed16x = 40; ++ } + } + /* Running through this loop twice: + - First time find tCL at target frequency +@@ -1478,27 +3050,42 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, + /* get CL and T */ + if (!CLT_Fail) { + bytex = CLactual; +- if (tCKproposed16x == 20) +- byte = 7; +- else if (tCKproposed16x == 24) +- byte = 6; +- else if (tCKproposed16x == 30) +- byte = 5; +- else +- byte = 4; ++ if (is_fam15h()) { ++ if (tCKproposed16x == 17) ++ byte = 0x16; ++ else if (tCKproposed16x == 20) ++ byte = 0x12; ++ else if (tCKproposed16x == 24) ++ byte = 0xe; ++ else if (tCKproposed16x == 30) ++ byte = 0xa; ++ else if (tCKproposed16x == 40) ++ byte = 0x6; ++ else ++ byte = 0x4; ++ } else { ++ if (tCKproposed16x == 20) ++ byte = 7; ++ else if (tCKproposed16x == 24) ++ byte = 6; ++ else if (tCKproposed16x == 30) ++ byte = 5; ++ else ++ byte = 4; ++ } + } else { + /* mctHookManualCLOverride */ + /* TODO: */ + } + +- if (tCKproposed16x != 40) { ++ if (tCKproposed16x != min_frequency_tck16x) { + if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { + pDCTstat->DIMMAutoSpeed = byte; + pDCTstat->DIMMCASL = bytex; + break; + } else { + pDCTstat->TargetCASL = bytex; +- tCKproposed16x = 40; ++ tCKproposed16x = min_frequency_tck16x; + } + } else { + pDCTstat->DIMMAutoSpeed = byte; +@@ -1519,29 +3106,21 @@ static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, + static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 dev; +- u32 reg; +- u32 val; ++ if (!is_fam15h()) { ++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct); + +- mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct); ++ if (pDCTstat->GangedMode == 1) { ++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1); ++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1); ++ } + +- if (pDCTstat->GangedMode == 1) { +- mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1); +- mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1); +- } ++ set_2t_configuration(pMCTstat, pDCTstat, dct); + +- if ( pDCTstat->_2Tmode == 2) { +- dev = pDCTstat->dev_dct; +- reg = 0x94 + 0x100 * dct; /* Dram Configuration Hi */ +- val = Get_NB32(dev, reg); +- val |= 1 << 20; /* 2T CMD mode */ +- Set_NB32(dev, reg, val); ++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct); ++ mct_PlatformSpec(pMCTstat, pDCTstat, dct); ++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) ++ InitPhyCompensation(pMCTstat, pDCTstat, dct); + } +- +- mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct); +- mct_PlatformSpec(pMCTstat, pDCTstat, dct); +- if (pDCTstat->DIMMAutoSpeed == 4) +- InitPhyCompensation(pMCTstat, pDCTstat, dct); + mctHookAfterPSCfg(); + + return pDCTstat->ErrCode; +@@ -1553,11 +3132,11 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + u32 DramControl, DramTimingLo, Status; + u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2; + u32 val; +- u32 reg_off; + u32 dev; + u16 word; + u32 dword; + u8 byte; ++ uint32_t offset; + + DramConfigLo = 0; + DramConfigHi = 0; +@@ -1577,12 +3156,10 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + Status = pDCTstat->Status; + + dev = pDCTstat->dev_dct; +- reg_off = 0x100 * dct; +- + + /* Build Dram Control Register Value */ +- DramConfigMisc2 = Get_NB32 (dev, 0xA8 + reg_off); /* Dram Control*/ +- DramControl = Get_NB32 (dev, 0x78 + reg_off); /* Dram Control*/ ++ DramConfigMisc2 = Get_NB32_DCT(dev, dct, 0xA8); /* Dram Control*/ ++ DramControl = Get_NB32_DCT(dev, dct, 0x78); /* Dram Control*/ + + /* FIXME: Skip mct_checkForDxSupport */ + /* REV_CALL mct_DoRdPtrInit if not Dx */ +@@ -1624,8 +3201,12 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + DramConfigLo = mct_DisDllShutdownSR(pMCTstat, pDCTstat, DramConfigLo, dct); + + /* Build Dram Config Hi Register Value */ ++ if (is_fam15h()) ++ offset = 0x0; ++ else ++ offset = 0x1; + dword = pDCTstat->Speed; +- DramConfigHi |= dword - 1; /* get MemClk encoding */ ++ DramConfigHi |= dword - offset; /* get MemClk encoding */ + DramConfigHi |= 1 << MemClkFreqVal; + + if (Status & (1 << SB_Registered)) +@@ -1658,7 +3239,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + val = 0x0f; /* recommended setting (default) */ + DramConfigHi |= val << 24; + +- if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx)) ++ if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx | AMD_DR_Bx | AMD_FAM15_ALL)) + DramConfigHi |= 1 << DcqArbBypassEn; + + /* Build MemClkDis Value from Dram Timing Lo and +@@ -1669,7 +3250,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + NV_AllMemClks <>0 AND SB_DiagClks ==0 */ + + /* Dram Timing Low (owns Clock Enable bits) */ +- DramTimingLo = Get_NB32(dev, 0x88 + reg_off); ++ DramTimingLo = Get_NB32_DCT(dev, dct, 0x88); + if (mctGet_NVbits(NV_AllMemClks) == 0) { + /* Special Jedec SPD diagnostic bit - "enable all clocks" */ + if (!(pDCTstat->Status & (1<<SB_DiagClks))) { +@@ -1700,28 +3281,34 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + } + dword++ ; + } ++ DramTimingLo &= ~(0xff << 24); + DramTimingLo |= byte << 24; + } + } + +- printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %x\n", DramControl); +- printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %x\n", DramTimingLo); +- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %x\n", DramConfigMisc); +- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %x\n", DramConfigMisc2); +- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %x\n", DramConfigLo); +- printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %x\n", DramConfigHi); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %08x\n", DramControl); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %08x\n", DramTimingLo); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %08x\n", DramConfigMisc); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %08x\n", DramConfigMisc2); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %08x\n", DramConfigLo); ++ printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %08x\n", DramConfigHi); + + /* Write Values to the registers */ +- Set_NB32(dev, 0x78 + reg_off, DramControl); +- Set_NB32(dev, 0x88 + reg_off, DramTimingLo); +- Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc); ++ Set_NB32_DCT(dev, dct, 0x78, DramControl); ++ Set_NB32_DCT(dev, dct, 0x88, DramTimingLo); ++ Set_NB32_DCT(dev, dct, 0xa0, DramConfigMisc); + DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2); +- Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2); +- Set_NB32(dev, 0x90 + reg_off, DramConfigLo); ++ Set_NB32_DCT(dev, dct, 0xa8, DramConfigMisc2); ++ Set_NB32_DCT(dev, dct, 0x90, DramConfigLo); + ProgDramMRSReg_D(pMCTstat, pDCTstat, dct); +- dword = Get_NB32(dev, 0x94 + reg_off); ++ ++ if (is_fam15h()) ++ InitDDRPhy(pMCTstat, pDCTstat, dct); ++ ++ /* Write the DRAM Configuration High register, including memory frequency change */ ++ dword = Get_NB32_DCT(dev, dct, 0x94); + DramConfigHi |= dword; +- mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi); ++ mct_SetDramConfigHi_D(pMCTstat, pDCTstat, dct, DramConfigHi); + mct_EarlyArbEn_D(pMCTstat, pDCTstat, dct); + mctHookAfterAutoCfg(); + +@@ -1731,6 +3318,7 @@ static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, + printk(BIOS_DEBUG, "AutoConfig: ErrStatus %x\n", pDCTstat->ErrStatus); + printk(BIOS_DEBUG, "AutoConfig: ErrCode %x\n", pDCTstat->ErrCode); + printk(BIOS_DEBUG, "AutoConfig: Done\n\n"); ++ + AutoConfig_exit: + return pDCTstat->ErrCode; + } +@@ -1748,14 +3336,12 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat, + u32 val; + u32 reg; + u32 dev; +- u32 reg_off; + u8 byte; + u16 word; + u32 dword; + u16 smbaddr; + + dev = pDCTstat->dev_dct; +- reg_off = 0x100 * dct; + + BankAddrReg = 0; + for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel+=2) { +@@ -1820,10 +3406,10 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat, + /*set ChipSelect population indicator odd bits*/ + pDCTstat->CSPresent |= 1 << (ChipSel + 1); + +- reg = 0x60+(ChipSel<<1) + reg_off; /*Dram CS Mask Register */ ++ reg = 0x60+(ChipSel<<1); /*Dram CS Mask Register */ + val = csMask; + val &= 0x1FF83FE0; /* Mask out reserved bits.*/ +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + } else { + if (pDCTstat->DIMMSPDCSE & (1<<ChipSel)) + pDCTstat->CSTestFail |= (1<<ChipSel); +@@ -1847,8 +3433,8 @@ static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat, + if (!pDCTstat->CSPresent) + pDCTstat->ErrCode = SC_StopError; + +- reg = 0x80 + reg_off; /* Bank Addressing Register */ +- Set_NB32(dev, reg, BankAddrReg); ++ reg = 0x80; /* Bank Addressing Register */ ++ Set_NB32_DCT(dev, dct, reg, BankAddrReg); + + pDCTstat->CSPresent_DCT[dct] = pDCTstat->CSPresent; + /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ +@@ -1933,11 +3519,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + u16 word; + u32 dev; + u32 reg; +- u32 reg_off; + u32 val; + + dev = pDCTstat->dev_dct; +- reg_off = 0x100 * dct; + + _DSpareEn = 0; + +@@ -1974,11 +3558,11 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + BiggestBank = 0; + for (q = 0; q < MAX_CS_SUPPORTED; q++) { /* from DIMMS to CS */ + if (pDCTstat->CSPresent & (1 << q)) { /* bank present? */ +- reg = 0x40 + (q << 2) + reg_off; /* Base[q] reg.*/ +- val = Get_NB32(dev, reg); ++ reg = 0x40 + (q << 2); /* Base[q] reg.*/ ++ val = Get_NB32_DCT(dev, dct, reg); + if (!(val & 3)) { /* (CSEnable|Spare==1)bank is enabled already? */ +- reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/ +- val = Get_NB32(dev, reg); ++ reg = 0x60 + (q << 1); /*Mask[q] reg.*/ ++ val = Get_NB32_DCT(dev, dct, reg); + val >>= 19; + val++; + val <<= 19; +@@ -1994,7 +3578,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + if (BiggestBank !=0) { + curcsBase = nxtcsBase; /* curcsBase=nxtcsBase*/ + /* DRAM CS Base b Address Register offset */ +- reg = 0x40 + (b << 2) + reg_off; ++ reg = 0x40 + (b << 2); + if (_DSpareEn) { + BiggestBank = 0; + val = 1 << Spare; /* Spare Enable*/ +@@ -2013,7 +3597,7 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + } + } + } +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + if (_DSpareEn) + _DSpareEn = 0; + else +@@ -2024,9 +3608,9 @@ static void StitchMemory_D(struct MCTStatStruc *pMCTstat, + /* bank present but disabled?*/ + if ( pDCTstat->CSTestFail & (1 << p)) { + /* DRAM CS Base b Address Register offset */ +- reg = (p << 2) + 0x40 + reg_off; ++ reg = (p << 2) + 0x40; + val = 1 << TestFail; +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + } + } + +@@ -2064,7 +3648,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + u16 i, j, k; + u8 smbaddr; + u8 SPDCtrl; +- u16 RegDIMMPresent, MaxDimms; ++ u16 RegDIMMPresent, LRDIMMPresent, MaxDimms; + u8 devwidth; + u16 DimmSlots; + u8 byte = 0, bytex; +@@ -2077,6 +3661,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT); + + RegDIMMPresent = 0; ++ LRDIMMPresent = 0; + pDCTstat->DimmQRPresent = 0; + + for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) { +@@ -2115,6 +3700,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + pDCTstat->DimmManufacturerID[i] |= ((uint64_t)mctRead_SPD(smbaddr, SPD_MANID_START + k)) << (k * 8); + for (k = 0; k < SPD_PARTN_LENGTH; k++) + pDCTstat->DimmPartNumber[i][k] = mctRead_SPD(smbaddr, SPD_PARTN_START + k); ++ pDCTstat->DimmPartNumber[i][SPD_PARTN_LENGTH] = 0; + pDCTstat->DimmRevisionNumber[i] = 0; + for (k = 0; k < 2; k++) + pDCTstat->DimmRevisionNumber[i] |= ((uint16_t)mctRead_SPD(smbaddr, SPD_REVNO_START + k)) << (k * 8); +@@ -2138,6 +3724,12 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + } else { + pDCTstat->DimmRegistered[i] = 0; + } ++ if (byte == JED_LRDIMM) { ++ LRDIMMPresent |= 1 << i; ++ pDCTstat->DimmLoadReduced[i] = 1; ++ } else { ++ pDCTstat->DimmLoadReduced[i] = 0; ++ } + /* Check ECC capable */ + byte = mctRead_SPD(smbaddr, SPD_BusWidth); + if (byte & JED_ECC) { +@@ -2221,6 +3813,7 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + printk(BIOS_DEBUG, "\t DIMMPresence: DIMMValid=%x\n", pDCTstat->DIMMValid); + printk(BIOS_DEBUG, "\t DIMMPresence: DIMMPresent=%x\n", pDCTstat->DIMMPresent); + printk(BIOS_DEBUG, "\t DIMMPresence: RegDIMMPresent=%x\n", RegDIMMPresent); ++ printk(BIOS_DEBUG, "\t DIMMPresence: LRDIMMPresent=%x\n", LRDIMMPresent); + printk(BIOS_DEBUG, "\t DIMMPresence: DimmECCPresent=%x\n", pDCTstat->DimmECCPresent); + printk(BIOS_DEBUG, "\t DIMMPresence: DimmPARPresent=%x\n", pDCTstat->DimmPARPresent); + printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx4Present=%x\n", pDCTstat->Dimmx4Present); +@@ -2247,6 +3840,16 @@ static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, + pDCTstat->Status |= 1<<SB_Registered; + } + } ++ if (LRDIMMPresent != 0) { ++ if ((LRDIMMPresent ^ pDCTstat->DIMMValid) !=0) { ++ /* module type DIMM mismatch (reg'ed, unbuffered) */ ++ pDCTstat->ErrStatus |= 1<<SB_DimmMismatchM; ++ pDCTstat->ErrCode = SC_StopError; ++ } else{ ++ /* all DIMMs are registered */ ++ pDCTstat->Status |= 1<<SB_LoadReduced; ++ } ++ } + if (pDCTstat->DimmECCPresent != 0) { + if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) { + /* all DIMMs are ECC capable */ +@@ -2284,6 +3887,26 @@ static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i) + return p[i]; + } + ++static void mct_preInitDCT(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ u8 err_code; ++ ++ /* Preconfigure DCT0 */ ++ DCTPreInit_D(pMCTstat, pDCTstat, 0); ++ ++ /* Configure DCT1 if unganged and enabled*/ ++ if (!pDCTstat->GangedMode) { ++ if (pDCTstat->DIMMValidDCT[1] > 0) { ++ err_code = pDCTstat->ErrCode; /* save DCT0 errors */ ++ pDCTstat->ErrCode = 0; ++ DCTPreInit_D(pMCTstat, pDCTstat, 1); ++ if (pDCTstat->ErrCode == 2) /* DCT1 is not Running */ ++ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */ ++ } ++ } ++} ++ + static void mct_initDCT(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) + { +@@ -2295,7 +3918,7 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat, + if (pDCTstat->ErrCode == SC_FatalErr) { + /* Do nothing goto exitDCTInit; any fatal errors? */ + } else { +- /* Configure DCT1 if unganged and enabled*/ ++ /* Configure DCT1 if unganged and enabled */ + if (!pDCTstat->GangedMode) { + if (pDCTstat->DIMMValidDCT[1] > 0) { + err_code = pDCTstat->ErrCode; /* save DCT0 errors */ +@@ -2305,17 +3928,21 @@ static void mct_initDCT(struct MCTStatStruc *pMCTstat, + pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */ + } else { + val = 1 << DisDramInterface; +- Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val); ++ ++ /* To maximize power savings when DisDramInterface=1b, ++ * all of the MemClkDis bits should also be set. ++ */ ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x88, 0xff000000); + } + } + } +-/* exitDCTInit: */ + } + + static void mct_DramInit(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat); ++ mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat, dct); + mct_DramInit_Sw_D(pMCTstat, pDCTstat, dct); + /* mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); */ + } +@@ -2343,7 +3970,8 @@ static u8 mct_setMode(struct MCTStatStruc *pMCTstat, + if (byte) + pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); /* Set temp. to avoid setting of ganged mode */ + +- if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) { ++ if ((!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) && (pDCTstat->LogicalCPUID & AMD_FAM10_ALL)) { ++ /* Ganged channel mode not supported on Family 15h or higher */ + pDCTstat->GangedMode = 1; + /* valid 128-bit mode population. */ + pDCTstat->Status |= 1 << SB_128bitmode; +@@ -2387,10 +4015,8 @@ void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data) + + u32 Get_NB32_index_wait(u32 dev, u32 index_reg, u32 index) + { +- + u32 dword; + +- + index &= ~(1 << DctAccessWrite); + Set_NB32(dev, index_reg, index); + do { +@@ -2405,7 +4031,6 @@ void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data) + { + u32 dword; + +- + Set_NB32(dev, index_reg + 0x4, data); + index |= (1 << DctAccessWrite); + Set_NB32(dev, index_reg, index); +@@ -2420,16 +4045,17 @@ static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat, + { + /* mct_checkForCxDxSupport_D */ + if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) { ++ /* Family 10h Errata 322: Address and Command Fine Delay Values May Be Incorrect */ + /* 1. Write 00000000h to F2x[1,0]9C_xD08E000 */ +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0x0D08E000, 0); ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D08E000, 0); + /* 2. If DRAM Configuration Register[MemClkFreq] (F2x[1,0]94[2:0]) is + greater than or equal to 011b (DDR-800 and higher), + then write 00000080h to F2x[1,0]9C_xD02E001, + else write 00000090h to F2x[1,0]9C_xD02E001. */ +- if (pDCTstat->Speed >= 4) +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x80); ++ if (pDCTstat->Speed >= mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x80); + else +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x90); ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, 0x98, 0x0D02E001, 0x90); + } + return pDCTstat->ErrCode; + } +@@ -2455,9 +4081,9 @@ static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat, + i_end = dct + 1; + } + for (i=i_start; i<i_end; i++) { +- index_reg = 0x98 + (i * 0x100); +- Set_NB32_index_wait(dev, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */ +- Set_NB32_index_wait(dev, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */ ++ index_reg = 0x98; ++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */ ++ Set_NB32_index_wait_DCT(dev, i, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */ + } + + return pDCTstat->ErrCode; +@@ -2511,14 +4137,14 @@ static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat, + } + + if (pDCTstat->DIMMValidDCT[0] == 0) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); + val |= 1 << DisDramInterface; +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val); + } + if (pDCTstat->DIMMValidDCT[1] == 0) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); + val |= 1 << DisDramInterface; +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val); + } + + printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status); +@@ -2648,21 +4274,20 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { + u32 reg; +- u32 reg_off = 0x100 * dct; + u32 val; + u32 dword; + u32 dev = pDCTstat->dev_dct; + +- Get_DqsRcvEnGross_Diff(pDCTstat, dev, 0x98 + reg_off); +- Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98 + reg_off); ++ Get_DqsRcvEnGross_Diff(pDCTstat, dev, dct, 0x98); ++ Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98); + Get_Trdrd(pMCTstat, pDCTstat, dct); + Get_Twrwr(pMCTstat, pDCTstat, dct); + Get_Twrrd(pMCTstat, pDCTstat, dct); + Get_TrwtTO(pMCTstat, pDCTstat, dct); + Get_TrwtWB(pMCTstat, pDCTstat); + +- reg = 0x8C + reg_off; /* Dram Timing Hi */ +- val = Get_NB32(dev, reg); ++ reg = 0x8C; /* Dram Timing Hi */ ++ val = Get_NB32_DCT(dev, dct, reg); + val &= 0xffff0300; + dword = pDCTstat->TrwtTO; + val |= dword << 4; +@@ -2674,10 +4299,10 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat, + val |= dword << 14; + dword = pDCTstat->TrwtWB; + val |= dword; +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + +- reg = 0x78 + reg_off; +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, dct, reg); + val &= 0xFFFFC0FF; + dword = pDCTstat->Twrrd >> 2; + val |= dword << 8; +@@ -2685,7 +4310,7 @@ static void Set_OtherTiming(struct MCTStatStruc *pMCTstat, + val |= dword << 10; + dword = pDCTstat->Trdrd >> 2; + val |= dword << 12; +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + } + + static void Get_Trdrd(struct MCTStatStruc *pMCTstat, +@@ -2755,18 +4380,17 @@ static void Get_TrwtWB(struct MCTStatStruc *pMCTstat, + static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 val1, val2; + +- val1 = Get_NB32(dev, reg_off + 0x88) & 0xF; +- val2 = (Get_NB32(dev, reg_off + 0x84) >> 20) & 7; ++ val1 = Get_NB32_DCT(dev, dct, 0x88) & 0xF; ++ val2 = (Get_NB32_DCT(dev, dct, 0x84) >> 20) & 7; + + return val1 - val2; + } + + static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, +- u32 dev, u32 index_reg) ++ u32 dev, uint8_t dct, u32 index_reg) + { + u8 Smallest, Largest; + u32 val; +@@ -2776,12 +4400,12 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, + DqsRcvEnGrossDelay of any other DIMM is equal to the Critical + Gross Delay Difference (CGDD) */ + /* DqsRcvEn byte 1,0 */ +- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x10); ++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x10); + Largest = val & 0xFF; + Smallest = (val >> 8) & 0xFF; + + /* DqsRcvEn byte 3,2 */ +- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11); ++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x11); + byte = val & 0xFF; + bytex = (val >> 8) & 0xFF; + if (bytex < Smallest) +@@ -2790,7 +4414,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, + Largest = byte; + + /* DqsRcvEn byte 5,4 */ +- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20); ++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x20); + byte = val & 0xFF; + bytex = (val >> 8) & 0xFF; + if (bytex < Smallest) +@@ -2799,7 +4423,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, + Largest = byte; + + /* DqsRcvEn byte 7,6 */ +- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21); ++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x21); + byte = val & 0xFF; + bytex = (val >> 8) & 0xFF; + if (bytex < Smallest) +@@ -2809,7 +4433,7 @@ static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, + + if (pDCTstat->DimmECCPresent> 0) { + /*DqsRcvEn Ecc */ +- val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12); ++ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, dct, index_reg, 0x12); + byte = val & 0xFF; + bytex = (val >> 8) & 0xFF; + if (bytex < Smallest) +@@ -2873,7 +4497,7 @@ static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, + } + + static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat, +- u32 dev, u32 index_reg, ++ u32 dev, uint8_t dct, u32 index_reg, + u32 index) + { + u8 Smallest, Largest; +@@ -2891,7 +4515,7 @@ static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat, + + for (i=0; i < 8; i+=2) { + if ( pDCTstat->DIMMValid & (1 << i)) { +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + val &= 0x00E000E0; + byte = (val >> 5) & 0xFF; + if (byte < Smallest) +@@ -2929,7 +4553,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, + Smallest = 3; + Largest = 0; + for (i=0; i < 2; i++) { +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + val &= 0x60606060; + val >>= 5; + for (j=0; j < 4; j++) { +@@ -2945,7 +4569,7 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, + + if (pDCTstat->DimmECCPresent > 0) { + index++; +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + val &= 0x00000060; + val >>= 5; + byte = val & 0xFF; +@@ -2965,25 +4589,30 @@ static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, + static void mct_PhyController_Config(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 index_reg = 0x98 + 0x100 * dct; ++ uint8_t index; ++ uint32_t dword; ++ u32 index_reg = 0x98; + u32 dev = pDCTstat->dev_dct; +- u32 val; + +- if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3)) { ++ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3 | AMD_FAM15_ALL)) { + if (pDCTstat->Dimmx4Present == 0) { +- /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for power saving */ +- val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0F13); /* Agesa v3 v6 might be wrong here. */ +- val |= 1 << 7; /* BIOS should set this bit when x4 DIMMs are not present */ +- Set_NB32_index_wait(dev, index_reg, 0x0D0F0F13, val); ++ /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for ++ * additional power saving when x4 DIMMs are not present. ++ */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8)); ++ dword |= (0x1 << 7); /* RxDqsUDllPowerDown = 1 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0013 | (index << 8), dword); ++ } + } + } + +- if (pDCTstat->LogicalCPUID & AMD_DR_DAC2_OR_C3) { ++ if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_FAM15_ALL)) { + if (pDCTstat->DimmECCPresent == 0) { + /* Set bit4 PwrDn to register F2x[1, 0]98_x0D0F0830 for power saving */ +- val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0830); +- val |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */ +- Set_NB32_index_wait(dev, index_reg, 0x0D0F0830, val); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830); ++ dword |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0830, dword); + } + } + +@@ -3024,21 +4653,61 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat, + val &= ~(1 << 12); + + val &= 0x0FFFFFFF; +- switch (pDCTstat->Speed) { +- case 4: +- val |= 0x50000000; /* 5 for DDR800 */ +- break; +- case 5: +- val |= 0x60000000; /* 6 for DDR1066 */ +- break; +- case 6: +- val |= 0x80000000; /* 8 for DDR800 */ +- break; +- default: +- val |= 0x90000000; /* 9 for DDR1600 */ +- break; ++ if (!is_fam15h()) { ++ switch (pDCTstat->Speed) { ++ case 4: ++ val |= 0x50000000; /* 5 for DDR800 */ ++ break; ++ case 5: ++ val |= 0x60000000; /* 6 for DDR1066 */ ++ break; ++ case 6: ++ val |= 0x80000000; /* 8 for DDR800 */ ++ break; ++ default: ++ val |= 0x90000000; /* 9 for DDR1600 */ ++ break; ++ } + } + Set_NB32(pDCTstat->dev_dct, 0x1B0, val); ++ ++ if (is_fam15h()) { ++ uint8_t wm1; ++ uint8_t wm2; ++ ++ switch (pDCTstat->Speed) { ++ case 0x4: ++ wm1 = 0x3; ++ wm2 = 0x4; ++ break; ++ case 0x6: ++ wm1 = 0x3; ++ wm2 = 0x5; ++ break; ++ case 0xa: ++ wm1 = 0x4; ++ wm2 = 0x6; ++ break; ++ case 0xe: ++ wm1 = 0x5; ++ wm2 = 0x8; ++ break; ++ case 0x12: ++ wm1 = 0x6; ++ wm2 = 0x9; ++ break; ++ default: ++ wm1 = 0x7; ++ wm2 = 0xa; ++ break; ++ } ++ ++ val = Get_NB32(pDCTstat->dev_dct, 0x1B4); ++ val &= ~(0x3ff); ++ val |= ((wm2 & 0x1f) << 5); ++ val |= (wm1 & 0x1f); ++ Set_NB32(pDCTstat->dev_dct, 0x1B4, val); ++ } + } + } + +@@ -3055,16 +4724,103 @@ static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat, + } + } + ++void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ /* Force the NB P-state to P0 */ ++ uint32_t dword; ++ uint32_t dword2; ++ ++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x174); ++ if (!(dword & 0x1)) { ++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170); ++ pDCTstat->SwNbPstateLoDis = (dword >> 14) & 0x1; ++ pDCTstat->NbPstateDisOnP0 = (dword >> 13) & 0x1; ++ pDCTstat->NbPstateThreshold = (dword >> 9) & 0x7; ++ pDCTstat->NbPstateHi = (dword >> 6) & 0x3; ++ dword &= ~(0x1 << 14); /* SwNbPstateLoDis = 0 */ ++ dword &= ~(0x1 << 13); /* NbPstateDisOnP0 = 0 */ ++ dword &= ~(0x7 << 9); /* NbPstateThreshold = 0 */ ++ dword &= ~(0x3 << 3); /* NbPstateLo = NbPstateMaxVal */ ++ dword |= ((dword & 0x3) << 3); ++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword); ++ ++ /* Wait until CurNbPState == NbPstateLo */ ++ do { ++ dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174); ++ } while (((dword2 << 19) & 0x7) != (dword & 0x3)); ++ ++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170); ++ dword &= ~(0x3 << 6); /* NbPstateHi = 0 */ ++ dword |= (0x3 << 14); /* SwNbPstateLoDis = 1 */ ++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword); ++ ++ /* Wait until CurNbPState == 0 */ ++ do { ++ dword2 = Get_NB32(pDCTstat->dev_nbctl, 0x174); ++ } while (((dword2 << 19) & 0x7) != 0); ++ } ++} ++ ++void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ /* Restore normal NB P-state functionailty */ ++ uint32_t dword; ++ ++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x174); ++ if (!(dword & 0x1)) { ++ dword = Get_NB32(pDCTstat->dev_nbctl, 0x170); ++ dword &= ~(0x1 << 14); /* SwNbPstateLoDis*/ ++ dword |= ((pDCTstat->SwNbPstateLoDis & 0x1) << 14); ++ dword &= ~(0x1 << 13); /* NbPstateDisOnP0 */ ++ dword |= ((pDCTstat->NbPstateDisOnP0 & 0x1) << 13); ++ dword &= ~(0x7 << 9); /* NbPstateThreshold */ ++ dword |= ((pDCTstat->NbPstateThreshold & 0x7) << 9); ++ dword &= ~(0x3 << 6); /* NbPstateHi */ ++ dword |= ((pDCTstat->NbPstateHi & 0x3) << 3); ++ Set_NB32(pDCTstat->dev_nbctl, 0x170, dword); ++ } ++} ++ + static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat) + { +- mct_SetClToNB_D(pMCTstat, pDCTstat); +- mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat); ++ if (is_fam15h()) { ++ msr_t p0_state_msr; ++ uint8_t cpu_fid; ++ uint8_t cpu_did; ++ uint32_t cpu_divisor; ++ uint8_t boost_states; ++ ++ /* Retrieve the number of boost states */ ++ boost_states = (Get_NB32(pDCTstat->dev_link, 0x15c) >> 2) & 0x7; ++ ++ /* Retrieve and store the TSC frequency (P0 COF) */ ++ p0_state_msr = rdmsr(0xc0010064 + boost_states); ++ cpu_fid = p0_state_msr.lo & 0x3f; ++ cpu_did = (p0_state_msr.lo >> 6) & 0x7; ++ cpu_divisor = (0x1 << cpu_did); ++ pMCTstat->TSCFreq = (100 * (cpu_fid + 0x10)) / cpu_divisor; ++ ++ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat); ++ } else { ++ /* K10 BKDG v3.62 section 2.8.9.2 */ ++ printk(BIOS_DEBUG, "mct_InitialMCT_D: clear_legacy_Mode\n"); ++ clear_legacy_Mode(pMCTstat, pDCTstat); ++ ++ /* Northbridge configuration */ ++ mct_SetClToNB_D(pMCTstat, pDCTstat); ++ mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat); ++ } + } + + static u32 mct_NodePresent_D(void) + { + u32 val; +- val = 0x12001022; ++ if (is_fam15h()) ++ val = 0x16001022; ++ else ++ val = 0x12001022; + return val; + } + +@@ -3097,14 +4853,13 @@ static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat, + + /* Clear Legacy BIOS Mode bit */ + reg = 0x94; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 0, reg); + val &= ~(1<<LegacyBiosMode); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 0, reg, val); + +- reg = 0x94 + 0x100; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 1, reg); + val &= ~(1<<LegacyBiosMode); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 1, reg, val); + } + + static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat, +@@ -3171,7 +4926,7 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat, + { + u32 val; + u32 dev = pDCTstat->dev_dct; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u32 index; + u16 word; + +@@ -3186,9 +4941,9 @@ static void SetCSTriState(struct MCTStatStruc *pMCTstat, + } + word = (~word) & 0xFF; + index = 0x0c; +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + val |= word; +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val); + } + + static void SetCKETriState(struct MCTStatStruc *pMCTstat, +@@ -3196,7 +4951,7 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat, + { + u32 val; + u32 dev; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u32 index; + u16 word; + +@@ -3208,14 +4963,14 @@ static void SetCKETriState(struct MCTStatStruc *pMCTstat, + word = pDCTstat->CSPresent; + + index = 0x0c; +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + if ((word & 0x55) == 0) + val |= 1 << 12; + + if ((word & 0xAA) == 0) + val |= 1 << 13; + +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val); + } + + static void SetODTTriState(struct MCTStatStruc *pMCTstat, +@@ -3223,7 +4978,7 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat, + { + u32 val; + u32 dev; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u8 cs; + u32 index; + u8 odt; +@@ -3257,86 +5012,281 @@ static void SetODTTriState(struct MCTStatStruc *pMCTstat, + } + + index = 0x0C; +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); + val |= ((odt & 0xFF) << 8); /* set bits 11:8 ODTTriState[3:0] */ +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val); ++ ++} ++ ++/* Family 15h */ ++static void InitDDRPhy(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct) ++{ ++ uint8_t index; ++ uint32_t dword; ++ uint8_t ddr_voltage_index; ++ uint8_t amd_voltage_level_index = 0; ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); + ++ /* Find current DDR supply voltage for this DCT */ ++ ddr_voltage_index = dct_ddr_voltage_index(pDCTstat, dct); ++ ++ /* Fam15h BKDG v3.14 section 2.10.5.3 ++ * The remainder of the Phy Initialization algorithm picks up in phyAssistedMemFnceTraining ++ */ ++ for (dct = 0; dct < 2; dct++) { ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000b, 0x80000000); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe013, 0x00000118); ++ ++ /* Program desired VDDIO level */ ++ if (ddr_voltage_index & 0x4) { ++ /* 1.25V */ ++ amd_voltage_level_index = 0x2; ++ } else if (ddr_voltage_index & 0x2) { ++ /* 1.35V */ ++ amd_voltage_level_index = 0x1; ++ } else if (ddr_voltage_index & 0x1) { ++ /* 1.50V */ ++ amd_voltage_level_index = 0x0; ++ } ++ ++ /* D18F2x9C_x0D0F_0[F,8:0]1F_dct[1:0][RxVioLvl] */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8)); ++ dword &= ~(0x3 << 3); ++ dword |= (amd_voltage_level_index << 3); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f001f | (index << 8), dword); ++ } ++ ++ /* D18F2x9C_x0D0F_[C,8,2][2:0]1F_dct[1:0][RxVioLvl] */ ++ for (index = 0; index < 0x3; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8)); ++ dword &= ~(0x3 << 3); ++ dword |= (amd_voltage_level_index << 3); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f201f | (index << 8), dword); ++ } ++ for (index = 0; index < 0x2; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8)); ++ dword &= ~(0x3 << 3); ++ dword |= (amd_voltage_level_index << 3); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f801f | (index << 8), dword); ++ } ++ for (index = 0; index < 0x1; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8)); ++ dword &= ~(0x3 << 3); ++ dword |= (amd_voltage_level_index << 3); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc01f | (index << 8), dword); ++ } ++ ++ /* D18F2x9C_x0D0F_4009_dct[1:0][CmpVioLvl, ComparatorAdjust] */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009); ++ dword &= ~(0x0000c00c); ++ dword |= (amd_voltage_level_index << 14); ++ dword |= (amd_voltage_level_index << 2); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f4009, dword); ++ } ++ ++ printk(BIOS_DEBUG, "%s: Done\n", __func__); + } + + static void InitPhyCompensation(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { + u8 i; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u32 dev = pDCTstat->dev_dct; +- u32 val; + u32 valx = 0; +- u32 dword; ++ uint8_t index; ++ uint32_t dword; + const u8 *p; + +- val = Get_NB32_index_wait(dev, index_reg, 0x00); +- dword = 0; +- for (i=0; i < 6; i++) { +- switch (i) { +- case 0: +- case 4: +- p = Table_Comp_Rise_Slew_15x; +- valx = p[(val >> 16) & 3]; +- break; +- case 1: +- case 5: +- p = Table_Comp_Fall_Slew_15x; +- valx = p[(val >> 16) & 3]; +- break; +- case 2: +- p = Table_Comp_Rise_Slew_20x; +- valx = p[(val >> 8) & 3]; +- break; +- case 3: +- p = Table_Comp_Fall_Slew_20x; +- valx = p[(val >> 8) & 3]; +- break; ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); ++ ++ if (is_fam15h()) { ++ /* Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.3.4 */ ++ uint32_t tx_pre; ++ uint32_t drive_strength; ++ ++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003); ++ dword |= (0x3 << 13); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword); ++ ++ /* Determine TxPreP/TxPreN for data lanes (Stage 1) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 20) & 0x7; /* DqsDrvStren */ ++ tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for data lanes (Stage 1) */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8)); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0006 | (index << 8), dword); ++ } + ++ /* Determine TxPreP/TxPreN for data lanes (Stage 2) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 16) & 0x7; /* DataDrvStren */ ++ tx_pre = fam15h_phy_predriver_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for data lanes (Stage 2) */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8)); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000a | (index << 8), dword); ++ } ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8)); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0002 | (index << 8), dword); + } +- dword |= valx << (5 * i); +- } + +- /* Override/Exception */ +- if (!pDCTstat->GangedMode) { +- i = 0; /* use i for the dct setting required */ +- if (pDCTstat->MAdimms[0] < 4) +- i = 1; +- if (((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) && (pDCTstat->MAdimms[i] == 4)) { +- dword &= 0xF18FFF18; +- index_reg = 0x98; /* force dct = 0 */ ++ /* Determine TxPreP/TxPreN for command/address lines (Stage 1) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 4) & 0x7; /* CsOdtDrvStren */ ++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for command/address lines (Stage 1) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8006, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f800a, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8002, dword); ++ ++ /* Determine TxPreP/TxPreN for command/address lines (Stage 2) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 8) & 0x7; /* AddrCmdDrvStren */ ++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for command/address lines (Stage 2) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8106, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f810a, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc006, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00a, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc00e, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012); ++ dword &= ~(0xfff); ++ dword |= tx_pre; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc012, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8102, dword); ++ ++ /* Determine TxPreP/TxPreN for command/address lines (Stage 3) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 0) & 0x7; /* CkeDrvStren */ ++ tx_pre = fam15h_phy_predriver_cmd_addr_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for command/address lines (Stage 3) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc002, dword); ++ ++ /* Determine TxPreP/TxPreN for clock lines */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000000); ++ drive_strength = (dword >> 12) & 0x7; /* ClkDrvStren */ ++ tx_pre = fam15h_phy_predriver_clk_calibration_code(pDCTstat, dct, drive_strength); ++ ++ /* Program TxPreP/TxPreN for clock lines */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2002, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2102, dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202); ++ dword &= ~(0xfff); ++ dword |= (0x8000 | tx_pre); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2202, dword); ++ } else { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00); ++ dword = 0; ++ for (i=0; i < 6; i++) { ++ switch (i) { ++ case 0: ++ case 4: ++ p = Table_Comp_Rise_Slew_15x; ++ valx = p[(dword >> 16) & 3]; ++ break; ++ case 1: ++ case 5: ++ p = Table_Comp_Fall_Slew_15x; ++ valx = p[(dword >> 16) & 3]; ++ break; ++ case 2: ++ p = Table_Comp_Rise_Slew_20x; ++ valx = p[(dword >> 8) & 3]; ++ break; ++ case 3: ++ p = Table_Comp_Fall_Slew_20x; ++ valx = p[(dword >> 8) & 3]; ++ break; ++ } ++ dword |= valx << (5 * i); + } ++ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0a, dword); + } + +- Set_NB32_index_wait(dev, index_reg, 0x0a, dword); ++ printk(BIOS_DEBUG, "%s: Done\n", __func__); + } + + static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 reg; +- u32 val; +- u32 dev = pDCTstat->dev_dct; +- +- /* GhEnhancement #18429 modified by askar: For low NB CLK : +- * Memclk ratio, the DCT may need to arbitrate early to avoid +- * unnecessary bubbles. +- * bit 19 of F2x[1,0]78 Dram Control Register, set this bit only when +- * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive) +- */ +- reg = 0x78 + 0x100 * dct; +- val = Get_NB32(dev, reg); +- +- if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) +- val |= (1 << EarlyArbEn); +- else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat)) +- val |= (1 << EarlyArbEn); +- +- Set_NB32(dev, reg, val); ++ if (!is_fam15h()) { ++ u32 reg; ++ u32 val; ++ u32 dev = pDCTstat->dev_dct; ++ ++ /* GhEnhancement #18429 modified by askar: For low NB CLK : ++ * Memclk ratio, the DCT may need to arbitrate early to avoid ++ * unnecessary bubbles. ++ * bit 19 of F2x[1,0]78 Dram Control Register, set this bit only when ++ * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive) ++ */ ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, dct, reg); ++ ++ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) ++ val |= (1 << EarlyArbEn); ++ else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat)) ++ val |= (1 << EarlyArbEn); ++ ++ Set_NB32_DCT(dev, dct, reg, val); ++ } + } + + static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat, +@@ -3359,9 +5309,9 @@ static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat, + NbDid |= 1; + + reg = 0x94; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 0, reg); + if (!(val & (1 << MemClkFreqVal))) +- val = Get_NB32(dev, reg + 0x100); /* get the DCT1 value */ ++ val = Get_NB32_DCT(dev, 1, reg); /* get the DCT1 value */ + + val &= 0x07; + val += 3; +@@ -3430,28 +5380,204 @@ static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat, + } + + static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstat) ++ struct DCTStatStruc *pDCTstat, u8 dct) ++{ ++ mct_ProgramODT_D(pMCTstat, pDCTstat, dct); ++} ++ ++static void mct_ProgramODT_D(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct) + { + u8 i; +- u32 reg_off, dword; ++ u32 dword; + u32 dev = pDCTstat->dev_dct; + +- if (pDCTstat->LogicalCPUID & AMD_DR_Dx) { ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ if (is_fam15h()) { ++ /* Obtain number of DIMMs on channel */ ++ uint8_t dimm_count = pDCTstat->MAdimms[dct]; ++ uint8_t rank_count_dimm0; ++ uint8_t rank_count_dimm1; ++ uint32_t odt_pattern_0; ++ uint32_t odt_pattern_1; ++ uint32_t odt_pattern_2; ++ uint32_t odt_pattern_3; ++ uint8_t write_odt_duration; ++ uint8_t read_odt_duration; ++ uint8_t write_odt_delay; ++ uint8_t read_odt_delay; ++ ++ /* Select appropriate ODT pattern for installed DIMMs ++ * Refer to the Fam15h BKDG Rev. 3.14, page 149 onwards ++ */ ++ if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED]) { ++ if (MaxDimmsInstallable == 2) { ++ if (dimm_count == 1) { ++ /* 1 DIMM detected */ ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ if (rank_count_dimm1 == 1) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00020000; ++ } else if (rank_count_dimm1 == 2) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x08020000; ++ } else if (rank_count_dimm1 == 4) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x020a0000; ++ odt_pattern_3 = 0x080a0000; ++ } else { ++ /* Fallback */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x08020000; ++ } ++ } else { ++ /* 2 DIMMs detected */ ++ rank_count_dimm0 = pDCTstat->C_DCTPtr[dct]->DimmRanks[0]; ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ if ((rank_count_dimm0 < 4) && (rank_count_dimm1 < 4)) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x01010202; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x09030603; ++ } else if ((rank_count_dimm0 < 4) && (rank_count_dimm1 == 4)) { ++ odt_pattern_0 = 0x01010000; ++ odt_pattern_1 = 0x01010a0a; ++ odt_pattern_2 = 0x01090000; ++ odt_pattern_3 = 0x01030e0b; ++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 < 4)) { ++ odt_pattern_0 = 0x00000202; ++ odt_pattern_1 = 0x05050202; ++ odt_pattern_2 = 0x00000206; ++ odt_pattern_3 = 0x0d070203; ++ } else if ((rank_count_dimm0 == 4) && (rank_count_dimm1 == 4)) { ++ odt_pattern_0 = 0x05050a0a; ++ odt_pattern_1 = 0x05050a0a; ++ odt_pattern_2 = 0x050d0a0e; ++ odt_pattern_3 = 0x05070a0b; ++ } else { ++ /* Fallback */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00000000; ++ } ++ } ++ } else { ++ /* FIXME ++ * 3 DIMMs per channel UNIMPLEMENTED ++ */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00000000; ++ } ++ } else if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) { ++ /* TODO ++ * Load reduced dimms UNIMPLEMENTED ++ */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00000000; ++ } else { ++ if (MaxDimmsInstallable == 2) { ++ if (dimm_count == 1) { ++ /* 1 DIMM detected */ ++ rank_count_dimm1 = pDCTstat->C_DCTPtr[dct]->DimmRanks[1]; ++ if (rank_count_dimm1 == 1) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00020000; ++ } else if (rank_count_dimm1 == 2) { ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x08020000; ++ } else { ++ /* Fallback */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x08020000; ++ } ++ } else { ++ /* 2 DIMMs detected */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x01010202; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x09030603; ++ } ++ } else { ++ /* FIXME ++ * 3 DIMMs per channel UNIMPLEMENTED ++ */ ++ odt_pattern_0 = 0x00000000; ++ odt_pattern_1 = 0x00000000; ++ odt_pattern_2 = 0x00000000; ++ odt_pattern_3 = 0x00000000; ++ } ++ } ++ ++ if (pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED]) { ++ /* TODO ++ * Load reduced dimms UNIMPLEMENTED ++ */ ++ write_odt_duration = 0x0; ++ read_odt_duration = 0x0; ++ write_odt_delay = 0x0; ++ read_odt_delay = 0x0; ++ } else { ++ uint8_t tcl; ++ uint8_t tcwl; ++ tcl = Get_NB32_DCT(dev, dct, 0x200) & 0x1f; ++ tcwl = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f; ++ ++ write_odt_duration = 0x6; ++ read_odt_duration = 0x6; ++ write_odt_delay = 0x0; ++ if (tcl > tcwl) ++ read_odt_delay = tcl - tcwl; ++ else ++ read_odt_delay = 0x0; ++ } ++ ++ /* Program ODT pattern */ ++ Set_NB32_DCT(dev, dct, 0x230, odt_pattern_1); ++ Set_NB32_DCT(dev, dct, 0x234, odt_pattern_0); ++ Set_NB32_DCT(dev, dct, 0x238, odt_pattern_3); ++ Set_NB32_DCT(dev, dct, 0x23c, odt_pattern_2); ++ dword = Get_NB32_DCT(dev, dct, 0x240); ++ dword &= ~(0x7 << 12); /* WrOdtOnDuration = write_odt_duration */ ++ dword |= (write_odt_duration & 0x7) << 12; ++ dword &= ~(0x7 << 8); /* WrOdtTrnOnDly = write_odt_delay */ ++ dword |= (write_odt_delay & 0x7) << 8; ++ dword &= ~(0xf << 4); /* RdOdtOnDuration = read_odt_duration */ ++ dword |= (read_odt_duration & 0xf) << 4; ++ dword &= ~(0xf); /* RdOdtTrnOnDly = read_odt_delay */ ++ dword |= (read_odt_delay & 0xf); ++ Set_NB32_DCT(dev, dct, 0x240, dword); ++ } else if (pDCTstat->LogicalCPUID & AMD_DR_Dx) { + if (pDCTstat->Speed == 3) + dword = 0x00000800; + else + dword = 0x00000000; + for (i=0; i < 2; i++) { +- reg_off = 0x100 * i; +- Set_NB32(dev, 0x98 + reg_off, 0x0D000030); +- Set_NB32(dev, 0x9C + reg_off, dword); +- Set_NB32(dev, 0x98 + reg_off, 0x4D040F30); +- +- /* FIXME +- * Mainboards need to be able to specify the maximum number of DIMMs installable per channel +- * For now assume a maximum of 2 DIMMs per channel can be installed +- */ +- uint8_t MaxDimmsInstallable = 2; ++ Set_NB32_DCT(dev, i, 0x98, 0x0D000030); ++ Set_NB32_DCT(dev, i, 0x9C, dword); ++ Set_NB32_DCT(dev, i, 0x98, 0x4D040F30); + + /* Obtain number of DIMMs on channel */ + uint8_t dimm_count = pDCTstat->MAdimms[i]; +@@ -3463,7 +5589,7 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, + uint32_t odt_pattern_3; + + /* Select appropriate ODT pattern for installed DIMMs +- * Refer to the BKDG Rev. 3.62, page 120 onwards ++ * Refer to the Fam10h BKDG Rev. 3.62, page 120 onwards + */ + if (pDCTstat->C_DCTPtr[i]->Status[DCT_STATUS_REGISTERED]) { + if (MaxDimmsInstallable == 2) { +@@ -3574,10 +5700,10 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, + } + + /* Program ODT pattern */ +- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x180, odt_pattern_1); +- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x181, odt_pattern_0); +- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x182, odt_pattern_3); +- Set_NB32_index_wait(dev, 0xf0 + reg_off, 0x183, odt_pattern_2); ++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x180, odt_pattern_1); ++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x181, odt_pattern_0); ++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x182, odt_pattern_3); ++ Set_NB32_index_wait_DCT(dev, i, 0xf0, 0x183, odt_pattern_2); + } + } + } +@@ -3585,34 +5711,32 @@ static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, + static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct, val; + + /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */ + if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) { +- Set_NB32(dev, 0x9C + reg_off, 0x1C); +- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006); +- Set_NB32(dev, 0x9C + reg_off, 0x13D); +- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007); ++ Set_NB32_DCT(dev, dct, 0x9C, 0x1C); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006); ++ Set_NB32_DCT(dev, dct, 0x9C, 0x13D); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007); + +- val = Get_NB32(dev, 0x90 + reg_off); ++ val = Get_NB32_DCT(dev, dct, 0x90); + val &= ~(1 << 27/* DisDllShutdownSR */); +- Set_NB32(dev, 0x90 + reg_off, val); ++ Set_NB32_DCT(dev, dct, 0x90, val); + } + } + + static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + + /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */ + if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) { +- Set_NB32(dev, 0x9C + reg_off, 0x7D0); +- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006); +- Set_NB32(dev, 0x9C + reg_off, 0x190); +- Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007); ++ Set_NB32_DCT(dev, dct, 0x9C, 0x7D0); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE006); ++ Set_NB32_DCT(dev, dct, 0x9C, 0x190); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D0FE007); + + DramConfigLo |= /* DisDllShutdownSR */ 1 << 27; + } +@@ -3704,52 +5828,61 @@ void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat, + DramMRS |= 1 << 23; + } + } +- /* +- DRAM MRS Register +- DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7) +- */ +- DramMRS |= 1 << 2; +- /* Dram nominal termination: */ +- byte = pDCTstat->MAdimms[dct]; +- if (!(pDCTstat->Status & (1 << SB_Registered))) { +- DramMRS |= 1 << 7; /* 60 ohms */ +- if (byte & 2) { +- if (pDCTstat->Speed < 6) +- DramMRS |= 1 << 8; /* 40 ohms */ +- else +- DramMRS |= 1 << 9; /* 30 ohms */ ++ ++ if (is_fam15h()) { ++ DramMRS |= (0x1 << 23); /* PchgPDModeSel = 1 */ ++ } else { ++ /* ++ DRAM MRS Register ++ DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7) ++ */ ++ DramMRS |= 1 << 2; ++ /* Dram nominal termination: */ ++ byte = pDCTstat->MAdimms[dct]; ++ if (!(pDCTstat->Status & (1 << SB_Registered))) { ++ DramMRS |= 1 << 7; /* 60 ohms */ ++ if (byte & 2) { ++ if (pDCTstat->Speed < 6) ++ DramMRS |= 1 << 8; /* 40 ohms */ ++ else ++ DramMRS |= 1 << 9; /* 30 ohms */ ++ } + } +- } +- /* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */ +- if (!(pDCTstat->Status & (1 << SB_Registered))) { +- if (byte >= 2) { +- if (pDCTstat->Speed == 7) +- DramMRS |= 1 << 10; +- else +- DramMRS |= 1 << 11; ++ /* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */ ++ if (!(pDCTstat->Status & (1 << SB_Registered))) { ++ if (byte >= 2) { ++ if (pDCTstat->Speed == 7) ++ DramMRS |= 1 << 10; ++ else ++ DramMRS |= 1 << 11; ++ } ++ } else { ++ DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte); + } +- } else { +- DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte); ++ ++ /* Qoff=0, output buffers enabled */ ++ /* Tcwl */ ++ DramMRS |= (pDCTstat->Speed - 4) << 20; ++ /* ASR=1, auto self refresh */ ++ /* SRT=0 */ ++ DramMRS |= 1 << 18; + } + + /* burst length control */ + if (pDCTstat->Status & (1 << SB_128bitmode)) + DramMRS |= 1 << 1; +- /* Qoff=0, output buffers enabled */ +- /* Tcwl */ +- DramMRS |= (pDCTstat->Speed - 4) << 20; +- /* ASR=1, auto self refresh */ +- /* SRT=0 */ +- DramMRS |= 1 << 18; +- +- dword = Get_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84); +- dword &= ~0x00FC2F8F; ++ ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x84); ++ if (is_fam15h()) ++ dword &= ~0x00800003; ++ else ++ dword &= ~0x00fc2f8f; + dword |= DramMRS; +- Set_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84, dword); ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0x84, dword); + } + +-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, +- u32 DramConfigHi) ++void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi) + { + /* Bug#15114: Comp. update interrupted by Freq. change can cause + * subsequent update to be invalid during any MemClk frequency change: +@@ -3778,45 +5911,86 @@ void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, + */ + + u32 dev = pDCTstat->dev_dct; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u32 index; + +- u32 val; ++ uint32_t dword; ++ ++ if (is_fam15h()) { ++ /* Initial setup for frequency change ++ * 9C_x0000_0004 must be configured before MemClkFreqVal is set ++ */ + +- index = 0x08; +- val = Get_NB32_index_wait(dev, index_reg, index); +- if (!(val & (1 << DisAutoComp))) +- Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp)); ++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */ ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x00000190; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword); + +- mct_Wait(100); ++ dword = Get_NB32_DCT(dev, dct, 0x94); ++ dword &= ~(1 << MemClkFreqVal); ++ Set_NB32_DCT(dev, dct, 0x94, dword); + +- Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi); ++ dword = DramConfigHi; ++ dword &= ~(1 << MemClkFreqVal); ++ Set_NB32_DCT(dev, dct, 0x94, dword); ++ ++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct); ++ set_2t_configuration(pMCTstat, pDCTstat, dct); ++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, dct); ++ mct_PlatformSpec(pMCTstat, pDCTstat, dct); ++ } else { ++ index = 0x08; ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); ++ if (!(dword & (1 << DisAutoComp))) ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, dword | (1 << DisAutoComp)); ++ ++ mct_Wait(100); ++ } ++ ++ /* Program the DRAM Configuration High register */ ++ Set_NB32_DCT(dev, dct, 0x94, DramConfigHi); ++ ++ if (is_fam15h()) { ++ /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */ ++ do { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94); ++ } while (dword & (1 << FreqChgInProg)); ++ ++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */ ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x0000000f; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, dct, index_reg, 0x0d0fe006, dword); ++ } + } + + static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) + { +- u8 Node; +- struct DCTStatStruc *pDCTstat; ++ if (!is_fam15h()) { ++ u8 Node; ++ struct DCTStatStruc *pDCTstat; + +- /* Errata 178 +- * +- * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations +- * In TX FIFO +- * Solution: BIOS should program DRAM Control Register[RdPtrInit] = +- * 5h, (F2x[1, 0]78[3:0] = 5h). +- * Silicon Status: Fixed In Rev B0 +- * +- * Bug#15880: Determine validity of reset settings for DDR PHY timing. +- * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training. +- */ +- for (Node = 0; Node < 8; Node++) { +- pDCTstat = pDCTstatA + Node; ++ /* Errata 178 ++ * ++ * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations ++ * In TX FIFO ++ * Solution: BIOS should program DRAM Control Register[RdPtrInit] = ++ * 5h, (F2x[1, 0]78[3:0] = 5h). ++ * Silicon Status: Fixed In Rev B0 ++ * ++ * Bug#15880: Determine validity of reset settings for DDR PHY timing. ++ * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training. ++ */ ++ for (Node = 0; Node < 8; Node++) { ++ pDCTstat = pDCTstatA + Node; + +- if (pDCTstat->NodePresent) { +- mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */ +- mct_ResetDLL_D(pMCTstat, pDCTstat, 0); +- mct_ResetDLL_D(pMCTstat, pDCTstat, 1); ++ if (pDCTstat->NodePresent) { ++ mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */ ++ mct_ResetDLL_D(pMCTstat, pDCTstat, 0); ++ mct_ResetDLL_D(pMCTstat, pDCTstat, 1); ++ } + } + } + } +@@ -3827,7 +6001,6 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat, + { + u8 Receiver; + u32 dev = pDCTstat->dev_dct; +- u32 reg_off = 0x100 * dct; + u32 addr; + u32 lo, hi; + u8 wrap32dis = 0; +@@ -3838,6 +6011,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat, + return; + } + ++ /* Skip reset DLL for Family 15h */ ++ if (is_fam15h()) { ++ return; ++ } ++ + addr = HWCR; + _RDMSR(addr, &lo, &hi); + if(lo & (1<<17)) { /* save the old value */ +@@ -3857,11 +6035,11 @@ static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat, + mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr); /* cache fills */ + + /* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */ +- Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00008000); ++ Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00008000); + mct_Wait(80); /* wait >= 300ns */ + + /* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */ +- Set_NB32_index_wait(dev, 0x98 + reg_off, 0xD080F0C, 0x00000000); ++ Set_NB32_index_wait_DCT(dev, dct, 0x98, 0xD080F0C, 0x00000000); + mct_Wait(800); /* wait >= 2us */ + break; + } +@@ -3901,39 +6079,39 @@ static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat, + static void SetDllSpeedUp_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 val; +- u32 dev = pDCTstat->dev_dct; +- u32 reg_off = 0x100 * dct; +- +- if (pDCTstat->Speed >= 7) { /* DDR1600 and above */ +- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */ +- Set_NB32(dev, reg_off + 0x98, 0x0D080F10); +- val = Get_NB32(dev, reg_off + 0x9C); +- val |= 1 < 13; +- Set_NB32(dev, reg_off + 0x9C, val); +- Set_NB32(dev, reg_off + 0x98, 0x4D080F10); +- +- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */ +- Set_NB32(dev, reg_off + 0x98, 0x0D080F11); +- val = Get_NB32(dev, reg_off + 0x9C); +- val |= 1 < 13; +- Set_NB32(dev, reg_off + 0x9C, val); +- Set_NB32(dev, reg_off + 0x98, 0x4D080F11); +- +- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */ +- Set_NB32(dev, reg_off + 0x98, 0x0D088F30); +- val = Get_NB32(dev, reg_off + 0x9C); +- val |= 1 < 13; +- Set_NB32(dev, reg_off + 0x9C, val); +- Set_NB32(dev, reg_off + 0x98, 0x4D088F30); +- +- /* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */ +- Set_NB32(dev, reg_off + 0x98, 0x0D08CF30); +- val = Get_NB32(dev, reg_off + 0x9C); +- val |= 1 < 13; +- Set_NB32(dev, reg_off + 0x9C, val); +- Set_NB32(dev, reg_off + 0x98, 0x4D08CF30); +- ++ if (!is_fam15h()) { ++ u32 val; ++ u32 dev = pDCTstat->dev_dct; ++ ++ if (pDCTstat->Speed >= mhz_to_memclk_config(800)) { /* DDR1600 and above */ ++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */ ++ Set_NB32_DCT(dev, dct, 0x98, 0x0D080F10); ++ val = Get_NB32_DCT(dev, dct, 0x9C); ++ val |= 1 < 13; ++ Set_NB32_DCT(dev, dct, 0x9C, val); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D080F10); ++ ++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */ ++ Set_NB32_DCT(dev, dct, 0x98, 0x0D080F11); ++ val = Get_NB32_DCT(dev, dct, 0x9C); ++ val |= 1 < 13; ++ Set_NB32_DCT(dev, dct, 0x9C, val); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D080F11); ++ ++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */ ++ Set_NB32_DCT(dev, dct, 0x98, 0x0D088F30); ++ val = Get_NB32_DCT(dev, dct, 0x9C); ++ val |= 1 < 13; ++ Set_NB32_DCT(dev, dct, 0x9C, val); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D088F30); ++ ++ /* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */ ++ Set_NB32_DCT(dev, dct, 0x98, 0x0D08CF30); ++ val = Get_NB32_DCT(dev, dct, 0x9C); ++ val |= 1 < 13; ++ Set_NB32_DCT(dev, dct, 0x9C, val); ++ Set_NB32_DCT(dev, dct, 0x98, 0x4D08CF30); ++ } + } + } + +@@ -3961,7 +6139,6 @@ static void SyncSetting(struct DCTStatStruc *pDCTstat) + static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) { + + u32 val; +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + + if (pDCTstat->LogicalCPUID & (AMD_DR_B2 | AMD_DR_B3)) { +@@ -3969,16 +6146,16 @@ static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) { + val = Get_NB32(dev, 0x110); + if (!(val & (1 << DramEnabled))) { + /* If 50 us expires while DramEnable =0 then do the following */ +- val = Get_NB32(dev, 0x90 + reg_off); ++ val = Get_NB32_DCT(dev, dct, 0x90); + val &= ~(1 << Width128); /* Program Width128 = 0 */ +- Set_NB32(dev, 0x90 + reg_off, val); ++ Set_NB32_DCT(dev, dct, 0x90, val); + +- val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05); /* Perform dummy CSR read to F2x09C_x05 */ ++ val = Get_NB32_index_wait_DCT(dev, dct, 0x98, 0x05); /* Perform dummy CSR read to F2x09C_x05 */ + + if (pDCTstat->GangedMode) { +- val = Get_NB32(dev, 0x90 + reg_off); ++ val = Get_NB32_DCT(dev, dct, 0x90); + val |= 1 << Width128; /* Program Width128 = 0 */ +- Set_NB32(dev, 0x90 + reg_off, val); ++ Set_NB32_DCT(dev, dct, 0x90, val); + } + } + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +index a947c2d..50fbff7 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h +@@ -76,6 +76,8 @@ + /* #define PA_EXT_DCTADDL (((00 << 3)+5) << 8) */ /*Node x DCT function, Additional Registers PCI Address bits [15:0]*/ + + #define PA_NBMISC(Node) ((((0x18+Node) << 3)+3) << 12) /*Node 0 Misc PCI Address bits [15:0]*/ ++#define PA_LINK(Node) ((((0x18+Node) << 3)+4) << 12) /*Node 0 Link Control bits [15:0]*/ ++#define PA_NBCTL(Node) ((((0x18+Node) << 3)+5) << 12) /*Node 0 NB Control PCI Address bits [15:0]*/ + /* #define PA_NBDEVOP (((00 << 3)+3) << 8) */ /*Node 0 Misc PCI Address bits [15:0]*/ + + #define DCC_EN 1 /* X:2:0x94[19]*/ +@@ -129,7 +131,7 @@ + #define X4Dimm 12 /* func 2, offset 90h, bit 12*/ + #define UnBuffDimm 16 /* func 2, offset 90h, bit 16*/ + #define DimmEcEn 19 /* func 2, offset 90h, bit 19*/ +-#define MemClkFreqVal 3 /* func 2, offset 94h, bit 3*/ ++#define MemClkFreqVal ((is_fam15h())?7:3) /* func 2, offset 94h, bit 3 or 7*/ + #define RDqsEn 12 /* func 2, offset 94h, bit 12*/ + #define DisDramInterface 14 /* func 2, offset 94h, bit 14*/ + #define PowerDownEn 15 /* func 2, offset 94h, bit 15*/ +@@ -204,6 +206,7 @@ + #define JED_PROBEMSK 0x40 /*Analysis Probe installed*/ + #define JED_RDIMM 0x1 /* RDIMM */ + #define JED_MiniRDIMM 0x5 /* Mini-RDIMM */ ++ #define JED_LRDIMM 0xb /* Load-reduced DIMM */ + #define SPD_Density 4 /* Bank address bits,SDRAM capacity */ + #define SPD_Addressing 5 /* Row/Column address bits */ + #define SPD_Voltage 6 /* Supported voltage bitfield */ +@@ -297,6 +300,7 @@ struct MCTStatStruc { + of sub 4GB dram hole for HW remapping.*/ + u32 Sub4GCacheTop; /* If not zero, the 32-bit top of cacheable memory.*/ + u32 SysLimit; /* LIMIT[39:8] (system address)*/ ++ uint32_t TSCFreq; + } __attribute__((packed)); + + /*============================================================================= +@@ -320,7 +324,8 @@ struct MCTStatStruc { + + struct DCTStatStruc { /* A per Node structure*/ + /* DCTStatStruct_F - start */ +- u8 Node_ID; /* Node ID of current controller*/ ++ u8 Node_ID; /* Node ID of current controller */ ++ uint8_t stopDCT; /* Set if the DCT will be stopped */ + u8 ErrCode; /* Current error condition of Node + 0= no error + 1= Variance Error, DCT is running but not in an optimal configuration. +@@ -464,7 +469,7 @@ struct DCTStatStruc { /* A per Node structure*/ + /* CH A byte lane 0 - 7 maximum filtered window passing DQS delay value*/ + /* CH B byte lane 0 - 7 minimum filtered window passing DQS delay value*/ + /* CH B byte lane 0 - 7 maximum filtered window passing DQS delay value*/ +- u32 LogicalCPUID; /* The logical CPUID of the node*/ ++ uint64_t LogicalCPUID; /* The logical CPUID of the node*/ + u16 HostBiosSrvc1; /* Word sized general purpose field for use by host BIOS. Scratch space.*/ + u32 HostBiosSrvc2; /* Dword sized general purpose field for use by host BIOS. Scratch space.*/ + u16 DimmQRPresent; /* QuadRank DIMM present?*/ +@@ -558,12 +563,20 @@ struct DCTStatStruc { /* A per Node structure*/ + u8 ClToNB_flag; /* is used to restore ClLinesToNbDis bit after memory */ + u32 NodeSysBase; /* for channel interleave usage */ + ++ /* Fam15h specific backup variables */ ++ uint8_t SwNbPstateLoDis; ++ uint8_t NbPstateDisOnP0; ++ uint8_t NbPstateThreshold; ++ uint8_t NbPstateHi; ++ + /* New for LB Support */ + u8 NodePresent; + u32 dev_host; + u32 dev_map; + u32 dev_dct; + u32 dev_nbmisc; ++ u32 dev_link; ++ u32 dev_nbctl; + u8 TargetFreq; + u8 TargetCASL; + u8 CtrlWrd3; +@@ -596,9 +609,10 @@ struct DCTStatStruc { /* A per Node structure*/ + uint8_t DimmBanks[MAX_DIMMS_SUPPORTED]; + uint8_t DimmWidth[MAX_DIMMS_SUPPORTED]; + uint8_t DimmRegistered[MAX_DIMMS_SUPPORTED]; ++ uint8_t DimmLoadReduced[MAX_DIMMS_SUPPORTED]; + + uint64_t DimmManufacturerID[MAX_DIMMS_SUPPORTED]; +- char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH]; ++ char DimmPartNumber[MAX_DIMMS_SUPPORTED][SPD_PARTN_LENGTH+1]; + uint16_t DimmRevisionNumber[MAX_DIMMS_SUPPORTED]; + uint32_t DimmSerialNumber[MAX_DIMMS_SUPPORTED]; + } __attribute__((packed)); +@@ -701,7 +715,64 @@ struct amd_s3_persistent_mct_channel_data { + /* Other (1 dword) */ + uint32_t f3x58; + +- /* TOTAL: 250 dwords */ ++ /* Family 15h-specific registers (90 dwords) */ ++ uint32_t f2x200; ++ uint32_t f2x204; ++ uint32_t f2x208; ++ uint32_t f2x20c; ++ uint32_t f2x210[4]; /* [nb pstate] */ ++ uint32_t f2x214; ++ uint32_t f2x218; ++ uint32_t f2x21c; ++ uint32_t f2x22c; ++ uint32_t f2x230; ++ uint32_t f2x234; ++ uint32_t f2x238; ++ uint32_t f2x23c; ++ uint32_t f2x240; ++ uint32_t f2x9cx0d0fe003; ++ uint32_t f2x9cx0d0fe013; ++ uint32_t f2x9cx0d0f0_8_0_1f[9]; /* [lane]*/ ++ uint32_t f2x9cx0d0f201f; ++ uint32_t f2x9cx0d0f211f; ++ uint32_t f2x9cx0d0f221f; ++ uint32_t f2x9cx0d0f801f; ++ uint32_t f2x9cx0d0f811f; ++ uint32_t f2x9cx0d0f821f; ++ uint32_t f2x9cx0d0fc01f; ++ uint32_t f2x9cx0d0fc11f; ++ uint32_t f2x9cx0d0fc21f; ++ uint32_t f2x9cx0d0f4009; ++ uint32_t f2x9cx0d0f0_8_0_02[9]; /* [lane]*/ ++ uint32_t f2x9cx0d0f0_8_0_06[9]; /* [lane]*/ ++ uint32_t f2x9cx0d0f0_8_0_0a[9]; /* [lane]*/ ++ uint32_t f2x9cx0d0f2002; ++ uint32_t f2x9cx0d0f2102; ++ uint32_t f2x9cx0d0f2202; ++ uint32_t f2x9cx0d0f8002; ++ uint32_t f2x9cx0d0f8006; ++ uint32_t f2x9cx0d0f800a; ++ uint32_t f2x9cx0d0f8102; ++ uint32_t f2x9cx0d0f8106; ++ uint32_t f2x9cx0d0f810a; ++ uint32_t f2x9cx0d0fc002; ++ uint32_t f2x9cx0d0fc006; ++ uint32_t f2x9cx0d0fc00a; ++ uint32_t f2x9cx0d0fc00e; ++ uint32_t f2x9cx0d0fc012; ++ uint32_t f2x9cx0d0f2031; ++ uint32_t f2x9cx0d0f2131; ++ uint32_t f2x9cx0d0f2231; ++ uint32_t f2x9cx0d0f8031; ++ uint32_t f2x9cx0d0f8131; ++ uint32_t f2x9cx0d0f8231; ++ uint32_t f2x9cx0d0fc031; ++ uint32_t f2x9cx0d0fc131; ++ uint32_t f2x9cx0d0fc231; ++ uint32_t f2x9cx0d0f0_0_f_31[9]; /* [lane] */ ++ uint32_t f2x9cx0d0f8021; ++ ++ /* TOTAL: 340 dwords */ + } __attribute__((packed)); + + struct amd_s3_persistent_node_data { +@@ -746,18 +817,19 @@ struct amd_s3_persistent_data { + Local Configuration Status (DCTStatStruc.Status[31:0]) + ===============================================================================*/ + #define SB_Registered 0 /* All DIMMs are Registered*/ +-#define SB_ECCDIMMs 1 /* All banks ECC capable*/ +-#define SB_PARDIMMs 2 /* All banks Addr/CMD Parity capable*/ +-#define SB_DiagClks 3 /* Jedec ALL slots clock enable diag mode*/ +-#define SB_128bitmode 4 /* DCT in 128-bit mode operation*/ +-#define SB_64MuxedMode 5 /* DCT in 64-bit mux'ed mode.*/ +-#define SB_2TMode 6 /* 2T CMD timing mode is enabled.*/ +-#define SB_SWNodeHole 7 /* Remapping of Node Base on this Node to create a gap.*/ +-#define SB_HWHole 8 /* Memory Hole created on this Node using HW remapping.*/ +-#define SB_Over400MHz 9 /* DCT freq >= 400MHz flag*/ +-#define SB_DQSPos_Pass2 10 /* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/ +-#define SB_DQSRcvLimit 11 /* Using for DQSRcvEnTrain to know we have reached to upper bound.*/ +-#define SB_ExtConfig 12 /* Indicator the default setting for extend PCI configuration support*/ ++#define SB_LoadReduced 1 /* All DIMMs are Load-Reduced*/ ++#define SB_ECCDIMMs 2 /* All banks ECC capable*/ ++#define SB_PARDIMMs 3 /* All banks Addr/CMD Parity capable*/ ++#define SB_DiagClks 4 /* Jedec ALL slots clock enable diag mode*/ ++#define SB_128bitmode 5 /* DCT in 128-bit mode operation*/ ++#define SB_64MuxedMode 6 /* DCT in 64-bit mux'ed mode.*/ ++#define SB_2TMode 7 /* 2T CMD timing mode is enabled.*/ ++#define SB_SWNodeHole 8 /* Remapping of Node Base on this Node to create a gap.*/ ++#define SB_HWHole 9 /* Memory Hole created on this Node using HW remapping.*/ ++#define SB_Over400MHz 10 /* DCT freq >= 400MHz flag*/ ++#define SB_DQSPos_Pass2 11 /* Using for TrainDQSPos DIMM0/1, when freq>=400MHz*/ ++#define SB_DQSRcvLimit 12 /* Using for DQSRcvEnTrain to know we have reached to upper bound.*/ ++#define SB_ExtConfig 13 /* Indicator the default setting for extend PCI configuration support*/ + + + /*=============================================================================== +@@ -775,17 +847,18 @@ struct amd_s3_persistent_data { + 266=266MHz (DDR533) + 333=333MHz (DDR667) + 400=400MHz (DDR800)*/ +-#define NV_ECC_CAP 4 /* Bus ECC capable (1-bits) ++#define NV_MIN_MEMCLK 4 /* Minimum platform demonstrated Memclock (10-bits) */ ++#define NV_ECC_CAP 5 /* Bus ECC capable (1-bits) + 0=Platform not capable + 1=Platform is capable*/ +-#define NV_4RANKType 5 /* Quad Rank DIMM slot type (2-bits) ++#define NV_4RANKType 6 /* Quad Rank DIMM slot type (2-bits) + 0=Normal + 1=R4 (4-Rank Registered DIMMs in AMD server configuration) + 2=S4 (Unbuffered SO-DIMMs)*/ +-#define NV_BYPMAX 6 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition). ++#define NV_BYPMAX 7 /* Value to set DcqBypassMax field (See Function 2, Offset 94h, [27:24] of BKDG for field definition). + 4=4 times bypass (normal for non-UMA systems) + 7=7 times bypass (normal for UMA systems)*/ +-#define NV_RDWRQBYP 7 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition). ++#define NV_RDWRQBYP 8 /* Value to set RdWrQByp field (See Function 2, Offset A0h, [3:2] of BKDG for field definition). + 2=8 times (normal for non-UMA systems) + 3=16 times (normal for UMA systems)*/ + +@@ -848,8 +921,9 @@ struct amd_s3_persistent_data { + #define NV_ECCRedir 54 /* Dram ECC Redirection enable*/ + #define NV_DramBKScrub 55 /* Dram ECC Background Scrubber CTL*/ + #define NV_L2BKScrub 56 /* L2 ECC Background Scrubber CTL*/ +-#define NV_DCBKScrub 57 /* DCache ECC Background Scrubber CTL*/ +-#define NV_CS_SpareCTL 58 /* Chip Select Spare Control bit 0: ++#define NV_L3BKScrub 57 /* L3 ECC Background Scrubber CTL*/ ++#define NV_DCBKScrub 58 /* DCache ECC Background Scrubber CTL*/ ++#define NV_CS_SpareCTL 59 /* Chip Select Spare Control bit 0: + 0=disable Spare + 1=enable Spare */ + /* Chip Select Spare Control bit 1-4: +@@ -900,10 +974,12 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, u8 FinalVa + void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel); + void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct); + void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct); +-void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi); ++void mct_SetDramConfigHi_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 dct, u32 DramConfigHi); + void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct); + void mct_SetClToNB_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); + void mct_SetWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); ++void mct_ForceNBPState0_En_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); ++void mct_ForceNBPState0_Dis_Fam15(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); + void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 Pass); + void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 _DisableDramECC); + u32 procOdtWorkaround(struct DCTStatStruc *pDCTstat, u32 dct, u32 val); +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h +index c40ea1a..f6aa755 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h +@@ -98,6 +98,15 @@ static u32 bsf(u32 x) + + u32 SetUpperFSbase(u32 addr_hi); + ++static void proc_MFENCE(void) ++{ ++ __asm__ volatile ( ++ "outb %%al, $0xed\n\t" /* _EXECFENCE */ ++ "mfence\n\t" ++ :::"memory" ++ ); ++} ++ + static void proc_CLFLUSH(u32 addr_hi) + { + SetUpperFSbase(addr_hi); +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c +index 126642b..3df262b 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -17,6 +18,8 @@ + * Foundation, Inc. + */ + ++/* AM3/ASB2/C32/G34 DDR3 */ ++ + static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload, + u32 *AddrTmgCTL, u32 *ODC_CTL, + u8 *CMDmode); +@@ -24,17 +27,23 @@ static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload, + void mctGet_PS_Cfg_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u32 dct) + { +- Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed, +- pDCTstat->MAload[dct], +- &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]), +- &pDCTstat->_2Tmode); ++ if (is_fam15h()) { ++ pDCTstat->CH_ADDR_TMG[dct] = fam15h_address_timing_compensation_code(pDCTstat, dct); ++ pDCTstat->CH_ODC_CTL[dct] = fam15h_output_driver_compensation_code(pDCTstat, dct); ++ pDCTstat->_2Tmode = fam15h_slow_access_mode(pDCTstat, dct); ++ } else { ++ Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[dct], pDCTstat->Speed, ++ pDCTstat->MAload[dct], ++ &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]), ++ &pDCTstat->_2Tmode); ++ ++ pDCTstat->CH_ODC_CTL[dct] |= 0x20000000; /* 60ohms */ ++ } + + pDCTstat->CH_EccDQSLike[0] = 0x0403; + pDCTstat->CH_EccDQSScale[0] = 0x70; + pDCTstat->CH_EccDQSLike[1] = 0x0403; + pDCTstat->CH_EccDQSScale[1] = 0x70; +- +- pDCTstat->CH_ODC_CTL[dct] |= 0x20000000; /* 60ohms */ + } + + /* +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c +index f1fd7a5..a1cdfa6 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -35,7 +36,6 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + + u32 dev; + u32 reg; +- u32 reg_off; + u32 val; + u32 val_lo, val_hi; + +@@ -44,16 +44,15 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + EnChipSels = 0; + + dev = pDCTstat->dev_dct; +- reg_off = 0x100 * dct; + + ChipSel = 0; /* Find out if current configuration is capable */ + while (DoIntlv && (ChipSel < MAX_CS_SUPPORTED)) { +- reg = 0x40+(ChipSel<<2) + reg_off; /* Dram CS Base 0 */ +- val = Get_NB32(dev, reg); ++ reg = 0x40+(ChipSel<<2); /* Dram CS Base 0 */ ++ val = Get_NB32_DCT(dev, dct, reg); + if ( val & (1<<CSEnable)) { + EnChipSels++; +- reg = 0x60+((ChipSel>>1)<<2)+reg_off; /*Dram CS Mask 0 */ +- val = Get_NB32(dev, reg); ++ reg = 0x60+((ChipSel>>1)<<2); /*Dram CS Mask 0 */ ++ val = Get_NB32_DCT(dev, dct, reg); + val >>= 19; + val &= 0x3ff; + val++; +@@ -63,8 +62,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + /*If mask sizes not same then skip */ + if (val != MemSize) + break; +- reg = 0x80 + reg_off; /*Dram Bank Addressing */ +- val = Get_NB32(dev, reg); ++ reg = 0x80; /*Dram Bank Addressing */ ++ val = Get_NB32_DCT(dev, dct, reg); + val >>= (ChipSel>>1)<<2; + val &= 0x0f; + if(EnChipSels == 1) +@@ -103,8 +102,8 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + BitDelta = bsf(AddrHiMask) - bsf(AddrLoMask); + + for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel++) { +- reg = 0x40+(ChipSel<<2) + reg_off; /*Dram CS Base 0 */ +- val = Get_NB32(dev, reg); ++ reg = 0x40+(ChipSel<<2); /*Dram CS Base 0 */ ++ val = Get_NB32_DCT(dev, dct, reg); + if (val & 3) { + val_lo = val & AddrLoMask; + val_hi = val & AddrHiMask; +@@ -114,13 +113,13 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + val_hi >>= BitDelta; + val |= val_lo; + val |= val_hi; +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + + if(ChipSel & 1) + continue; + +- reg = 0x60 + ((ChipSel>>1)<<2) + reg_off; /*Dram CS Mask 0 */ +- val = Get_NB32(dev, reg); ++ reg = 0x60 + ((ChipSel>>1)<<2); /*Dram CS Mask 0 */ ++ val = Get_NB32_DCT(dev, dct, reg); + val_lo = val & AddrLoMask; + val_hi = val & AddrHiMask; + val &= AddrLoMaskN; +@@ -129,7 +128,7 @@ void InterleaveBanks_D(struct MCTStatStruc *pMCTstat, + val_hi >>= BitDelta; + val |= val_lo; + val |= val_hi; +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + } + } + } /* DoIntlv */ +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c +index cc2f43a..740edae 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c +@@ -18,6 +18,12 @@ + * Foundation, Inc. + */ + ++static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, ++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg); ++ ++static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, ++ uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg); ++ + static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u16 like, + u8 scale, u8 ChipSel); +@@ -37,7 +43,7 @@ static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat, + u32 addr_lo); + static void SetTargetWTIO_D(u32 TestAddr); + static void ResetTargetWTIO_D(void); +-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index); ++void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index); + u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); + static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat, +@@ -54,6 +60,7 @@ static void proc_IOCLFLUSH_D(u32 addr_hi); + static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel); + + #define DQS_TRAIN_DEBUG 0 ++// #define PRINT_PASS_FAIL_BITMAPS 1 + + static void print_debug_dqs(const char *str, u32 val, u8 level) + { +@@ -198,18 +205,20 @@ void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat, + pDCTstat = pDCTstatA + Node; + + if (pDCTstat->DCTSysLimit) { +- val = Get_NB32(pDCTstat->dev_dct, 0x78); +- val |= 1 <<DqsRcvEnTrain; +- Set_NB32(pDCTstat->dev_dct, 0x78, val); +- val = Get_NB32(pDCTstat->dev_dct, 0x78 + 0x100); +- val |= 1 <<DqsRcvEnTrain; +- Set_NB32(pDCTstat->dev_dct, 0x78 + 0x100, val); ++ if (!is_fam15h()) { ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x78); ++ val |= 1 <<DqsRcvEnTrain; ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x78, val); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x78); ++ val |= 1 <<DqsRcvEnTrain; ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x78, val); ++ } + mct_TrainRcvrEn_D(pMCTstat, pDCTstat, Pass); + } + } + } + +-static void SetEccDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, ++static void SetEccDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 ChipSel) + { + u8 channel; +@@ -268,68 +277,150 @@ static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat, + pDCTstat->DQSDelay = (u8)DQSDelay; + } + +-static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg) ++static void read_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint32_t dword; ++ uint32_t mask; ++ ++ if (is_fam15h()) ++ mask = 0xff; ++ else ++ mask = 0x7f; ++ ++ /* Lanes 0 - 3 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8)); ++ delay[3] = (dword >> 24) & mask; ++ delay[2] = (dword >> 16) & mask; ++ delay[1] = (dword >> 8) & mask; ++ delay[0] = dword & mask; ++ ++ /* Lanes 4 - 7 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8)); ++ delay[7] = (dword >> 24) & mask; ++ delay[6] = (dword >> 16) & mask; ++ delay[5] = (dword >> 8) & mask; ++ delay[4] = dword & mask; ++ ++ /* Lane 8 (ECC) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8)); ++ delay[8] = dword & mask; ++} ++ ++static void write_dqs_write_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) + { + uint32_t dword; ++ uint32_t mask; ++ ++ if (is_fam15h()) ++ mask = 0xff; ++ else ++ mask = 0x7f; + + /* Lanes 0 - 3 */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8)); +- dword &= ~0x7f7f7f7f; +- dword |= (delay[3] & 0x7f) << 24; +- dword |= (delay[2] & 0x7f) << 16; +- dword |= (delay[1] & 0x7f) << 8; +- dword |= delay[0] & 0x7f; +- Set_NB32_index_wait(dev, index_reg, 0x1 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8)); ++ dword &= ~(mask << 24); ++ dword &= ~(mask << 16); ++ dword &= ~(mask << 8); ++ dword &= ~mask; ++ dword |= (delay[3] & mask) << 24; ++ dword |= (delay[2] & mask) << 16; ++ dword |= (delay[1] & mask) << 8; ++ dword |= delay[0] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x1 | (dimm << 8), dword); + + /* Lanes 4 - 7 */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8)); +- dword &= ~0x7f7f7f7f; +- dword |= (delay[7] & 0x7f) << 24; +- dword |= (delay[6] & 0x7f) << 16; +- dword |= (delay[5] & 0x7f) << 8; +- dword |= delay[4] & 0x7f; +- Set_NB32_index_wait(dev, index_reg, 0x2 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8)); ++ dword &= ~(mask << 24); ++ dword &= ~(mask << 16); ++ dword &= ~(mask << 8); ++ dword &= ~mask; ++ dword |= (delay[7] & mask) << 24; ++ dword |= (delay[6] & mask) << 16; ++ dword |= (delay[5] & mask) << 8; ++ dword |= delay[4] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x2 | (dimm << 8), dword); + + /* Lane 8 (ECC) */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8)); +- dword &= ~0x0000007f; +- dword |= delay[8] & 0x7f; +- Set_NB32_index_wait(dev, index_reg, 0x3 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8)); ++ dword &= ~mask; ++ dword |= delay[8] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x3 | (dimm << 8), dword); + } + +-static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dimm, uint32_t index_reg) ++static void read_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) + { + uint32_t dword; ++ uint32_t mask; ++ ++ if (is_fam15h()) ++ mask = 0x3e; ++ else ++ mask = 0x3f; + + /* Lanes 0 - 3 */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8)); +- dword &= ~0x3f3f3f3f; +- dword |= (delay[3] & 0x3f) << 24; +- dword |= (delay[2] & 0x3f) << 16; +- dword |= (delay[1] & 0x3f) << 8; +- dword |= delay[0] & 0x3f; +- Set_NB32_index_wait(dev, index_reg, 0x5 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8)); ++ delay[3] = (dword >> 24) & mask; ++ delay[2] = (dword >> 16) & mask; ++ delay[1] = (dword >> 8) & mask; ++ delay[0] = dword & mask; + + /* Lanes 4 - 7 */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8)); +- dword &= ~0x3f3f3f3f; +- dword |= (delay[7] & 0x3f) << 24; +- dword |= (delay[6] & 0x3f) << 16; +- dword |= (delay[5] & 0x3f) << 8; +- dword |= delay[4] & 0x3f; +- Set_NB32_index_wait(dev, index_reg, 0x6 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8)); ++ delay[7] = (dword >> 24) & mask; ++ delay[6] = (dword >> 16) & mask; ++ delay[5] = (dword >> 8) & mask; ++ delay[4] = dword & mask; + + /* Lane 8 (ECC) */ +- dword = Get_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8)); +- dword &= ~0x0000003f; +- dword |= delay[8] & 0x3f; +- Set_NB32_index_wait(dev, index_reg, 0x7 | (dimm << 8), dword); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8)); ++ delay[8] = dword & mask; ++} ++ ++static void write_dqs_read_data_timing_registers(uint16_t* delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint32_t dword; ++ uint32_t mask; ++ ++ if (is_fam15h()) ++ mask = 0x3e; ++ else ++ mask = 0x3f; ++ ++ /* Lanes 0 - 3 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8)); ++ dword &= ~(mask << 24); ++ dword &= ~(mask << 16); ++ dword &= ~(mask << 8); ++ dword &= ~mask; ++ dword |= (delay[3] & mask) << 24; ++ dword |= (delay[2] & mask) << 16; ++ dword |= (delay[1] & mask) << 8; ++ dword |= delay[0] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x5 | (dimm << 8), dword); ++ ++ /* Lanes 4 - 7 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8)); ++ dword &= ~(mask << 24); ++ dword &= ~(mask << 16); ++ dword &= ~(mask << 8); ++ dword &= ~mask; ++ dword |= (delay[7] & mask) << 24; ++ dword |= (delay[6] & mask) << 16; ++ dword |= (delay[5] & mask) << 8; ++ dword |= delay[4] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x6 | (dimm << 8), dword); ++ ++ /* Lane 8 (ECC) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8)); ++ dword &= ~mask; ++ dword |= delay[8] & mask; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x7 | (dimm << 8), dword); + } + + /* DQS Position Training + * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.3 + */ +-static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, ++static void TrainDQSRdWrPos_D_Fam10(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) + { + u32 Errors; +@@ -406,7 +497,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + if (pDCTstat->DIMMValidDCT[Channel] == 0) /* mct_BeforeTrainDQSRdWrPos_D */ + continue; + +- index_reg = 0x98 + 0x100 * Channel; ++ index_reg = 0x98; + + dual_rank = 0; + Receiver = mct_InitReceiver_D(pDCTstat, Channel); +@@ -462,7 +553,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + break; + + /* Commit the current Write Data Timing settings to the hardware registers */ +- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Write the DRAM training pattern to the base test address */ + WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8); +@@ -479,7 +570,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + current_read_dqs_delay[lane] = test_read_dqs_delay; + + /* Commit the current Read DQS Timing Control settings to the hardware registers */ +- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Initialize test result variable */ + bytelane_test_results = 0xff; +@@ -545,7 +636,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + passing_dqs_delay_found[lane] = 1; + + /* Commit the current Read DQS Timing Control settings to the hardware registers */ +- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Exit the DRAM Write Data Timing Loop */ + write_dqs_delay_stepping_done[lane] = 1; +@@ -579,7 +670,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + current_write_dqs_delay[lane] = test_write_dqs_delay; + + /* Commit the current Write Data Timing settings to the hardware registers */ +- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Write the DRAM training pattern to the base test address */ + WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8); +@@ -674,7 +765,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + current_read_dqs_delay[lane] = (best_pos + (best_count / 2)); + + /* Commit the current Read DQS Timing Control settings to the hardware registers */ +- write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Save the final Read DQS Timing Control settings for later use */ + pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane]; +@@ -717,7 +808,7 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + current_write_dqs_delay[lane] = (best_pos + (best_count / 2)); + + /* Commit the current Write Data Timing settings to the hardware registers */ +- write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, Channel, (Receiver >> 1), index_reg); + + /* Save the final Write Data Timing settings for later use */ + pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane]; +@@ -787,6 +878,831 @@ static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat, + printk(BIOS_DEBUG, "TrainDQSRdWrPos: Done\n\n"); + } + ++/* Calcuate and set MaxRdLatency ++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.5 ++ */ ++static void Calc_SetMaxRdLatency_D_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct) ++{ ++ uint8_t dimm; ++ uint8_t lane; ++ uint32_t dword; ++ uint32_t dword2; ++ uint32_t max_delay; ++ uint8_t mem_clk = 0; ++ uint8_t nb_pstate; ++ uint32_t nb_clk; ++ uint32_t p = 0; ++ uint32_t n = 0; ++ uint32_t t = 0; ++ uint16_t current_phy_phase_delay[MAX_BYTE_LANES]; ++ uint16_t current_read_dqs_delay[MAX_BYTE_LANES]; ++ ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; ++ ++ /* P is specified in PhyCLKs (1/2 MEMCLKs) */ ++ for (nb_pstate = 0; nb_pstate < 2; nb_pstate++) { ++ /* 2.10.5.8.5 (2) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004); ++ if ((!(dword & (0x1 << 21))) && (!(dword & (0x1 << 13))) && (!(dword & (0x1 << 5)))) ++ p += 1; ++ else ++ p += 2; ++ ++ /* 2.10.5.8.5 (3) */ ++ dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210) & 0xf; /* Retrieve RdPtrInit */ ++ p += (9 - dword); ++ ++ /* 2.10.5.8.5 (4) */ ++ p += 5; ++ ++ /* 2.10.5.8.5 (5) */ ++ dword = Get_NB32_DCT(dev, dct, 0xa8); ++ dword2 = Get_NB32_DCT(dev, dct, 0x90); ++ if ((!(dword & (0x1 << 5))) && (!(dword2 & (0x1 << 16)))) ++ p += 2; ++ ++ /* 2.10.5.8.5 (6) */ ++ dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f; /* Retrieve Tcl */ ++ p += (2 * (dword - 1)); ++ ++ /* 2.10.5.8.5 (7) */ ++ max_delay = 0; ++ for (dimm = 0; dimm < 4; dimm++) { ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, dimm * 2)) ++ continue; ++ ++ read_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg); ++ read_read_dqs_timing_control_registers(current_read_dqs_delay, dev, dct, dimm, index_reg); ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) ++ if ((current_phy_phase_delay[lane] + current_read_dqs_delay[lane]) > max_delay) ++ max_delay = (current_phy_phase_delay[lane] + current_read_dqs_delay[lane]); ++ } ++ p += (max_delay >> 5); ++ ++ /* 2.10.5.8.5 (8) */ ++ p += 5; ++ ++ /* 2.10.5.8.5 (9) */ ++ t += 800; ++ ++ /* 2.10.5.8.5 (10) */ ++ mem_clk = Get_NB32_DCT(dev, dct, 0x94) & 0x1f; ++ dword = Get_NB32(pDCTstat->dev_nbctl, (0x160 + (nb_pstate * 4))); /* Retrieve NbDid, NbFid */ ++ nb_clk = (200 * (((dword >> 1) & 0x1f) + 0x4)) / (((dword >> 7) & 0x1)?2:1); ++ n = (((((uint64_t)p * 1000000000000ULL)/(((uint64_t)fam15h_freq_tab[mem_clk] * 1000000ULL) * 2)) + ((uint64_t)t)) * ((uint64_t)nb_clk * 1000)) / 1000000000ULL; ++ ++ /* 2.10.5.8.5 (11) */ ++ n -= 1; ++ ++ /* 2.10.5.8.5 (12) */ ++ dword = Get_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210); ++ dword &= ~(0x3ff << 22); ++ dword |= (((n - 1) & 0x3ff) << 22); ++ Set_NB32_DCT_NBPstate(dev, dct, nb_pstate, 0x210, dword); ++ ++ /* Save result for later use */ ++ pDCTstat->CH_MaxRdLat[dct] = n; ++ } ++} ++ ++static void start_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver) ++{ ++ uint32_t dword; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ /* 2.10.5.7.1.1 ++ * It appears that the DCT only supports 8-beat burst length mode, ++ * so do nothing here... ++ */ ++ ++ /* Wait for CmdSendInProg == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ } while (dword & (0x1 << 12)); ++ ++ /* Set CmdTestEnable = 1 */ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword |= (0x1 << 2); ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.1 Send Activate Command (Target A) */ ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */ ++ dword |= ((0x1 << Receiver) << 22); ++ dword &= ~(0x7 << 19); /* CmdBank = 0 */ ++ dword &= ~(0x3ffff); /* CmdAddress = 0 */ ++ dword |= (0x1 << 31); /* SendActCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x28c, dword); ++ ++ /* Wait for SendActCmd == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ } while (dword & (0x1 << 31)); ++ ++ /* Wait 75 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75); ++ ++ /* 2.10.5.8.6.1.1 Send Activate Command (Target B) */ ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */ ++ dword |= ((0x1 << Receiver) << 22); ++ dword &= ~(0x7 << 19); /* CmdBank = 1 */ ++ dword |= (0x1 << 19); ++ dword &= ~(0x3ffff); /* CmdAddress = 0 */ ++ dword |= (0x1 << 31); /* SendActCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x28c, dword); ++ ++ /* Wait for SendActCmd == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ } while (dword & (0x1 << 31)); ++ ++ /* Wait 75 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75); ++} ++ ++static void stop_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver) ++{ ++ uint32_t dword; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ /* 2.10.5.8.6.1.1 Send Precharge Command */ ++ /* Wait 25 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */ ++ dword |= ((0x1 << Receiver) << 22); ++ dword &= ~(0x7 << 19); /* CmdBank = 0 */ ++ dword &= ~(0x3ffff); /* CmdAddress = 0x400 */ ++ dword |= 0x400; ++ dword |= (0x1 << 30); /* SendPchgCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x28c, dword); ++ ++ /* Wait for SendPchgCmd == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ } while (dword & (0x1 << 30)); ++ ++ /* Wait 25 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25); ++ ++ /* Set CmdTestEnable = 0 */ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword &= ~(0x1 << 2); ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++} ++ ++static void read_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane) ++{ ++ uint32_t dword; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver); ++ ++ /* 2.10.5.8.6.1.2 */ ++ /* Configure DQMask */ ++ if (lane < 4) { ++ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8))); ++ Set_NB32_DCT(dev, dct, 0x278, ~0x0); ++ } else if (lane < 8) { ++ Set_NB32_DCT(dev, dct, 0x274, ~0x0); ++ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8))); ++ } else { ++ Set_NB32_DCT(dev, dct, 0x274, ~0x0); ++ Set_NB32_DCT(dev, dct, 0x278, ~0x0); ++ } ++ ++ dword = Get_NB32_DCT(dev, dct, 0x27c); ++ dword &= ~(0xff); /* EccMask = 0 */ ++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0)) ++ dword |= 0xff; /* EccMask = 0xff */ ++ Set_NB32_DCT(dev, dct, 0x27c, dword); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x270); ++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */ ++// dword |= (0x55555); ++ dword |= (0x44443); /* Use AGESA seed */ ++ Set_NB32_DCT(dev, dct, 0x270, dword); ++ ++ /* 2.10.5.8.4 */ ++ dword = Get_NB32_DCT(dev, dct, 0x260); ++ dword &= ~(0x1fffff); /* CmdCount = 256 */ ++ dword |= 256; ++ Set_NB32_DCT(dev, dct, 0x260, dword); ++ ++ /* Configure Target A */ ++ dword = Get_NB32_DCT(dev, dct, 0x254); ++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */ ++ dword |= (Receiver & 0x7) << 24; ++ dword &= ~(0x7 << 21); /* TgtBank = 0 */ ++ dword &= ~(0x3ff); /* TgtAddress = 0 */ ++ Set_NB32_DCT(dev, dct, 0x254, dword); ++ ++ /* Configure Target B */ ++ dword = Get_NB32_DCT(dev, dct, 0x258); ++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */ ++ dword |= (Receiver & 0x7) << 24; ++ dword &= ~(0x7 << 21); /* TgtBank = 1 */ ++ dword |= (0x1 << 21); ++ dword &= ~(0x3ff); /* TgtAddress = 0 */ ++ Set_NB32_DCT(dev, dct, 0x258, dword); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword |= (0x1 << 3); /* ResetAllErr = 1 */ ++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */ ++ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */ ++ dword |= (0x1 << 8); ++ dword &= ~(0x7 << 5); /* CmdType = 0 (Read) */ ++ dword |= (0x1 << 11); /* SendCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10)))); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword &= ~(0x1 << 11); /* SendCmd = 0 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver); ++} ++ ++static void write_dram_dqs_training_pattern_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver, uint8_t lane) ++{ ++ uint32_t dword; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ start_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver); ++ ++ /* 2.10.5.8.6.1.2 */ ++ /* Configure DQMask */ ++ if (lane < 4) { ++ Set_NB32_DCT(dev, dct, 0x274, ~(0xff << (lane * 8))); ++ Set_NB32_DCT(dev, dct, 0x278, ~0x0); ++ } else if (lane < 8) { ++ Set_NB32_DCT(dev, dct, 0x274, ~0x0); ++ Set_NB32_DCT(dev, dct, 0x278, ~(0xff << (lane * 8))); ++ } else { ++ Set_NB32_DCT(dev, dct, 0x274, ~0x0); ++ Set_NB32_DCT(dev, dct, 0x278, ~0x0); ++ } ++ ++ dword = Get_NB32_DCT(dev, dct, 0x27c); ++ dword &= ~(0xff); /* EccMask = 0 */ ++ if ((lane != 8) || (pDCTstat->DimmECCPresent == 0)) ++ dword |= 0xff; /* EccMask = 0xff */ ++ Set_NB32_DCT(dev, dct, 0x27c, dword); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x270); ++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */ ++// dword |= (0x55555); ++ dword |= (0x44443); /* Use AGESA seed */ ++ Set_NB32_DCT(dev, dct, 0x270, dword); ++ ++ /* 2.10.5.8.4 */ ++ dword = Get_NB32_DCT(dev, dct, 0x260); ++ dword &= ~(0x1fffff); /* CmdCount = 256 */ ++ dword |= 256; ++ Set_NB32_DCT(dev, dct, 0x260, dword); ++ ++ /* Configure Target A */ ++ dword = Get_NB32_DCT(dev, dct, 0x254); ++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */ ++ dword |= (Receiver & 0x7) << 24; ++ dword &= ~(0x7 << 21); /* TgtBank = 0 */ ++ dword &= ~(0x3ff); /* TgtAddress = 0 */ ++ Set_NB32_DCT(dev, dct, 0x254, dword); ++ ++ /* Configure Target B */ ++ dword = Get_NB32_DCT(dev, dct, 0x258); ++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */ ++ dword |= (Receiver & 0x7) << 24; ++ dword &= ~(0x7 << 21); /* TgtBank = 1 */ ++ dword |= (0x1 << 21); ++ dword &= ~(0x3ff); /* TgtAddress = 0 */ ++ Set_NB32_DCT(dev, dct, 0x258, dword); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword |= (0x1 << 3); /* ResetAllErr = 1 */ ++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */ ++ dword &= ~(0x3 << 8); /* CmdTgt = 1 (Alternate between Target A and Target B) */ ++ dword |= (0x1 << 8); ++ dword &= ~(0x7 << 5); /* CmdType = 1 (Write) */ ++ dword |= (0x1 << 5); ++ dword |= (0x1 << 11); /* SendCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10)))); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword &= ~(0x1 << 11); /* SendCmd = 0 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ stop_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver); ++} ++ ++/* DQS Position Training ++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.4 ++ */ ++static uint8_t TrainDQSRdWrPos_D_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t receiver_start, uint8_t receiver_end, uint8_t lane_start, uint8_t lane_end) ++{ ++ uint8_t dimm; ++ uint8_t lane; ++ uint32_t dword; ++ uint32_t Errors; ++ uint8_t Receiver; ++ uint8_t dual_rank; ++ uint8_t write_iter; ++ uint8_t read_iter; ++ uint16_t initial_write_dqs_delay[MAX_BYTE_LANES]; ++ uint16_t initial_read_dqs_delay[MAX_BYTE_LANES]; ++ uint16_t initial_write_data_timing[MAX_BYTE_LANES]; ++ uint16_t current_write_data_delay[MAX_BYTE_LANES]; ++ uint16_t current_read_dqs_delay[MAX_BYTE_LANES]; ++ uint16_t current_write_dqs_delay[MAX_BYTE_LANES]; ++ uint8_t passing_dqs_delay_found[MAX_BYTE_LANES]; ++ uint8_t dqs_results_array[2][(lane_end - lane_start)][32][32]; /* [rank][lane][write step][read step] */ ++ ++ uint8_t last_pos = 0; ++ uint8_t cur_count = 0; ++ uint8_t best_pos = 0; ++ uint8_t best_count = 0; ++ ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ /* Calculate and program MaxRdLatency */ ++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct); ++ ++ Errors = 0; ++ dual_rank = 0; ++ Receiver = mct_InitReceiver_D(pDCTstat, dct); ++ if (receiver_start > Receiver) ++ Receiver = receiver_start; ++ ++ /* There are four receiver pairs, loosely associated with chipselects. ++ * This is essentially looping over each DIMM. ++ */ ++ for (; Receiver < receiver_end; Receiver += 2) { ++ dimm = (Receiver >> 1); ++ if ((Receiver & 0x1) == 0) { ++ /* Even rank of DIMM */ ++ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver+1)) ++ dual_rank = 1; ++ else ++ dual_rank = 0; ++ } ++ ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) { ++ continue; ++ } ++ ++ /* Initialize variables */ ++ for (lane = lane_start; lane < lane_end; lane++) { ++ passing_dqs_delay_found[lane] = 0; ++ } ++ memset(dqs_results_array, 0, sizeof(dqs_results_array)); ++ ++ /* Read initial read / write DQS delays */ ++ read_dqs_write_timing_control_registers(initial_write_dqs_delay, dev, dct, dimm, index_reg); ++ read_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg); ++ ++ /* Read current settings of other (previously trained) lanes */ ++ read_dqs_write_data_timing_registers(initial_write_data_timing, dev, dct, dimm, index_reg); ++ memcpy(current_write_data_delay, initial_write_data_timing, sizeof(current_write_data_delay)); ++ ++ for (lane = lane_start; lane < lane_end; lane++) { ++ /* 2.10.5.8.4 (2) ++ * For each Write Data Delay value from Write DQS Delay to Write DQS Delay + 1 UI ++ */ ++ for (current_write_data_delay[lane] = initial_write_dqs_delay[lane]; current_write_data_delay[lane] < (initial_write_dqs_delay[lane] + 0x20); current_write_data_delay[lane]++) { ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 16 current_write_data_delay[lane] ", current_write_data_delay[lane], 6); ++ ++ /* 2.10.5.8.4 (2 A) ++ * Commit the current Write Data Timing settings to the hardware registers ++ */ ++ write_dqs_write_data_timing_registers(current_write_data_delay, dev, dct, dimm, index_reg); ++ ++ /* 2.10.5.8.4 (2 B) ++ * Write the DRAM training pattern to the test address ++ */ ++ write_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane); ++ ++ /* Read current settings of other (previously trained) lanes */ ++ read_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg); ++ ++ /* 2.10.5.8.4 (2 C) ++ * For each Read DQS Delay value from 0 to 1 UI ++ */ ++ for (current_read_dqs_delay[lane] = 0; current_read_dqs_delay[lane] < 0x40; current_read_dqs_delay[lane] += 2) { ++ print_debug_dqs("\t\t\t\t\tTrainDQSRdWrPos: 161 current_read_dqs_delay[lane] ", current_read_dqs_delay[lane], 6); ++ ++ /* 2.10.5.8.4 (2 A i) ++ * Commit the current Read DQS Timing Control settings to the hardware registers ++ */ ++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg); ++ ++ /* 2.10.5.8.4 (2 A ii) ++ * Read the DRAM training pattern from the test address ++ */ ++ read_dram_dqs_training_pattern_fam15(pMCTstat, pDCTstat, dct, Receiver, lane); ++ ++ /* 2.10.5.8.4 (2 A iii) ++ * Record pass / fail status ++ */ ++ dword = Get_NB32_DCT(dev, dct, 0x268) & 0x3ffff; ++ if (dword & (0x3 << (lane * 2))) ++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 0; /* Fail */ ++ else ++ dqs_results_array[Receiver & 0x1][lane - lane_start][current_write_data_delay[lane] - initial_write_dqs_delay[lane]][current_read_dqs_delay[lane] >> 1] = 1; /* Pass */ ++ } ++ } ++ ++ if (dual_rank && (Receiver & 0x1)) { ++ /* Overlay the previous rank test results with the current rank */ ++ for (write_iter = 0; write_iter < 32; write_iter++) { ++ for (read_iter = 0; read_iter < 32; read_iter++) { ++ if ((dqs_results_array[0][lane - lane_start][write_iter][read_iter]) ++ && (dqs_results_array[1][lane - lane_start][write_iter][read_iter])) ++ dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 1; ++ else ++ dqs_results_array[1][lane - lane_start][write_iter][read_iter] = 0; ++ } ++ } ++ } ++ ++ /* Determine location and length of longest consecutive string of read passing values ++ * Output is stored in best_pos and best_count ++ */ ++ last_pos = 0; ++ cur_count = 0; ++ best_pos = 0; ++ best_count = 0; ++ for (write_iter = 0; write_iter < 32; write_iter++) { ++ for (read_iter = 0; read_iter < 32; read_iter++) { ++ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (read_iter < 31)) { ++ /* Pass */ ++ cur_count++; ++ } else { ++ /* Failure or end of loop */ ++ if (cur_count > best_count) { ++ best_count = cur_count; ++ best_pos = last_pos; ++ } ++ cur_count = 0; ++ last_pos = read_iter; ++ } ++ } ++ last_pos = 0; ++ } ++ ++ if (best_count > 2) { ++ /* Restore current settings of other (previously trained) lanes to the active array */ ++ memcpy(current_read_dqs_delay, initial_read_dqs_delay, sizeof(current_read_dqs_delay)); ++ ++ /* Program the Read DQS Timing Control register with the center of the passing window */ ++ current_read_dqs_delay[lane] = ((best_pos << 1) + ((best_count << 1) / 2)); ++ passing_dqs_delay_found[lane] = 1; ++ ++ /* Commit the current Read DQS Timing Control settings to the hardware registers */ ++ write_dqs_read_data_timing_registers(current_read_dqs_delay, dev, dct, dimm, index_reg); ++ ++ /* Save the final Read DQS Timing Control settings for later use */ ++ pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_READDIR][lane] = current_read_dqs_delay[lane]; ++ ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 142 largest read passing region ", best_count, 4); ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 143 largest read passing region start ", best_pos, 4); ++ } else { ++ /* Reprogram the Read DQS Timing Control register with the original settings */ ++ write_dqs_read_data_timing_registers(initial_read_dqs_delay, dev, dct, dimm, index_reg); ++ } ++ ++ /* Determine location and length of longest consecutive string of write passing values ++ * Output is stored in best_pos and best_count ++ */ ++ last_pos = 0; ++ cur_count = 0; ++ best_pos = 0; ++ best_count = 0; ++ for (read_iter = 0; read_iter < 32; read_iter++) { ++ for (write_iter = 0; write_iter < 32; write_iter++) { ++ if ((dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) && (write_iter < 31)) { ++ /* Pass */ ++ cur_count++; ++ } else { ++ /* Failure or end of loop */ ++ if (cur_count > best_count) { ++ best_count = cur_count; ++ best_pos = last_pos; ++ } ++ cur_count = 0; ++ last_pos = write_iter; ++ } ++ } ++ last_pos = 0; ++ } ++ ++ if (best_count > 2) { ++ /* Restore current settings of other (previously trained) lanes to the active array */ ++ memcpy(current_write_dqs_delay, initial_write_data_timing, sizeof(current_write_data_delay)); ++ ++ /* Program the Write DQS Timing Control register with the optimal region within the passing window */ ++ if (pDCTstat->Status & (1 << SB_LoadReduced)) ++ current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 3)); ++ else ++ current_write_dqs_delay[lane] = ((best_pos + initial_write_dqs_delay[lane]) + (best_count / 2)); ++ passing_dqs_delay_found[lane] = 1; ++ ++ /* Commit the current Write DQS Timing Control settings to the hardware registers */ ++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg); ++ ++ /* Save the final Write Data Timing settings for later use */ ++ pDCTstat->CH_D_DIR_B_DQS[dct][Receiver >> 1][DQS_WRITEDIR][lane] = current_write_dqs_delay[lane]; ++ ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 144 largest write passing region ", best_count, 4); ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 145 largest write passing region start ", best_pos, 4); ++ } else { ++ /* Reprogram the Write DQS Timing Control register with the original settings */ ++ write_dqs_write_data_timing_registers(current_write_dqs_delay, dev, dct, dimm, index_reg); ++ } ++ } ++ ++#ifdef PRINT_PASS_FAIL_BITMAPS ++ for (lane = lane_start; lane < lane_end; lane++) { ++ for (read_iter = 0; read_iter < 32; read_iter++) { ++ for (write_iter = 0; write_iter < 32; write_iter++) { ++ if (dqs_results_array[Receiver & 0x1][lane - lane_start][write_iter][read_iter]) ++ printk(BIOS_DEBUG, "+"); ++ else ++ printk(BIOS_DEBUG, "."); ++ } ++ printk(BIOS_DEBUG, "\n"); ++ } ++ printk(BIOS_DEBUG, "\n\n"); ++ } ++#endif ++ ++ /* Flag failure(s) if present */ ++ for (lane = lane_start; lane < lane_end; lane++) { ++ if (!passing_dqs_delay_found[lane]) { ++ print_debug_dqs("\t\t\t\tTrainDQSRdWrPos: 121 Unable to find passing region for lane ", lane, 2); ++ ++ /* Flag absence of passing window */ ++ Errors |= 1 << SB_NODQSPOS; ++ } ++ } ++ ++ pDCTstat->TrainErrors |= Errors; ++ pDCTstat->ErrStatus |= Errors; ++ ++#if DQS_TRAIN_DEBUG > 0 ++ { ++ u8 val; ++ u8 i; ++ u8 ChannelDTD, ReceiverDTD, Dir; ++ u8 *p; ++ ++ for (Dir = 0; Dir < 2; Dir++) { ++ if (Dir == 1) { ++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n"); ++ } else { ++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n"); ++ } ++ for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) { ++ printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD); ++ for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) { ++ printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD); ++ p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir]; ++ for (i=0;i<8; i++) { ++ val = p[i]; ++ printk(BIOS_DEBUG, " %02x", val); ++ } ++ printk(BIOS_DEBUG, "\n"); ++ } ++ } ++ } ++ ++ } ++#endif ++ } ++ ++ /* Return 1 on success, 0 on failure */ ++ return !Errors; ++} ++ ++/* DQS Receiver Enable Cycle Training ++ * Algorithm detailed in the Fam15h BKDG Rev. 3.14 section 2.10.5.8.3 ++ */ ++static void TrainDQSReceiverEnCyc_D_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat) ++{ ++ u32 Errors; ++ u8 Receiver; ++ u8 _DisableDramECC = 0; ++ u8 _Wrap32Dis = 0, _SSE2 = 0; ++ ++ u32 addr; ++ u32 cr4; ++ u32 lo, hi; ++ ++ uint8_t dct; ++ uint8_t prev; ++ uint8_t dimm; ++ uint8_t lane; ++ uint32_t dword; ++ uint32_t rx_en_offset; ++ uint16_t initial_phy_phase_delay[MAX_BYTE_LANES]; ++ uint16_t current_phy_phase_delay[MAX_BYTE_LANES]; ++ uint8_t dqs_results_array[1024]; ++ ++ uint16_t ren_step = 0x40; ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ print_debug_dqs("\nTrainDQSReceiverEnCyc: Node_ID ", pDCTstat->Node_ID, 0); ++ cr4 = read_cr4(); ++ if (cr4 & (1<<9)) { ++ _SSE2 = 1; ++ } ++ cr4 |= (1<<9); /* OSFXSR enable SSE2 */ ++ write_cr4(cr4); ++ ++ addr = HWCR; ++ _RDMSR(addr, &lo, &hi); ++ if (lo & (1<<17)) { ++ _Wrap32Dis = 1; ++ } ++ lo |= (1<<17); /* HWCR.wrap32dis */ ++ _WRMSR(addr, lo, hi); /* allow 64-bit memory references in real mode */ ++ ++ /* Disable ECC correction of reads on the dram bus. */ ++ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat); ++ ++ Errors = 0; ++ ++ for (dct = 0; dct < 2; dct++) { ++ /* Program D18F2x9C_x0D0F_E003_dct[1:0][DisAutoComp, DisablePredriverCal] */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003); ++ dword &= ~(0x3 << 13); ++ dword |= (0x1 << 13); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fe003, dword); ++ } ++ ++ for (dct = 0; dct < 2; dct++) { ++ /* 2.10.5.6 */ ++ fam15EnableTrainingMode(pMCTstat, pDCTstat, dct, 1); ++ ++ /* 2.10.5.8.3 */ ++ Receiver = mct_InitReceiver_D(pDCTstat, dct); ++ ++ /* There are four receiver pairs, loosely associated with chipselects. ++ * This is essentially looping over each DIMM. ++ */ ++ for (; Receiver < 8; Receiver += 2) { ++ dimm = (Receiver >> 1); ++ ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) { ++ continue; ++ } ++ ++ /* 2.10.5.8.3 (2) */ ++ read_dqs_receiver_enable_control_registers(initial_phy_phase_delay, dev, dct, dimm, index_reg); ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ /* Initialize variables */ ++ memset(dqs_results_array, 0, sizeof(dqs_results_array)); ++ ++ /* 2.10.5.8.3 (1) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8)); ++ dword |= (0x1 << 8); /* BlockRxDqsLock = 1 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword); ++ ++ /* 2.10.5.8.3 (3) */ ++ rx_en_offset = (initial_phy_phase_delay[lane] + 0x10) % 0x40; ++ ++ /* 2.10.5.8.3 (4) */ ++ for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) { ++ /* 2.10.5.8.3 (4 A) */ ++ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg); ++ ++ /* Calculate and program MaxRdLatency */ ++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, dct); ++ ++ /* 2.10.5.8.3 (4 B) */ ++ dqs_results_array[current_phy_phase_delay[lane]] = TrainDQSRdWrPos_D_Fam15(pMCTstat, pDCTstat, dct, Receiver, Receiver + 2, lane, lane + 1); ++ } ++ ++#ifdef PRINT_PASS_FAIL_BITMAPS ++ uint16_t iter; ++ for (iter = 0; iter < 0x3ff; iter++) { ++ if (dqs_results_array[iter]) ++ printk(BIOS_DEBUG, "+"); ++ else ++ printk(BIOS_DEBUG, "."); ++ } ++ printk(BIOS_DEBUG, "\n"); ++#endif ++ ++ /* 2.10.5.8.3 (5) */ ++ prev = 0; ++ for (current_phy_phase_delay[lane] = rx_en_offset; current_phy_phase_delay[lane] < 0x3ff; current_phy_phase_delay[lane] += ren_step) { ++ if ((dqs_results_array[current_phy_phase_delay[lane]] == 0) && (prev == 1)) { ++ /* Restore last known good delay */ ++ current_phy_phase_delay[lane] -= ren_step; ++ ++ /* 2.10.5.8.3 (5 A B) */ ++ current_phy_phase_delay[lane] -= 0x10; ++ ++ /* Update hardware registers with final values */ ++ write_dqs_receiver_enable_control_registers(current_phy_phase_delay, dev, dct, dimm, index_reg); ++ break; ++ } ++ prev = dqs_results_array[current_phy_phase_delay[lane]]; ++ } ++ ++ /* 2.10.5.8.3 (6) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8)); ++ dword &= ~(0x1 << 8); /* BlockRxDqsLock = 0 */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0030 | (lane << 8), dword); ++ } ++ ++#if DQS_TRAIN_DEBUG > 0 ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc_D_Fam15 DQS receiver enable timing: "); ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ printk(BIOS_DEBUG, " %03x", current_phy_phase_delay[lane]); ++ } ++ printk(BIOS_DEBUG, "\n"); ++#endif ++ } ++ } ++ ++ pDCTstat->TrainErrors |= Errors; ++ pDCTstat->ErrStatus |= Errors; ++ ++#if DQS_TRAIN_DEBUG > 0 ++ { ++ u8 val; ++ u8 i; ++ u8 ChannelDTD, ReceiverDTD, Dir; ++ u8 *p; ++ ++ for (Dir = 0; Dir < 2; Dir++) { ++ if (Dir == 1) { ++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n"); ++ } else { ++ printk(BIOS_DEBUG, "TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n"); ++ } ++ for (ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) { ++ printk(BIOS_DEBUG, "Channel: %02x\n", ChannelDTD); ++ for (ReceiverDTD = 0; ReceiverDTD < MAX_CS_SUPPORTED; ReceiverDTD += 2) { ++ printk(BIOS_DEBUG, "\t\tReceiver: %02x:", ReceiverDTD); ++ p = pDCTstat->CH_D_DIR_B_DQS[ChannelDTD][ReceiverDTD >> 1][Dir]; ++ for (i=0;i<8; i++) { ++ val = p[i]; ++ printk(BIOS_DEBUG, " %02x", val); ++ } ++ printk(BIOS_DEBUG, "\n"); ++ } ++ } ++ } ++ ++ } ++#endif ++ if (_DisableDramECC) { ++ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC); ++ } ++ if (!_Wrap32Dis) { ++ addr = HWCR; ++ _RDMSR(addr, &lo, &hi); ++ lo &= ~(1<<17); /* restore HWCR.wrap32dis */ ++ _WRMSR(addr, lo, hi); ++ } ++ if (!_SSE2){ ++ cr4 = read_cr4(); ++ cr4 &= ~(1<<9); /* restore cr4.OSFXSR */ ++ write_cr4(cr4); ++ } ++ ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Status %x\n", pDCTstat->Status); ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: TrainErrors %x\n", pDCTstat->TrainErrors); ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrStatus %x\n", pDCTstat->ErrStatus); ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: ErrCode %x\n", pDCTstat->ErrCode); ++ printk(BIOS_DEBUG, "TrainDQSReceiverEnCyc: Done\n\n"); ++} ++ + static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u32 *buffer) + { +@@ -869,18 +1785,17 @@ static u8 ChipSelPresent_D(struct MCTStatStruc *pMCTstat, + u32 val; + u32 reg; + u32 dev = pDCTstat->dev_dct; +- u32 reg_off; ++ uint8_t dct = 0; + u8 ret = 0; + +- if (!pDCTstat->GangedMode) { +- reg_off = 0x100 * Channel; +- } else { +- reg_off = 0; +- } ++ if (!pDCTstat->GangedMode) ++ dct = Channel; ++ else ++ dct = 0; + + if (ChipSel < MAX_CS_SUPPORTED){ +- reg = 0x40 + (ChipSel << 2) + reg_off; +- val = Get_NB32(dev, reg); ++ reg = 0x40 + (ChipSel << 2); ++ val = Get_NB32_DCT(dev, dct, reg); + if (val & ( 1 << 0)) + ret = 1; + } +@@ -1085,12 +2000,12 @@ u32 SetUpperFSbase(u32 addr_hi) + return addr_hi << 8; + } + +-void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index) ++void ResetDCTWrPtr_D(u32 dev, uint8_t dct, u32 index_reg, u32 index) + { + u32 val; + +- val = Get_NB32_index_wait(dev, index_reg, index); +- Set_NB32_index_wait(dev, index_reg, index, val); ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val); + } + + void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, +@@ -1103,9 +2018,13 @@ void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat, + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + pDCTstat = pDCTstatA + Node; + if (pDCTstat->DCTSysLimit) { +- TrainDQSRdWrPos_D(pMCTstat, pDCTstat); +- for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) { +- SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel); ++ if (is_fam15h()) { ++ TrainDQSReceiverEnCyc_D_Fam15(pMCTstat, pDCTstat); ++ } else { ++ TrainDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat); ++ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) { ++ SetEccDQSRdWrPos_D_Fam10(pMCTstat, pDCTstat, ChipSel); ++ } + } + } + } +@@ -1126,19 +2045,18 @@ u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat, + + dev = pDCTstat->dev_dct; + reg = 0x90; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 0, reg); + if (val & (1<<DimmEcEn)) { + _DisableDramECC |= 0x01; + val &= ~(1<<DimmEcEn); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 0, reg, val); + } + if (!pDCTstat->GangedMode) { +- reg = 0x190; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 1, reg); + if (val & (1<<DimmEcEn)) { + _DisableDramECC |= 0x02; + val &= ~(1<<DimmEcEn); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 1, reg, val); + } + } + return _DisableDramECC; +@@ -1157,15 +2075,14 @@ void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat, + + if ((_DisableDramECC & 0x01) == 0x01) { + reg = 0x90; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 0, reg); + val |= (1<<DimmEcEn); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 0, reg, val); + } + if ((_DisableDramECC & 0x02) == 0x02) { +- reg = 0x190; +- val = Get_NB32(dev, reg); ++ val = Get_NB32_DCT(dev, 1, reg); + val |= (1<<DimmEcEn); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, 1, reg, val); + } + } + +@@ -1177,7 +2094,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat, + { + u8 ByteLane; + u32 val; +- u32 index_reg = 0x98 + 0x100 * pDCTstat->Channel; ++ u32 index_reg = 0x98; + u8 shift; + u32 dqs_delay = (u32)pDCTstat->DQSDelay; + u32 dev = pDCTstat->dev_dct; +@@ -1205,7 +2122,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat, + + index += (ChipSel>>1) << 8; + +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index); + if (ByteLane < 8) { + if (pDCTstat->Direction == DQS_WRITEDIR) { + dqs_delay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][ChipSel>>1][ByteLane]; +@@ -1215,7 +2132,7 @@ static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat, + } + val &= ~(0x7f << shift); + val |= (dqs_delay << shift); +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, pDCTstat->Channel, index_reg, index, val); + } + } + +@@ -1241,7 +2158,7 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat, + u8 Channel, u8 receiver, u8 *valid) + { + u32 val; +- u32 reg_off = 0; ++ uint8_t dct = 0; + u32 reg; + u32 dword; + u32 dev = pDCTstat->dev_dct; +@@ -1250,12 +2167,12 @@ u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat, + + + if (!pDCTstat->GangedMode) { +- reg_off = 0x100 * Channel; ++ dct = Channel; + } + + /* get the local base addr of the chipselect */ +- reg = 0x40 + (receiver << 2) + reg_off; +- val = Get_NB32(dev, reg); ++ reg = 0x40 + (receiver << 2); ++ val = Get_NB32_DCT(dev, dct, reg); + + val &= ~0xe007c01f; + +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c +index 0c52791..11f1b2c 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -91,19 +92,21 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) + + /* Construct these booleans, based on setup options, for easy handling + later in this procedure */ +- OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */ ++ OB_NBECC = mctGet_NVbits(NV_NBECC); /* MCA ECC (MCE) enable bit */ + +- OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */ ++ OB_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */ + +- OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */ ++ OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */ ++ OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */ + +- OF_ScrubCTL = 0; /* Scrub CTL for Dcache, L2, and dram */ +- nvbits = mctGet_NVbits(NV_DCBKScrub); +- /* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */ +- OF_ScrubCTL |= (u32) nvbits << 16; ++ if (!is_fam15h()) { ++ nvbits = mctGet_NVbits(NV_DCBKScrub); ++ /* mct_AdjustScrub_D(pDCTstatA, &nvbits); */ /* Need not adjust */ ++ OF_ScrubCTL |= (u32) nvbits << 16; + +- nvbits = mctGet_NVbits(NV_L2BKScrub); +- OF_ScrubCTL |= (u32) nvbits << 8; ++ nvbits = mctGet_NVbits(NV_L2BKScrub); ++ OF_ScrubCTL |= (u32) nvbits << 8; ++ } + + nvbits = mctGet_NVbits(NV_DramBKScrub); + OF_ScrubCTL |= nvbits; +@@ -131,7 +134,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) + pDCTstat->ErrStatus |= (1 << SB_DramECCDis); + } + AllECC = 0; +- LDramECC =0; ++ LDramECC = 0; + } + } else { + AllECC = 0; +@@ -140,7 +143,7 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) + if (OB_NBECC) { + mct_EnableDatIntlv_D(pMCTstat, pDCTstat); + dev = pDCTstat->dev_nbmisc; +- reg =0x44; /* MCA NB Configuration */ ++ reg = 0x44; /* MCA NB Configuration */ + val = Get_NB32(dev, reg); + val |= 1 << 22; /* EccEn */ + Set_NB32(dev, reg, val); +@@ -177,6 +180,10 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) + /*WE/RE is checked because memory config may have been */ + if((val & 3)==3) { /* Node has dram populated */ + if (isDramECCEn_D(pDCTstat)) { /* if ECC is enabled on this dram */ ++ if (is_fam15h()) { ++ /* Erratum 505 */ ++ fam15h_switch_dct(pDCTstat->dev_map, 0); ++ } + dev = pDCTstat->dev_nbmisc; + val = curBase << 8; + if(OB_ECCRedir) { +@@ -187,16 +194,18 @@ u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA) + Set_NB32(dev, 0x60, val); /* Dram Scrub Addr High */ + Set_NB32(dev, 0x58, OF_ScrubCTL); /*Scrub Control */ + +- /* Divisor should not be set deeper than +- * divide by 16 when Dcache scrubber or +- * L2 scrubber is enabled. +- */ +- if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) { +- val = Get_NB32(dev, 0x84); +- if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */ +- val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */ +- val |= 0x80000000; /* set it to divide-by-16 */ +- Set_NB32(dev, 0x84, val); ++ if (!is_fam15h()) { ++ /* Divisor should not be set deeper than ++ * divide by 16 when Dcache scrubber or ++ * L2 scrubber is enabled. ++ */ ++ if ((OF_ScrubCTL & (0x1F << 16)) || (OF_ScrubCTL & (0x1F << 8))) { ++ val = Get_NB32(dev, 0x84); ++ if ((val & 0xE0000000) > 0x80000000) { /* Get F3x84h[31:29]ClkDivisor for C1 */ ++ val &= 0x1FFFFFFF; /* If ClkDivisor is deeper than divide-by-16 */ ++ val |= 0x80000000; /* set it to divide-by-16 */ ++ Set_NB32(dev, 0x84, val); ++ } + } + } + } /* this node has ECC enabled dram */ +@@ -267,8 +276,8 @@ static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat) + } + for(i=0; i<ch_end; i++) { + if(pDCTstat->DIMMValidDCT[i] > 0){ +- reg = 0x90 + i * 0x100; /* Dram Config Low */ +- val = Get_NB32(dev, reg); ++ reg = 0x90; /* Dram Config Low */ ++ val = Get_NB32_DCT(dev, i, reg); + if(val & (1<<DimmEcEn)) { + /* set local flag 'dram ecc capable' */ + isDimmECCEn = 1; +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c +index 0112732..a6b9dcb 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -25,8 +26,8 @@ void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat, + u32 dev = pDCTstat->dev_dct; + + /*flag for selecting HW/SW DRAM Init HW DRAM Init */ +- reg = 0x90 + 0x100 * dct; /*DRAM Configuration Low */ +- val = Get_NB32(dev, reg); ++ reg = 0x90; /*DRAM Configuration Low */ ++ val = Get_NB32_DCT(dev, dct, reg); + val |= (1<<InitDram); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, dct, reg, val); + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c +index 60bc01d..5e81808 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c +@@ -18,10 +18,12 @@ + * Foundation, Inc. + */ + +-static void SetTargetFreq(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstat); +-static void AgesaHwWlPhase1(sMCTStruct *pMCTData, +- sDCTStruct *pDCTData, u8 dimm, u8 pass); ++static void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); ++static void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); ++static void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 dct, u8 dimm, u8 pass); + static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); + static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); + static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat); +@@ -56,7 +58,7 @@ static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pD + Addl_Index = 0x32; + Addl_Index += DimmNum * 3; + +- val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index); ++ val = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, Channel, 0x98, Addl_Index); + if (OddByte) + val >>= 16; + /* Save WrDqs to stack for later usage */ +@@ -74,13 +76,13 @@ static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStr + { + u32 val; + +- val = Get_NB32(pDCTstat->dev_dct, 0x8C); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C); + val &= ~(1 << DisAutoRefresh); +- Set_NB32(pDCTstat->dev_dct, 0x8C, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val); + +- val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C); + val &= ~(1 << DisAutoRefresh); +- Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val); + } + + static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat, +@@ -88,13 +90,13 @@ static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat, + { + u32 val; + +- val = Get_NB32(pDCTstat->dev_dct, 0x8C); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C); + val |= 1 << DisAutoRefresh; +- Set_NB32(pDCTstat->dev_dct, 0x8C, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x8C, val); + +- val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C); + val |= 1 << DisAutoRefresh; +- Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x8C, val); + } + + +@@ -118,8 +120,11 @@ static void PhyWLPass1(struct MCTStatStruc *pMCTstat, + DIMMValid = pDCTstat->DIMMValid; + PrepareC_DCT(pMCTstat, pDCTstat, dct); + for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) { +- if (DIMMValid & (1 << (dimm << 1))) +- AgesaHwWlPhase1(pDCTstat->C_MCTPtr, DCTPtr, dimm, FirstPass); ++ if (DIMMValid & (1 << (dimm << 1))) { ++ AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, FirstPass); ++ AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, FirstPass); ++ AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, FirstPass); ++ } + } + } + } +@@ -146,27 +151,40 @@ static void PhyWLPass2(struct MCTStatStruc *pMCTstat, + pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq; + pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL; + SPD2ndTiming(pMCTstat, pDCTstat, dct); +- ProgDramMRSReg_D(pMCTstat, pDCTstat, dct); +- PlatformSpec_D(pMCTstat, pDCTstat, dct); +- fenceDynTraining_D(pMCTstat, pDCTstat, dct); ++ if (!is_fam15h()) { ++ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct); ++ PlatformSpec_D(pMCTstat, pDCTstat, dct); ++ fenceDynTraining_D(pMCTstat, pDCTstat, dct); ++ } + Restore_OnDimmMirror(pMCTstat, pDCTstat); + StartupDCT_D(pMCTstat, pDCTstat, dct); + Clear_OnDimmMirror(pMCTstat, pDCTstat); + SetDllSpeedUp_D(pMCTstat, pDCTstat, dct); + DisableAutoRefresh_D(pMCTstat, pDCTstat); + for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) { +- if (DIMMValid & (1 << (dimm << 1))) +- AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass); ++ if (DIMMValid & (1 << (dimm << 1))) { ++ AgesaHwWlPhase1(pMCTstat, pDCTstat, dct, dimm, SecondPass); ++ AgesaHwWlPhase2(pMCTstat, pDCTstat, dct, dimm, SecondPass); ++ AgesaHwWlPhase3(pMCTstat, pDCTstat, dct, dimm, SecondPass); ++ } + } + } + } + ++static uint16_t fam15h_next_highest_memclk_freq(uint16_t memclk_freq) ++{ ++ uint16_t fam15h_next_highest_freq_tab[] = {0, 0, 0, 0, 0x6, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12, 0, 0, 0, 0x16, 0, 0, 0, 0x16}; ++ return fam15h_next_highest_freq_tab[memclk_freq]; ++} ++ + /* Write Levelization Training + * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.1 + */ + static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstat) ++ struct DCTStatStruc *pDCTstat, uint8_t Pass) + { ++ uint16_t final_target_freq; ++ + pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr); + pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]); + pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]); +@@ -182,16 +200,39 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat, + pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0]; + } + +- PhyWLPass1(pMCTstat, pDCTstat, 0); +- PhyWLPass1(pMCTstat, pDCTstat, 1); ++ if (Pass == FirstPass) { ++ PhyWLPass1(pMCTstat, pDCTstat, 0); ++ PhyWLPass1(pMCTstat, pDCTstat, 1); ++ } ++ ++ if (Pass == SecondPass) { ++ if (pDCTstat->TargetFreq > mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { ++ /* 8.Prepare the memory subsystem for the target MEMCLK frequency. ++ * NOTE: BIOS must program both DCTs to the same frequency. ++ * NOTE: Fam15h steps the frequency, Fam10h slams the frequency. ++ */ ++ final_target_freq = pDCTstat->TargetFreq; ++ ++ while (pDCTstat->Speed != final_target_freq) { ++ if (is_fam15h()) ++ pDCTstat->TargetFreq = fam15h_next_highest_memclk_freq(pDCTstat->Speed); ++ else ++ pDCTstat->TargetFreq = final_target_freq; ++ SetTargetFreq(pMCTstat, pDCTstat); ++ PhyWLPass2(pMCTstat, pDCTstat, 0); ++ PhyWLPass2(pMCTstat, pDCTstat, 1); ++ } ++ ++ pDCTstat->TargetFreq = final_target_freq; + +- if (pDCTstat->TargetFreq > 4) { +- /* 8.Prepare the memory subsystem for the target MEMCLK frequency. +- * Note: BIOS must program both DCTs to the same frequency. +- */ +- SetTargetFreq(pMCTstat, pDCTstat); +- PhyWLPass2(pMCTstat, pDCTstat, 0); +- PhyWLPass2(pMCTstat, pDCTstat, 1); ++ uint8_t dct; ++ for (dct = 0; dct < 2; dct++) { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ memcpy(pDCTData->WLGrossDelayFinalPass, pDCTData->WLGrossDelayPrevPass, sizeof(pDCTData->WLGrossDelayPrevPass)); ++ memcpy(pDCTData->WLFineDelayFinalPass, pDCTData->WLFineDelayPrevPass, sizeof(pDCTData->WLFineDelayPrevPass)); ++ pDCTData->WLCriticalGrossDelayFinalPass = pDCTData->WLCriticalGrossDelayPrevPass; ++ } ++ } + } + + SetEccWrDQS_D(pMCTstat, pDCTstat); +@@ -200,7 +241,7 @@ static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat, + } + + void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, +- struct DCTStatStruc *pDCTstatA) ++ struct DCTStatStruc *pDCTstatA, uint8_t Pass) + { + u8 Node; + +@@ -211,7 +252,7 @@ void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, + if (pDCTstat->NodePresent) { + mctSMBhub_Init(Node); + Clear_OnDimmMirror(pMCTstat, pDCTstat); +- WriteLevelization_HW(pMCTstat, pDCTstat); ++ WriteLevelization_HW(pMCTstat, pDCTstat, Pass); + Restore_OnDimmMirror(pMCTstat, pDCTstat); + } + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c +index cda9c6b..5ef4a2c 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c +@@ -34,7 +34,7 @@ u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2) + + if (pDCTstat->LogicalCPUID & AMD_DR_Cx) + misc2 |= 1 << OdtSwizzle; +- val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x78); + + val &= 7; + val = ((~val) & 0xff) + 1; +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c +index bd8b7fb..5ea7fa6 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -23,7 +24,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat, + u8 Dimms, DimmNum, MaxDimm, Speed; + u32 val; + u32 dct = 0; +- u32 reg_off = 0; + + DimmNum = (MrsChipSel >> 20) & 0xFE; + +@@ -41,7 +41,6 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat, + dct = 1; + DimmNum ++; + } +- reg_off = 0x100 * dct; + Dimms = pDCTstat->MAdimms[dct]; + + val = 0; +@@ -95,21 +94,21 @@ static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat, + static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u32 val) + { +- u32 reg_off = 0; ++ uint8_t dct = 0; + u32 dev = pDCTstat->dev_dct; + + if (pDCTstat->CSPresent_DCT[0] > 0) { +- reg_off = 0; ++ dct = 0; + } else if (pDCTstat->CSPresent_DCT[1] > 0 ){ +- reg_off = 0x100; ++ dct = 1; + } + +- val |= Get_NB32(dev, reg_off + 0x7C) & ~0xFFFFFF; ++ val |= Get_NB32_DCT(dev, dct, 0x7C) & ~0xFFFFFF; + val |= 1 << SendControlWord; +- Set_NB32(dev, reg_off + 0x7C, val); ++ Set_NB32_DCT(dev, dct, 0x7C, val); + + do { +- val = Get_NB32(dev, reg_off + 0x7C); ++ val = Get_NB32_DCT(dev, dct, 0x7C); + } while (val & (1 << SendControlWord)); + } + +@@ -119,7 +118,6 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, + u8 MrsChipSel; + u32 dev = pDCTstat->dev_dct; + u32 val, cw; +- u32 reg_off = 0x100 * dct; + + mct_Wait(1600); + +@@ -127,7 +125,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, + + for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) { + if (pDCTstat->CSPresent & (1 << MrsChipSel)) { +- val = Get_NB32(dev, reg_off + 0xA8); ++ val = Get_NB32_DCT(dev, dct, 0xa8); + val &= ~(0xF << 8); + + switch (MrsChipSel) { +@@ -144,7 +142,7 @@ void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, + case 7: + val |= (3 << 6) << 8; + } +- Set_NB32(dev, reg_off + 0xA8 , val); ++ Set_NB32_DCT(dev, dct, 0xa8, val); + + for (cw=0; cw <=15; cw ++) { + mct_Wait(1600); +@@ -171,10 +169,10 @@ void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat, + for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) { + if (pDCTstat->CSPresent & (1 << MrsChipSel)) { + /* 2. Program F2x[1, 0]A8[CtrlWordCS]=bit mask for target chip selects. */ +- val = Get_NB32(dev, 0xA8); /* TODO: dct * 0x100 + 0xA8 */ ++ val = Get_NB32_DCT(dev, 0, 0xA8); /* TODO: dct 0 / 1 select */ + val &= ~(0xFF << 8); + val |= (0x3 << (MrsChipSel & 0xFE)) << 8; +- Set_NB32(dev, 0xA8, val); /* TODO: dct * 0x100 + 0xA8 */ ++ Set_NB32_DCT(dev, 0, 0xA8, val); /* TODO: dct 0 / 1 select */ + + /* Resend control word 10 */ + mct_Wait(1600); +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c +index b21b96a..51cbf16 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c +@@ -18,17 +18,182 @@ + * Foundation, Inc. + */ + ++static uint8_t fam15_dimm_dic(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) ++{ ++ uint8_t dic; ++ ++ /* Calculate DIC based on recommendations in MR1_dct[1:0] */ ++ if (pDCTstat->Status & (1 << SB_LoadReduced)) { ++ /* TODO ++ * LRDIMM unimplemented ++ */ ++ dic = 0x0; ++ } else { ++ dic = 0x1; ++ } ++ ++ return dic; ++} ++ ++static uint8_t fam15_rttwr(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) ++{ ++ uint8_t term = 0; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; ++ uint8_t frequency_index; ++ uint8_t rank_count = pDCTData->DimmRanks[dimm]; ++ ++ if (is_fam15h()) ++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ else ++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7; ++ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ if (is_fam15h()) { ++ if (pDCTstat->Status & (1 << SB_Registered)) { ++ /* TODO ++ * RDIMM unimplemented ++ */ ++ } else { ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 56 */ ++ if (MaxDimmsInstallable == 1) { ++ term = 0x0; ++ } else if (MaxDimmsInstallable == 2) { ++ if ((number_of_dimms == 2) && (frequency_index == 0x12)) { ++ term = 0x1; ++ } else if (number_of_dimms == 1) { ++ term = 0x0; ++ } else { ++ term = 0x2; ++ } ++ } else if (MaxDimmsInstallable == 3) { ++ if (number_of_dimms == 1) { ++ if (frequency_index <= 0xa) { ++ term = 0x2; ++ } else { ++ if (rank_count < 3) { ++ term = 0x1; ++ } else { ++ term = 0x2; ++ } ++ } ++ } else if (number_of_dimms == 2) { ++ term = 0x2; ++ } ++ } ++ } else { ++ /* TODO ++ * Other sockets unimplemented ++ */ ++ } ++ } ++ } ++ ++ return term; ++} ++ ++static uint8_t fam15_rttnom(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) ++{ ++ uint8_t term = 0; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; ++ uint8_t frequency_index; ++ ++ if (is_fam15h()) ++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x1f; ++ else ++ frequency_index = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0x94) & 0x7; ++ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ if (is_fam15h()) { ++ if (pDCTstat->Status & (1 << SB_LoadReduced)) { ++ /* TODO ++ * LRDIMM unimplemented ++ */ ++ } else if (pDCTstat->Status & (1 << SB_Registered)) { ++ /* TODO ++ * RDIMM unimplemented ++ */ ++ } else { ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 56 */ ++ if (MaxDimmsInstallable == 1) { ++ if ((frequency_index == 0x4) || (frequency_index == 0x6)) ++ term = 0x2; ++ else if ((frequency_index == 0xa) || (frequency_index == 0xe)) ++ term = 0x1; ++ else ++ term = 0x3; ++ } ++ if (MaxDimmsInstallable == 2) { ++ if (number_of_dimms == 1) { ++ if (frequency_index <= 0x6) { ++ term = 0x2; ++ } else if (frequency_index <= 0xe) { ++ term = 0x1; ++ } else { ++ term = 0x3; ++ } ++ } else { ++ if (frequency_index <= 0xa) { ++ term = 0x3; ++ } else if (frequency_index <= 0xe) { ++ term = 0x5; ++ } else { ++ term = 0x4; ++ } ++ } ++ } else if (MaxDimmsInstallable == 3) { ++ if (number_of_dimms == 1) { ++ term = 0x0; ++ } else if (number_of_dimms == 2) { ++ if (frequency_index <= 0xa) { ++ if (rank == 1) { ++ term = 0x0; ++ } else { ++ term = 0x3; ++ } ++ } else if (frequency_index <= 0xe) { ++ if (rank == 1) { ++ term = 0x0; ++ } else { ++ term = 0x5; ++ } ++ } ++ } ++ } ++ } else { ++ /* TODO ++ * Other sockets unimplemented ++ */ ++ } ++ } ++ } ++ ++ return term; ++} ++ + static void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + + static void mct_DCTAccessDone(struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 val; + + do { +- val = Get_NB32(dev, reg_off + 0x98); ++ val = Get_NB32_DCT(dev, dct, 0x98); + } while (!(val & (1 << DctAccessDone))); + } + +@@ -54,9 +219,15 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting, + if (MR_register_setting & (1 << 6)) ret |= 1 << 5; + if (MR_register_setting & (1 << 7)) ret |= 1 << 8; + if (MR_register_setting & (1 << 8)) ret |= 1 << 7; +- if (MR_register_setting & (1 << 16)) ret |= 1 << 17; +- if (MR_register_setting & (1 << 17)) ret |= 1 << 16; +- MR_register_setting &= ~0x301f8; ++ if (is_fam15h()) { ++ if (MR_register_setting & (1 << 18)) ret |= 1 << 19; ++ if (MR_register_setting & (1 << 19)) ret |= 1 << 18; ++ MR_register_setting &= ~0x000c01f8; ++ } else { ++ if (MR_register_setting & (1 << 16)) ret |= 1 << 17; ++ if (MR_register_setting & (1 << 17)) ret |= 1 << 16; ++ MR_register_setting &= ~0x000301f8; ++ } + MR_register_setting |= ret; + } + } +@@ -65,47 +236,76 @@ static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting, + + static void mct_SendMrsCmd(struct DCTStatStruc *pDCTstat, u8 dct, u32 EMRS) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 val; + +- val = Get_NB32(dev, reg_off + 0x7C); +- val &= ~0xFFFFFF; ++ val = Get_NB32_DCT(dev, dct, 0x7c); ++ val &= ~0x00ffffff; + val |= EMRS; + val |= 1 << SendMrsCmd; +- Set_NB32(dev, reg_off + 0x7C, val); ++ Set_NB32_DCT(dev, dct, 0x7c, val); + + do { +- val = Get_NB32(dev, reg_off + 0x7C); ++ val = Get_NB32_DCT(dev, dct, 0x7c); + } while (val & (1 << SendMrsCmd)); + } + + static u32 mct_MR2(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 dword, ret; + +- ret = 0x20000; +- ret |= MrsChipSel; ++ if (is_fam15h()) { ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ ++ /* The formula for chip select number is: CS = dimm*2+rank */ ++ uint8_t dimm = MrsChipSel / 2; ++ uint8_t rank = MrsChipSel % 2; + +- /* program MrsAddress[5:3]=CAS write latency (CWL): +- * based on F2x[1,0]84[Tcwl] */ +- dword = Get_NB32(dev, reg_off + 0x84); +- dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword); ++ /* FIXME: These parameters should be configurable ++ * For now, err on the side of caution and enable automatic 2x refresh ++ * when the DDR temperature rises above the internal limits ++ */ ++ uint8_t force_2x_self_refresh = 0; /* ASR */ ++ uint8_t auto_2x_self_refresh = 1; /* SRT */ + +- ret |= ((dword >> 20) & 7) << 3; ++ ret = 0x80000; ++ ret |= (MrsChipSel << 21); + +- /* program MrsAddress[6]=auto self refresh method (ASR): +- based on F2x[1,0]84[ASR] +- program MrsAddress[7]=self refresh temperature range (SRT): +- based on F2x[1,0]84[ASR and SRT] */ +- ret |= ((dword >> 18) & 3) << 6; ++ /* Set self refresh parameters */ ++ ret |= (force_2x_self_refresh << 6); ++ ret |= (auto_2x_self_refresh << 7); + +- /* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR) +- based on F2x[1,0]84[DramTermDyn] */ +- ret |= ((dword >> 10) & 3) << 9; ++ /* Obtain Tcwl, adjust, and set CWL with the adjusted value */ ++ dword = Get_NB32_DCT(dev, dct, 0x20c) & 0x1f; ++ ret |= ((dword - 5) << 3); ++ ++ /* Obtain and set RttWr */ ++ ret |= (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); ++ } else { ++ ret = 0x20000; ++ ret |= (MrsChipSel << 20); ++ ++ /* program MrsAddress[5:3]=CAS write latency (CWL): ++ * based on F2x[1,0]84[Tcwl] */ ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ dword = mct_AdjustSPDTimings(pMCTstat, pDCTstat, dword); ++ ++ ret |= ((dword >> 20) & 7) << 3; ++ ++ /* program MrsAddress[6]=auto self refresh method (ASR): ++ * based on F2x[1,0]84[ASR] ++ * program MrsAddress[7]=self refresh temperature range (SRT): ++ * based on F2x[1,0]84[ASR and SRT] ++ */ ++ ret |= ((dword >> 18) & 3) << 6; ++ ++ /* program MrsAddress[10:9]=dynamic termination during writes (RTT_WR) ++ * based on F2x[1,0]84[DramTermDyn] ++ */ ++ ret |= ((dword >> 10) & 3) << 9; ++ } + + return ret; + } +@@ -113,20 +313,28 @@ static u32 mct_MR2(struct MCTStatStruc *pMCTstat, + static u32 mct_MR3(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 dword, ret; + +- ret = 0x30000; +- ret |= MrsChipSel; ++ if (is_fam15h()) { ++ ret = 0xc0000; ++ ret |= (MrsChipSel << 21); + +- /* program MrsAddress[1:0]=multi purpose register address location +- (MPR Location):based on F2x[1,0]84[MprLoc] +- program MrsAddress[2]=multi purpose register +- (MPR):based on F2x[1,0]84[MprEn] +- */ +- dword = Get_NB32(dev, reg_off + 0x84); +- ret |= (dword >> 24) & 7; ++ /* Program MPR and MPRLoc to 0 */ ++ // ret |= 0x0; /* MPR */ ++ // ret |= (0x0 << 2); /* MPRLoc */ ++ } else { ++ ret = 0x30000; ++ ret |= (MrsChipSel << 20); ++ ++ /* program MrsAddress[1:0]=multi purpose register address location ++ * (MPR Location):based on F2x[1,0]84[MprLoc] ++ * program MrsAddress[2]=multi purpose register ++ * (MPR):based on F2x[1,0]84[MprEn] ++ */ ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ ret |= (dword >> 24) & 7; ++ } + + return ret; + } +@@ -134,48 +342,93 @@ static u32 mct_MR3(struct MCTStatStruc *pMCTstat, + static u32 mct_MR1(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 dword, ret; + +- ret = 0x10000; +- ret |= MrsChipSel; +- +- /* program MrsAddress[5,1]=output driver impedance control (DIC): +- * based on F2x[1,0]84[DrvImpCtrl] */ +- dword = Get_NB32(dev, reg_off + 0x84); +- if (dword & (1 << 3)) +- ret |= 1 << 5; +- if (dword & (1 << 2)) +- ret |= 1 << 1; +- +- /* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT): +- based on F2x[1,0]84[DramTerm] */ +- if (!(pDCTstat->Status & (1 << SB_Registered))) { +- if (dword & (1 << 9)) +- ret |= 1 << 9; +- if (dword & (1 << 8)) +- ret |= 1 << 6; +- if (dword & (1 << 7)) +- ret |= 1 << 2; ++ if (is_fam15h()) { ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ ++ /* Set defaults */ ++ uint8_t qoff = 0; /* Enable output buffers */ ++ uint8_t wrlvl = 0; /* Disable write levelling */ ++ uint8_t tqds = 0; ++ uint8_t rttnom = 0; ++ uint8_t dic = 0; ++ uint8_t additive_latency = 0; ++ uint8_t dll_enable = 0; ++ ++ ret = 0x40000; ++ ret |= (MrsChipSel << 21); ++ ++ /* The formula for chip select number is: CS = dimm*2+rank */ ++ uint8_t dimm = MrsChipSel / 2; ++ uint8_t rank = MrsChipSel % 2; ++ ++ /* Determine if TQDS should be set */ ++ if ((pDCTstat->Dimmx8Present & (1 << dimm)) ++ && (((dimm & 0x1)?(pDCTstat->Dimmx4Present&0x55):(pDCTstat->Dimmx4Present&0xaa)) != 0x0) ++ && (pDCTstat->Status & (1 << SB_LoadReduced))) ++ tqds = 1; ++ ++ /* Obtain RttNom */ ++ rttnom = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); ++ ++ /* Obtain DIC */ ++ dic = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); ++ ++ /* Load data into MRS word */ ++ ret |= (qoff & 0x1) << 12; ++ ret |= (tqds & 0x1) << 11; ++ ret |= ((rttnom & 0x4) >> 2) << 9; ++ ret |= ((rttnom & 0x2) >> 1) << 6; ++ ret |= ((rttnom & 0x1) >> 0) << 2; ++ ret |= (wrlvl & 0x1) << 7; ++ ret |= ((dic & 0x2) >> 1) << 5; ++ ret |= ((dic & 0x1) >> 0) << 1; ++ ret |= (additive_latency & 0x3) << 3; ++ ret |= (dll_enable & 0x1); + } else { +- ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel); +- } ++ ret = 0x10000; ++ ret |= (MrsChipSel << 20); ++ ++ /* program MrsAddress[5,1]=output driver impedance control (DIC): ++ * based on F2x[1,0]84[DrvImpCtrl] ++ */ ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ if (dword & (1 << 3)) ++ ret |= 1 << 5; ++ if (dword & (1 << 2)) ++ ret |= 1 << 1; ++ ++ /* program MrsAddress[9,6,2]=nominal termination resistance of ODT (RTT): ++ * based on F2x[1,0]84[DramTerm] ++ */ ++ if (!(pDCTstat->Status & (1 << SB_Registered))) { ++ if (dword & (1 << 9)) ++ ret |= 1 << 9; ++ if (dword & (1 << 8)) ++ ret |= 1 << 6; ++ if (dword & (1 << 7)) ++ ret |= 1 << 2; ++ } else { ++ ret |= mct_MR1Odt_RDimm(pMCTstat, pDCTstat, dct, MrsChipSel); ++ } + +- /* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */ +- if (Get_NB32(dev, reg_off + 0x94) & (1 << RDqsEn)) { +- u8 bit; +- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ +- bit = (ret >> 21) << 1; +- if ((dct & 1) != 0) +- bit ++; +- if (pDCTstat->Dimmx8Present & (1 << bit)) +- ret |= 1 << 11; +- } ++ /* program MrsAddress[11]=TDQS: based on F2x[1,0]94[RDqsEn] */ ++ if (Get_NB32_DCT(dev, dct, 0x94) & (1 << RDqsEn)) { ++ u8 bit; ++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ ++ bit = (ret >> 21) << 1; ++ if ((dct & 1) != 0) ++ bit ++; ++ if (pDCTstat->Dimmx8Present & (1 << bit)) ++ ret |= 1 << 11; ++ } + +- /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */ +- if (dword & (1 << 13)) +- ret |= 1 << 12; ++ /* program MrsAddress[12]=QOFF: based on F2x[1,0]84[Qoff] */ ++ if (dword & (1 << 13)) ++ ret |= 1 << 12; ++ } + + return ret; + } +@@ -183,60 +436,139 @@ static u32 mct_MR1(struct MCTStatStruc *pMCTstat, + static u32 mct_MR0(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 dword, ret, dword2; + +- ret = 0x00000; +- ret |= MrsChipSel; +- +- /* program MrsAddress[1:0]=burst length and control method +- (BL):based on F2x[1,0]84[BurstCtrl] */ +- dword = Get_NB32(dev, reg_off + 0x84); +- ret |= dword & 3; +- +- /* program MrsAddress[3]=1 (BT):interleaved */ +- ret |= 1 << 3; +- +- /* program MrsAddress[6:4,2]=read CAS latency +- (CL):based on F2x[1,0]88[Tcl] */ +- dword2 = Get_NB32(dev, reg_off + 0x88); +- ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */ +- ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */ +- +- /* program MrsAddress[12]=0 (PPD):slow exit */ +- if (dword & (1 << 23)) +- ret |= 1 << 12; +- +- /* program MrsAddress[11:9]=write recovery for auto-precharge +- (WR):based on F2x[1,0]84[Twr] */ +- ret |= ((dword >> 4) & 7) << 9; +- +- /* program MrsAddress[8]=1 (DLL):DLL reset +- just issue DLL reset at first time */ +- ret |= 1 << 8; ++ if (is_fam15h()) { ++ ret = 0x00000; ++ ret |= (MrsChipSel << 21); ++ ++ /* Set defaults */ ++ uint8_t ppd = 0; ++ uint8_t wr_ap = 0; ++ uint8_t dll_reset = 1; ++ uint8_t test_mode = 0; ++ uint8_t cas_latency = 0; ++ uint8_t read_burst_type = 1; ++ uint8_t burst_length = 0; ++ ++ /* Obtain PchgPDModeSel */ ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ ppd = (dword >> 23) & 0x1; ++ ++ /* Obtain Twr */ ++ dword = Get_NB32_DCT(dev, dct, 0x22c) & 0x1f; ++ ++ /* Calculate wr_ap (Fam15h BKDG v3.14 Table 82) */ ++ if (dword == 0x10) ++ wr_ap = 0x0; ++ else if (dword == 0x5) ++ wr_ap = 0x1; ++ else if (dword == 0x6) ++ wr_ap = 0x2; ++ else if (dword == 0x7) ++ wr_ap = 0x3; ++ else if (dword == 0x8) ++ wr_ap = 0x4; ++ else if (dword == 0xa) ++ wr_ap = 0x5; ++ else if (dword == 0xc) ++ wr_ap = 0x6; ++ else if (dword == 0xe) ++ wr_ap = 0x7; ++ ++ /* Obtain Tcl */ ++ dword = Get_NB32_DCT(dev, dct, 0x200) & 0x1f; ++ ++ /* Calculate cas_latency (Fam15h BKDG v3.14 Table 83) */ ++ if (dword == 0x5) ++ cas_latency = 0x2; ++ else if (dword == 0x6) ++ cas_latency = 0x4; ++ else if (dword == 0x7) ++ cas_latency = 0x6; ++ else if (dword == 0x8) ++ cas_latency = 0x8; ++ else if (dword == 0x9) ++ cas_latency = 0xa; ++ else if (dword == 0xa) ++ cas_latency = 0xc; ++ else if (dword == 0xb) ++ cas_latency = 0xe; ++ else if (dword == 0xc) ++ cas_latency = 0x1; ++ else if (dword == 0xd) ++ cas_latency = 0x3; ++ else if (dword == 0xe) ++ cas_latency = 0x5; ++ else if (dword == 0xf) ++ cas_latency = 0x7; ++ else if (dword == 0x10) ++ cas_latency = 0x9; ++ ++ /* Obtain BurstCtrl */ ++ burst_length = Get_NB32_DCT(dev, dct, 0x84) & 0x3; ++ ++ /* Load data into MRS word */ ++ ret |= (ppd & 0x1) << 12; ++ ret |= (wr_ap & 0x3) << 9; ++ ret |= (dll_reset & 0x1) << 8; ++ ret |= (test_mode & 0x1) << 7; ++ ret |= ((cas_latency & 0xe) >> 1) << 4; ++ ret |= ((cas_latency & 0x1) >> 0) << 2; ++ ret |= (read_burst_type & 0x1) << 3; ++ ret |= (burst_length & 0x3); ++ } else { ++ ret = 0x00000; ++ ret |= (MrsChipSel << 20); ++ ++ /* program MrsAddress[1:0]=burst length and control method ++ (BL):based on F2x[1,0]84[BurstCtrl] */ ++ dword = Get_NB32_DCT(dev, dct, 0x84); ++ ret |= dword & 3; ++ ++ /* program MrsAddress[3]=1 (BT):interleaved */ ++ ret |= 1 << 3; ++ ++ /* program MrsAddress[6:4,2]=read CAS latency ++ (CL):based on F2x[1,0]88[Tcl] */ ++ dword2 = Get_NB32_DCT(dev, dct, 0x88); ++ ret |= (dword2 & 0x7) << 4; /* F2x88[2:0] to MrsAddress[6:4] */ ++ ret |= ((dword2 & 0x8) >> 3) << 2; /* F2x88[3] to MrsAddress[2] */ ++ ++ /* program MrsAddress[12]=0 (PPD):slow exit */ ++ if (dword & (1 << 23)) ++ ret |= 1 << 12; ++ ++ /* program MrsAddress[11:9]=write recovery for auto-precharge ++ (WR):based on F2x[1,0]84[Twr] */ ++ ret |= ((dword >> 4) & 7) << 9; ++ ++ /* program MrsAddress[8]=1 (DLL):DLL reset ++ just issue DLL reset at first time */ ++ ret |= 1 << 8; ++ } + + return ret; + } + + static void mct_SendZQCmd(struct DCTStatStruc *pDCTstat, u8 dct) + { +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 dword; + + /*1.Program MrsAddress[10]=1 + 2.Set SendZQCmd=1 + */ +- dword = Get_NB32(dev, reg_off + 0x7C); ++ dword = Get_NB32_DCT(dev, dct, 0x7C); + dword &= ~0xFFFFFF; + dword |= 1 << 10; + dword |= 1 << SendZQCmd; +- Set_NB32(dev, reg_off + 0x7C, dword); ++ Set_NB32_DCT(dev, dct, 0x7C, dword); + + /* Wait for SendZQCmd=0 */ + do { +- dword = Get_NB32(dev, reg_off + 0x7C); ++ dword = Get_NB32_DCT(dev, dct, 0x7C); + } while (dword & (1 << SendZQCmd)); + + /* 4.Wait 512 MEMCLKs */ +@@ -248,31 +580,30 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, + { + u8 MrsChipSel; + u32 dword; +- u32 reg_off = 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + +- if (pDCTstat->DIMMAutoSpeed == 4) { ++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { + /* 3.Program F2x[1,0]7C[EnDramInit]=1 */ +- dword = Get_NB32(dev, reg_off + 0x7C); ++ dword = Get_NB32_DCT(dev, dct, 0x7c); + dword |= 1 << EnDramInit; +- Set_NB32(dev, reg_off + 0x7C, dword); ++ Set_NB32_DCT(dev, dct, 0x7c, dword); + mct_DCTAccessDone(pDCTstat, dct); + + /* 4.wait 200us */ + mct_Wait(40000); + +- /* 5.On revision C processors, program F2x[1, 0]7C[DeassertMemRstX] = 1. */ +- dword = Get_NB32(dev, reg_off + 0x7C); ++ /* 5.Program F2x[1, 0]7C[DeassertMemRstX] = 1. */ ++ dword = Get_NB32_DCT(dev, dct, 0x7c); + dword |= 1 << DeassertMemRstX; +- Set_NB32(dev, reg_off + 0x7C, dword); ++ Set_NB32_DCT(dev, dct, 0x7c, dword); + + /* 6.wait 500us */ + mct_Wait(200000); + + /* 7.Program F2x[1,0]7C[AssertCke]=1 */ +- dword = Get_NB32(dev, reg_off + 0x7C); ++ dword = Get_NB32_DCT(dev, dct, 0x7c); + dword |= 1 << AssertCke; +- Set_NB32(dev, reg_off + 0x7C, dword); ++ Set_NB32_DCT(dev, dct, 0x7c, dword); + + /* 8.wait 360ns */ + mct_Wait(80); +@@ -281,6 +612,13 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, + * must be done for each chip select pair */ + if (pDCTstat->Status & (1 << SB_Registered)) + mct_DramControlReg_Init_D(pMCTstat, pDCTstat, dct); ++ ++ /* The following steps are performed with load reduced DIMMs only and ++ * must be done for each DIMM */ ++ // if (pDCTstat->Status & (1 << SB_LoadReduced)) ++ /* TODO ++ * Implement LRDIMM configuration ++ */ + } + + /* The following steps are performed once for unbuffered DIMMs and once for each +@@ -289,23 +627,23 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, + if (pDCTstat->CSPresent & (1 << MrsChipSel)) { + u32 EMRS; + /* 13.Send EMRS(2) */ +- EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel << 20); ++ EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel); + EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); + mct_SendMrsCmd(pDCTstat, dct, EMRS); + /* 14.Send EMRS(3). Ordinarily at this time, MrsAddress[2:0]=000b */ +- EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel << 20); ++ EMRS= mct_MR3(pMCTstat, pDCTstat, dct, MrsChipSel); + EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); + mct_SendMrsCmd(pDCTstat, dct, EMRS); + /* 15.Send EMRS(1) */ +- EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel << 20); ++ EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel); + EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); + mct_SendMrsCmd(pDCTstat, dct, EMRS); + /* 16.Send MRS with MrsAddress[8]=1(reset the DLL) */ +- EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel << 20); ++ EMRS= mct_MR0(pMCTstat, pDCTstat, dct, MrsChipSel); + EMRS = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct); + mct_SendMrsCmd(pDCTstat, dct, EMRS); + +- if (pDCTstat->DIMMAutoSpeed == 4) ++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) + if (!(pDCTstat->Status & (1 << SB_Registered))) + break; /* For UDIMM, only send MR commands once per channel */ + } +@@ -314,16 +652,15 @@ void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, + MrsChipSel ++; + } + +- mct_Wait(100000); +- +- if (pDCTstat->DIMMAutoSpeed == 4) { ++ if (pDCTstat->DIMMAutoSpeed == mhz_to_memclk_config(mctGet_NVbits(NV_MIN_MEMCLK))) { + /* 17.Send two ZQCL commands */ + mct_SendZQCmd(pDCTstat, dct); + mct_SendZQCmd(pDCTstat, dct); ++ + /* 18.Program F2x[1,0]7C[EnDramInit]=0 */ +- dword = Get_NB32(dev, reg_off + 0x7C); ++ dword = Get_NB32_DCT(dev, dct, 0x7C); + dword &= ~(1 << EnDramInit); +- Set_NB32(dev, reg_off + 0x7C, dword); ++ Set_NB32_DCT(dev, dct, 0x7C, dword); + mct_DCTAccessDone(pDCTstat, dct); + } + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c +index 91e8f77..011a94f 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c +@@ -23,7 +23,10 @@ + Description: Receiver En and DQS Timing Training feature for DDR 3 MCT + ******************************************************************************/ + +-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, ++static int32_t abs(int32_t val); ++static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 Pass); ++static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 Pass); + static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat); +@@ -32,7 +35,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, + static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 Channel); + static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 DQSRcvEnDly); +-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat, ++static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct); + static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat); + +@@ -89,11 +92,154 @@ static void SetupRcvrPattern(struct MCTStatStruc *pMCTstat, + void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 Pass) + { +- if(mct_checkNumberOfDqsRcvEn_1Pass(Pass)) +- dqsTrainRcvrEn_SW(pMCTstat, pDCTstat, Pass); ++ if(mct_checkNumberOfDqsRcvEn_1Pass(Pass)) { ++ if (is_fam15h()) ++ dqsTrainRcvrEn_SW_Fam15(pMCTstat, pDCTstat, Pass); ++ else ++ dqsTrainRcvrEn_SW_Fam10(pMCTstat, pDCTstat, Pass); ++ } + } + +-static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg) ++static uint16_t fam15_receiver_enable_training_seed(struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t dimm, uint8_t rank, uint8_t package_type) ++{ ++ uint32_t dword; ++ uint16_t seed = 0; ++ ++ /* FIXME ++ * Mainboards need to be able to specify the maximum number of DIMMs installable per channel ++ * For now assume a maximum of 2 DIMMs per channel can be installed ++ */ ++ uint8_t MaxDimmsInstallable = 2; ++ ++ uint8_t channel = dct; ++ if (package_type == PT_GR) { ++ /* Get the internal node number */ ++ dword = Get_NB32(pDCTstat->dev_nbmisc, 0xe8); ++ dword = (dword >> 30) & 0x3; ++ if (dword == 1) { ++ channel += 2; ++ } ++ } ++ ++ if (pDCTstat->Status & (1 << SB_Registered)) { ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */ ++ if (MaxDimmsInstallable == 1) { ++ if (channel == 0) ++ seed = 0x43; ++ else if (channel == 1) ++ seed = 0x3f; ++ else if (channel == 2) ++ seed = 0x3a; ++ else if (channel == 3) ++ seed = 0x35; ++ } else if (MaxDimmsInstallable == 2) { ++ if (channel == 0) ++ seed = 0x54; ++ else if (channel == 1) ++ seed = 0x4d; ++ else if (channel == 2) ++ seed = 0x45; ++ else if (channel == 3) ++ seed = 0x40; ++ } else if (MaxDimmsInstallable == 3) { ++ if (channel == 0) ++ seed = 0x6b; ++ else if (channel == 1) ++ seed = 0x5e; ++ else if (channel == 2) ++ seed = 0x4b; ++ else if (channel == 3) ++ seed = 0x3d; ++ } ++ } else if (package_type == PT_C3) { ++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */ ++ if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) { ++ if (channel == 0) ++ seed = 0x3f; ++ else if (channel == 1) ++ seed = 0x3e; ++ } else if (MaxDimmsInstallable == 3) { ++ if (channel == 0) ++ seed = 0x47; ++ else if (channel == 1) ++ seed = 0x38; ++ } ++ } ++ } else if (pDCTstat->Status & (1 << SB_LoadReduced)) { ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */ ++ if (MaxDimmsInstallable == 1) { ++ if (channel == 0) ++ seed = 0x123; ++ else if (channel == 1) ++ seed = 0x122; ++ else if (channel == 2) ++ seed = 0x112; ++ else if (channel == 3) ++ seed = 0x102; ++ } ++ } else if (package_type == PT_C3) { ++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */ ++ if (channel == 0) ++ seed = 0x132; ++ else if (channel == 1) ++ seed = 0x122; ++ } ++ } else { ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 99 */ ++ if (MaxDimmsInstallable == 1) { ++ if (channel == 0) ++ seed = 0x3e; ++ else if (channel == 1) ++ seed = 0x38; ++ else if (channel == 2) ++ seed = 0x37; ++ else if (channel == 3) ++ seed = 0x31; ++ } else if (MaxDimmsInstallable == 2) { ++ if (channel == 0) ++ seed = 0x51; ++ else if (channel == 1) ++ seed = 0x4a; ++ else if (channel == 2) ++ seed = 0x46; ++ else if (channel == 3) ++ seed = 0x3f; ++ } else if (MaxDimmsInstallable == 3) { ++ if (channel == 0) ++ seed = 0x5e; ++ else if (channel == 1) ++ seed = 0x52; ++ else if (channel == 2) ++ seed = 0x48; ++ else if (channel == 3) ++ seed = 0x3c; ++ } ++ } else if (package_type == PT_C3) { ++ /* Socket C32: Fam15h BKDG v3.14 Table 100 */ ++ if ((MaxDimmsInstallable == 1) || (MaxDimmsInstallable == 2)) { ++ if (channel == 0) ++ seed = 0x39; ++ else if (channel == 1) ++ seed = 0x32; ++ } else if (MaxDimmsInstallable == 3) { ++ if (channel == 0) ++ seed = 0x45; ++ else if (channel == 1) ++ seed = 0x37; ++ } ++ } else if (package_type == PT_M2) { ++ /* Socket AM3: Fam15h BKDG v3.14 Table 101 */ ++ seed = 0x3a; ++ } ++ } ++ ++ return seed; ++} ++ ++static void read_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) + { + uint8_t lane; + uint32_t dword; +@@ -111,7 +257,7 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela + if (lane == 8) + wdt_reg = 0x32; + wdt_reg += dimm * 3; +- dword = Get_NB32_index_wait(dev, index_reg, wdt_reg); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg); + if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) + current_total_delay[lane] = (dword & 0x00ff0000) >> 16; + if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) +@@ -119,12 +265,124 @@ static void read_dqs_write_timing_control_registers(uint16_t* current_total_dela + } + } + +-static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dimm, uint32_t index_reg) ++#ifdef UNUSED_CODE ++static void write_dqs_write_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t dword; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t ret_reg; ++ if ((lane == 0) || (lane == 1)) ++ ret_reg = 0x30; ++ if ((lane == 2) || (lane == 3)) ++ ret_reg = 0x31; ++ if ((lane == 4) || (lane == 5)) ++ ret_reg = 0x40; ++ if ((lane == 6) || (lane == 7)) ++ ret_reg = 0x41; ++ if (lane == 8) ++ ret_reg = 0x32; ++ ret_reg += dimm * 3; ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg); ++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) { ++ dword &= ~(0xff << 16); ++ dword |= (current_total_delay[lane] & 0xff) << 16; ++ } ++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) { ++ dword &= ~0xff; ++ dword |= current_total_delay[lane] & 0xff; ++ } ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword); ++ } ++} ++#endif ++ ++static void write_write_data_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t dword; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t wdt_reg; ++ ++ /* Calculate Write Data Timing register location */ ++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3)) ++ wdt_reg = 0x1; ++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7)) ++ wdt_reg = 0x2; ++ if (lane == 8) ++ wdt_reg = 0x3; ++ wdt_reg |= (dimm << 8); ++ ++ /* Set Write Data Timing register values */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg); ++ if ((lane == 7) || (lane == 3)) { ++ dword &= ~(0x7f << 24); ++ dword |= (current_total_delay[lane] & 0x7f) << 24; ++ } ++ if ((lane == 6) || (lane == 2)) { ++ dword &= ~(0x7f << 16); ++ dword |= (current_total_delay[lane] & 0x7f) << 16; ++ } ++ if ((lane == 5) || (lane == 1)) { ++ dword &= ~(0x7f << 8); ++ dword |= (current_total_delay[lane] & 0x7f) << 8; ++ } ++ if ((lane == 8) || (lane == 4) || (lane == 0)) { ++ dword &= ~0x7f; ++ dword |= current_total_delay[lane] & 0x7f; ++ } ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, wdt_reg, dword); ++ } ++} ++ ++static void read_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t mask; ++ uint32_t dword; ++ ++ if (is_fam15h()) ++ mask = 0x3ff; ++ else ++ mask = 0x1ff; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t ret_reg; ++ if ((lane == 0) || (lane == 1)) ++ ret_reg = 0x10; ++ if ((lane == 2) || (lane == 3)) ++ ret_reg = 0x11; ++ if ((lane == 4) || (lane == 5)) ++ ret_reg = 0x20; ++ if ((lane == 6) || (lane == 7)) ++ ret_reg = 0x21; ++ if (lane == 8) ++ ret_reg = 0x12; ++ ret_reg += dimm * 3; ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg); ++ if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) { ++ current_total_delay[lane] = (dword & (mask << 16)) >> 16; ++ } ++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) { ++ current_total_delay[lane] = dword & mask; ++ } ++ } ++} ++ ++static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) + { + uint8_t lane; ++ uint32_t mask; + uint32_t dword; + +- for (lane = 0; lane < 8; lane++) { ++ if (is_fam15h()) ++ mask = 0x3ff; ++ else ++ mask = 0x1ff; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { + uint32_t ret_reg; + if ((lane == 0) || (lane == 1)) + ret_reg = 0x10; +@@ -134,17 +392,125 @@ static void write_dqs_receiver_enable_control_registers(uint16_t* current_total_ + ret_reg = 0x20; + if ((lane == 6) || (lane == 7)) + ret_reg = 0x21; ++ if (lane == 8) ++ ret_reg = 0x12; + ret_reg += dimm * 3; +- dword = Get_NB32_index_wait(dev, index_reg, ret_reg); ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg); + if ((lane == 7) || (lane == 5) || (lane == 3) || (lane == 1)) { +- dword &= ~(0x1ff << 16); +- dword |= (current_total_delay[lane] & 0x1ff) << 16; ++ dword &= ~(mask << 16); ++ dword |= (current_total_delay[lane] & mask) << 16; + } +- if ((lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) { +- dword &= ~0x1ff; +- dword |= current_total_delay[lane] & 0x1ff; ++ if ((lane == 8) || (lane == 6) || (lane == 4) || (lane == 2) || (lane == 0)) { ++ dword &= ~mask; ++ dword |= current_total_delay[lane] & mask; + } +- Set_NB32_index_wait(dev, index_reg, ret_reg, dword); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, ret_reg, dword); ++ } ++} ++ ++static void read_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t dword; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t prc_reg; ++ ++ /* Calculate DRAM Phase Recovery Control register location */ ++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3)) ++ prc_reg = 0x50; ++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7)) ++ prc_reg = 0x51; ++ if (lane == 8) ++ prc_reg = 0x52; ++ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg); ++ if ((lane == 7) || (lane == 3)) { ++ current_total_delay[lane] = (dword >> 24) & 0x7f; ++ } ++ if ((lane == 6) || (lane == 2)) { ++ current_total_delay[lane] = (dword >> 16) & 0x7f; ++ } ++ if ((lane == 5) || (lane == 1)) { ++ current_total_delay[lane] = (dword >> 8) & 0x7f; ++ } ++ if ((lane == 8) || (lane == 4) || (lane == 0)) { ++ current_total_delay[lane] = dword & 0x7f; ++ } ++ } ++} ++ ++static void write_dram_phase_recovery_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t dword; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t prc_reg; ++ ++ /* Calculate DRAM Phase Recovery Control register location */ ++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3)) ++ prc_reg = 0x50; ++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7)) ++ prc_reg = 0x51; ++ if (lane == 8) ++ prc_reg = 0x52; ++ ++ /* Set DRAM Phase Recovery Control register values */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg); ++ if ((lane == 7) || (lane == 3)) { ++ dword &= ~(0x7f << 24); ++ dword |= (current_total_delay[lane] & 0x7f) << 24; ++ } ++ if ((lane == 6) || (lane == 2)) { ++ dword &= ~(0x7f << 16); ++ dword |= (current_total_delay[lane] & 0x7f) << 16; ++ } ++ if ((lane == 5) || (lane == 1)) { ++ dword &= ~(0x7f << 8); ++ dword |= (current_total_delay[lane] & 0x7f) << 8; ++ } ++ if ((lane == 8) || (lane == 4) || (lane == 0)) { ++ dword &= ~0x7f; ++ dword |= current_total_delay[lane] & 0x7f; ++ } ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, prc_reg, dword); ++ } ++} ++ ++static void read_read_dqs_timing_control_registers(uint16_t* current_total_delay, uint32_t dev, uint8_t dct, uint8_t dimm, uint32_t index_reg) ++{ ++ uint8_t lane; ++ uint32_t dword; ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint32_t rdt_reg; ++ ++ /* Calculate DRAM Read DQS Timing register location */ ++ if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3)) ++ rdt_reg = 0x5; ++ if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7)) ++ rdt_reg = 0x6; ++ if (lane == 8) ++ rdt_reg = 0x7; ++ rdt_reg |= (dimm << 8); ++ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, rdt_reg); ++ if ((lane == 7) || (lane == 3)) { ++ current_total_delay[lane] = (dword >> 24) & 0x3f; ++ } ++ if ((lane == 6) || (lane == 2)) { ++ current_total_delay[lane] = (dword >> 16) & 0x3f; ++ } ++ if ((lane == 5) || (lane == 1)) { ++ current_total_delay[lane] = (dword >> 8) & 0x3f; ++ } ++ if ((lane == 8) || (lane == 4) || (lane == 0)) { ++ current_total_delay[lane] = dword & 0x3f; ++ } ++ ++ if (is_fam15h()) ++ current_total_delay[lane] >>= 1; + } + } + +@@ -160,10 +526,11 @@ static uint32_t convert_testaddr_and_channel_to_address(struct DCTStatStruc *pDC + return testaddr; + } + +-/* DQS Receiver Enable Training +- * Algorithm detailed in the Fam10h BKDG Rev. 3.62 section 2.8.9.9.2 ++/* DQS Receiver Enable Training (Family 10h) ++ * Algorithm detailed in: ++ * The Fam10h BKDG Rev. 3.62 section 2.8.9.9.2 + */ +-static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, ++static void dqsTrainRcvrEn_SW_Fam10(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 Pass) + { + u8 Channel; +@@ -171,7 +538,6 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + u8 Addl_Index = 0; + u8 Receiver; + u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0; +- u8 Final_Value; + u16 CTLRMaxDelay; + u16 MaxDelay_CH[2]; + u32 TestAddr0, TestAddr1, TestAddr0B, TestAddr1B; +@@ -188,6 +554,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + u32 lo, hi; + + uint32_t dword; ++ uint8_t dimm; + uint8_t rank; + uint8_t lane; + uint16_t current_total_delay[MAX_BYTE_LANES]; +@@ -214,14 +581,13 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + } + + for (ch = ch_start; ch < ch_end; ch++) { +- reg = 0x78 + (0x100 * ch); +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, ch, reg); + val &= ~(0x3ff << 22); +- val |= (0x0c8 << 22); /* Max Rd Lat */ +- Set_NB32(dev, reg, val); ++ val |= (0x0c8 << 22); /* MaxRdLatency = 0xc8 */ ++ Set_NB32_DCT(dev, ch, reg, val); + } + +- Final_Value = 1; + if (Pass == FirstPass) { + mct_InitDQSPos4RcvrEn_D(pMCTstat, pDCTstat); + } else { +@@ -260,7 +626,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + + CTLRMaxDelay = 0; + MaxDelay_CH[Channel] = 0; +- index_reg = 0x98 + 0x100 * Channel; ++ index_reg = 0x98; + + Receiver = mct_InitReceiver_D(pDCTstat, Channel); + /* There are four receiver pairs, loosely associated with chipselects. +@@ -268,6 +634,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + */ + for (; Receiver < 8; Receiver += 2) { + Addl_Index = (Receiver >> 1) * 3 + 0x10; ++ dimm = (Receiver >> 1); + + print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2); + +@@ -284,45 +651,14 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + /* 2.8.9.9.2 (1, 6) + * Retrieve gross and fine timing fields from write DQS registers + */ +- read_dqs_write_timing_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + + /* 2.8.9.9.2 (1) + * Program the Write Data Timing and Write ECC Timing register to + * the values stored in the DQS Write Timing Control register + * for each lane + */ +- for (lane = 0; lane < MAX_BYTE_LANES; lane++) { +- uint32_t wdt_reg; +- +- /* Calculate Write Data Timing register location */ +- if ((lane == 0) || (lane == 1) || (lane == 2) || (lane == 3)) +- wdt_reg = 0x1; +- if ((lane == 4) || (lane == 5) || (lane == 6) || (lane == 7)) +- wdt_reg = 0x2; +- if (lane == 8) +- wdt_reg = 0x3; +- wdt_reg |= ((Receiver / 2) << 8); +- +- /* Set Write Data Timing register values */ +- dword = Get_NB32_index_wait(dev, index_reg, wdt_reg); +- if ((lane == 7) || (lane == 3)) { +- dword &= ~(0x7f << 24); +- dword |= (current_total_delay[lane] & 0x7f) << 24; +- } +- if ((lane == 6) || (lane == 2)) { +- dword &= ~(0x7f << 16); +- dword |= (current_total_delay[lane] & 0x7f) << 16; +- } +- if ((lane == 5) || (lane == 1)) { +- dword &= ~(0x7f << 8); +- dword |= (current_total_delay[lane] & 0x7f) << 8; +- } +- if ((lane == 8) || (lane == 4) || (lane == 0)) { +- dword &= ~0x7f; +- dword |= current_total_delay[lane] & 0x7f; +- } +- Set_NB32_index_wait(dev, index_reg, wdt_reg, dword); +- } ++ write_write_data_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + + /* 2.8.9.9.2 (2) + * Program the Read DQS Timing Control and the Read DQS ECC Timing Control registers +@@ -336,12 +672,12 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + rdt_reg = 0x6; + if (lane == 8) + rdt_reg = 0x7; +- rdt_reg |= ((Receiver / 2) << 8); ++ rdt_reg |= (dimm << 8); + if (lane == 8) + dword = 0x0000003f; + else + dword = 0x3f3f3f3f; +- Set_NB32_index_wait(dev, index_reg, rdt_reg, dword); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, rdt_reg, dword); + } + + /* 2.8.9.9.2 (3) +@@ -371,7 +707,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + print_debug_dqs("\t\tTrainRcvEn53: TestAddr1B ", TestAddr1B, 2); + + /* 2.8.9.9.2 (4, 5) +- * Write 1 cache line of the appropriate test pattern to each test addresse ++ * Write 1 cache line of the appropriate test pattern to each test address + */ + mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, 0); /* rank 0 of DIMM, testpattern 0 */ + mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, 1); /* rank 0 of DIMM, testpattern 1 */ +@@ -390,7 +726,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + /* 2.8.9.9.2 (6) + * Write gross and fine timing fields to read DQS registers + */ +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + + /* 2.8.9.9.2 (7) + * Loop over all delay values up to 1 MEMCLK (0x40 delay steps) from the initial delay values +@@ -417,8 +753,8 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + break; + + /* 2.8.9.9.2 (7 A) +- * Loop over all ranks +- */ ++ * Loop over all ranks ++ */ + for (rank = 0; rank < (_2Ranks + 1); rank++) { + /* 2.8.9.9.2 (7 A a-d) + * Read the first test address of the current rank +@@ -434,17 +770,17 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + */ + proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B); + result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel)); +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1); + result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel)); +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + } else { + proc_IOCLFLUSH_D((rank == 0)?TestAddr0:TestAddr1); + result_qword1 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0:TestAddr1, Channel)); +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + proc_IOCLFLUSH_D((rank == 0)?TestAddr0B:TestAddr1B); + result_qword2 = read64_fs(convert_testaddr_and_channel_to_address(pDCTstat, (rank == 0)?TestAddr0B:TestAddr1B, Channel)); +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + } + /* 2.8.9.9.2 (7 A e) + * Compare both read patterns and flag passing ranks/lanes +@@ -533,7 +869,7 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + } + + /* Update delays in hardware */ +- write_dqs_receiver_enable_control_registers(current_total_delay, dev, (Receiver >> 1), index_reg); ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); + + /* Save previous results for comparison in the next iteration */ + for (lane = 0; lane < 8; lane++) +@@ -587,7 +923,483 @@ static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat, + mct_SetMaxLatency_D(pDCTstat, Channel, CTLRMaxDelay); /* program Ch A/B MaxAsyncLat to correspond with max delay */ + } + +- ResetDCTWrPtr_D(dev, index_reg, Addl_Index); ++ for (Channel = 0; Channel < 2; Channel++) { ++ ResetDCTWrPtr_D(dev, Channel, index_reg, Addl_Index); ++ } ++ ++ if(_DisableDramECC) { ++ mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC); ++ } ++ ++ if (Pass == FirstPass) { ++ /*Disable DQSRcvrEn training mode */ ++ mct_DisableDQSRcvEn_D(pDCTstat); ++ } ++ ++ if(!_Wrap32Dis) { ++ msr = HWCR; ++ _RDMSR(msr, &lo, &hi); ++ lo &= ~(1<<17); /* restore HWCR.wrap32dis */ ++ _WRMSR(msr, lo, hi); ++ } ++ if(!_SSE2){ ++ cr4 = read_cr4(); ++ cr4 &= ~(1<<9); /* restore cr4.OSFXSR */ ++ write_cr4(cr4); ++ } ++ ++#if DQS_TRAIN_DEBUG > 0 ++ { ++ u8 ChannelDTD; ++ printk(BIOS_DEBUG, "TrainRcvrEn: CH_MaxRdLat:\n"); ++ for(ChannelDTD = 0; ChannelDTD<2; ChannelDTD++) { ++ printk(BIOS_DEBUG, "Channel:%x: %x\n", ++ ChannelDTD, pDCTstat->CH_MaxRdLat[ChannelDTD]); ++ } ++ } ++#endif ++ ++#if DQS_TRAIN_DEBUG > 0 ++ { ++ u16 valDTD; ++ u8 ChannelDTD, ReceiverDTD; ++ u8 i; ++ u16 *p; ++ ++ printk(BIOS_DEBUG, "TrainRcvrEn: CH_D_B_RCVRDLY:\n"); ++ for(ChannelDTD = 0; ChannelDTD < 2; ChannelDTD++) { ++ printk(BIOS_DEBUG, "Channel:%x\n", ChannelDTD); ++ for(ReceiverDTD = 0; ReceiverDTD<8; ReceiverDTD+=2) { ++ printk(BIOS_DEBUG, "\t\tReceiver:%x:", ReceiverDTD); ++ p = pDCTstat->CH_D_B_RCVRDLY[ChannelDTD][ReceiverDTD>>1]; ++ for (i=0;i<8; i++) { ++ valDTD = p[i]; ++ printk(BIOS_DEBUG, " %03x", valDTD); ++ } ++ printk(BIOS_DEBUG, "\n"); ++ } ++ } ++ } ++#endif ++ ++ printk(BIOS_DEBUG, "TrainRcvrEn: Status %x\n", pDCTstat->Status); ++ printk(BIOS_DEBUG, "TrainRcvrEn: ErrStatus %x\n", pDCTstat->ErrStatus); ++ printk(BIOS_DEBUG, "TrainRcvrEn: ErrCode %x\n", pDCTstat->ErrCode); ++ printk(BIOS_DEBUG, "TrainRcvrEn: Done\n\n"); ++} ++ ++/* DQS Receiver Enable Training Pattern Generation (Family 15h) ++ * Algorithm detailed in: ++ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2 (4) ++ */ ++static void generate_dram_receiver_enable_training_pattern_fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, uint8_t dct, uint8_t Receiver) ++{ ++ uint32_t dword; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ /* 2.10.5.7.1.1 ++ * It appears that the DCT only supports 8-beat burst length mode, ++ * so do nothing here... ++ */ ++ ++ /* Wait for CmdSendInProg == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ } while (dword & (0x1 << 12)); ++ ++ /* Set CmdTestEnable = 1 */ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword |= (0x1 << 2); ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.1 Send Activate Command */ ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */ ++ dword |= ((0x1 << Receiver) << 22); ++ dword &= ~(0x7 << 19); /* CmdBank = 0 */ ++ dword &= ~(0x3ffff); /* CmdAddress = 0 */ ++ dword |= (0x1 << 31); /* SendActCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x28c, dword); ++ ++ /* Wait for SendActCmd == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ } while (dword & (0x1 << 31)); ++ ++ /* Wait 75 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 75); ++ ++ /* 2.10.5.8.6.1.2 */ ++ Set_NB32_DCT(dev, dct, 0x274, 0x0); /* DQMask = 0 */ ++ Set_NB32_DCT(dev, dct, 0x278, 0x0); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x27c); ++ dword &= ~(0xff); /* EccMask = 0 */ ++ if (pDCTstat->DimmECCPresent == 0) ++ dword |= 0xff; /* EccMask = 0xff */ ++ Set_NB32_DCT(dev, dct, 0x27c, dword); ++ ++ /* 2.10.5.8.6.1.2 */ ++ dword = Get_NB32_DCT(dev, dct, 0x270); ++ dword &= ~(0x7ffff); /* DataPrbsSeed = 55555 */ ++// dword |= (0x55555); ++ dword |= (0x44443); /* Use AGESA seed */ ++ Set_NB32_DCT(dev, dct, 0x270, dword); ++ ++ /* 2.10.5.8.2 (4) */ ++ dword = Get_NB32_DCT(dev, dct, 0x260); ++ dword &= ~(0x1fffff); /* CmdCount = 192 */ ++ dword |= 192; ++ Set_NB32_DCT(dev, dct, 0x260, dword); ++ ++#if 0 ++ /* TODO: This applies to Fam15h model 10h and above only */ ++ /* Program Bubble Count and CmdStreamLen */ ++ dword = Get_NB32_DCT(dev, dct, 0x25c); ++ dword &= ~(0x3ff << 12); /* BubbleCnt = 0 */ ++ dword &= ~(0x3ff << 22); /* BubbleCnt2 = 0 */ ++ dword &= ~(0xff); /* CmdStreamLen = 1 */ ++ dword |= 0x1; ++ Set_NB32_DCT(dev, dct, 0x25c, dword); ++#endif ++ ++ /* Configure Target A */ ++ dword = Get_NB32_DCT(dev, dct, 0x254); ++ dword &= ~(0x7 << 24); /* TgtChipSelect = Receiver */ ++ dword |= (Receiver & 0x7) << 24; ++ dword &= ~(0x7 << 21); /* TgtBank = 0 */ ++ dword &= ~(0x3ff); /* TgtAddress = 0 */ ++ Set_NB32_DCT(dev, dct, 0x254, dword); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword |= (0x1 << 3); /* ResetAllErr = 1 */ ++ dword &= ~(0x1 << 4); /* StopOnErr = 0 */ ++ dword &= ~(0x3 << 8); /* CmdTgt = 0 (Target A) */ ++ dword &= ~(0x7 << 5); /* CmdType = 0 (Read) */ ++ dword |= (0x1 << 11); /* SendCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.2 Wait for TestStatus == 1 and CmdSendInProg == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ } while ((dword & (0x1 << 12)) || (!(dword & (0x1 << 10)))); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword &= ~(0x1 << 11); /* SendCmd = 0 */ ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++ ++ /* 2.10.5.8.6.1.1 Send Precharge Command */ ++ /* Wait 25 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25); ++ ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ dword &= ~(0xff << 22); /* CmdChipSelect = Receiver */ ++ dword |= ((0x1 << Receiver) << 22); ++ dword &= ~(0x7 << 19); /* CmdBank = 0 */ ++ dword &= ~(0x3ffff); /* CmdAddress = 0x400 */ ++ dword |= 0x400; ++ dword |= (0x1 << 30); /* SendPchgCmd = 1 */ ++ Set_NB32_DCT(dev, dct, 0x28c, dword); ++ ++ /* Wait for SendPchgCmd == 0 */ ++ do { ++ dword = Get_NB32_DCT(dev, dct, 0x28c); ++ } while (dword & (0x1 << 30)); ++ ++ /* Wait 25 MEMCLKs. */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 25); ++ ++ /* Set CmdTestEnable = 0 */ ++ dword = Get_NB32_DCT(dev, dct, 0x250); ++ dword &= ~(0x1 << 2); ++ Set_NB32_DCT(dev, dct, 0x250, dword); ++} ++ ++/* DQS Receiver Enable Training (Family 15h) ++ * Algorithm detailed in: ++ * The Fam15h BKDG Rev. 3.14 section 2.10.5.8.2 ++ * This algorithm runs once at the lowest supported MEMCLK, ++ * then once again at the highest supported MEMCLK. ++ */ ++static void dqsTrainRcvrEn_SW_Fam15(struct MCTStatStruc *pMCTstat, ++ struct DCTStatStruc *pDCTstat, u8 Pass) ++{ ++ u8 Channel; ++ u8 _2Ranks; ++ u8 Addl_Index = 0; ++ u8 Receiver; ++ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0; ++ u32 Errors; ++ ++ u32 val; ++ u32 dev; ++ u32 index_reg; ++ u32 ch_start, ch_end, ch; ++ u32 msr; ++ u32 cr4; ++ u32 lo, hi; ++ ++ uint32_t dword; ++ uint8_t dimm; ++ uint8_t rank; ++ uint8_t lane; ++ uint8_t mem_clk; ++ uint16_t initial_seed; ++ uint16_t current_total_delay[MAX_BYTE_LANES]; ++ uint16_t dqs_ret_pass1_total_delay[MAX_BYTE_LANES]; ++ uint16_t rank0_current_total_delay[MAX_BYTE_LANES]; ++ uint16_t phase_recovery_delays[MAX_BYTE_LANES]; ++ uint16_t seed[MAX_BYTE_LANES]; ++ uint16_t seed_gross[MAX_BYTE_LANES]; ++ uint16_t seed_fine[MAX_BYTE_LANES]; ++ uint16_t seed_pre_gross[MAX_BYTE_LANES]; ++ ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; ++ ++ print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0); ++ print_debug_dqs("TrainRcvEn: Pass", Pass, 0); ++ ++ dev = pDCTstat->dev_dct; ++ index_reg = 0x98; ++ ch_start = 0; ++ ch_end = 2; ++ ++ for (ch = ch_start; ch < ch_end; ch++) { ++ uint8_t max_rd_latency = 0x55; ++ uint8_t p_state; ++ ++ /* 2.10.5.6 */ ++ fam15EnableTrainingMode(pMCTstat, pDCTstat, ch, 1); ++ ++ /* 2.10.5.2 */ ++ for (p_state = 0; p_state < 3; p_state++) { ++ val = Get_NB32_DCT_NBPstate(dev, ch, p_state, 0x210); ++ val &= ~(0x3ff << 22); /* MaxRdLatency = max_rd_latency */ ++ val |= (max_rd_latency & 0x3ff) << 22; ++ Set_NB32_DCT_NBPstate(dev, ch, p_state, 0x210, val); ++ } ++ } ++ ++ if (Pass != FirstPass) { ++ pDCTstat->DimmTrainFail = 0; ++ pDCTstat->CSTrainFail = ~pDCTstat->CSPresent; ++ } ++ ++ cr4 = read_cr4(); ++ if(cr4 & ( 1 << 9)) { /* save the old value */ ++ _SSE2 = 1; ++ } ++ cr4 |= (1 << 9); /* OSFXSR enable SSE2 */ ++ write_cr4(cr4); ++ ++ msr = HWCR; ++ _RDMSR(msr, &lo, &hi); ++ /* FIXME: Why use SSEDIS */ ++ if(lo & (1 << 17)) { /* save the old value */ ++ _Wrap32Dis = 1; ++ } ++ lo |= (1 << 17); /* HWCR.wrap32dis */ ++ lo &= ~(1 << 15); /* SSEDIS */ ++ _WRMSR(msr, lo, hi); /* Setting wrap32dis allows 64-bit memory references in real mode */ ++ ++ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat); ++ ++ Errors = 0; ++ dev = pDCTstat->dev_dct; ++ ++ for (Channel = 0; Channel < 2; Channel++) { ++ print_debug_dqs("\tTrainRcvEn51: Node ", pDCTstat->Node_ID, 1); ++ print_debug_dqs("\tTrainRcvEn51: Channel ", Channel, 1); ++ pDCTstat->Channel = Channel; ++ ++ mem_clk = Get_NB32_DCT(dev, Channel, 0x94) & 0x1f; ++ ++ Receiver = mct_InitReceiver_D(pDCTstat, Channel); ++ /* There are four receiver pairs, loosely associated with chipselects. ++ * This is essentially looping over each DIMM. ++ */ ++ for (; Receiver < 8; Receiver += 2) { ++ Addl_Index = (Receiver >> 1) * 3 + 0x10; ++ dimm = (Receiver >> 1); ++ ++ print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2); ++ ++ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) { ++ continue; ++ } ++ ++ /* Retrieve the total delay values from pass 1 of DQS receiver enable training */ ++ if (Pass != FirstPass) { ++ read_dqs_receiver_enable_control_registers(dqs_ret_pass1_total_delay, dev, Channel, dimm, index_reg); ++ } ++ ++ /* 2.10.5.8.2 ++ * Loop over all ranks ++ */ ++ if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1)) ++ _2Ranks = 1; ++ else ++ _2Ranks = 0; ++ for (rank = 0; rank < (_2Ranks + 1); rank++) { ++ /* 2.10.5.8.2 (1) ++ * Specify the target DIMM to be trained ++ * Set TrNibbleSel = 0 ++ * ++ * TODO: Add support for x4 DIMMs ++ */ ++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008); ++ dword &= ~(0x3 << 4); /* TrDimmSel */ ++ dword |= ((dimm & 0x3) << 4); ++ dword &= ~(0x1 << 2); /* TrNibbleSel */ ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword); ++ ++ /* 2.10.5.8.2 (2) ++ * Retrieve gross and fine timing fields from write DQS registers ++ */ ++ read_dqs_write_timing_control_registers(current_total_delay, dev, Channel, dimm, index_reg); ++ ++ /* 2.10.5.8.2.1 ++ * Generate the DQS Receiver Enable Training Seed Values ++ */ ++ if (Pass == FirstPass) { ++ initial_seed = fam15_receiver_enable_training_seed(pDCTstat, Channel, dimm, rank, package_type); ++ ++ /* Adjust seed for the minimum platform supported frequency */ ++ initial_seed = (uint16_t) (((((uint64_t) initial_seed) * ++ fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100))); ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ uint16_t wl_pass1_delay; ++ wl_pass1_delay = current_total_delay[lane]; ++ ++ seed[lane] = initial_seed + wl_pass1_delay; ++ } ++ } else { ++ uint8_t addr_prelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ ++ uint16_t register_delay; ++ int16_t seed_prescaling; ++ ++ memcpy(current_total_delay, dqs_ret_pass1_total_delay, sizeof(current_total_delay)); ++ if ((pDCTstat->Status & (1 << SB_Registered))) { ++ if (addr_prelaunch) ++ register_delay = 0x30; ++ else ++ register_delay = 0x20; ++ } else if ((pDCTstat->Status & (1 << SB_LoadReduced))) { ++ /* TODO ++ * Load reduced DIMM support unimplemented ++ */ ++ register_delay = 0x0; ++ } else { ++ register_delay = 0x0; ++ } ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ seed_prescaling = current_total_delay[lane] - register_delay - 0x20; ++ seed[lane] = (uint16_t) (register_delay + ((((uint64_t) seed_prescaling) * fam15h_freq_tab[mem_clk] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100))); ++ } ++ } ++ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ seed_gross[lane] = (seed[lane] >> 5) & 0x1f; ++ seed_fine[lane] = seed[lane] & 0x1f; ++ ++ /*if (seed_gross[lane] == 0) ++ seed_pre_gross[lane] = 0; ++ else */if (seed_gross[lane] & 0x1) ++ seed_pre_gross[lane] = 1; ++ else ++ seed_pre_gross[lane] = 2; ++ ++ /* Calculate phase recovery delays */ ++ phase_recovery_delays[lane] = ((seed_pre_gross[lane] & 0x1f) << 5) | (seed_fine[lane] & 0x1f); ++ ++ /* Set the gross delay. ++ * NOTE: While the BKDG states to only program DqsRcvEnGrossDelay, this appears ++ * to have been a misprint as DqsRcvEnFineDelay should be set to zero as well. ++ */ ++ current_total_delay[lane] = ((seed_gross[lane] & 0x1f) << 5); ++ } ++ ++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (5 6) ++ * Program PhRecFineDly and PhRecGrossDly ++ */ ++ write_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg); ++ ++ /* 2.10.5.8.2 (2) / 2.10.5.8.2.1 (7) ++ * Program the DQS Receiver Enable delay values for each lane ++ */ ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); ++ ++ /* 2.10.5.8.2 (3) ++ * Program DqsRcvTrEn = 1 ++ */ ++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008); ++ dword |= (0x1 << 13); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword); ++ ++ /* 2.10.5.8.2 (4) ++ * Issue 192 read requests to the target rank ++ */ ++ generate_dram_receiver_enable_training_pattern_fam15(pMCTstat, pDCTstat, Channel, Receiver + (rank & 0x1)); ++ ++ /* 2.10.5.8.2 (5) ++ * Program DqsRcvTrEn = 0 ++ */ ++ dword = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008); ++ dword &= ~(0x1 << 13); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 0x00000008, dword); ++ ++ /* 2.10.5.8.2 (6) ++ * Read PhRecGrossDly, PhRecFineDly ++ */ ++ read_dram_phase_recovery_control_registers(phase_recovery_delays, dev, Channel, dimm, index_reg); ++ ++ /* 2.10.5.8.2 (7) ++ * Calculate and program the DQS Receiver Enable delay values ++ */ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ current_total_delay[lane] = (phase_recovery_delays[lane] & 0x1f); ++ current_total_delay[lane] |= ((seed_gross[lane] + ((phase_recovery_delays[lane] >> 5) & 0x1f) - seed_pre_gross[lane] + 1) << 5); ++ if (lane == 8) ++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane]; ++ else ++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane]; ++ } ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); ++ ++ if (rank == 0) { ++ /* Back up the Rank 0 delays for later use */ ++ memcpy(rank0_current_total_delay, current_total_delay, sizeof(current_total_delay)); ++ } ++ ++ if (rank == 1) { ++ /* 2.10.5.8.2 (8) ++ * Compute the average delay across both ranks and program the result into ++ * the DQS Receiver Enable delay registers ++ */ ++ for (lane = 0; lane < MAX_BYTE_LANES; lane++) { ++ current_total_delay[lane] = (rank0_current_total_delay[lane] + current_total_delay[lane]) / 2; ++ if (lane == 8) ++ pDCTstat->CH_D_BC_RCVRDLY[Channel][dimm] = current_total_delay[lane]; ++ else ++ pDCTstat->CH_D_B_RCVRDLY[Channel][dimm][lane] = current_total_delay[lane]; ++ } ++ write_dqs_receiver_enable_control_registers(current_total_delay, dev, Channel, dimm, index_reg); ++ } ++ } ++ ++#if DQS_TRAIN_DEBUG > 0 ++ for (lane = 0; lane < 8; lane++) ++ print_debug_dqs_pair("\t\tTrainRcvEn55: Lane ", lane, " current_total_delay ", current_total_delay[lane], 2); ++#endif ++ } ++ } ++ ++ /* Calculate and program MaxRdLatency */ ++ Calc_SetMaxRdLatency_D_Fam15(pMCTstat, pDCTstat, Channel); + + if(_DisableDramECC) { + mct_EnableDimmEccEn_D(pMCTstat, pDCTstat, _DisableDramECC); +@@ -674,10 +1486,10 @@ static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat) + } + + for (ch=0; ch<ch_end; ch++) { +- reg = 0x78 + 0x100 * ch; +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, ch, reg); + val &= ~(1 << DqsRcvEnTrain); +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, ch, reg, val); + } + } + +@@ -718,7 +1530,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, + /* get the register index from table */ + index = Table_DQSRcvEn_Offset[i >> 1]; + index += Addl_Index; /* DIMMx DqsRcvEn byte0 */ +- val = Get_NB32_index_wait(dev, index_reg, index); ++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, index); + if(i & 1) { + /* odd byte lane */ + val &= ~(0x1ff << 16); +@@ -728,7 +1540,7 @@ void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u16 RcvrEnDly, + val &= ~0x1ff; + val |= (RcvrEnDly & 0x1ff); + } +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val); + } + + } +@@ -742,7 +1554,6 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + u32 reg; + u32 SubTotal; + u32 index_reg; +- u32 reg_off; + u32 val; + + uint8_t cpu_val_n; +@@ -777,17 +1588,16 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + Channel = 0; + + dev = pDCTstat->dev_dct; +- reg_off = 0x100 * Channel; +- index_reg = 0x98 + reg_off; ++ index_reg = 0x98; + + /* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/ +- val = Get_NB32(dev, 0x88 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x88); + SubTotal = ((val & 0x0f) + 4) << 1; /* SubTotal is 1/2 Memclk unit */ + + /* If registered DIMMs are being used then + * add 1 MEMCLK to the sub-total. + */ +- val = Get_NB32(dev, 0x90 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x90); + if(!(val & (1 << UnBuffDimm))) + SubTotal += 2; + +@@ -795,7 +1605,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + * add 1, else add 2 to the sub-total. + * if (AddrCmdSetup || CsOdtSetup || CkeSetup) then K := K + 2; + */ +- val = Get_NB32_index_wait(dev, index_reg, 0x04); ++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04); + if(!(val & 0x00202020)) + SubTotal += 1; + else +@@ -803,7 +1613,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + + /* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs, + * then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */ +- val = Get_NB32(dev, 0x78 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x78); + SubTotal += 8 - (val & 0x0f); + + /* Convert bits 7-5 (also referred to as the coarse delay) of +@@ -824,7 +1634,7 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + * clocks (NCLKs) + */ + SubTotal *= 200 * ((Get_NB32(pDCTstat->dev_nbmisc, 0xd4) & 0x1f) + 4); +- SubTotal /= freq_tab[((Get_NB32(pDCTstat->dev_dct, 0x94 + reg_off) & 0x7) - 3)]; ++ SubTotal /= freq_tab[((Get_NB32_DCT(pDCTstat->dev_dct, Channel, 0x94) & 0x7) - 3)]; + SubTotal = (SubTotal + (2 - 1)) / 2; /* Round up */ + + /* Add "N" NCLKs to the sub-total. "N" represents part of the +@@ -841,13 +1651,13 @@ static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u16 D + /* Program the F2x[1, 0]78[MaxRdLatency] register with + * the total delay value (in NCLKs). + */ +- reg = 0x78 + reg_off; +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, Channel, reg); + val &= ~(0x3ff << 22); + val |= (SubTotal & 0x3ff) << 22; + + /* program MaxRdLatency to correspond with current delay */ +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, Channel, reg, val); + } + + static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, +@@ -877,7 +1687,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, + u32 dword; + u8 dn = 4; /* TODO: Rev C could be 4 */ + u32 dev = pDCTstat->dev_dct; +- u32 index_reg = 0x98 + 0x100 * Channel; ++ u32 index_reg = 0x98; + + /* FIXME: add Cx support */ + dword = 0x00000000; +@@ -885,7 +1695,7 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, + for(j=0; j<dn; j++) + /* DIMM0 Write Data Timing Low */ + /* DIMM0 Write ECC Timing */ +- Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword); + } + + /* errata #180 */ +@@ -893,13 +1703,13 @@ static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat, + for(i=5; i<=6; i++) { + for(j=0; j<dn; j++) + /* DIMM0 Read DQS Timing Control Low */ +- Set_NB32_index_wait(dev, index_reg, i + 0x100 * j, dword); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, i + 0x100 * j, dword); + } + + dword = 0x0000002f; + for(j=0; j<dn; j++) + /* DIMM0 Read DQS ECC Timing Control */ +- Set_NB32_index_wait(dev, index_reg, 7 + 0x100 * j, dword); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, 7 + 0x100 * j, dword); + } + + void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel) +@@ -912,13 +1722,13 @@ void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel) + u32 val; + + dev = pDCTstat->dev_dct; +- index_reg = 0x98 + Channel * 0x100; ++ index_reg = 0x98; + index = 0x12; + p = pDCTstat->CH_D_BC_RCVRDLY[Channel]; + print_debug_dqs("\t\tSetEccDQSRcvrPos: Channel ", Channel, 2); + for(ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) { + val = p[ChipSel>>1]; +- Set_NB32_index_wait(dev, index_reg, index, val); ++ Set_NB32_index_wait_DCT(dev, Channel, index_reg, index, val); + print_debug_dqs_pair("\t\tSetEccDQSRcvrPos: ChipSel ", + ChipSel, " rcvr_delay ", val, 2); + index += 3; +@@ -1002,95 +1812,305 @@ void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, + u8 Node = 0; + struct DCTStatStruc *pDCTstat; + ++ printk(BIOS_DEBUG, "%s: Start\n", __func__); ++ + /* FIXME: skip for Ax */ +- while (Node < MAX_NODES_SUPPORTED) { ++ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + pDCTstat = pDCTstatA + Node; ++ if (!pDCTstat->NodePresent) ++ continue; ++ ++ if (pDCTstat->DCTSysLimit) { ++ if (is_fam15h()) { ++ /* Fam15h BKDG v3.14 section 2.10.5.3.3 ++ * This picks up where InitDDRPhy left off ++ */ ++ uint8_t dct; ++ uint8_t index; ++ uint32_t dword; ++ uint32_t datc_backup; ++ uint32_t training_dword; ++ uint32_t fence2_config_dword; ++ uint32_t fence_tx_pad_config_dword; ++ uint32_t index_reg = 0x98; ++ uint32_t dev = pDCTstat->dev_dct; ++ ++ for (dct = 0; dct < 2; dct++) { ++ if (!pDCTstat->DIMMValidDCT[dct]) ++ continue; ++ ++ /* Back up D18F2x9C_x0000_0004_dct[1:0] */ ++ datc_backup = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004); ++ ++ /* FenceTrSel = 0x2 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008); ++ dword &= ~(0x3 << 6); ++ dword |= (0x2 << 6); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword); ++ ++ /* Set phase recovery seed values */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013); ++ ++ training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct); ++ ++ /* Save calculated fence value to the TX DLL */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c); ++ dword &= ~(0x1f << 26); ++ dword |= ((training_dword & 0x1f) << 26); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword); ++ ++ /* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x1 */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8)); ++ dword &= ~(0x7 << 12); ++ dword |= (0x1 << 12); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword); ++ } ++ ++ /* FenceTrSel = 0x1 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008); ++ dword &= ~(0x3 << 6); ++ dword |= (0x1 << 6); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword); ++ ++ /* Set phase recovery seed values */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013); ++ ++ training_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct); ++ ++ /* Save calculated fence value to the RX DLL */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c); ++ dword &= ~(0x1f << 21); ++ dword |= ((training_dword & 0x1f) << 21); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword); ++ ++ /* D18F2x9C_x0D0F_0[F,8:0]0F_dct[1:0][AlwaysEnDllClks]=0x0 */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8)); ++ dword &= ~(0x7 << 12); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f000f | (index << 8), dword); ++ } ++ ++ /* FenceTrSel = 0x3 */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008); ++ dword &= ~(0x3 << 6); ++ dword |= (0x3 << 6); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000008, dword); ++ ++ /* Set phase recovery seed values */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000050, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000051, 0x13131313); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000052, 0x00000013); ++ ++ fence_tx_pad_config_dword = fenceDynTraining_D(pMCTstat, pDCTstat, dct); ++ ++ /* Save calculated fence value to the TX Pad */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c); ++ dword &= ~(0x1f << 16); ++ dword |= ((fence_tx_pad_config_dword & 0x1f) << 16); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c, dword); ++ ++ /* Program D18F2x9C_x0D0F_[C,8,2][2:0]31_dct[1:0] */ ++ training_dword = fence_tx_pad_config_dword; ++ if (fence_tx_pad_config_dword < 16) ++ training_dword |= (0x1 << 4); ++ else ++ training_dword = 0; ++ for (index = 0; index < 0x3; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8)); ++ dword &= ~(0x1f); ++ dword |= (training_dword & 0x1f); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f2031 | (index << 8), dword); ++ } ++ for (index = 0; index < 0x3; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8)); ++ dword &= ~(0x1f); ++ dword |= (training_dword & 0x1f); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f8031 | (index << 8), dword); ++ } ++ for (index = 0; index < 0x3; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8)); ++ dword &= ~(0x1f); ++ dword |= (training_dword & 0x1f); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0fc031 | (index << 8), dword); ++ } ++ ++ /* Assemble Fence2 configuration word (Fam15h BKDG v3.14 page 331) */ ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0000000c); ++ fence2_config_dword = 0; ++ ++ /* TxPad */ ++ training_dword = (dword >> 16) & 0x1f; ++ if (training_dword < 16) ++ training_dword |= 0x10; ++ else ++ training_dword = 0; ++ fence2_config_dword |= training_dword; ++ ++ /* RxDll */ ++ training_dword = (dword >> 21) & 0x1f; ++ if (training_dword < 16) ++ training_dword |= 0x10; ++ else ++ training_dword = 0; ++ fence2_config_dword |= (training_dword << 10); ++ ++ /* TxDll */ ++ training_dword = (dword >> 26) & 0x1f; ++ if (training_dword < 16) ++ training_dword |= 0x10; ++ else ++ training_dword = 0; ++ fence2_config_dword |= (training_dword << 5); ++ ++ /* Program D18F2x9C_x0D0F_0[F,8:0]31_dct[1:0] */ ++ for (index = 0; index < 0x9; index++) { ++ dword = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8)); ++ dword &= ~(0x7fff); ++ dword |= fence2_config_dword; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0d0f0031 | (index << 8), dword); ++ } + +- if(pDCTstat->DCTSysLimit) { +- fenceDynTraining_D(pMCTstat, pDCTstat, 0); +- fenceDynTraining_D(pMCTstat, pDCTstat, 1); ++ /* Restore D18F2x9C_x0000_0004_dct[1:0] */ ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x00000004, datc_backup); ++ } ++ } else { ++ fenceDynTraining_D(pMCTstat, pDCTstat, 0); ++ fenceDynTraining_D(pMCTstat, pDCTstat, 1); ++ } + } +- Node++; + } ++ ++ printk(BIOS_DEBUG, "%s: Done\n", __func__); + } + +-static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat, ++static uint32_t fenceDynTraining_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) + { + u16 avRecValue; + u32 val; + u32 dev; +- u32 index_reg = 0x98 + 0x100 * dct; ++ u32 index_reg = 0x98; + u32 index; + +- /* BIOS first programs a seed value to the phase recovery engine +- * (recommended 19) registers. +- * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and +- * F2x[1,0]9C_x52.) . +- */ + dev = pDCTstat->dev_dct; +- for (index = 0x50; index <= 0x52; index ++) { +- val = (FenceTrnFinDlySeed & 0x1F); +- if (index != 0x52) { +- val |= val << 8 | val << 16 | val << 24; ++ ++ if (is_fam15h()) { ++ /* Set F2x[1,0]9C_x08[PhyFenceTrEn] */ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08); ++ val |= 1 << PhyFenceTrEn; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val); ++ ++ /* Wait 2000 MEMCLKs */ ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 2000); ++ ++ /* Clear F2x[1,0]9C_x08[PhyFenceTrEn] */ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08); ++ val &= ~(1 << PhyFenceTrEn); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val); ++ ++ /* BIOS reads the phase recovery engine registers ++ * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. ++ * Average the fine delay components only. ++ */ ++ avRecValue = 0; ++ for (index = 0x50; index <= 0x52; index++) { ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); ++ avRecValue += val & 0x1f; ++ if (index != 0x52) { ++ avRecValue += (val >> 8) & 0x1f; ++ avRecValue += (val >> 16) & 0x1f; ++ avRecValue += (val >> 24) & 0x1f; ++ } + } +- Set_NB32_index_wait(dev, index_reg, index, val); +- } + +- /* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */ +- val = Get_NB32_index_wait(dev, index_reg, 0x08); +- val |= 1 << PhyFenceTrEn; +- Set_NB32_index_wait(dev, index_reg, 0x08, val); +- +- /* Wait 200 MEMCLKs. */ +- mct_Wait(50000); /* wait 200us */ +- +- /* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */ +- val = Get_NB32_index_wait(dev, index_reg, 0x08); +- val &= ~(1 << PhyFenceTrEn); +- Set_NB32_index_wait(dev, index_reg, 0x08, val); +- +- /* BIOS reads the phase recovery engine registers +- * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */ +- avRecValue = 0; +- for (index = 0x50; index <= 0x52; index ++) { +- val = Get_NB32_index_wait(dev, index_reg, index); +- avRecValue += val & 0x7F; +- if (index != 0x52) { +- avRecValue += (val >> 8) & 0x7F; +- avRecValue += (val >> 16) & 0x7F; +- avRecValue += (val >> 24) & 0x7F; ++ val = avRecValue / 9; ++ if (avRecValue % 9) ++ val++; ++ avRecValue = val; ++ ++ if (avRecValue < 6) ++ avRecValue = 0; ++ else ++ avRecValue -= 6; ++ ++ return avRecValue; ++ } else { ++ /* BIOS first programs a seed value to the phase recovery engine ++ * (recommended 19) registers. ++ * Dram Phase Recovery Control Register (F2x[1,0]9C_x[51:50] and ++ * F2x[1,0]9C_x52.) . ++ */ ++ for (index = 0x50; index <= 0x52; index ++) { ++ val = (FenceTrnFinDlySeed & 0x1F); ++ if (index != 0x52) { ++ val |= val << 8 | val << 16 | val << 24; ++ } ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, index, val); + } +- } + +- val = avRecValue / 9; +- if (avRecValue % 9) +- val++; +- avRecValue = val; ++ /* Set F2x[1,0]9C_x08[PhyFenceTrEn]=1. */ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08); ++ val |= 1 << PhyFenceTrEn; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val); ++ ++ /* Wait 200 MEMCLKs. */ ++ mct_Wait(50000); /* wait 200us */ ++ ++ /* Clear F2x[1,0]9C_x08[PhyFenceTrEn]=0. */ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x08); ++ val &= ~(1 << PhyFenceTrEn); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x08, val); ++ ++ /* BIOS reads the phase recovery engine registers ++ * F2x[1,0]9C_x[51:50] and F2x[1,0]9C_x52. */ ++ avRecValue = 0; ++ for (index = 0x50; index <= 0x52; index ++) { ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, index); ++ avRecValue += val & 0x7F; ++ if (index != 0x52) { ++ avRecValue += (val >> 8) & 0x7F; ++ avRecValue += (val >> 16) & 0x7F; ++ avRecValue += (val >> 24) & 0x7F; ++ } ++ } + +- /* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */ +- /* inlined mct_AdjustFenceValue() */ +- /* TODO: The RBC0 is not supported. */ +- /* if (pDCTstat->LogicalCPUID & AMD_RB_C0) +- avRecValue -= 3; +- else +- */ +- if (pDCTstat->LogicalCPUID & AMD_DR_Dx) +- avRecValue -= 8; +- else if (pDCTstat->LogicalCPUID & AMD_DR_Cx) +- avRecValue -= 8; +- else if (pDCTstat->LogicalCPUID & AMD_DR_Bx) +- avRecValue -= 8; +- +- val = Get_NB32_index_wait(dev, index_reg, 0x0C); +- val &= ~(0x1F << 16); +- val |= (avRecValue & 0x1F) << 16; +- Set_NB32_index_wait(dev, index_reg, 0x0C, val); +- +- /* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register +- * delays (both channels). */ +- val = Get_NB32_index_wait(dev, index_reg, 0x04); +- Set_NB32_index_wait(dev, index_reg, 0x04, val); ++ val = avRecValue / 9; ++ if (avRecValue % 9) ++ val++; ++ avRecValue = val; ++ ++ /* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */ ++ /* inlined mct_AdjustFenceValue() */ ++ /* TODO: The RBC0 is not supported. */ ++ /* if (pDCTstat->LogicalCPUID & AMD_RB_C0) ++ avRecValue -= 3; ++ else ++ */ ++ if (pDCTstat->LogicalCPUID & AMD_DR_Dx) ++ avRecValue -= 8; ++ else if (pDCTstat->LogicalCPUID & AMD_DR_Cx) ++ avRecValue -= 8; ++ else if (pDCTstat->LogicalCPUID & AMD_DR_Bx) ++ avRecValue -= 8; ++ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C); ++ val &= ~(0x1F << 16); ++ val |= (avRecValue & 0x1F) << 16; ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x0C, val); ++ ++ /* Rewrite F2x[1,0]9C_x04-DRAM Address/Command Timing Control Register ++ * delays (both channels). ++ */ ++ val = Get_NB32_index_wait_DCT(dev, dct, index_reg, 0x04); ++ Set_NB32_index_wait_DCT(dev, dct, index_reg, 0x04, val); ++ ++ return avRecValue; ++ } + } + + void mct_Wait(u32 cycles) +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c +index f01e011..55068ce 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c +@@ -21,8 +21,14 @@ + u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass) + { + u8 ret = 1; +- if (pass == SecondPass) +- ret = 0; ++ ++ if (is_fam15h()) { ++ /* Fam15h needs two passes */ ++ ret = 1; ++ } else { ++ if (pass == SecondPass) ++ ret = 0; ++ } + + return ret; + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c +index 920f514..68acc75 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c +@@ -218,12 +218,12 @@ static void mct_setMaxRdLatTrnVal_D(struct DCTStatStruc *pDCTstat, + } + + dev = pDCTstat->dev_dct; +- reg = 0x78 + Channel * 0x100; +- val = Get_NB32(dev, reg); ++ reg = 0x78; ++ val = Get_NB32_DCT(dev, Channel, reg); + val &= ~(0x3ff<<22); + val |= MaxRdLatVal<<22; + /* program MaxRdLatency to correspond with current delay */ +- Set_NB32(dev, reg, val); ++ Set_NB32_DCT(dev, Channel, reg, val); + } + + static u8 CompareMaxRdLatTestPattern_D(u32 pattern_buf, u32 addr) +@@ -320,30 +320,28 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat, + u32 valx; + u32 valxx; + u32 index_reg; +- u32 reg_off; + u32 dev; + + if(pDCTstat->GangedMode) + Channel = 0; + +- index_reg = 0x98 + 0x100 * Channel; ++ index_reg = 0x98; + +- reg_off = 0x100 * Channel; + dev = pDCTstat->dev_dct; + + /* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/ +- val = Get_NB32(dev, 0x88 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x88); + SubTotal = ((val & 0x0f) + 1) << 1; /* SubTotal is 1/2 Memclk unit */ + + /* If registered DIMMs are being used then add 1 MEMCLK to the sub-total*/ +- val = Get_NB32(dev, 0x90 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x90); + if(!(val & (1 << UnBuffDimm))) + SubTotal += 2; + + /*If the address prelaunch is setup for 1/2 MEMCLKs then add 1, + * else add 2 to the sub-total. if (AddrCmdSetup || CsOdtSetup + * || CkeSetup) then K := K + 2; */ +- val = Get_NB32_index_wait(dev, index_reg, 0x04); ++ val = Get_NB32_index_wait_DCT(dev, Channel, index_reg, 0x04); + if(!(val & 0x00202020)) + SubTotal += 1; + else +@@ -351,7 +349,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat, + + /* If the F2x[1, 0]78[RdPtrInit] field is 4, 5, 6 or 7 MEMCLKs, + * then add 4, 3, 2, or 1 MEMCLKs, respectively to the sub-total. */ +- val = Get_NB32(dev, 0x78 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x78); + SubTotal += 8 - (val & 0x0f); + + /* Convert bits 7-5 (also referred to as the course delay) of the current +@@ -367,7 +365,7 @@ u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat, + + /*New formula: + SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */ +- val = Get_NB32(dev, 0x94 + reg_off); ++ val = Get_NB32_DCT(dev, Channel, 0x94); + /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */ + val &= 7; + if (val >= 3) { +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c +index 1c3e322..0ff4484 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c +@@ -83,6 +83,12 @@ void PrepareC_DCT(struct MCTStatStruc *pMCTstat, + pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0; + } + ++ if (pDCTstat->Status & (1 << SB_LoadReduced)) { ++ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 1; ++ } else { ++ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_LOAD_REDUCED] = 0; ++ } ++ + pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present; + + for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) { +@@ -103,13 +109,13 @@ void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDC + { + u32 val; + +- val = Get_NB32(pDCTstat->dev_dct, 0x94); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); + val |= 1 << 11; +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val); + +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); + val |= 1 << 11; +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val); + } + + void DisableZQcalibration(struct MCTStatStruc *pMCTstat, +@@ -117,15 +123,15 @@ void DisableZQcalibration(struct MCTStatStruc *pMCTstat, + { + u32 val; + +- val = Get_NB32(pDCTstat->dev_dct, 0x94); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); + val &= ~(1 << 11); + val &= ~(1 << 10); +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, val); + +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); + val &= ~(1 << 11); + val &= ~(1 << 10); +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, val); + } + + static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat, +@@ -142,23 +148,23 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat, + + /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */ + if (DCT0Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x90); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); + val |= 1 << EnterSelfRef; +- Set_NB32(pDCTstat->dev_dct, 0x90, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val); + } + if (DCT1Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); + val |= 1 << EnterSelfRef; +- Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val); + } + /* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */ + if (DCT0Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x90); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); + } while (val & (1 <<EnterSelfRef)); + if (DCT1Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); + } while (val & (1 <<EnterSelfRef)); + } + +@@ -168,8 +174,11 @@ static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat, + static void ChangeMemClk(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) + { +- u8 DCT0Present, DCT1Present; +- u32 val; ++ uint8_t DCT0Present; ++ uint8_t DCT1Present; ++ uint32_t dword; ++ uint32_t mask; ++ uint32_t offset; + + DCT0Present = pDCTstat->DIMMValidDCT[0]; + if (pDCTstat->GangedMode) +@@ -177,76 +186,134 @@ static void ChangeMemClk(struct MCTStatStruc *pMCTstat, + else + DCT1Present = pDCTstat->DIMMValidDCT[1]; + +- /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */ +- if (DCT0Present) { +- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8); +- val |= 1 << DisAutoComp; +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val); +- } +- if (DCT1Present) { +- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8); +- val |= 1 << DisAutoComp; +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val); ++ if (is_fam15h()) { ++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0x190 */ ++ if (DCT0Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x00000190; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword); ++ } ++ if (DCT1Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x00000190; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword); ++ } ++ } else { ++ /* Program F2x[1, 0]9C[DisAutoComp]=1. */ ++ if (DCT0Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8); ++ dword |= 1 << DisAutoComp; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword); ++ mct_Wait(100); /* Wait for 5us */ ++ } ++ if (DCT1Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8); ++ dword |= 1 << DisAutoComp; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword); ++ mct_Wait(100); /* Wait for 5us */ ++ } + } + + /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */ + if (DCT0Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94); +- val &= ~(1 << MemClkFreqVal); +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); ++ dword &= ~(1 << MemClkFreqVal); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword); + } + if (DCT1Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); +- val &= ~(1 << MemClkFreqVal); +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); ++ dword &= ~(1 << MemClkFreqVal); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword); + } + + /* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */ ++ if (is_fam15h()) { ++ offset = 0x0; ++ mask = 0x1f; ++ } else { ++ offset = 0x1; ++ mask = 0x7; ++ } + if (DCT0Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94); +- val &= 0xFFFFFFF8; +- val |= pDCTstat->TargetFreq - 1; +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); ++ dword &= ~mask; ++ dword |= (pDCTstat->TargetFreq - offset) & mask; ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword); + } + if (DCT1Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); +- val &= 0xFFFFFFF8; +- val |= pDCTstat->TargetFreq - 1; +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); ++ dword &= ~mask; ++ dword |= (pDCTstat->TargetFreq - offset) & mask; ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword); ++ } ++ ++ if (is_fam15h()) { ++ if (DCT0Present) { ++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 0); ++ set_2t_configuration(pMCTstat, pDCTstat, 0); ++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 0); ++ mct_PlatformSpec(pMCTstat, pDCTstat, 0); ++ } ++ if (DCT1Present) { ++ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1); ++ set_2t_configuration(pMCTstat, pDCTstat, 1); ++ mct_BeforePlatformSpec(pMCTstat, pDCTstat, 1); ++ mct_PlatformSpec(pMCTstat, pDCTstat, 1); ++ } + } + + /* Program F2x[1, 0]94[MemClkFreqVal] = 1. */ + if (DCT0Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94); +- val |= 1 << MemClkFreqVal; +- Set_NB32(pDCTstat->dev_dct, 0x94, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); ++ dword |= 1 << MemClkFreqVal; ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x94, dword); + } + if (DCT1Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); +- val |= 1 << MemClkFreqVal; +- Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); ++ dword |= 1 << MemClkFreqVal; ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x94, dword); + } + + /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */ + if (DCT0Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x94); +- } while (val & (1 << FreqChgInProg)); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x94); ++ } while (dword & (1 << FreqChgInProg)); + if (DCT1Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); +- } while (val & (1 << FreqChgInProg)); +- +- /* Program F2x[1, 0]94[MemClkFreqVal] = 0. */ +- if (DCT0Present) { +- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8); +- val &= ~(1 << DisAutoComp); +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 8, val); +- } +- if (DCT1Present) { +- val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8); +- val &= ~(1 << DisAutoComp); +- Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + 0x100, 8, val); ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x94); ++ } while (dword & (1 << FreqChgInProg)); ++ ++ if (is_fam15h()) { ++ /* Program D18F2x9C_x0D0F_E006_dct[1:0][PllLockTime] = 0xf */ ++ if (DCT0Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x0000000f; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 0x0d0fe006, dword); ++ } ++ if (DCT1Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006); ++ dword &= ~(0x0000ffff); ++ dword |= 0x0000000f; ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 0x0d0fe006, dword); ++ } ++ } else { ++ /* Program F2x[1, 0]9C[DisAutoComp] = 0. */ ++ if (DCT0Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8); ++ dword &= ~(1 << DisAutoComp); ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 0, 0x98, 8, dword); ++ mct_Wait(15000); /* Wait for 750us */ ++ } ++ if (DCT1Present) { ++ dword = Get_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8); ++ dword &= ~(1 << DisAutoComp); ++ Set_NB32_index_wait_DCT(pDCTstat->dev_dct, 1, 0x98, 8, dword); ++ mct_Wait(15000); /* Wait for 750us */ ++ } + } + } + +@@ -267,29 +334,46 @@ static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat, + + /* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */ + if (DCT0Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x90); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); + val |= 1 << ExitSelfRef; +- Set_NB32(pDCTstat->dev_dct, 0x90, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, val); + } + if (DCT1Present) { +- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); + val |= 1 << ExitSelfRef; +- Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, val); + } + /* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */ + if (DCT0Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x90); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); + } while (val & (1 << ExitSelfRef)); + if (DCT1Present) + do { +- val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100); ++ val = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); + } while (val & (1 << ExitSelfRef)); + } + + void SetTargetFreq(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat) + { ++ uint32_t dword; ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ ++ if (is_fam15h()) { ++ /* Program F2x[1, 0]90[DisDllShutDownSR]=1. */ ++ if (pDCTstat->DIMMValidDCT[0]) { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); ++ dword |= (0x1 << 27); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword); ++ } ++ if (pDCTstat->DIMMValidDCT[1]) { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); ++ dword |= (0x1 << 27); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword); ++ } ++ } ++ + /* Program F2x[1,0]90[EnterSelfRefresh]=1. + * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0. + */ +@@ -305,11 +389,38 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat, + */ + ChangeMemClk(pMCTstat, pDCTstat); + ++ if (is_fam15h()) { ++ uint8_t dct; ++ for (dct = 0; dct < 2; dct++) { ++ if (pDCTstat->DIMMValidDCT[dct]) { ++ phyAssistedMemFnceTraining(pMCTstat, pDCTstat); ++ InitPhyCompensation(pMCTstat, pDCTstat, dct); ++ } ++ } ++ } ++ + /* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs. + * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. + */ + ExitSelfRefresh(pMCTstat, pDCTstat); + ++ if (is_fam15h()) { ++ if ((package_type == PT_C3) || (package_type == PT_GR)) { ++ /* Socket C32 or G34 */ ++ /* Program F2x[1, 0]90[DisDllShutDownSR]=0. */ ++ if (pDCTstat->DIMMValidDCT[0]) { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 0, 0x90); ++ dword &= ~(0x1 << 27); ++ Set_NB32_DCT(pDCTstat->dev_dct, 0, 0x90, dword); ++ } ++ if (pDCTstat->DIMMValidDCT[1]) { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, 1, 0x90); ++ dword &= ~(0x1 << 27); ++ Set_NB32_DCT(pDCTstat->dev_dct, 1, 0x90, dword); ++ } ++ } ++ } ++ + /* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */ + mct_Wait(250); + +@@ -336,13 +447,13 @@ void SetTargetFreq(struct MCTStatStruc *pMCTstat, + static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set) + { + u32 val; +- u32 reg_off = dct * 0x100 + 0x44; +- while (reg_off < (dct * 0x100 + 0x60)) { +- val = Get_NB32(pDCTstat->dev_dct, reg_off); ++ u32 reg = 0x44; ++ while (reg < 0x60) { ++ val = Get_NB32_DCT(pDCTstat->dev_dct, dct, reg); + if (val & (1 << CSEnable)) + set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror)); +- Set_NB32(pDCTstat->dev_dct, reg_off, val); +- reg_off += 8; ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, reg, val); ++ reg += 8; + } + } + +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c +index 9f42d54..7ea7901 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c +@@ -30,13 +30,22 @@ + * + *---------------------------------------------------------------------------- + */ +-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue); +-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue); +-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl); +-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm); +-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass); +-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr); +-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm); ++u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue); ++u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue); ++void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ++ u8 dct, u8 dimm, BOOL wl); ++void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm); ++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass); ++void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass); ++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass); ++ ++static int32_t abs(int32_t val) { ++ if (val < 0) ++ val *= -1; ++ ++ return val; ++} ++ + /* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS +@@ -62,34 +71,55 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm); + * OUT + *----------------------------------------------------------------------------- + */ +-void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, +- u8 dimm, u8 pass) ++void AgesaHwWlPhase1(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ++ u8 dct, u8 dimm, u8 pass) + { + u8 ByteLane; + u32 Value, Addr; + u16 Addl_Data_Offset, Addl_Data_Port; ++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + + pDCTData->WLPass = pass; + /* 1. Specify the target DIMM that is to be trained by programming + * F2x[1, 0]9C_x08[TrDimmSel]. + */ +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart, +- TrDimmSelEnd,(u32)dimm); ++ TrDimmSelEnd, (u32)dimm); ++ ++ if (is_fam15h()) { ++ /* Set TrNibbleSel = 0 ++ * ++ * TODO: Add support for x4 DIMMs ++ */ ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_ADD_DCT_PHY_CONTROL_REG, 2, ++ 2, (u32)0); ++ } ++ + /* 2. Prepare the DIMMs for write levelization using DDR3-defined + * MR commands. */ +- prepareDimms(pMCTData, pDCTData,dimm, TRUE); ++ prepareDimms(pMCTstat, pDCTstat, dct, dimm, TRUE); ++ + /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to + * satisfy DDR3-defined internal DRAM timing. + */ +- pMCTData->AgesaDelay(40); ++ if (is_fam15h()) ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 40); ++ else ++ pMCTData->AgesaDelay(40); ++ + /* 4. Configure the processor's DDR phy for write levelization training: */ +- procConifg(pMCTData,pDCTData, dimm, pass); ++ procConfig(pMCTstat, pDCTstat, dct, dimm, pass); ++ + /* 5. Begin write levelization training: +- * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */ +- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ * Program F2x[1, 0]9C_x08[WrtLvTrEn]=1. */ ++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) ++ { ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1); ++ } + else + { + /* Broadcast write to all D3Dbyte chipset register offset 0xc +@@ -98,7 +128,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + * retain value of 3:2 (Trdimmsel) + * reset bit 5 (FrzPR) + */ +- if (pDCTData->DctTrain) ++ if (dct) + { + Addl_Data_Offset=0x198; + Addl_Data_Port=0x19C; +@@ -123,29 +153,127 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + DctAccessDone, DctAccessDone)) == 0); + } + ++ if (is_fam15h()) ++ proc_MFENCE(); ++ + /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */ +- pMCTData->AgesaDelay(140); ++ if (is_fam15h()) ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 200); ++ else ++ pMCTData->AgesaDelay(140); ++ + /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */ +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 0); ++ + /* Read from registers F2x[1, 0]9C_x[51:50] and F2x[1, 0]9C_x52 + * to get the gross and fine delay settings + * for the target DIMM and save these values. */ +- ByteLane = 0; +- while (ByteLane < MAX_BYTE_LANES) +- { +- getWLByteDelay(pDCTData,ByteLane, dimm); +- setWLByteDelay(pDCTData,ByteLane, dimm, 1); +- ByteLane++; ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ getWLByteDelay(pDCTstat, dct, ByteLane, dimm, pass); ++ } ++ ++ pDCTData->WLCriticalGrossDelayPrevPass = 0x1f; ++} ++ ++void AgesaHwWlPhase2(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ++ u8 dct, u8 dimm, u8 pass) ++{ ++ u8 ByteLane; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ ++ if (is_fam15h()) { ++ int32_t gross_diff[MAX_BYTE_LANES]; ++ int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass; ++ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm); ++ ++ /* Calculate the Critical Gross Delay */ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ /* Calculate the gross delay differential for this lane */ ++ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane]; ++ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane]; ++ ++ /* WrDqDqsEarly values greater than 2 are reserved */ ++ if (gross_diff[ByteLane] < -2) ++ gross_diff[ByteLane] = -2; ++ ++ /* Update the Critical Gross Delay */ ++ if (gross_diff[ByteLane] < cgd) ++ cgd = gross_diff[ByteLane]; ++ } ++ ++ pDCTData->WLCriticalGrossDelayPrevPass = cgd; ++ ++ /* Compensate for occasional noise/instability causing sporadic training failure */ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ uint16_t total_delay_seed = ((pDCTData->WLSeedGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLSeedFineDelay[index+ByteLane] & 0x1f); ++ uint16_t total_delay_phy = ((pDCTData->WLGrossDelay[index+ByteLane] & 0x1f) << 5) | (pDCTData->WLFineDelay[index+ByteLane] & 0x1f); ++ if (abs(total_delay_phy - total_delay_seed) > 0x20) { ++ printk(BIOS_DEBUG, "%s: overriding faulty phy value\n", __func__); ++ pDCTData->WLGrossDelay[index+ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane]; ++ pDCTData->WLFineDelay[index+ByteLane] = pDCTData->WLSeedFineDelay[index+ByteLane]; ++ } ++ } ++ } ++} ++ ++void AgesaHwWlPhase3(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ++ u8 dct, u8 dimm, u8 pass) ++{ ++ u8 ByteLane; ++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ ++ if (is_fam15h()) { ++ uint32_t dword; ++ int32_t gross_diff[MAX_BYTE_LANES]; ++ int32_t cgd = pDCTData->WLCriticalGrossDelayPrevPass; ++ uint8_t index = (uint8_t)(MAX_BYTE_LANES * dimm); ++ ++ /* Apply offset(s) if needed */ ++ if (cgd < 0) { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8); ++ dword &= ~(0x3 << 24); /* WrDqDqsEarly = abs(cgd) */ ++ dword |= ((abs(cgd) & 0x3) << 24); ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword); ++ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ /* Calculate the gross delay differential for this lane */ ++ gross_diff[ByteLane] = pDCTData->WLSeedGrossDelay[index+ByteLane] + pDCTData->WLGrossDelay[index+ByteLane]; ++ gross_diff[ByteLane] -= pDCTData->WLSeedPreGrossDelay[index+ByteLane]; ++ ++ /* Prevent underflow in the presence of noise / instability*/ ++ if (gross_diff[ByteLane] < cgd) ++ gross_diff[ByteLane] = cgd; ++ ++ pDCTData->WLGrossDelay[index+ByteLane] = (gross_diff[ByteLane] + (abs(cgd) & 0x3)); ++ } ++ } else { ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8); ++ dword &= ~(0x3 << 24); /* WrDqDqsEarly = 0 */ ++ Set_NB32_DCT(pDCTstat->dev_dct, dct, 0xa8, dword); ++ } ++ } ++ ++ /* Write the adjusted gross and fine delay settings ++ * to the target DIMM. */ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 1, pass); + } + + /* 6. Configure DRAM Phy Control Register so that the phy stops driving + * write levelization ODT. */ +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0); + ++ if (is_fam15h()) ++ proc_MFENCE(); ++ + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ +- pMCTData->AgesaDelay(10); ++ if (is_fam15h()) ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10); ++ else ++ pMCTData->AgesaDelay(10); + + /* 7. Program the target DIMM back to normal operation by configuring + * the following (See section 2.8.5.4.1.1 +@@ -155,7 +283,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + * For a two DIMM system, program the Rtt value for the target DIMM + * to the normal operating termination: + */ +- prepareDimms(pMCTData, pDCTData,dimm,FALSE); ++ prepareDimms(pMCTstat, pDCTstat, dct, dimm, FALSE); + } + + /*---------------------------------------------------------------------------- +@@ -165,7 +293,7 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + */ + + /*----------------------------------------------------------------------------- +- * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) ++ * u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) + * + * Description: + * This function swaps the bits in MSR register value +@@ -177,12 +305,17 @@ void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData, + * + * ---------------------------------------------------------------------------- + */ +-u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) ++u32 swapAddrBits_wl(struct DCTStatStruc *pDCTstat, uint8_t dct, uint32_t MRSValue) + { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + u32 tempW, tempW1; + +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); ++ if (is_fam15h()) ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15); ++ else ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10); + if (tempW1 & 1) + { + if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) +@@ -201,7 +334,7 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) + } + + /*----------------------------------------------------------------------------- +- * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) ++ * u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) + * + * Description: + * This function swaps the bits in MSR register value +@@ -213,12 +346,17 @@ u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue) + * + * ---------------------------------------------------------------------------- + */ +-u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue) ++u32 swapBankBits(struct DCTStatStruc *pDCTstat, uint8_t dct, u32 MRSValue) + { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + u32 tempW, tempW1; + +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd); ++ if (is_fam15h()) ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15); ++ else ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10); + if (tempW1 & 1) + { + if ((pDCTData->Status[DCT_STATUS_OnDimmMirror])) +@@ -269,7 +407,7 @@ static uint16_t unbuffered_dimm_nominal_termination_emrs(uint8_t number_of_dimms + return term; + } + +-static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count, uint8_t rank) ++static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms, uint8_t frequency_index, uint8_t rank_count) + { + uint16_t term; + +@@ -300,27 +438,27 @@ static uint16_t unbuffered_dimm_dynamic_termination_emrs(uint8_t number_of_dimms + * + * Description: + * This function prepares DIMMS for training +- * +- * Parameters: +- * IN OUT *DCTData - Pointer to buffer with information about each DCT +- * *SPDData - Pointer to buffer with information about each DIMMs +- * SPD information +- * *MCTData - Pointer to buffer with runtime parameters, +- * IN Dimm - Logical DIMM number +- * WL - indicates if the routine is used for Write levelization +- * training +- * +- * OUT +- * ++ * Fam10h: BKDG Rev. 3.62 section 2.8.9.9.1 ++ * Fam15h: BKDG Rev. 3.14 section 2.10.5.8.1 + * ---------------------------------------------------------------------------- + */ +-void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) ++void prepareDimms(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, ++ u8 dct, u8 dimm, BOOL wl) + { + u32 tempW, tempW1, tempW2, MrsBank; + u8 rank, currDimm, MemClkFreq; ++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint8_t number_of_dimms = pDCTData->MaxDimmsInstalled; + +- MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, ++ if (is_fam15h()) { ++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 4); ++ } else { ++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); ++ } + /* Configure the DCT to send initialization MR commands to the target DIMM + * by programming the F2x[1,0]7C register using the following steps. + */ +@@ -328,52 +466,95 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) + while ((rank < pDCTData->DimmRanks[dimm]) && (rank < 2)) + { + /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank to be trained. */ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, dimm*2+rank); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, dimm*2+rank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, dimm*2+rank); ++ + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * register that defines the required DDR3-defined function for write + * levelization. + */ +- MrsBank = swapBankBits(pDCTData,1); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); ++ MrsBank = swapBankBits(pDCTstat, dct, 1); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank); ++ + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * for write levelization. + */ + tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0 */ + +- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ +- tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); +- if (tempW2) +- { +- if (pDCTData->DimmX8Present[dimm]) +- tempW |= 0x800; ++ /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */ ++ if (is_fam15h()) { ++ tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; ++ tempW &= ~(0x0244); ++ } else { ++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ ++ tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); ++ if (tempW2) ++ { ++ if (pDCTData->DimmX8Present[dimm]) ++ tempW |= 0x800; ++ } + } + + /* determine Rtt_Nom for WL & Normal mode */ +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) { +- tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); +- } else { ++ if (is_fam15h()) { + if (wl) { +- if (rank == 0) { +- /* Get Rtt_WR for the current DIMM and rank */ +- uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); +- +- /* Convert dynamic termination code to corresponding nominal termination code */ +- if (dynamic_term == 0x200) +- tempW1 = 0x04; +- else if (dynamic_term == 0x400) +- tempW1 = 0x40; +- else +- tempW1 = 0x0; ++ if (number_of_dimms > 1) { ++ if (rank == 0) { ++ /* Get Rtt_WR for the current DIMM and rank */ ++ tempW2 = fam15_rttwr(pDCTstat, dct, dimm, rank, package_type); ++ } else { ++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); ++ } + } else { +- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); + } + } else { +- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); ++ } ++ tempW1 = 0; ++ tempW1 |= ((tempW2 & 0x4) >> 2) << 9; ++ tempW1 |= ((tempW2 & 0x2) >> 1) << 6; ++ tempW1 |= ((tempW2 & 0x1) >> 0) << 2; ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) { ++ tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); ++ } else { ++ if (wl) { ++ if (number_of_dimms > 1) { ++ if (rank == 0) { ++ /* Get Rtt_WR for the current DIMM and rank */ ++ uint16_t dynamic_term = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]); ++ ++ /* Convert dynamic termination code to corresponding nominal termination code */ ++ if (dynamic_term == 0x200) ++ tempW1 = 0x04; ++ else if (dynamic_term == 0x400) ++ tempW1 = 0x40; ++ else ++ tempW1 = 0x0; ++ } else { ++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ } ++ } else { ++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ } ++ } else { ++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ } + } + } ++ ++ /* Apply Rtt_Nom to the MRS control word */ + tempW=tempW|tempW1; + + /* All ranks of the target DIMM are set to write levelization mode. */ +@@ -393,68 +574,105 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) + tempW = bitTestSet(tempW1, Qoff); + } + } +- /* Program MrsAddress[5,1]=output driver impedance control (DIC): +- * based on F2x[1,0]84[DrvImpCtrl] +- */ +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); ++ ++ /* Program MrsAddress[5,1]=output driver impedance control (DIC) */ ++ if (is_fam15h()) { ++ tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); ++ } else { ++ /* Read DIC from F2x[1,0]84[DrvImpCtrl] */ ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); ++ } ++ ++ /* Apply DIC to the MRS control word */ + if (bitTest(tempW1, 1)) + tempW = bitTestSet(tempW, 5); + if (bitTest(tempW1, 0)) + tempW = bitTestSet(tempW, 1); + +- tempW = swapAddrBits_wl(pDCTData, tempW); ++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW); ++ ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW); + +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + * the specified DIMM. + */ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ +- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, ++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } ++ + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * register that defines the required DDR3-defined function for Rtt_WR. + */ +- MrsBank = swapBankBits(pDCTData,2); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); ++ MrsBank = swapBankBits(pDCTstat, dct, 2); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank); ++ + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * for Rtt_WR (DRAMTermDyn). + */ + tempW = 0;/* PASR = 0,*/ +- /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, +- * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); +- if (bitTest(tempW1,19)) +- {tempW = bitTestSet(tempW, 7);} +- if (bitTest(tempW1,18)) +- {tempW = bitTestSet(tempW, 6);} +- /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ +- tempW=tempW|((tempW1&0x00700000)>>17); +- /* workaround for DR-B0 */ +- if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) +- tempW+=0x8; ++ ++ /* Retrieve normal settings of the MRS control word and clear Rtt_WR */ ++ if (is_fam15h()) { ++ tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; ++ tempW &= ~(0x0600); ++ } else { ++ /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, ++ * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); ++ if (bitTest(tempW1,19)) ++ {tempW = bitTestSet(tempW, 7);} ++ if (bitTest(tempW1,18)) ++ {tempW = bitTestSet(tempW, 6);} ++ /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ ++ tempW=tempW|((tempW1&0x00700000)>>17); ++ /* workaround for DR-B0 */ ++ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) ++ tempW+=0x8; ++ } ++ + /* determine Rtt_WR for WL & Normal mode */ +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) +- tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); +- else +- tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm], rank); ++ if (is_fam15h()) { ++ tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) ++ tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank); ++ else ++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[dimm]); ++ } ++ ++ /* Apply Rtt_WR to the MRS control word */ + tempW=tempW|tempW1; +- tempW = swapAddrBits_wl(pDCTData,tempW); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); ++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW); ++ + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + the specified DIMM.*/ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); ++ + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ +- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, ++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } +@@ -473,97 +691,163 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) + rank = 0; + while ((rank < pDCTData->DimmRanks[currDimm]) && (rank < 2)) + { +- + /* Program F2x[1, 0]7C[MrsChipSel[2:0]] for the current rank + * to be trained. + */ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd, currDimm*2+rank); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam15, MrsChipSelEndFam15, currDimm*2+rank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsChipSelStartFam10, MrsChipSelEndFam10, currDimm*2+rank); ++ + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal + * DRAM register that defines the required DDR3-defined function + * for write levelization. + */ +- MrsBank = swapBankBits(pDCTData,1); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); ++ MrsBank = swapBankBits(pDCTstat, dct, 1); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank); ++ + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required + * DDR3-defined function for write levelization. + */ + tempW = 0;/* DLL_DIS = 0, DIC = 0, AL = 0, TDQS = 0, Level=0, Qoff=0 */ + +- /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ +- tempW2 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); +- if (tempW2) +- { +- if (pDCTData->DimmX8Present[currDimm]) +- tempW |= 0x800; ++ /* Retrieve normal settings of the MRS control word and clear Rtt_Nom */ ++ if (is_fam15h()) { ++ tempW = mct_MR1(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; ++ tempW &= ~(0x0244); ++ } else { ++ /* Set TDQS=1b for x8 DIMM, TDQS=0b for x4 DIMM, when mixed x8 & x4 */ ++ tempW2 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_CONFIG_HIGH, RDqsEn, RDqsEn); ++ if (tempW2) ++ { ++ if (pDCTData->DimmX8Present[currDimm]) ++ tempW |= 0x800; ++ } + } + + /* determine Rtt_Nom for WL & Normal mode */ +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) +- tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); +- else +- tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ if (is_fam15h()) { ++ tempW2 = fam15_rttnom(pDCTstat, dct, dimm, rank, package_type); ++ tempW1 = 0; ++ tempW1 |= ((tempW2 & 0x4) >> 2) << 9; ++ tempW1 |= ((tempW2 & 0x2) >> 1) << 6; ++ tempW1 |= ((tempW2 & 0x1) >> 0) << 2; ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) ++ tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); ++ else ++ tempW1 = unbuffered_dimm_nominal_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ } ++ ++ /* Apply Rtt_Nom to the MRS control word */ + tempW=tempW|tempW1; +- /* program MrsAddress[5,1]=output driver impedance control (DIC): +- * based on F2x[1,0]84[DrvImpCtrl] */ +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); ++ ++ /* Program MrsAddress[5,1]=output driver impedance control (DIC) */ ++ if (is_fam15h()) { ++ tempW1 = fam15_dimm_dic(pDCTstat, dct, dimm, rank, package_type); ++ } else { ++ /* Read DIC from F2x[1,0]84[DrvImpCtrl] */ ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_MRS_REGISTER, DrvImpCtrlStart, DrvImpCtrlEnd); ++ } ++ ++ /* Apply DIC to the MRS control word */ + if (bitTest(tempW1,1)) + {tempW = bitTestSet(tempW, 5);} + if (bitTest(tempW1,0)) + {tempW = bitTestSet(tempW, 1);} +- tempW = swapAddrBits_wl(pDCTData,tempW); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); ++ ++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW); ++ ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW); ++ + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command + * to the specified DIMM. + */ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, ++ set_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); ++ + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ +- while ((get_Bits(pDCTData, pDCTData->CurrDct, ++ while ((get_Bits(pDCTData, dct, + pDCTData->NodeId, FUN_DCT, DRAM_INIT, + SendMrsCmd, SendMrsCmd)) == 1); ++ + /* Program F2x[1, 0]7C[MrsBank[2:0]] for the appropriate internal DRAM + * register that defines the required DDR3-defined function for Rtt_WR. + */ +- MrsBank = swapBankBits(pDCTData,2); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsBankStart, MrsBankEnd, MrsBank); ++ MrsBank = swapBankBits(pDCTstat, dct, 2); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam15, MrsBankEndFam15, MrsBank); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsBankStartFam10, MrsBankEndFam10, MrsBank); ++ + /* Program F2x[1, 0]7C[MrsAddress[15:0]] to the required DDR3-defined function + * for Rtt_WR (DRAMTermDyn). + */ + tempW = 0;/* PASR = 0,*/ +- /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, +- * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ +- tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); +- if (bitTest(tempW1,19)) +- {tempW = bitTestSet(tempW, 7);} +- if (bitTest(tempW1,18)) +- {tempW = bitTestSet(tempW, 6);} +- /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ +- tempW=tempW|((tempW1&0x00700000)>>17); +- /* workaround for DR-B0 */ +- if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) +- tempW+=0x8; ++ ++ /* Retrieve normal settings of the MRS control word and clear Rtt_WR */ ++ if (is_fam15h()) { ++ tempW = mct_MR2(pMCTstat, pDCTstat, dct, dimm*2+rank) & 0xffff; ++ tempW &= ~(0x0600); ++ } else { ++ /* program MrsAddress[7,6,5:3]=SRT,ASR,CWL, ++ * based on F2x[1,0]84[19,18,22:20]=,SRT,ASR,Tcwl */ ++ tempW1 = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_MRS_REGISTER, PCI_MIN_LOW, PCI_MAX_HIGH); ++ if (bitTest(tempW1,19)) ++ {tempW = bitTestSet(tempW, 7);} ++ if (bitTest(tempW1,18)) ++ {tempW = bitTestSet(tempW, 6);} ++ /* tempW=tempW|(((tempW1>>20)&0x7)<<3); */ ++ tempW=tempW|((tempW1&0x00700000)>>17); ++ /* workaround for DR-B0 */ ++ if ((pDCTData->LogicalCPUID & AMD_DR_Bx) && (pDCTData->Status[DCT_STATUS_REGISTERED])) ++ tempW+=0x8; ++ } ++ + /* determine Rtt_WR for WL & Normal mode */ +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) +- tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); +- else +- tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm], rank); ++ if (is_fam15h()) { ++ tempW1 = (fam15_rttwr(pDCTstat, dct, dimm, rank, package_type) << 9); ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) ++ tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank); ++ else ++ tempW1 = unbuffered_dimm_dynamic_termination_emrs(pDCTData->MaxDimmsInstalled, MemClkFreq, pDCTData->DimmRanks[currDimm]); ++ } ++ ++ /* Apply Rtt_WR to the MRS control word */ + tempW=tempW|tempW1; +- tempW = swapAddrBits_wl(pDCTData,tempW); +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, +- DRAM_INIT, MrsAddressStart, MrsAddressEnd, tempW); ++ tempW = swapAddrBits_wl(pDCTstat, dct, tempW); ++ if (is_fam15h()) ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam15, MrsAddressEndFam15, tempW); ++ else ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, ++ DRAM_INIT, MrsAddressStartFam10, MrsAddressEndFam10, tempW); ++ + /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to + the specified DIMM.*/ +- set_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, FUN_DCT, ++ set_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_INIT, SendMrsCmd, SendMrsCmd, 1); ++ + /* Wait for F2x[1, 0]7C[SendMrsCmd] to be cleared by hardware. */ +- while ((get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, ++ while ((get_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1) + { + } +@@ -587,29 +871,60 @@ void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl) + * OUT + * ---------------------------------------------------------------------------- + */ +-void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm) ++void programODT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm) + { ++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ + u8 WrLvOdt1=0; + +- if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) { +- if ((pDCTData->DctCSPresent & 0x05) == 0x05) { +- WrLvOdt1 = 0x03; +- } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) { +- WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2); ++ if (is_fam15h()) { ++ /* Convert DIMM number to CS */ ++ uint32_t dword; ++ uint8_t cs; ++ uint8_t rank = 0; ++ ++ cs = (dimm * 2) + rank; ++ ++ /* Fetch preprogammed ODT pattern from configuration registers */ ++ dword = Get_NB32_DCT(pDCTstat->dev_dct, dct, ((cs>3)?0x23c:0x238)); ++ if ((cs == 7) || (cs == 3)) ++ WrLvOdt1 = ((dword >> 24) & 0xf); ++ else if ((cs == 6) || (cs == 2)) ++ WrLvOdt1 = ((dword >> 16) & 0xf); ++ else if ((cs == 5) || (cs == 1)) ++ WrLvOdt1 = ((dword >> 8) & 0xf); ++ else if ((cs == 4) || (cs == 0)) ++ WrLvOdt1 = (dword & 0xf); ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED] == 0) { ++ if ((pDCTData->DctCSPresent & 0x05) == 0x05) { ++ WrLvOdt1 = 0x03; ++ } else if (bitTest((u32)pDCTData->DctCSPresent,(u8)(dimm*2+1))) { ++ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm+2); ++ } else { ++ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm); ++ } + } else { +- WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm); ++ WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); + } +- } else { +- WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm); + } + +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1); + + } + ++#ifdef UNUSED_CODE ++static uint16_t fam15h_next_lowest_memclk_freq(uint16_t memclk_freq) ++{ ++ uint16_t fam15h_next_lowest_freq_tab[] = {0, 0, 0, 0, 0x4, 0, 0x4, 0, 0, 0, 0x6, 0, 0, 0, 0xa, 0, 0, 0, 0xe, 0, 0, 0, 0x12}; ++ return fam15h_next_lowest_freq_tab[memclk_freq]; ++} ++#endif ++ + /*----------------------------------------------------------------------------- +- * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass) ++ * void procConfig(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass) + * + * Description: + * This function programs the ODT values for the NB +@@ -622,31 +937,43 @@ void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm) + * OUT + * ---------------------------------------------------------------------------- + */ +-void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) ++void procConfig(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, uint8_t dct, u8 dimm, u8 pass) + { +- u8 ByteLane, Seed_Gross, Seed_Fine, MemClkFreq; ++ u8 ByteLane, MemClkFreq; ++ int32_t Seed_Gross; ++ int32_t Seed_Fine; ++ uint8_t Seed_PreGross; + u32 Value, Addr; + u16 Addl_Data_Offset, Addl_Data_Port; +- u16 freq_tab[] = {400, 533, 667, 800}; ++ sMCTStruct *pMCTData = pDCTstat->C_MCTPtr; ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; ++ u16 fam10h_freq_tab[] = {400, 533, 667, 800}; ++ uint16_t fam15h_freq_tab[] = {0, 0, 0, 0, 333, 0, 400, 0, 0, 0, 533, 0, 0, 0, 667, 0, 0, 0, 800, 0, 0, 0, 933}; + +- /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */ +- MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId, +- FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); ++ if (is_fam15h()) { ++ /* MemClkFreq: 0x4: 333MHz; 0x6: 400MHz; 0xa: 533MHz; 0xe: 667MHz; 0x12: 800MHz; 0x16: 933MHz */ ++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 4); ++ } else { ++ /* MemClkFreq: 3: 400MHz; 4: 533MHz; 5: 667MHz; 6: 800MHz */ ++ MemClkFreq = get_Bits(pDCTData, dct, pDCTData->NodeId, ++ FUN_DCT, DRAM_CONFIG_HIGH, 0, 2); ++ } + + /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the + * current memory subsystem configuration. + */ +- programODT(pMCTData, pDCTData, dimm); ++ programODT(pMCTstat, pDCTstat, dct, dimm); + + /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */ +- if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) { +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ if (pDCTData->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx | AMD_FAM15_ALL)) { ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, (u32)1); + } + else + { + /* Program WrLvOdtEn=1 through set bit 12 of D3CSODT reg offset 0 for Rev.B */ +- if (pDCTData->DctTrain) ++ if (dct) + { + Addl_Data_Offset=0x198; + Addl_Data_Port=0x19C; +@@ -669,33 +996,94 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) + DctAccessDone, DctAccessDone)) == 0); + } + ++ if (is_fam15h()) ++ proc_MFENCE(); ++ + /* Wait 10 MEMCLKs to allow for ODT signal settling. */ +- pMCTData->AgesaDelay(10); ++ if (is_fam15h()) ++ precise_memclk_delay_fam15(pMCTstat, pDCTstat, dct, 10); ++ else ++ pMCTData->AgesaDelay(10); ++ ++ /* Program write levelling seed values */ + if (pass == 1) + { +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) +- { +- if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain)))) ++ /* Pass 1 */ ++ if (is_fam15h()) { ++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ ++ uint8_t package_type = mctGet_NVbits(NV_PACK_TYPE); ++ uint16_t Seed_Total = 0; ++ if (package_type == PT_GR) { ++ /* Socket G34: Fam15h BKDG v3.14 Table 96 */ ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) { ++ Seed_Total = 0x41; ++ } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) { ++ Seed_Total = 0x0; ++ } else { ++ Seed_Total = 0xf; ++ } ++ } else if (package_type == PT_C3) { ++ /* Socket C32: Fam15h BKDG v3.14 Table 97 */ ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) { ++ Seed_Total = 0x3e; ++ } else if (pDCTData->Status[DCT_STATUS_LOAD_REDUCED]) { ++ Seed_Total = 0x0; ++ } else { ++ Seed_Total = 0x12; ++ } ++ } else if (package_type == PT_M2) { ++ /* Socket AM3: Fam15h BKDG v3.14 Table 98 */ ++ Seed_Total = 0xf; ++ } ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) ++ Seed_Total += ((AddrCmdPrelaunch)?0x10:0x0); ++ ++ /* Adjust seed for the minimum platform supported frequency */ ++ Seed_Total = (int32_t) (((((int64_t) Seed_Total) * ++ fam15h_freq_tab[MemClkFreq] * 100) / (mctGet_NVbits(NV_MIN_MEMCLK) * 100))); ++ ++ Seed_Gross = (Seed_Total >> 5) & 0x1f; ++ Seed_Fine = Seed_Total & 0x1f; ++ ++ /* Save seed values for later use */ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; ++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; ++ ++ if (Seed_Gross == 0) ++ Seed_PreGross = 0; ++ else if (Seed_Gross & 0x1) ++ Seed_PreGross = 1; ++ else ++ Seed_PreGross = 2; ++ ++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; ++ } ++ } else { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) + { +- Seed_Gross = 0x02; +- Seed_Fine = 0x16; ++ if(pDCTData->RegMan1Present & ((1<<(dimm*2+dct)))) ++ { ++ Seed_Gross = 0x02; ++ Seed_Fine = 0x16; ++ } ++ else ++ { ++ Seed_Gross = 0x02; ++ Seed_Fine = 0x00; ++ } + } + else + { +- Seed_Gross = 0x02; +- Seed_Fine = 0x00; +- } +- } +- else +- { +- if (MemClkFreq == 6) { +- /* DDR-800 */ +- Seed_Gross = 0x00; +- Seed_Fine = 0x1a; +- } else { +- /* Use settings for DDR-400 (interpolated from BKDG) */ +- Seed_Gross = 0x00; +- Seed_Fine = 0x0d; ++ if (MemClkFreq == 6) { ++ /* DDR-800 */ ++ Seed_Gross = 0x00; ++ Seed_Fine = 0x1a; ++ } else { ++ /* Use settings for DDR-400 (interpolated from BKDG) */ ++ Seed_Gross = 0x00; ++ Seed_Fine = 0x0d; ++ } + } + } + for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) +@@ -711,39 +1099,91 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) + pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; + pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; + } +- } else { /* Pass 2 */ ++ } else { ++ /* Pass 2 */ + /* From BKDG, Write Leveling Seed Value. */ +- u32 RegisterDelay, SeedTotal; +- for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) +- { +- if (pDCTData->Status[DCT_STATUS_REGISTERED]) +- RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */ +- else +- RegisterDelay = 0; +- SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | +- (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5); +- /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization +- training) - RegisterDelay. */ +- SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) * +- freq_tab[MemClkFreq-3] * 100) / (freq_tab[0] * 100))); +- Seed_Gross = SeedTotal / 32; +- Seed_Fine = SeedTotal & 0x1f; +- if (Seed_Gross == 0) +- Seed_Gross = 0; +- else if (Seed_Gross & 0x1) +- Seed_Gross = 1; +- else +- Seed_Gross = 2; +- pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; +- pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; ++ if (is_fam15h()) { ++ uint32_t RegisterDelay; ++ int32_t SeedTotal; ++ int32_t SeedTotalPreScaling; ++ uint8_t AddrCmdPrelaunch = 0; /* TODO: Fetch the correct value from RC2[0] */ ++ ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) { ++ if (AddrCmdPrelaunch) ++ RegisterDelay = 0x30; ++ else ++ RegisterDelay = 0x20; ++ } else { ++ RegisterDelay = 0; ++ } ++ /* Retrieve WrDqDqsEarly */ ++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId), FUN_DCT, 0xa8), 25, 24, &Value); ++ ++ /* Calculate adjusted seed values */ ++ SeedTotal = (pDCTData->WLFineDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | ++ ((pDCTData->WLGrossDelayPrevPass[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) << 5); ++ SeedTotalPreScaling = (SeedTotal - RegisterDelay - (0x20 * Value)); ++ SeedTotal = (int32_t) (RegisterDelay + ((((int64_t) SeedTotalPreScaling) * ++ fam15h_freq_tab[MemClkFreq] * 100) / (fam15h_freq_tab[pDCTData->WLPrevMemclkFreq] * 100))); ++ ++ if (SeedTotal >= 0) { ++ Seed_Gross = SeedTotal / 32; ++ Seed_Fine = SeedTotal % 32; ++ } else { ++ Seed_Gross = (SeedTotal / 32) - 1; ++ Seed_Fine = (SeedTotal % 32) + 32; ++ } ++ ++ if (Seed_Gross == 0) ++ Seed_PreGross = 0; ++ else if (Seed_Gross & 0x1) ++ Seed_PreGross = 1; ++ else ++ Seed_PreGross = 2; ++ ++ /* Save seed values for later use */ ++ pDCTData->WLSeedGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; ++ pDCTData->WLSeedFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; ++ pDCTData->WLSeedPreGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; ++ ++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_PreGross; ++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; ++ } ++ } else { ++ u32 RegisterDelay, SeedTotal; ++ for (ByteLane = 0; ByteLane < MAX_BYTE_LANES; ByteLane++) ++ { ++ if (pDCTData->Status[DCT_STATUS_REGISTERED]) ++ RegisterDelay = 0x20; /* TODO: ((RCW2 & BIT0) == 0) ? 0x20 : 0x30; */ ++ else ++ RegisterDelay = 0; ++ SeedTotal = (pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] & 0x1f) | ++ (pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] << 5); ++ /* SeedTotalPreScaling = (the total delay value in F2x[1, 0]9C_x[4A:30] from pass 1 of write levelization ++ training) - RegisterDelay. */ ++ SeedTotal = (uint16_t) (RegisterDelay + ((((uint64_t) SeedTotal - RegisterDelay) * ++ fam10h_freq_tab[MemClkFreq-3] * 100) / (fam10h_freq_tab[0] * 100))); ++ Seed_Gross = SeedTotal / 32; ++ Seed_Fine = SeedTotal & 0x1f; ++ if (Seed_Gross == 0) ++ Seed_Gross = 0; ++ else if (Seed_Gross & 0x1) ++ Seed_Gross = 1; ++ else ++ Seed_Gross = 2; ++ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross; ++ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine; ++ } + } + } + +- setWLByteDelay(pDCTData, ByteLane, dimm, 0); ++ pDCTData->WLPrevMemclkFreq = MemClkFreq; ++ setWLByteDelay(pDCTstat, dct, ByteLane, dimm, 0, pass); + } + + /*----------------------------------------------------------------------------- +- * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){ ++ * void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm){ + * + * Description: + * This function writes the write levelization byte delay for the Phase +@@ -763,8 +1203,9 @@ void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass) + * + *----------------------------------------------------------------------------- + */ +-void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) ++void setWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, u8 targetAddr, uint8_t pass) + { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr; + u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW; + +@@ -777,22 +1218,26 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) + EccValue = 0; + while (ByteLane < MAX_BYTE_LANES) + { +- /* This subtract 0xC workaround might be temporary. */ +- if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+pDCTData->DctTrain)))) +- { +- tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane]; +- tempW -= 0xC; +- pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5); +- pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F); +- } +- grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; +- /* Adjust seed gross delay overflow (greater than 3): +- * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5). +- * - Keep original seed gross delay for later reference. +- */ +- if(grossDelayValue >= 3) +- { +- grossDelayValue = (grossDelayValue&1)? 1 : 2; ++ if (is_fam15h()) { ++ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; ++ } else { ++ /* This subtract 0xC workaround might be temporary. */ ++ if ((pDCTData->WLPass==2) && (pDCTData->RegMan1Present & (1<<(dimm*2+dct)))) ++ { ++ tempW = (pDCTData->WLGrossDelay[index+ByteLane] << 5) | pDCTData->WLFineDelay[index+ByteLane]; ++ tempW -= 0xC; ++ pDCTData->WLGrossDelay[index+ByteLane] = (u8)(tempW >> 5); ++ pDCTData->WLFineDelay[index+ByteLane] = (u8)(tempW & 0x1F); ++ } ++ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; ++ /* Adjust seed gross delay overflow (greater than 3): ++ * - Program seed gross delay as 2 (gross is 4 or 6) or 1 (gross is 5). ++ * - Keep original seed gross delay for later reference. ++ */ ++ if(grossDelayValue >= 3) ++ { ++ grossDelayValue = (grossDelayValue&1)? 1 : 2; ++ } + } + fineDelayValue = pDCTData->WLFineDelay[index+ByteLane]; + if (ByteLane < 4) +@@ -803,15 +1248,16 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) + EccValue = ((grossDelayValue << 5) | fineDelayValue); + ByteLane++; + } +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_PHASE_REC_CTRL_LOW, 0, 31, (u32)ValueLow); +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh); +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue); + } + else + { ++ /* Fam10h BKDG Rev. 3.62 2.8.9.9.1 (6) */ + index = (u8)(MAX_BYTE_LANES * dimm); + grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane]; + fineDelayValue = pDCTData->WLFineDelay[index+ByteLane]; +@@ -841,16 +1287,24 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) + grossStartLoc = (u8)(fineEndLoc + 1); + grossEndLoc = (u8)(grossStartLoc + 1); + +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue); +- set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT, ++ set_DCT_ADDR_Bits(pDCTData, dct, pDCTData->NodeId, FUN_DCT, + (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue); ++ ++ pDCTData->WLFineDelayPrevPass[index+ByteLane] = fineDelayValue; ++ pDCTData->WLGrossDelayPrevPass[index+ByteLane] = grossDelayValue; ++ if (pass == FirstPass) { ++ pDCTData->WLFineDelayFirstPass[index+ByteLane] = fineDelayValue; ++ pDCTData->WLGrossDelayFirstPass[index+ByteLane] = grossDelayValue; ++ pDCTData->WLCriticalGrossDelayFirstPass = pDCTData->WLCriticalGrossDelayPrevPass; ++ } + } + + } + + /*----------------------------------------------------------------------------- +- * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm) ++ * void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 Dimm) + * + * Description: + * This function reads the write levelization byte delay from the Phase +@@ -868,8 +1322,9 @@ void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr) + * + *----------------------------------------------------------------------------- + */ +-void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm) ++void getWLByteDelay(struct DCTStatStruc *pDCTstat, uint8_t dct, u8 ByteLane, u8 dimm, uint8_t pass) + { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[dct]; + u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index; + u32 addr, fine, gross; + tempB = 0; +@@ -890,25 +1345,31 @@ void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm) + grossStartLoc = (u8)(fineEndLoc + 1); + grossEndLoc = (u8)(grossStartLoc + 1); + +- fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, ++ fine = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc); +- gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, ++ gross = get_ADD_DCT_Bits(pDCTData, dct, pDCTData->NodeId, + FUN_DCT, (u16)addr, grossStartLoc, grossEndLoc); +- /* Adjust seed gross delay overflow (greater than 3): +- * - Adjust the trained gross delay to the original seed gross delay. +- */ +- if (pDCTData->WLGrossDelay[index+ByteLane] >= 3) { +- gross += pDCTData->WLGrossDelay[index+ByteLane]; +- if(pDCTData->WLGrossDelay[index+ByteLane] & 1) +- gross -= 1; +- else +- gross -= 2; +- } else if ((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) { +- /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative. +- * We will then round the negative number to 0. ++ ++ if (!is_fam15h()) { ++ /* Adjust seed gross delay overflow (greater than 3): ++ * - Adjust the trained gross delay to the original seed gross delay. + */ +- gross = 0; +- fine = 0; ++ if(pDCTData->WLGrossDelay[index+ByteLane] >= 3) ++ { ++ gross += pDCTData->WLGrossDelay[index+ByteLane]; ++ if(pDCTData->WLGrossDelay[index+ByteLane] & 1) ++ gross -= 1; ++ else ++ gross -= 2; ++ } ++ else if((pDCTData->WLGrossDelay[index+ByteLane] == 0) && (gross == 3)) ++ { ++ /* If seed gross delay is 0 but PRE result gross delay is 3, it is negative. ++ * We will then round the negative number to 0. ++ */ ++ gross = 0; ++ fine = 0; ++ } + } + pDCTData->WLFineDelay[index+ByteLane] = (u8)fine; + pDCTData->WLGrossDelay[index+ByteLane] = (u8)gross; +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c +index 0466c77..cf6afaa 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -134,24 +135,48 @@ static u32 get_Bits(sDCTStruct *pDCTData, + u16 offset, u8 low, u8 high) + { + u32 temp; ++ uint32_t dword; ++ + /* ASSERT(node < MAX_NODES); */ + if (dct == BOTH_DCTS) + { + /* Registers exist on DCT0 only */ ++ if (is_fam15h()) ++ { ++ /* Select DCT 0 */ ++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ dword &= ~0x1; ++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ } ++ + AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); + } + else + { +- if (dct == 1) ++ if (is_fam15h()) + { +- /* Write to dct 1 */ +- offset += 0x100; ++ /* Select DCT */ ++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ ++ /* Read from the selected DCT */ + AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); + } + else + { +- /* Write to dct 0 */ +- AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ if (dct == 1) ++ { ++ /* Read from dct 1 */ ++ offset += 0x100; ++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ } ++ else ++ { ++ /* Read from dct 0 */ ++ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ } + } + } + return temp; +@@ -184,25 +209,49 @@ static void set_Bits(sDCTStruct *pDCTData, + u16 offset, u8 low, u8 high, u32 value) + { + u32 temp; ++ uint32_t dword; ++ + temp = value; + + if (dct == BOTH_DCTS) + { + /* Registers exist on DCT0 only */ ++ if (is_fam15h()) ++ { ++ /* Select DCT 0 */ ++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ dword &= ~0x1; ++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ } ++ + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); + } + else + { +- if (dct == 1) ++ if (is_fam15h()) + { +- /* Write to dct 1 */ +- offset += 0x100; ++ /* Select DCT */ ++ AmdMemPCIRead(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ AmdMemPCIWrite(MAKE_SBDFO(0,0,24+node,1,0x10c), &dword); ++ ++ /* Write to the selected DCT */ + AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); + } + else + { +- /* Write to dct 0 */ +- AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ if (dct == 1) ++ { ++ /* Write to dct 1 */ ++ offset += 0x100; ++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ } ++ else ++ { ++ /* Write to dct 0 */ ++ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp); ++ } + } + } + } +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h +index f846d87..162340e 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h ++++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2010 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -33,7 +34,8 @@ + #define C_MAX_DIMMS 4 /* Maximum Number of DIMMs on each DCT */ + + /* STATUS Definition */ +-#define DCT_STATUS_REGISTERED 3 /* Registered DIMMs support */ ++#define DCT_STATUS_REGISTERED 3 /* Registered DIMMs support */ ++#define DCT_STATUS_LOAD_REDUCED 4 /* Load-Reduced DIMMs support */ + #define DCT_STATUS_OnDimmMirror 24 /* OnDimmMirror support */ + + /* PCI Defintions */ +@@ -78,12 +80,18 @@ + #define SendMrsCmd 26 + #define Qoff 12 + #define MRS_Level 7 +-#define MrsAddressStart 0 +-#define MrsAddressEnd 15 +-#define MrsBankStart 16 +-#define MrsBankEnd 18 +-#define MrsChipSelStart 20 +-#define MrsChipSelEnd 22 ++#define MrsAddressStartFam10 0 ++#define MrsAddressEndFam10 15 ++#define MrsAddressStartFam15 0 ++#define MrsAddressEndFam15 17 ++#define MrsBankStartFam10 16 ++#define MrsBankEndFam10 18 ++#define MrsBankStartFam15 18 ++#define MrsBankEndFam15 20 ++#define MrsChipSelStartFam10 20 ++#define MrsChipSelEndFam10 22 ++#define MrsChipSelStartFam15 21 ++#define MrsChipSelEndFam15 23 + #define ASR 18 + #define SRT 19 + #define DramTermDynStart 10 +@@ -115,10 +123,32 @@ typedef struct _sDCTStruct + u8 DctTrain; /* Current DCT being trained */ + u8 CurrDct; /* Current DCT number (0 or 1) */ + u8 DctCSPresent; /* Current DCT CS mapping */ ++ int32_t WLSeedGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Gross Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ int32_t WLSeedFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Fine Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ int32_t WLSeedPreGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Seed Pre-Gross Delay */ ++ /* per byte Lane Per Logical DIMM*/ + u8 WLGrossDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Gross Delay */ + /* per byte Lane Per Logical DIMM*/ + u8 WLFineDelay[MAX_BYTE_LANES*MAX_LDIMMS]; /* Write Levelization Fine Delay */ + /* per byte Lane Per Logical DIMM*/ ++ u8 WLGrossDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Gross Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ u8 WLFineDelayFirstPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* First-Pass Write Levelization Fine Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ u8 WLGrossDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Previous Pass Write Levelization Gross Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ u8 WLFineDelayPrevPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Previous Pass Write Levelization Fine Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ u8 WLGrossDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Final-Pass Write Levelization Gross Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ u8 WLFineDelayFinalPass[MAX_BYTE_LANES*MAX_LDIMMS]; /* Final-Pass Write Levelization Fine Delay */ ++ /* per byte Lane Per Logical DIMM*/ ++ int32_t WLCriticalGrossDelayFirstPass; ++ int32_t WLCriticalGrossDelayPrevPass; ++ int32_t WLCriticalGrossDelayFinalPass; ++ uint16_t WLPrevMemclkFreq; + u16 RegMan1Present; + u8 DimmPresent[MAX_TOTAL_DIMMS];/* Indicates which DIMMs are present */ + /* from Total Number of DIMMs(per Node)*/ +@@ -132,7 +162,7 @@ typedef struct _sDCTStruct + /* per byte lane */ + u8 MaxDimmsInstalled; /* Max Dimms Installed for current DCT */ + u8 DimmRanks[MAX_TOTAL_DIMMS]; /* Total Number of Ranks(per Dimm) */ +- u32 LogicalCPUID; ++ uint64_t LogicalCPUID; + u8 WLPass; + } sDCTStruct; + +diff --git a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +index c9bcac1..aa23951 100644 +--- a/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c ++++ b/src/northbridge/amd/amdmct/mct_ddr3/s3utils.c +@@ -18,6 +18,7 @@ + */ + + #include <string.h> ++#include <arch/cpu.h> + #include <arch/acpi.h> + #include <cpu/x86/msr.h> + #include <device/device.h> +@@ -32,6 +33,23 @@ + + #define S3NV_FILE_NAME "s3nv" + ++#ifdef __RAMSTAGE__ ++static inline uint8_t is_fam15h(void) ++{ ++ uint8_t fam15h = 0; ++ uint32_t family; ++ ++ family = cpuid_eax(0x80000001); ++ family = ((family & 0xf00000) >> 16) | ((family & 0xf00) >> 8); ++ ++ if (family >= 0x6f) ++ /* Family 15h or later */ ++ fam15h = 1; ++ ++ return fam15h; ++} ++#endif ++ + static ssize_t get_s3nv_file_offset(void); + + ssize_t get_s3nv_file_offset(void) +@@ -47,6 +65,28 @@ ssize_t get_s3nv_file_offset(void) + return s3nv_region.region.offset; + } + ++static uint32_t read_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg) { ++ if (is_fam15h()) { ++ uint32_t dword; ++#ifdef __PRE_RAM__ ++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1); ++#else ++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1)); ++#endif ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ } else { ++ /* Apply offset */ ++ reg += dct * 0x100; ++ } ++ ++ return pci_read_config32(dev, reg); ++} ++ + static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index) + { + uint32_t dword; +@@ -61,12 +101,54 @@ static uint32_t read_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg + return dword; + } + ++static uint32_t read_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index) ++{ ++ if (is_fam15h()) { ++ uint32_t dword; ++#ifdef __PRE_RAM__ ++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1); ++#else ++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1)); ++#endif ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ } else { ++ /* Apply offset */ ++ index_ctl_reg += dct * 0x100; ++ } ++ ++ return read_amd_dct_index_register(dev, index_ctl_reg, index); ++} ++ + #ifdef __RAMSTAGE__ + static uint64_t rdmsr_uint64_t(unsigned long index) { + msr_t msr = rdmsr(index); + return (((uint64_t)msr.hi) << 32) | ((uint64_t)msr.lo); + } + ++static uint32_t read_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg) { ++ uint32_t dword; ++ device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1)); ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ ++ /* Select NB Pstate index */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~(0x3 << 4); ++ dword |= (nb_pstate & 0x3) << 4; ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ ++ return pci_read_config32(dev, reg); ++} ++ + void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_data) + { + uint8_t i; +@@ -82,7 +164,8 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da + device_t dev_fn1 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 1)); + device_t dev_fn2 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 2)); + device_t dev_fn3 = dev_find_slot(0, PCI_DEVFN(0x18 + node, 3)); +- if ((!dev_fn1) || (!dev_fn2) || (!dev_fn3)) { ++ /* Test for node presence */ ++ if ((!dev_fn1) || (pci_read_config32(dev_fn1, PCI_VENDOR_ID) == 0xffffffff)) { + persistent_data->node[node].node_present = 0; + continue; + } +@@ -95,22 +178,22 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da + data->f2x110 = pci_read_config32(dev_fn2, 0x110); + + /* Stage 2 */ +- data->f1x40 = pci_read_config32(dev_fn1, 0x40 + (0x100 * channel)); +- data->f1x44 = pci_read_config32(dev_fn1, 0x44 + (0x100 * channel)); +- data->f1x48 = pci_read_config32(dev_fn1, 0x48 + (0x100 * channel)); +- data->f1x4c = pci_read_config32(dev_fn1, 0x4c + (0x100 * channel)); +- data->f1x50 = pci_read_config32(dev_fn1, 0x50 + (0x100 * channel)); +- data->f1x54 = pci_read_config32(dev_fn1, 0x54 + (0x100 * channel)); +- data->f1x58 = pci_read_config32(dev_fn1, 0x58 + (0x100 * channel)); +- data->f1x5c = pci_read_config32(dev_fn1, 0x5c + (0x100 * channel)); +- data->f1x60 = pci_read_config32(dev_fn1, 0x60 + (0x100 * channel)); +- data->f1x64 = pci_read_config32(dev_fn1, 0x64 + (0x100 * channel)); +- data->f1x68 = pci_read_config32(dev_fn1, 0x68 + (0x100 * channel)); +- data->f1x6c = pci_read_config32(dev_fn1, 0x6c + (0x100 * channel)); +- data->f1x70 = pci_read_config32(dev_fn1, 0x70 + (0x100 * channel)); +- data->f1x74 = pci_read_config32(dev_fn1, 0x74 + (0x100 * channel)); +- data->f1x78 = pci_read_config32(dev_fn1, 0x78 + (0x100 * channel)); +- data->f1x7c = pci_read_config32(dev_fn1, 0x7c + (0x100 * channel)); ++ data->f1x40 = read_config32_dct(dev_fn1, node, channel, 0x40); ++ data->f1x44 = read_config32_dct(dev_fn1, node, channel, 0x44); ++ data->f1x48 = read_config32_dct(dev_fn1, node, channel, 0x48); ++ data->f1x4c = read_config32_dct(dev_fn1, node, channel, 0x4c); ++ data->f1x50 = read_config32_dct(dev_fn1, node, channel, 0x50); ++ data->f1x54 = read_config32_dct(dev_fn1, node, channel, 0x54); ++ data->f1x58 = read_config32_dct(dev_fn1, node, channel, 0x58); ++ data->f1x5c = read_config32_dct(dev_fn1, node, channel, 0x5c); ++ data->f1x60 = read_config32_dct(dev_fn1, node, channel, 0x60); ++ data->f1x64 = read_config32_dct(dev_fn1, node, channel, 0x64); ++ data->f1x68 = read_config32_dct(dev_fn1, node, channel, 0x68); ++ data->f1x6c = read_config32_dct(dev_fn1, node, channel, 0x6c); ++ data->f1x70 = read_config32_dct(dev_fn1, node, channel, 0x70); ++ data->f1x74 = read_config32_dct(dev_fn1, node, channel, 0x74); ++ data->f1x78 = read_config32_dct(dev_fn1, node, channel, 0x78); ++ data->f1x7c = read_config32_dct(dev_fn1, node, channel, 0x7c); + data->f1xf0 = pci_read_config32(dev_fn1, 0xf0); + data->f1x120 = pci_read_config32(dev_fn1, 0x120); + data->f1x124 = pci_read_config32(dev_fn1, 0x124); +@@ -134,75 +217,144 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da + data->msrc001001f = rdmsr_uint64_t(0xc001001f); + + /* Stage 3 */ +- data->f2x40 = pci_read_config32(dev_fn2, 0x40 + (0x100 * channel)); +- data->f2x44 = pci_read_config32(dev_fn2, 0x44 + (0x100 * channel)); +- data->f2x48 = pci_read_config32(dev_fn2, 0x48 + (0x100 * channel)); +- data->f2x4c = pci_read_config32(dev_fn2, 0x4c + (0x100 * channel)); +- data->f2x50 = pci_read_config32(dev_fn2, 0x50 + (0x100 * channel)); +- data->f2x54 = pci_read_config32(dev_fn2, 0x54 + (0x100 * channel)); +- data->f2x58 = pci_read_config32(dev_fn2, 0x58 + (0x100 * channel)); +- data->f2x5c = pci_read_config32(dev_fn2, 0x5c + (0x100 * channel)); +- data->f2x60 = pci_read_config32(dev_fn2, 0x60 + (0x100 * channel)); +- data->f2x64 = pci_read_config32(dev_fn2, 0x64 + (0x100 * channel)); +- data->f2x68 = pci_read_config32(dev_fn2, 0x68 + (0x100 * channel)); +- data->f2x6c = pci_read_config32(dev_fn2, 0x6c + (0x100 * channel)); +- data->f2x78 = pci_read_config32(dev_fn2, 0x78 + (0x100 * channel)); +- data->f2x7c = pci_read_config32(dev_fn2, 0x7c + (0x100 * channel)); +- data->f2x80 = pci_read_config32(dev_fn2, 0x80 + (0x100 * channel)); +- data->f2x84 = pci_read_config32(dev_fn2, 0x84 + (0x100 * channel)); +- data->f2x88 = pci_read_config32(dev_fn2, 0x88 + (0x100 * channel)); +- data->f2x8c = pci_read_config32(dev_fn2, 0x8c + (0x100 * channel)); +- data->f2x90 = pci_read_config32(dev_fn2, 0x90 + (0x100 * channel)); +- data->f2xa4 = pci_read_config32(dev_fn2, 0xa4 + (0x100 * channel)); +- data->f2xa8 = pci_read_config32(dev_fn2, 0xa8 + (0x100 * channel)); ++ data->f2x40 = read_config32_dct(dev_fn2, node, channel, 0x40); ++ data->f2x44 = read_config32_dct(dev_fn2, node, channel, 0x44); ++ data->f2x48 = read_config32_dct(dev_fn2, node, channel, 0x48); ++ data->f2x4c = read_config32_dct(dev_fn2, node, channel, 0x4c); ++ data->f2x50 = read_config32_dct(dev_fn2, node, channel, 0x50); ++ data->f2x54 = read_config32_dct(dev_fn2, node, channel, 0x54); ++ data->f2x58 = read_config32_dct(dev_fn2, node, channel, 0x58); ++ data->f2x5c = read_config32_dct(dev_fn2, node, channel, 0x5c); ++ data->f2x60 = read_config32_dct(dev_fn2, node, channel, 0x60); ++ data->f2x64 = read_config32_dct(dev_fn2, node, channel, 0x64); ++ data->f2x68 = read_config32_dct(dev_fn2, node, channel, 0x68); ++ data->f2x6c = read_config32_dct(dev_fn2, node, channel, 0x6c); ++ data->f2x78 = read_config32_dct(dev_fn2, node, channel, 0x78); ++ data->f2x7c = read_config32_dct(dev_fn2, node, channel, 0x7c); ++ data->f2x80 = read_config32_dct(dev_fn2, node, channel, 0x80); ++ data->f2x84 = read_config32_dct(dev_fn2, node, channel, 0x84); ++ data->f2x88 = read_config32_dct(dev_fn2, node, channel, 0x88); ++ data->f2x8c = read_config32_dct(dev_fn2, node, channel, 0x8c); ++ data->f2x90 = read_config32_dct(dev_fn2, node, channel, 0x90); ++ data->f2xa4 = read_config32_dct(dev_fn2, node, channel, 0xa4); ++ data->f2xa8 = read_config32_dct(dev_fn2, node, channel, 0xa8); ++ ++ /* Family 15h-specific configuration */ ++ if (is_fam15h()) { ++ data->f2x200 = read_config32_dct(dev_fn2, node, channel, 0x200); ++ data->f2x204 = read_config32_dct(dev_fn2, node, channel, 0x204); ++ data->f2x208 = read_config32_dct(dev_fn2, node, channel, 0x208); ++ data->f2x20c = read_config32_dct(dev_fn2, node, channel, 0x20c); ++ for (i=0; i<4; i++) ++ data->f2x210[i] = read_config32_dct_nbpstate(dev_fn2, node, channel, i, 0x210); ++ data->f2x214 = read_config32_dct(dev_fn2, node, channel, 0x214); ++ data->f2x218 = read_config32_dct(dev_fn2, node, channel, 0x218); ++ data->f2x21c = read_config32_dct(dev_fn2, node, channel, 0x21c); ++ data->f2x22c = read_config32_dct(dev_fn2, node, channel, 0x22c); ++ data->f2x230 = read_config32_dct(dev_fn2, node, channel, 0x230); ++ data->f2x234 = read_config32_dct(dev_fn2, node, channel, 0x234); ++ data->f2x238 = read_config32_dct(dev_fn2, node, channel, 0x238); ++ data->f2x23c = read_config32_dct(dev_fn2, node, channel, 0x23c); ++ data->f2x240 = read_config32_dct(dev_fn2, node, channel, 0x240); ++ ++ data->f2x9cx0d0fe003 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe003); ++ data->f2x9cx0d0fe013 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe013); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_8_0_1f[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f001f | (i << 8)); ++ data->f2x9cx0d0f201f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f201f); ++ data->f2x9cx0d0f211f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f211f); ++ data->f2x9cx0d0f221f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f221f); ++ data->f2x9cx0d0f801f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f801f); ++ data->f2x9cx0d0f811f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f811f); ++ data->f2x9cx0d0f821f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f821f); ++ data->f2x9cx0d0fc01f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc01f); ++ data->f2x9cx0d0fc11f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc11f); ++ data->f2x9cx0d0fc21f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc21f); ++ data->f2x9cx0d0f4009 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f4009); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_8_0_02[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0002 | (i << 8)); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_8_0_06[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0006 | (i << 8)); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_8_0_0a[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f000a | (i << 8)); ++ ++ data->f2x9cx0d0f2002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2002); ++ data->f2x9cx0d0f2102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2102); ++ data->f2x9cx0d0f2202 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2202); ++ data->f2x9cx0d0f8002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8002); ++ data->f2x9cx0d0f8006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8006); ++ data->f2x9cx0d0f800a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f800a); ++ data->f2x9cx0d0f8102 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8102); ++ data->f2x9cx0d0f8106 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8106); ++ data->f2x9cx0d0f810a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f810a); ++ data->f2x9cx0d0fc002 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc002); ++ data->f2x9cx0d0fc006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc006); ++ data->f2x9cx0d0fc00a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00a); ++ data->f2x9cx0d0fc00e = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc00e); ++ data->f2x9cx0d0fc012 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc012); ++ ++ data->f2x9cx0d0f2031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2031); ++ data->f2x9cx0d0f2131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2131); ++ data->f2x9cx0d0f2231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2231); ++ data->f2x9cx0d0f8031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8031); ++ data->f2x9cx0d0f8131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8131); ++ data->f2x9cx0d0f8231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8231); ++ data->f2x9cx0d0fc031 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc031); ++ data->f2x9cx0d0fc131 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc131); ++ data->f2x9cx0d0fc231 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fc231); ++ for (i=0; i<9; i++) ++ data->f2x9cx0d0f0_0_f_31[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0031 | (i << 8)); ++ ++ data->f2x9cx0d0f8021 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f8021); ++ } + + /* Stage 4 */ +- data->f2x94 = pci_read_config32(dev_fn2, 0x94 + (0x100 * channel)); ++ data->f2x94 = read_config32_dct(dev_fn2, node, channel, 0x94); + + /* Stage 6 */ + for (i=0; i<9; i++) + for (j=0; j<3; j++) +- data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4)); +- data->f2x9cx00 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x00); +- data->f2x9cx0a = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0a); +- data->f2x9cx0c = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0c); ++ data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4)); ++ data->f2x9cx00 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x00); ++ data->f2x9cx0a = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0a); ++ data->f2x9cx0c = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0c); + + /* Stage 7 */ +- data->f2x9cx04 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x04); ++ data->f2x9cx04 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x04); + + /* Stage 9 */ +- data->f2x9cx0d0fe006 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe006); +- data->f2x9cx0d0fe007 = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0fe007); ++ data->f2x9cx0d0fe006 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe006); ++ data->f2x9cx0d0fe007 = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0fe007); + + /* Stage 10 */ + for (i=0; i<12; i++) +- data->f2x9cx10[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x10 + i); ++ data->f2x9cx10[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x10 + i); + for (i=0; i<12; i++) +- data->f2x9cx20[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x20 + i); ++ data->f2x9cx20[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x20 + i); + for (i=0; i<4; i++) + for (j=0; j<3; j++) +- data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j)); ++ data->f2x9cx3_0_0_3_1[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x01 + i) + (0x100 * j)); + for (i=0; i<4; i++) + for (j=0; j<3; j++) +- data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j)); +- data->f2x9cx0d = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d); ++ data->f2x9cx3_0_0_7_5[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, (0x05 + i) + (0x100 * j)); ++ data->f2x9cx0d = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d); + for (i=0; i<9; i++) +- data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8)); ++ data->f2x9cx0d0f0_f_0_13[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0013 | (i << 8)); + for (i=0; i<9; i++) +- data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8)); ++ data->f2x9cx0d0f0_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0030 | (i << 8)); + for (i=0; i<4; i++) +- data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8)); ++ data->f2x9cx0d0f2_f_0_30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f2030 | (i << 8)); + for (i=0; i<2; i++) + for (j=0; j<3; j++) +- data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4)); +- data->f2x9cx0d0f812f = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x0d0f812f); ++ data->f2x9cx0d0f8_8_4_0[i][j] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4)); ++ data->f2x9cx0d0f812f = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x0d0f812f); + + /* Stage 11 */ + if (IS_ENABLED(CONFIG_DIMM_DDR3)) { + for (i=0; i<12; i++) +- data->f2x9cx30[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x30 + i); ++ data->f2x9cx30[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x30 + i); + for (i=0; i<12; i++) +- data->f2x9cx40[i] = read_amd_dct_index_register(dev_fn2, 0x98 + (0x100 * channel), 0x40 + i); ++ data->f2x9cx40[i] = read_amd_dct_index_register_dct(dev_fn2, node, channel, 0x98, 0x40 + i); + } + + /* Other */ +@@ -212,6 +364,43 @@ void copy_mct_data_to_save_variable(struct amd_s3_persistent_data* persistent_da + } + } + #else ++static void write_config32_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t reg, uint32_t value) { ++ if (is_fam15h()) { ++ uint32_t dword; ++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1); ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ } else { ++ /* Apply offset */ ++ reg += dct * 0x100; ++ } ++ ++ pci_write_config32(dev, reg, value); ++} ++ ++static void write_config32_dct_nbpstate(device_t dev, uint8_t node, uint8_t dct, uint8_t nb_pstate, uint32_t reg, uint32_t value) { ++ uint32_t dword; ++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1); ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ ++ /* Select NB Pstate index */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~(0x3 << 4); ++ dword |= (nb_pstate & 0x3) << 4; ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ ++ pci_write_config32(dev, reg, value); ++} ++ + static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, uint32_t index, uint32_t value) + { + uint32_t dword; +@@ -223,6 +412,25 @@ static void write_amd_dct_index_register(device_t dev, uint32_t index_ctl_reg, u + dword = pci_read_config32(dev, index_ctl_reg); + } while (!(dword & (1 << 31))); + } ++ ++static void write_amd_dct_index_register_dct(device_t dev, uint8_t node, uint8_t dct, uint32_t index_ctl_reg, uint32_t index, uint32_t value) ++{ ++ if (is_fam15h()) { ++ uint32_t dword; ++ device_t dev_fn1 = PCI_DEV(0, 0x18 + node, 1); ++ ++ /* Select DCT */ ++ dword = pci_read_config32(dev_fn1, 0x10c); ++ dword &= ~0x1; ++ dword |= (dct & 0x1); ++ pci_write_config32(dev_fn1, 0x10c, dword); ++ } else { ++ /* Apply offset */ ++ index_ctl_reg += dct * 0x100; ++ } ++ ++ return write_amd_dct_index_register(dev, index_ctl_reg, index, value); ++} + #endif + + #ifdef __PRE_RAM__ +@@ -262,31 +470,31 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!persistent_data->node[node].node_present) + continue; + +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x40 + (0x100 * channel), data->f1x40); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x44 + (0x100 * channel), data->f1x44); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x48 + (0x100 * channel), data->f1x48); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x4c + (0x100 * channel), data->f1x4c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x50 + (0x100 * channel), data->f1x50); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x54 + (0x100 * channel), data->f1x54); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x58 + (0x100 * channel), data->f1x58); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x5c + (0x100 * channel), data->f1x5c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x60 + (0x100 * channel), data->f1x60); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x64 + (0x100 * channel), data->f1x64); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x68 + (0x100 * channel), data->f1x68); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x6c + (0x100 * channel), data->f1x6c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x70 + (0x100 * channel), data->f1x70); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x74 + (0x100 * channel), data->f1x74); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x78 + (0x100 * channel), data->f1x78); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x7c + (0x100 * channel), data->f1x7c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0xf0 + (0x100 * channel), data->f1xf0); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x120 + (0x100 * channel), data->f1x120); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 1), 0x124 + (0x100 * channel), data->f1x124); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x10c + (0x100 * channel), data->f2x10c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x114 + (0x100 * channel), data->f2x114); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x118 + (0x100 * channel), data->f2x118); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x11c + (0x100 * channel), data->f2x11c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x1b0 + (0x100 * channel), data->f2x1b0); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 3), 0x44 + (0x100 * channel), data->f3x44); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x40, data->f1x40); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x44, data->f1x44); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x48, data->f1x48); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x4c, data->f1x4c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x50, data->f1x50); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x54, data->f1x54); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x58, data->f1x58); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x5c, data->f1x5c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x60, data->f1x60); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x64, data->f1x64); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x68, data->f1x68); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x6c, data->f1x6c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x70, data->f1x70); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x74, data->f1x74); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x78, data->f1x78); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x7c, data->f1x7c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0xf0, data->f1xf0); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x120, data->f1x120); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 1), node, channel, 0x124, data->f1x124); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x10c, data->f2x10c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x114, data->f2x114); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x118, data->f2x118); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x11c, data->f2x11c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x1b0, data->f2x1b0); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 3), node, channel, 0x44, data->f3x44); + for (i=0; i<16; i++) { + wrmsr_uint64_t(0x00000200 | i, data->msr0000020[i]); + } +@@ -313,31 +521,97 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!persistent_data->node[node].node_present) + continue; + +- ganged = !!(data->f2x110 & 0x10); ++ if (is_fam15h()) ++ ganged = 0; ++ else ++ ganged = !!(data->f2x110 & 0x10); + if ((ganged == 1) && (channel > 0)) + continue; + +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x40 + (0x100 * channel), data->f2x40); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x44 + (0x100 * channel), data->f2x44); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x48 + (0x100 * channel), data->f2x48); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x4c + (0x100 * channel), data->f2x4c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x50 + (0x100 * channel), data->f2x50); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x54 + (0x100 * channel), data->f2x54); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x58 + (0x100 * channel), data->f2x58); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x5c + (0x100 * channel), data->f2x5c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x60 + (0x100 * channel), data->f2x60); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x64 + (0x100 * channel), data->f2x64); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x68 + (0x100 * channel), data->f2x68); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x6c + (0x100 * channel), data->f2x6c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x78 + (0x100 * channel), data->f2x78); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x7c + (0x100 * channel), data->f2x7c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x80 + (0x100 * channel), data->f2x80); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x84 + (0x100 * channel), data->f2x84); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x88 + (0x100 * channel), data->f2x88); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x8c + (0x100 * channel), data->f2x8c); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), data->f2x90); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa4 + (0x100 * channel), data->f2xa4); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0xa8 + (0x100 * channel), data->f2xa8); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x40, data->f2x40); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x44, data->f2x44); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x48, data->f2x48); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x4c, data->f2x4c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x50, data->f2x50); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x54, data->f2x54); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x58, data->f2x58); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x5c, data->f2x5c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x60, data->f2x60); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x64, data->f2x64); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x68, data->f2x68); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x6c, data->f2x6c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x78, data->f2x78); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x7c, data->f2x7c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x80, data->f2x80); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x84, data->f2x84); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x88, data->f2x88); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x8c, data->f2x8c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, data->f2x90); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa4, data->f2xa4); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0xa8, data->f2xa8); ++ } ++ } ++ ++ /* Family 15h-specific configuration */ ++ if (is_fam15h()) { ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ /* Initialize DCT */ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0000000b, 0x80000000); ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013); ++ dword &= ~0xffff; ++ dword |= 0x118; ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, dword); ++ ++ /* Restore values */ ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x200, data->f2x200); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x204, data->f2x204); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x208, data->f2x208); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x20c, data->f2x20c); ++ for (i=0; i<4; i++) ++ write_config32_dct_nbpstate(PCI_DEV(0, 0x18 + node, 2), node, channel, i, 0x210, data->f2x210[i]); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x214, data->f2x214); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x218, data->f2x218); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x21c, data->f2x21c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x22c, data->f2x22c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x230, data->f2x230); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x234, data->f2x234); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x238, data->f2x238); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x23c, data->f2x23c); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x240, data->f2x240); ++ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe013, data->f2x9cx0d0fe013); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f001f | (i << 8), data->f2x9cx0d0f0_8_0_1f[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f201f, data->f2x9cx0d0f201f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f211f, data->f2x9cx0d0f211f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f221f, data->f2x9cx0d0f221f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f801f, data->f2x9cx0d0f801f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f811f, data->f2x9cx0d0f811f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f821f, data->f2x9cx0d0f821f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc01f, data->f2x9cx0d0fc01f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc11f, data->f2x9cx0d0fc11f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc21f, data->f2x9cx0d0fc21f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f4009, data->f2x9cx0d0f4009); ++ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2031, data->f2x9cx0d0f2031); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2131, data->f2x9cx0d0f2131); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2231, data->f2x9cx0d0f2231); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8031, data->f2x9cx0d0f8031); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8131, data->f2x9cx0d0f8131); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8231, data->f2x9cx0d0f8231); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc031, data->f2x9cx0d0fc031); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc131, data->f2x9cx0d0fc131); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc231, data->f2x9cx0d0fc231); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0031 | (i << 8), data->f2x9cx0d0f0_0_f_31[i]); ++ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8021, data->f2x9cx0d0f8021); ++ } + } + } + +@@ -348,33 +622,44 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!persistent_data->node[node].node_present) + continue; + +- ganged = !!(data->f2x110 & 0x10); ++ if (is_fam15h()) ++ ganged = 0; ++ else ++ ganged = !!(data->f2x110 & 0x10); + if ((ganged == 1) && (channel > 0)) + continue; + +- /* Disable PHY auto-compensation engine */ +- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); +- if (!(dword & (1 << 30))) { +- dword |= (1 << 30); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword); +- +- /* Wait for 5us */ +- mct_Wait(100); ++ if (is_fam15h()) { ++ /* Program PllLockTime = 0x190 */ ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006); ++ dword &= ~0xffff; ++ dword |= 0x190; ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword); ++ ++ /* Program MemClkFreqVal = 0 */ ++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94); ++ dword &= (0x1 << 7); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, dword); ++ ++ /* Restore DRAM Adddress/Timing Control Register */ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04); ++ } else { ++ /* Disable PHY auto-compensation engine */ ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08); ++ if (!(dword & (1 << 30))) { ++ dword |= (1 << 30); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword); ++ ++ /* Wait for 5us */ ++ mct_Wait(100); ++ } + } + + /* Restore DRAM Configuration High Register */ +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x94 + (0x100 * channel), data->f2x94); +- +- /* Enable PHY auto-compensation engine */ +- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); +- dword &= ~(1 << 30); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08, dword); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94, data->f2x94); + } + } + +- /* Wait for 750us */ +- mct_Wait(15000); +- + /* Stage 5 */ + for (node = 0; node < MAX_NODES_SUPPORTED; node++) { + for (channel = 0; channel < 2; channel++) { +@@ -382,17 +667,40 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!persistent_data->node[node].node_present) + continue; + +- ganged = !!(data->f2x110 & 0x10); ++ if (is_fam15h()) ++ ganged = 0; ++ else ++ ganged = !!(data->f2x110 & 0x10); + if ((ganged == 1) && (channel > 0)) + continue; + ++ dct_enabled = !(data->f2x94 & (1 << 14)); ++ if (!dct_enabled) ++ continue; ++ + /* Wait for any pending PHY frequency changes to complete */ + do { +- dword = read_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x08); ++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x94); + } while (dword & (1 << 21)); ++ ++ if (is_fam15h()) { ++ /* Program PllLockTime = 0xf */ ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006); ++ dword &= ~0xffff; ++ dword |= 0xf; ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, dword); ++ } else { ++ /* Enable PHY auto-compensation engine */ ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08); ++ dword &= ~(1 << 30); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x08, dword); ++ } + } + } + ++ /* Wait for 750us */ ++ mct_Wait(15000); ++ + /* Stage 6 */ + for (node = 0; node < MAX_NODES_SUPPORTED; node++) { + for (channel = 0; channel < 2; channel++) { +@@ -402,10 +710,49 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + + for (i=0; i<9; i++) + for (j=0; j<3; j++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x00, data->f2x9cx00); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0a, data->f2x9cx0a); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0c, data->f2x9cx0c); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f0_f_8_0_0_8_4_0[i][j]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x00, data->f2x9cx00); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0a, data->f2x9cx0a); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0c, data->f2x9cx0c); ++ } ++ } ++ ++ /* Family 15h-specific configuration */ ++ if (is_fam15h()) { ++ for (node = 0; node < MAX_NODES_SUPPORTED; node++) { ++ for (channel = 0; channel < 2; channel++) { ++ struct amd_s3_persistent_mct_channel_data* data = &persistent_data->node[node].channel[channel]; ++ if (!persistent_data->node[node].node_present) ++ continue; ++ ++ dword = read_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003); ++ dword |= (0x3 << 13); /* DisAutoComp, DisablePredriverCal = 1 */ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, dword); ++ ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0006 | (i << 8), data->f2x9cx0d0f0_8_0_06[i]); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f000a | (i << 8), data->f2x9cx0d0f0_8_0_0a[i]); ++ for (i=0; i<9; i++) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0002 | (i << 8), (0x8000 | data->f2x9cx0d0f0_8_0_02[i])); ++ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8006, data->f2x9cx0d0f8006); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f800a, data->f2x9cx0d0f800a); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8106, data->f2x9cx0d0f8106); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f810a, data->f2x9cx0d0f810a); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc006, data->f2x9cx0d0fc006); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00a, data->f2x9cx0d0fc00a); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc00e, data->f2x9cx0d0fc00e); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc012, data->f2x9cx0d0fc012); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8002, (0x8000 | data->f2x9cx0d0f8002)); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f8102, (0x8000 | data->f2x9cx0d0f8102)); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fc002, (0x8000 | data->f2x9cx0d0fc002)); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2002, (0x8000 | data->f2x9cx0d0f2002)); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2102, (0x8000 | data->f2x9cx0d0f2102)); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2202, (0x8000 | data->f2x9cx0d0f2202)); ++ ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe003, data->f2x9cx0d0fe003); ++ } + } + } + +@@ -416,11 +763,15 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!persistent_data->node[node].node_present) + continue; + +- ganged = !!(data->f2x110 & 0x10); ++ if (is_fam15h()) ++ ganged = 0; ++ else ++ ganged = !!(data->f2x110 & 0x10); + if ((ganged == 1) && (channel > 0)) + continue; + +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x04, data->f2x9cx04); ++ if (!is_fam15h()) ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x04, data->f2x9cx04); + } + } + +@@ -435,16 +786,19 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + if (!dct_enabled) + continue; + +- ganged = !!(data->f2x110 & 0x10); ++ if (is_fam15h()) ++ ganged = 0; ++ else ++ ganged = !!(data->f2x110 & 0x10); + if ((ganged == 1) && (channel > 0)) + continue; + + printk(BIOS_SPEW, "Taking DIMMs out of self refresh node: %d channel: %d\n", node, channel); + + /* Exit self refresh mode */ +- dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel)); ++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90); + dword |= (1 << 1); +- pci_write_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel), dword); ++ write_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90, dword); + } + } + +@@ -463,12 +817,12 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + + /* Wait for transition from self refresh mode to complete */ + do { +- dword = pci_read_config32(PCI_DEV(0, 0x18 + node, 2), 0x90 + (0x100 * channel)); ++ dword = read_config32_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x90); + } while (dword & (1 << 1)); + + /* Restore registers */ +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe006, data->f2x9cx0d0fe006); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0fe007, data->f2x9cx0d0fe007); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe006, data->f2x9cx0d0fe006); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0fe007, data->f2x9cx0d0fe007); + } + } + +@@ -480,26 +834,26 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + continue; + + for (i=0; i<12; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x10 + i, data->f2x9cx10[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x10 + i, data->f2x9cx10[i]); + for (i=0; i<12; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x20 + i, data->f2x9cx20[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x20 + i, data->f2x9cx20[i]); + for (i=0; i<4; i++) + for (j=0; j<3; j++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x01 + i) + (0x100 * j), data->f2x9cx3_0_0_3_1[i][j]); + for (i=0; i<4; i++) + for (j=0; j<3; j++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d, data->f2x9cx0d); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, (0x05 + i) + (0x100 * j), data->f2x9cx3_0_0_7_5[i][j]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d, data->f2x9cx0d); + for (i=0; i<9; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0013 | (i << 8), data->f2x9cx0d0f0_f_0_13[i]); + for (i=0; i<9; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0030 | (i << 8), data->f2x9cx0d0f0_f_0_30[i]); + for (i=0; i<4; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f2030 | (i << 8), data->f2x9cx0d0f2_f_0_30[i]); + for (i=0; i<2; i++) + for (j=0; j<3; j++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]); +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x0d0f812f, data->f2x9cx0d0f812f); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f0000 | (i << 8) | (j * 4), data->f2x9cx0d0f8_8_4_0[i][j]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x0d0f812f, data->f2x9cx0d0f812f); + } + } + +@@ -512,9 +866,9 @@ void restore_mct_data_from_save_variable(struct amd_s3_persistent_data* persiste + continue; + + for (i=0; i<12; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x30 + i, data->f2x9cx30[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x30 + i, data->f2x9cx30[i]); + for (i=0; i<12; i++) +- write_amd_dct_index_register(PCI_DEV(0, 0x18 + node, 2), 0x98 + (0x100 * channel), 0x40 + i, data->f2x9cx40[i]); ++ write_amd_dct_index_register_dct(PCI_DEV(0, 0x18 + node, 2), node, channel, 0x98, 0x40 + i, data->f2x9cx40[i]); + } + } + } +diff --git a/src/northbridge/amd/amdmct/wrappers/mcti.h b/src/northbridge/amd/amdmct/wrappers/mcti.h +index 38e66e1..2aba377 100644 +--- a/src/northbridge/amd/amdmct/wrappers/mcti.h ++++ b/src/northbridge/amd/amdmct/wrappers/mcti.h +@@ -2,6 +2,7 @@ + * This file is part of the coreboot project. + * + * Copyright (C) 2007 Advanced Micro Devices, Inc. ++ * Copyright (C) 2015 Timothy Pearson <tpearson@raptorengineeringinc.com>, Raptor Engineering + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by +@@ -62,10 +63,15 @@ UPDATE AS NEEDED + #endif + + #ifndef MEM_MAX_LOAD_FREQ +-#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */ +- #define MEM_MAX_LOAD_FREQ 800 +-#else +- #define MEM_MAX_LOAD_FREQ 400 ++#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */ ++ #define MEM_MAX_LOAD_FREQ 933 ++ #define MEM_MIN_PLATFORM_FREQ_FAM10 400 ++ #define MEM_MIN_PLATFORM_FREQ_FAM15 333 ++#else /* AMD_FAM10_DDR2 */ ++ #define MEM_MAX_LOAD_FREQ 400 ++ #define MEM_MIN_PLATFORM_FREQ_FAM10 200 ++ /* DDR2 not available on Family 15h */ ++ #define MEM_MIN_PLATFORM_FREQ_FAM15 0 + #endif + #endif + +diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c +index 47260f2..1d4eade 100644 +--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c ++++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c +@@ -44,7 +44,7 @@ + #define MINIMUM_DRAM_BELOW_4G 0x1000000 + + static const uint16_t ddr2_limits[4] = {400, 333, 266, 200}; +-static const uint16_t ddr3_limits[4] = {800, 666, 533, 400}; ++static const uint16_t ddr3_limits[16] = {933, 800, 666, 533, 400, 333, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + static u16 mctGet_NVbits(u8 index) + { +@@ -81,12 +81,19 @@ static u16 mctGet_NVbits(u8 index) + if (get_option(&nvram, "max_mem_clock") == CB_SUCCESS) { + int limit = val; + if (IS_ENABLED(CONFIG_DIMM_DDR3)) +- limit = ddr3_limits[nvram & 3]; ++ limit = ddr3_limits[nvram & 0xf]; + else if (IS_ENABLED(CONFIG_DIMM_DDR2)) +- limit = ddr2_limits[nvram & 3]; ++ limit = ddr2_limits[nvram & 0x3]; + val = min(limit, val); + } + break; ++ case NV_MIN_MEMCLK: ++ /* Minimum platform supported memclk */ ++ if (is_fam15h()) ++ val = MEM_MIN_PLATFORM_FREQ_FAM15; ++ else ++ val = MEM_MIN_PLATFORM_FREQ_FAM10; ++ break; + case NV_ECC_CAP: + #if SYSTEM_TYPE == SERVER + val = 1; /* memory bus ECC capable */ +@@ -254,6 +261,9 @@ static u16 mctGet_NVbits(u8 index) + case NV_L2BKScrub: + val = 0; /* Disabled - See L2Scrub in BKDG */ + break; ++ case NV_L3BKScrub: ++ val = 0; /* Disabled - See L3Scrub in BKDG */ ++ break; + case NV_DCBKScrub: + val = 0; /* Disabled - See DcacheScrub in BKDG */ + break; +@@ -299,10 +309,14 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat) + pDCTstat->PresetmaxFreq = mctGet_NVbits(NV_MAX_MEMCLK); + + /* Determine the number of installed DIMMs */ ++ uint8_t dimm; + int ch1_count = 0; + int ch2_count = 0; + uint8_t ch1_registered = 0; + uint8_t ch2_registered = 0; ++ uint8_t ch1_voltage = 0; ++ uint8_t ch2_voltage = 0; ++ uint8_t highest_rank_count[2]; + int i; + for (i = 0; i < 15; i = i + 2) { + if (pDCTstat->DIMMValid & (1 << i)) +@@ -321,8 +335,26 @@ static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat) + printk(BIOS_DEBUG, "mctGet_MaxLoadFreq: Channel 2: %d DIMM(s) detected\n", ch2_count); + } + ++#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */ ++ for (i = 0; i < 15; i = i + 2) { ++ if (pDCTstat->DIMMValid & (1 << i)) ++ ch1_voltage |= pDCTstat->DimmConfiguredVoltage[i]; ++ if (pDCTstat->DIMMValid & (1 << (i + 1))) ++ ch2_voltage |= pDCTstat->DimmConfiguredVoltage[i + 1]; ++ } ++#endif ++ ++ for (i = 0; i < 2; i++) { ++ sDCTStruct *pDCTData = pDCTstat->C_DCTPtr[i]; ++ highest_rank_count[i] = 0x0; ++ for (dimm = 0; dimm < 8; dimm++) { ++ if (pDCTData->DimmRanks[dimm] > highest_rank_count[i]) ++ highest_rank_count[i] = pDCTData->DimmRanks[dimm]; ++ } ++ } ++ + /* Set limits if needed */ +- pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), (ch1_registered || ch2_registered), pDCTstat->PresetmaxFreq); ++ pDCTstat->PresetmaxFreq = mct_MaxLoadFreq(max(ch1_count, ch2_count), max(highest_rank_count[0], highest_rank_count[1]), (ch1_registered || ch2_registered), (ch1_voltage | ch2_voltage), pDCTstat->PresetmaxFreq); + } + + #ifdef UNUSED_CODE +@@ -486,7 +518,7 @@ static void mctHookAfterAnyTraining(void) + { + } + +-static u32 mctGetLogicalCPUID_D(u8 node) ++static uint64_t mctGetLogicalCPUID_D(u8 node) + { + return mctGetLogicalCPUID(node); + } +-- +1.9.1 + |