aboutsummaryrefslogtreecommitdiff
path: root/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch')
-rw-r--r--resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch948
1 files changed, 948 insertions, 0 deletions
diff --git a/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch b/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch
new file mode 100644
index 00000000..22353241
--- /dev/null
+++ b/resources/libreboot/patch/kgpe-d16/0089-cpu-amd-model_10xxx-Fix-Family-15h-multiple-package-.patch
@@ -0,0 +1,948 @@
+From 37de124d5fb295bd2d39201a9c3a0a0805a38327 Mon Sep 17 00:00:00 2001
+From: Timothy Pearson <kb9vqf@pearsoncomputing.net>
+Date: Thu, 30 Jul 2015 14:07:15 -0500
+Subject: [PATCH 089/146] cpu/amd/model_10xxx: Fix Family 15h multiple package
+ support
+
+TEST: Booted ASUS KGPE-D16 with two Opteron 6328 processors
+and several different RDIMM configurations.
+---
+ src/cpu/amd/car/cache_as_ram.inc | 17 +++-
+ src/cpu/amd/model_10xxx/defaults.h | 101 ++++++++++++++++---
+ src/cpu/amd/model_10xxx/fidvid.c | 81 +++++++--------
+ src/cpu/amd/model_10xxx/init_cpus.c | 66 +++++++++++--
+ src/cpu/amd/quadcore/quadcore.c | 19 +---
+ src/cpu/amd/quadcore/quadcore_id.c | 1 -
+ src/mainboard/asus/kgpe-d16/romstage.c | 46 +++++++--
+ src/northbridge/amd/amdht/h3finit.c | 57 ++++++++++-
+ src/northbridge/amd/amdht/h3ncmn.c | 30 +++++-
+ src/northbridge/amd/amdht/ht_wrapper.c | 141 +++++++++++++++++++++++++--
+ src/northbridge/amd/amdmct/mct_ddr3/mct_d.c | 1 +
+ 11 files changed, 458 insertions(+), 102 deletions(-)
+
+diff --git a/src/cpu/amd/car/cache_as_ram.inc b/src/cpu/amd/car/cache_as_ram.inc
+index 9a51e3c..7d71171 100644
+--- a/src/cpu/amd/car/cache_as_ram.inc
++++ b/src/cpu/amd/car/cache_as_ram.inc
+@@ -525,8 +525,23 @@ CAR_FAM10_ap:
+ /* Fam10h NB config bit 54 was not set */
+ rolb %cl, %bl
+ roll_cfg:
++ jmp_if_not_fam15h(ap_apicid_ready)
++ cmp $0x5, %ecx
++ jne ap_apicid_ready
+
+- /* Calculate stack pointer. */
++ /* This is a multi-node CPU
++ * Adjust the maximum APIC ID to a more reasonable value
++ * given that no 32-core Family 15h processors exist
++ */
++ movl %ebx, %ecx
++ and $0x0f, %ecx /* Get lower 4 bits of CPU number */
++ and $0x60, %ebx /* Get node ID */
++ shrl $0x1, %ebx /* Shift node ID part of APIC ID down by 1 */
++ or %ecx, %ebx /* Recombine node ID and CPU number */
++
++ap_apicid_ready:
++
++ /* Calculate stack pointer using adjusted APIC ID stored in ebx */
+ movl $CacheSizeAPStack, %eax
+ mull %ebx
+ movl $(CacheBase + (CacheSize - (CacheSizeBSPStack + CacheSizeBSPSlush))), %esp
+diff --git a/src/cpu/amd/model_10xxx/defaults.h b/src/cpu/amd/model_10xxx/defaults.h
+index 24f87ba..513d169 100644
+--- a/src/cpu/amd/model_10xxx/defaults.h
++++ b/src/cpu/amd/model_10xxx/defaults.h
+@@ -244,18 +244,50 @@ static const struct {
+ { 0, 0x68, (AMD_DR_B0 | AMD_DR_B1),
+ AMD_PTYPE_SVR, 0x00200000, 0x00600000 }, /* [22:21] DsNpReqLmt0 = 01b */
+
+- { 0, 0x84, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0x84, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xA4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xA4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xC4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xC4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
+- { 0, 0xE4, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
++ { 0, 0xE4, AMD_FAM10_ALL, AMD_PTYPE_ALL,
+ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
+
++ /* FIXME
++ * Non-C32 packages only
++ */
++ { 0, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xA4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xC4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xE4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00000000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ /* FIXME
++ * C32 package only
++ */
++#if 0
++ { 0, 0x84, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xA4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xC4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++
++ { 0, 0xE4, AMD_FAM15_ALL, AMD_PTYPE_ALL,
++ 0x00002000, 0x00002000 }, /* [13] LdtStopTriEn = 1 */
++#endif
++
+ /* Link Global Retry Control Register */
+ { 0, 0x150, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL,
+ 0x00073900, 0x00073F00 },
+@@ -614,38 +646,79 @@ static const struct {
+ { 0x530A, AMD_DR_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x00004400, 0x00006400 }, /* HT_PHY_DLL_REG */
+
+- { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xCF, AMD_FAM10_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,
++ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
++ completeness */
++
++ { 0xCF, AMD_FAM10_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,
++ 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,
++ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
++ [21:14] LfcMin = 10h */
++
++ { 0xC1, AMD_FAM10_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,
++ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
++ [21:14] LfcMin = 08h */
++
++ { 0xC1, AMD_FAM10_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,
++ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
++ [20:16] RttIndex = 04h */
++
++/* FIXME
++ * Causes lockups for some reason when more than one package is installed
++ * Debug and reactivate!
++ */
++// #if 0
++ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xDF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x00000000, 0x000000FF }, /* Provide clear setting for logical
+ completeness */
+
+- { 0xCF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xCF, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x0000006D, 0x000000FF }, /* HT_PHY_HT1_FIFO_PTR_OPT_VALUE */
+
+- { 0xDF, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xDF, 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_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xD1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
++ { 0xC1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT3,
+ 0x08040000, 0x3FFFC000 }, /* [29:22] LfcMax = 20h,
+ [21:14] LfcMin = 10h */
+
+- { 0xD1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xD1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC1, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
++ { 0xC1, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_HT1,
+ 0x04020000, 0x3FFFC000 }, /* [29:22] LfcMax = 10h,
+ [21:14] LfcMin = 08h */
+
+- { 0xC0, (AMD_FAM10_ALL | AMD_FAM15_ALL), AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
++ { 0xC0, AMD_FAM15_ALL, AMD_PTYPE_ALL, HTPHY_LINKTYPE_ALL,
+ 0x40040000, 0xe01F0000 }, /* [31:29] RttCtl = 02h,
+- [20:16] RttIndex = 04h */
++ [20:16] RttIndex = 04h */
++// #endif
+ };
+diff --git a/src/cpu/amd/model_10xxx/fidvid.c b/src/cpu/amd/model_10xxx/fidvid.c
+index 0e870e3..fe1cfb4 100644
+--- a/src/cpu/amd/model_10xxx/fidvid.c
++++ b/src/cpu/amd/model_10xxx/fidvid.c
+@@ -633,44 +633,45 @@ static void prep_fid_change(void)
+ }
+
+ 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;
+- u8 timedout ;
+-
+- /* paranoia ? I fear when we run fixPsNbVidBeforeWR we can enter a
+- * P1 that is a copy of P0, therefore has the same NB DID but the
+- * TSC will count twice per tick, so we have to wait for twice the
+- * count to achieve the desired timeout. But I'm likely to
+- * misunderstand this...
+- */
+- u32 corrected_timeout = ( (pstate_msr.lo==1)
+- && (!(rdmsr(0xC0010065).lo & NB_DID_M_ON)) ) ?
+- WAIT_PSTATE_TIMEOUT*2 : WAIT_PSTATE_TIMEOUT ;
+- msr_t timeout;
+-
+- timeout.lo = initial_msr.lo + corrected_timeout ;
+- timeout.hi = initial_msr.hi;
+- if ( (((u32)0xffffffff) - initial_msr.lo) < corrected_timeout ) {
+- timeout.hi++;
+- }
+-
+- // assuming TSC ticks at 1.25 ns per tick (800 MHz)
+- do {
+- pstate_msr = rdmsr(CUR_PSTATE_MSR);
+- tsc_msr = rdmsr(TSC_MSR);
+- timedout = (tsc_msr.hi > timeout.hi)
+- || ((tsc_msr.hi == timeout.hi) && (tsc_msr.lo > timeout.lo ));
+- } while ( (pstate_msr.lo != target_pstate) && (! timedout) ) ;
+-
+- 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=%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);
+- } while ( pstate_msr.lo != target_pstate ) ;
+- }
++ msr_t initial_msr = rdmsr(TSC_MSR);
++ msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ msr_t tsc_msr;
++ u8 timedout ;
++
++ /* paranoia ? I fear when we run fixPsNbVidBeforeWR we can enter a
++ * P1 that is a copy of P0, therefore has the same NB DID but the
++ * TSC will count twice per tick, so we have to wait for twice the
++ * count to achieve the desired timeout. But I'm likely to
++ * misunderstand this...
++ */
++ u32 corrected_timeout = ((pstate_msr.lo==1)
++ && (!(rdmsr(0xC0010065).lo & NB_DID_M_ON)) ) ?
++ WAIT_PSTATE_TIMEOUT*2 : WAIT_PSTATE_TIMEOUT;
++ msr_t timeout;
++
++ timeout.lo = initial_msr.lo + corrected_timeout ;
++ timeout.hi = initial_msr.hi;
++ if ( (((u32)0xffffffff) - initial_msr.lo) < corrected_timeout ) {
++ timeout.hi++;
++ }
++
++ // assuming TSC ticks at 1.25 ns per tick (800 MHz)
++ do {
++ pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ tsc_msr = rdmsr(TSC_MSR);
++ timedout = (tsc_msr.hi > timeout.hi)
++ || ((tsc_msr.hi == timeout.hi) && (tsc_msr.lo > timeout.lo ));
++ } while ( (pstate_msr.lo != target_pstate) && (! timedout) ) ;
++
++ if (pstate_msr.lo != target_pstate) {
++ msr_t limit_msr = rdmsr(0xc0010061);
++ printk(BIOS_ERR, "*** APIC ID %02x: timed out waiting for P-state %01x. Current P-state %01x P-state current limit MSRC001_0061=%08x %08x\n",
++ cpuid_ebx(0x00000001) >> 24, target_pstate, pstate_msr.lo, limit_msr.hi, limit_msr.lo);
++
++ do { // should we just go on instead ?
++ pstate_msr = rdmsr(CUR_PSTATE_MSR);
++ } while ( pstate_msr.lo != target_pstate ) ;
++ }
+ }
+
+ static void set_pstate(u32 nonBoostedPState) {
+@@ -1063,13 +1064,13 @@ static int init_fidvid_bsp(u32 bsp_apicid, u32 nodes)
+ APs and BSP */
+ ap_apicidx.num = 0;
+
+- for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE_RANGE, store_ap_apicid, &ap_apicidx);
++ for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE_RANGE, -1, store_ap_apicid, &ap_apicidx);
+
+ for (i = 0; i < ap_apicidx.num; i++) {
+ init_fidvid_bsp_stage1(ap_apicidx.apicid[i], &fv);
+ }
+ #else
+- for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE0_ONLY, init_fidvid_bsp_stage1, &fv);
++ for_each_ap(bsp_apicid, CONFIG_SET_FIDVID_CORE0_ONLY, -1, init_fidvid_bsp_stage1, &fv);
+ #endif
+
+ print_debug_fv("common_fid = ", fv.common_fid);
+diff --git a/src/cpu/amd/model_10xxx/init_cpus.c b/src/cpu/amd/model_10xxx/init_cpus.c
+index 0d1f043..e7f146f 100644
+--- a/src/cpu/amd/model_10xxx/init_cpus.c
++++ b/src/cpu/amd/model_10xxx/init_cpus.c
+@@ -59,6 +59,8 @@ static void set_EnableCf8ExtCfg(void)
+ static void set_EnableCf8ExtCfg(void) { }
+ #endif
+
++// #define DEBUG_HT_SETUP 1
++// #define FAM10_AP_NODE_SEQUENTIAL_START 1
+
+ typedef void (*process_ap_t) (u32 apicid, void *gp);
+
+@@ -143,8 +145,8 @@ uint32_t get_boot_apic_id(uint8_t node, uint32_t core) {
+ //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)
++static void for_each_ap(uint32_t bsp_apicid, uint32_t core_range, int8_t node,
++ process_ap_t process_ap, void *gp)
+ {
+ // here assume the OS don't change our apicid
+ u32 ap_apicid;
+@@ -165,6 +167,9 @@ static void for_each_ap(u32 bsp_apicid, u32 core_range, process_ap_t process_ap,
+ }
+
+ for (i = 0; i < nodes; i++) {
++ if ((node >= 0) && (i != node))
++ continue;
++
+ cores_found = get_core_num_in_bsp(i);
+
+ u32 jstart, jend;
+@@ -280,7 +285,7 @@ void wait_all_other_cores_started(u32 bsp_apicid)
+ {
+ // all aps other than core0
+ printk(BIOS_DEBUG, "started ap apicid: ");
+- for_each_ap(bsp_apicid, 2, wait_ap_started, (void *)0);
++ for_each_ap(bsp_apicid, 2, -1, wait_ap_started, (void *)0);
+ printk(BIOS_DEBUG, "\n");
+ }
+
+@@ -373,8 +378,10 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ /* NB_CFG MSR is shared between cores, so we need make sure
+ core0 is done at first --- use wait_all_core0_started */
+ if (id.coreid == 0) {
+- set_apicid_cpuid_lo(); /* only set it on core0 */
+- set_EnableCf8ExtCfg(); /* only set it on core0 */
++ /* Set InitApicIdCpuIdLo / EnableCf8ExtCfg on core0 only */
++ if (!is_fam15h())
++ set_apicid_cpuid_lo();
++ set_EnableCf8ExtCfg();
+ #if CONFIG_ENABLE_APIC_EXT_ID
+ enable_apic_ext_id(id.nodeid);
+ #endif
+@@ -427,6 +434,7 @@ static u32 init_cpus(u32 cpu_init_detectedx, struct sys_info *sysinfo)
+ }
+ // Mark the core as started.
+ lapic_write(LAPIC_MSG_REG, (apicid << 24) | F10_APSTATE_STARTED);
++ printk(BIOS_DEBUG, "CPU APICID %02x start flag set\n", apicid);
+
+ if (apicid != bsp_apicid) {
+ /* Setup each AP's cores MSRs.
+@@ -588,6 +596,34 @@ static void setup_remote_node(u8 node)
+ }
+ #endif /* CONFIG_MAX_PHYSICAL_CPUS > 1 */
+
++//it is running on core0 of node0
++static void start_other_cores(uint32_t bsp_apicid)
++{
++ u32 nodes;
++ u32 nodeid;
++
++ // disable multi_core
++ if (read_option(multi_core, 0) != 0) {
++ printk(BIOS_DEBUG, "Skip additional core init\n");
++ return;
++ }
++
++ nodes = get_nodes();
++
++ for (nodeid = 0; nodeid < nodes; nodeid++) {
++ u32 cores = get_core_num_in_bsp(nodeid);
++ printk(BIOS_DEBUG, "init node: %02x cores: %02x pass 1\n", nodeid, cores);
++ if (cores > 0) {
++ real_start_other_core(nodeid, cores);
++#ifdef FAM10_AP_NODE_SEQUENTIAL_START
++ printk(BIOS_DEBUG, "waiting for core start on node %d...\n", nodeid);
++ for_each_ap(bsp_apicid, 2, nodeid, wait_ap_started, (void *)0);
++ printk(BIOS_DEBUG, "...started\n");
++#endif
++ }
++ }
++}
++
+ static void AMD_Errata281(u8 node, uint64_t revision, u32 platform)
+ {
+ /* Workaround for Transaction Scheduling Conflict in
+@@ -847,6 +883,10 @@ static void AMD_SetHtPhyRegister(u8 node, u8 link, u8 entry)
+
+ phyBase = ((u32) link << 3) | 0x180;
+
++ /* Determine if link is connected and abort if not */
++ if (!(pci_read_config32(NODE_PCI(node, 0), 0x98 + (link * 0x20)) & 0x1))
++ return;
++
+ /* Get the portal control register's initial value
+ * and update it to access the desired phy register
+ */
+@@ -1005,10 +1045,11 @@ static void cpuSetAMDPCI(u8 node)
+ * Hypertransport initialization has taken place. Also note
+ * that it is run for the first core on each node
+ */
+- u8 i, j;
++ uint8_t i;
++ uint8_t j;
+ u32 platform;
+ u32 val;
+- u8 offset;
++ uint8_t offset;
+ uint32_t dword;
+ uint64_t revision;
+
+@@ -1035,6 +1076,17 @@ static void cpuSetAMDPCI(u8 node)
+ }
+ }
+
++#ifdef DEBUG_HT_SETUP
++ /* Dump link settings */
++ for (i = 0; i < 4; i++) {
++ for (j = 0; j < 4; j++) {
++ printk(BIOS_DEBUG, "Node %d link %d: type register: %08x control register: %08x extended control sublink 0: %08x 1: %08x\n", i, j,
++ pci_read_config32(NODE_PCI(i, 0), 0x98 + (j * 0x20)), pci_read_config32(NODE_PCI(i, 0), 0x84 + (j * 0x20)),
++ pci_read_config32(NODE_PCI(i, 0), 0x170 + (j * 0x4)), pci_read_config32(NODE_PCI(i, 0), 0x180 + (j * 0x4)));
++ }
++ }
++#endif
++
+ for (i = 0; i < ARRAY_SIZE(fam10_htphy_default); i++) {
+ if ((fam10_htphy_default[i].revision & revision) &&
+ (fam10_htphy_default[i].platform & platform)) {
+diff --git a/src/cpu/amd/quadcore/quadcore.c b/src/cpu/amd/quadcore/quadcore.c
+index 8a9b5ed..9c31eac 100644
+--- a/src/cpu/amd/quadcore/quadcore.c
++++ b/src/cpu/amd/quadcore/quadcore.c
+@@ -31,21 +31,6 @@
+ 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;
+@@ -141,6 +126,7 @@ static void real_start_other_core(uint32_t nodeid, uint32_t cores)
+ }
+ }
+
++#if (!IS_ENABLED(CONFIG_CPU_AMD_MODEL_10XXX))
+ //it is running on core0 of node0
+ static void start_other_cores(void)
+ {
+@@ -157,9 +143,10 @@ 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 pass 1 \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);
+ }
+ }
+ }
++#endif
+diff --git a/src/cpu/amd/quadcore/quadcore_id.c b/src/cpu/amd/quadcore/quadcore_id.c
+index c0537b3..1f5cbd8 100644
+--- a/src/cpu/amd/quadcore/quadcore_id.c
++++ b/src/cpu/amd/quadcore/quadcore_id.c
+@@ -108,7 +108,6 @@ struct node_core_id get_node_core_id(u32 nb_cfg_54)
+ id.nodeid = apicid & 0x7;
+ }
+ }
+-
+ 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...
+diff --git a/src/mainboard/asus/kgpe-d16/romstage.c b/src/mainboard/asus/kgpe-d16/romstage.c
+index 0df2447..acc8308 100644
+--- a/src/mainboard/asus/kgpe-d16/romstage.c
++++ b/src/mainboard/asus/kgpe-d16/romstage.c
+@@ -97,7 +97,18 @@ static void switch_spd_mux(uint8_t channel)
+ pci_write_config8(PCI_DEV(0, 0x14, 0), 0x54, byte);
+ }
+
+-static const uint8_t spd_addr[] = {
++static const uint8_t spd_addr_fam15[] = {
++ // Socket 0 Node 0 ("Node 0")
++ RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++ // Socket 0 Node 1 ("Node 1")
++ RC00, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++ // Socket 1 Node 0 ("Node 2")
++ RC01, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
++ // Socket 1 Node 1 ("Node 3")
++ RC01, DIMM4, DIMM5, 0, 0, DIMM6, DIMM7, 0, 0,
++};
++
++static const uint8_t spd_addr_fam10[] = {
+ // Socket 0 Node 0 ("Node 0")
+ RC00, DIMM0, DIMM1, 0, 0, DIMM2, DIMM3, 0, 0,
+ // Socket 0 Node 1 ("Node 1")
+@@ -117,10 +128,10 @@ static void activate_spd_rom(const struct mem_controller *ctrl) {
+ switch_spd_mux(0x2);
+ } else if (ctrl->node_id == 1) {
+ printk(BIOS_DEBUG, "enable_spd_node1()\n");
+- switch_spd_mux((sysinfo->nodes <= 2)?0x2:0x3);
++ switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x2:0x3);
+ } else if (ctrl->node_id == 2) {
+ printk(BIOS_DEBUG, "enable_spd_node2()\n");
+- switch_spd_mux((sysinfo->nodes <= 2)?0x3:0x2);
++ switch_spd_mux((is_fam15h() || (sysinfo->nodes <= 2))?0x3:0x2);
+ } else if (ctrl->node_id == 3) {
+ printk(BIOS_DEBUG, "enable_spd_node3()\n");
+ switch_spd_mux(0x3);
+@@ -306,18 +317,25 @@ void initialize_romstage_console_lock(void)
+
+ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ {
++ uint32_t esp;
++ __asm__ volatile (
++ "movl %%esp, %0"
++ : "=r" (esp)
++ );
++
+ struct sys_info *sysinfo = &sysinfo_car;
+
+ uint32_t bsp_apicid = 0, val;
+ uint8_t byte;
+ msr_t msr;
+
+- timestamp_init(timestamp_get());
+- timestamp_add_now(TS_START_ROMSTAGE);
+-
+ int s3resume = acpi_is_wakeup_s3();
+
+ if (!cpu_init_detectedx && boot_cpu()) {
++ /* Initial timestamp */
++ timestamp_init(timestamp_get());
++ timestamp_add_now(TS_START_ROMSTAGE);
++
+ /* Initialize the printk spinlock */
+ initialize_romstage_console_lock();
+
+@@ -344,6 +362,8 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ pci_write_config8(PCI_DEV(0, 0x14, 3), 0x78, byte);
+ }
+
++ printk(BIOS_SPEW, "Initial stack pointer: %08x\n", esp);
++
+ post_code(0x30);
+
+ if (bist == 0)
+@@ -397,7 +417,7 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ if (IS_ENABLED(CONFIG_LOGICAL_CPUS)) {
+ /* Core0 on each node is configured. Now setup any additional cores. */
+ printk(BIOS_DEBUG, "start_other_cores()\n");
+- start_other_cores();
++ start_other_cores(bsp_apicid);
+ post_code(0x37);
+ wait_all_other_cores_started(bsp_apicid);
+ }
+@@ -455,7 +475,10 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+
+ /* It's the time to set ctrl in sysinfo now; */
+ printk(BIOS_DEBUG, "fill_mem_ctrl() detected %d nodes\n", sysinfo->nodes);
+- fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr);
++ if (is_fam15h())
++ fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam15);
++ else
++ fill_mem_ctrl(sysinfo->nodes, sysinfo->ctrl, spd_addr_fam10);
+ post_code(0x3D);
+
+ #if 0
+@@ -527,5 +550,12 @@ void cache_as_ram_main(unsigned long bist, unsigned long cpu_init_detectedx)
+ */
+ BOOL AMD_CB_ManualBUIDSwapList (u8 node, u8 link, const u8 **List)
+ {
++ /* Force BUID to 0 */
++ static const u8 swaplist[] = {0, 0, 0xFF, 0, 0xFF};
++ if ((node == 0) && (link == 1)) { /* BSP SB link */
++ *List = swaplist;
++ return 1;
++ }
++
+ return 0;
+ }
+\ No newline at end of file
+diff --git a/src/northbridge/amd/amdht/h3finit.c b/src/northbridge/amd/amdht/h3finit.c
+index 849f4a8..82bf885 100644
+--- a/src/northbridge/amd/amdht/h3finit.c
++++ b/src/northbridge/amd/amdht/h3finit.c
+@@ -389,13 +389,49 @@ static u8 convertNodeToLink(u8 srcNode, u8 targetNode, sMainData *pDat)
+ */
+ static void htDiscoveryFloodFill(sMainData *pDat)
+ {
+- u8 currentNode = 0;
+- u8 currentLink;
++ uint8_t currentNode = 0;
++ uint8_t currentLink;
++ uint8_t currentLinkID;
++
++ /* NOTE
++ * Each node inside a dual node (socket G34) processor must share
++ * an adjacent node ID. Alter the link scan order such that the
++ * other internal node is always scanned first...
++ */
++ uint8_t currentLinkScanOrder_Default[8] = {0, 1, 2, 3, 4, 5, 6, 7};
++ uint8_t currentLinkScanOrder_G34_Fam10[8] = {1, 0, 2, 3, 4, 5, 6, 7};
++ uint8_t currentLinkScanOrder_G34_Fam15[8] = {2, 0, 1, 3, 4, 5, 6, 7};
++
++ uint8_t fam15h = 0;
++ uint8_t rev_gte_d = 0;
++ uint8_t dual_node = 0;
++ uint32_t f3xe8;
++ uint32_t family;
++ uint32_t model;
++
++ f3xe8 = pci_read_config32(NODE_PCI(0, 3), 0xe8);
++
++ 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;
++ }
++
++ if ((model >= 0x8) || fam15h)
++ /* Revision D or later */
++ rev_gte_d = 1;
++
++ if (rev_gte_d)
++ /* Check for dual node capability */
++ if (f3xe8 & 0x20000000)
++ dual_node = 1;
+
+ /* Entries are always added in pairs, the even indices are the 'source'
+ * side closest to the BSP, the odd indices are the 'destination' side
+ */
+-
+ while (currentNode <= pDat->NodesDiscovered)
+ {
+ u32 temp;
+@@ -423,11 +459,24 @@ static void htDiscoveryFloodFill(sMainData *pDat)
+ /* Enable routing tables on currentNode*/
+ pDat->nb->enableRoutingTables(currentNode, pDat->nb);
+
+- for (currentLink = 0; currentLink < pDat->nb->maxLinks; currentLink++)
++ for (currentLinkID = 0; currentLinkID < pDat->nb->maxLinks; currentLinkID++)
+ {
+ BOOL linkfound;
+ u8 token;
+
++ if (currentLinkID < 8) {
++ if (dual_node) {
++ if (fam15h)
++ currentLink = currentLinkScanOrder_G34_Fam15[currentLinkID];
++ else
++ currentLink = currentLinkScanOrder_G34_Fam10[currentLinkID];
++ } else {
++ currentLink = currentLinkScanOrder_Default[currentLinkID];
++ }
++ } else {
++ currentLink = currentLinkID;
++ }
++
+ if (pDat->HtBlock->AMD_CB_IgnoreLink && pDat->HtBlock->AMD_CB_IgnoreLink(currentNode, currentLink))
+ continue;
+
+diff --git a/src/northbridge/amd/amdht/h3ncmn.c b/src/northbridge/amd/amdht/h3ncmn.c
+index 8f9177f..1026d0e 100644
+--- a/src/northbridge/amd/amdht/h3ncmn.c
++++ b/src/northbridge/amd/amdht/h3ncmn.c
+@@ -51,8 +51,9 @@
+ #define REG_NODE_ID_0X60 0x60
+ #define REG_UNIT_ID_0X64 0x64
+ #define REG_LINK_TRANS_CONTROL_0X68 0x68
+-#define REG_LINK_INIT_CONTROL_0X6C 0x6C
++#define REG_LINK_INIT_CONTROL_0X6C 0x6c
+ #define REG_HT_CAP_BASE_0X80 0x80
++#define REG_NORTHBRIDGE_CFG_3X8C 0x8c
+ #define REG_HT_LINK_RETRY0_0X130 0x130
+ #define REG_HT_TRAFFIC_DIST_0X164 0x164
+ #define REG_HT_LINK_EXT_CONTROL0_0X170 0x170
+@@ -91,6 +92,21 @@
+ *** FAMILY/NORTHBRIDGE SPECIFIC FUNCTIONS ***
+ ***************************************************************************/
+
++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;
++}
++
+ /***************************************************************************//**
+ *
+ * SBDFO
+@@ -219,8 +235,18 @@ static void writeRoutingTable(u8 node, u8 target, u8 link, cNorthBridge *nb)
+
+ static void writeNodeID(u8 node, u8 nodeID, cNorthBridge *nb)
+ {
+- u32 temp = nodeID;
++ u32 temp;
+ ASSERT((node < nb->maxNodes) && (nodeID < nb->maxNodes));
++ if (is_fam15h()) {
++ temp = 1;
++ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
++ makePCIBusFromNode(node),
++ makePCIDeviceFromNode(node),
++ CPU_NB_FUNC_03,
++ REG_NORTHBRIDGE_CFG_3X8C),
++ 22, 22, &temp);
++ }
++ temp = nodeID;
+ AmdPCIWriteBits(MAKE_SBDFO(makePCISegmentFromNode(node),
+ makePCIBusFromNode(node),
+ makePCIDeviceFromNode(node),
+diff --git a/src/northbridge/amd/amdht/ht_wrapper.c b/src/northbridge/amd/amdht/ht_wrapper.c
+index 0c6b474..772db1c 100644
+--- a/src/northbridge/amd/amdht/ht_wrapper.c
++++ b/src/northbridge/amd/amdht/ht_wrapper.c
+@@ -90,16 +90,132 @@ static u32 get_nodes(void)
+ */
+ static void AMD_CB_EventNotify (u8 evtClass, u16 event, const u8 *pEventData0)
+ {
+- u8 i;
++ uint8_t i;
++ uint8_t log_level;
++ uint8_t dump_event_detail;
+
+- printk(BIOS_DEBUG, "AMD_CB_EventNotify()\n");
+- printk(BIOS_DEBUG, " event class: %02x\n event: %04x\n data: ", evtClass, event);
++ printk(BIOS_DEBUG, "AMD_CB_EventNotify(): ");
+
+- for (i = 0; i < *pEventData0; i++) {
+- printk(BIOS_DEBUG, " %02x ", *(pEventData0 + i));
++ /* Decode event */
++ dump_event_detail = 1;
++ switch (evtClass) {
++ case HT_EVENT_CLASS_CRITICAL:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "CRITICAL");
++ break;
++ case HT_EVENT_CLASS_ERROR:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "ERROR");
++ break;
++ case HT_EVENT_CLASS_HW_FAULT:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "HARDWARE FAULT");
++ break;
++ case HT_EVENT_CLASS_WARNING:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "WARNING");
++ break;
++ case HT_EVENT_CLASS_INFO:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "INFO");
++ break;
++ default:
++ log_level = BIOS_DEBUG;
++ printk(log_level, "UNKNOWN");
++ break;
+ }
+- printk(BIOS_DEBUG, "\n");
++ printk(log_level, ": ");
+
++ switch(event) {
++ case HT_EVENT_COH_EVENTS:
++ printk(log_level, "HT_EVENT_COH_EVENTS");
++ break;
++ case HT_EVENT_COH_NO_TOPOLOGY:
++ printk(log_level, "HT_EVENT_COH_NO_TOPOLOGY");
++ break;
++ case HT_EVENT_COH_LINK_EXCEED:
++ printk(log_level, "HT_EVENT_COH_LINK_EXCEED");
++ break;
++ case HT_EVENT_COH_FAMILY_FEUD:
++ printk(log_level, "HT_EVENT_COH_FAMILY_FEUD");
++ break;
++ case HT_EVENT_COH_NODE_DISCOVERED:
++ {
++ printk(log_level, "HT_EVENT_COH_NODE_DISCOVERED");
++ sHtEventCohNodeDiscovered *evt = (sHtEventCohNodeDiscovered*)pEventData0;
++ printk(log_level, ": node %d link %d new node: %d",
++ evt->node, evt->link, evt->newNode);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_COH_MPCAP_MISMATCH:
++ printk(log_level, "HT_EVENT_COH_MPCAP_MISMATCH");
++ break;
++ case HT_EVENT_NCOH_EVENTS:
++ printk(log_level, "HT_EVENT_NCOH_EVENTS");
++ break;
++ case HT_EVENT_NCOH_BUID_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_BUID_EXCEED");
++ break;
++ case HT_EVENT_NCOH_LINK_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_LINK_EXCEED");
++ break;
++ case HT_EVENT_NCOH_BUS_MAX_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_BUS_MAX_EXCEED");
++ break;
++ case HT_EVENT_NCOH_CFG_MAP_EXCEED:
++ printk(log_level, "HT_EVENT_NCOH_CFG_MAP_EXCEED");
++ break;
++ case HT_EVENT_NCOH_DEVICE_FAILED:
++ {
++ printk(log_level, "HT_EVENT_NCOH_DEVICE_FAILED");
++ sHtEventNcohDeviceFailed *evt = (sHtEventNcohDeviceFailed*)pEventData0;
++ printk(log_level, ": node %d link %d depth: %d attemptedBUID: %d",
++ evt->node, evt->link, evt->depth, evt->attemptedBUID);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_NCOH_AUTO_DEPTH:
++ {
++ printk(log_level, "HT_EVENT_NCOH_AUTO_DEPTH");
++ sHtEventNcohAutoDepth *evt = (sHtEventNcohAutoDepth*)pEventData0;
++ printk(log_level, ": node %d link %d depth: %d",
++ evt->node, evt->link, evt->depth);
++ dump_event_detail = 0;
++ break;
++ }
++ case HT_EVENT_OPT_EVENTS:
++ printk(log_level, "HT_EVENT_OPT_EVENTS");
++ break;
++ case HT_EVENT_OPT_REQUIRED_CAP_RETRY:
++ printk(log_level, "HT_EVENT_OPT_REQUIRED_CAP_RETRY");
++ break;
++ case HT_EVENT_OPT_REQUIRED_CAP_GEN3:
++ printk(log_level, "HT_EVENT_OPT_REQUIRED_CAP_GEN3");
++ break;
++ case HT_EVENT_HW_EVENTS:
++ printk(log_level, "HT_EVENT_HW_EVENTS");
++ break;
++ case HT_EVENT_HW_SYNCHFLOOD:
++ printk(log_level, "HT_EVENT_HW_SYNCHFLOOD");
++ break;
++ case HT_EVENT_HW_HTCRC:
++ printk(log_level, "HT_EVENT_HW_HTCRC");
++ break;
++ default:
++ printk(log_level, "HT_EVENT_UNKNOWN");
++ break;
++ }
++ printk(log_level, "\n");
++
++ if (dump_event_detail) {
++ printk(BIOS_DEBUG, " event class: %02x\n event: %04x\n data: ", evtClass, event);
++
++ for (i = 0; i < *pEventData0; i++) {
++ printk(BIOS_DEBUG, " %02x ", *(pEventData0 + i));
++ }
++ printk(BIOS_DEBUG, "\n");
++ }
+ }
+
+ /**
+@@ -208,9 +324,10 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ for (node = 0; node < node_count; node++) {
+ f3xe8 = pci_read_config32(NODE_PCI(node, 3), 0xe8);
+ 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);
++ printk(BIOS_DEBUG, "amd_ht_fixup(): node %d (internal node ID %d): disabling defective HT link", node, internal_node_number);
+ if (internal_node_number == 0) {
+ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x98:0xd8) & 0x1;
++ printk(BIOS_DEBUG, " (L3 connected: %d)\n", package_link_3_connected);
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x84:0xc4);
+@@ -232,15 +349,21 @@ static void amd_ht_fixup(struct sys_info *sysinfo) {
+ }
+ } else if (internal_node_number == 1) {
+ uint8_t package_link_3_connected = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xf8:0xb8) & 0x1;
++ printk(BIOS_DEBUG, " (L3 connected: %d)\n", package_link_3_connected);
+ if (package_link_3_connected) {
+ /* Set WidthIn and WidthOut to 0 */
+ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4);
+ dword &= ~0x77000000;
+ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0xe4:0xa4, dword);
+ /* Set Ganged to 1 */
+- dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174);
++ /* WARNING
++ * The Family 15h BKDG states that 0x18c should be set,
++ * however this is in error. 0x17c is the correct control
++ * register (sublink 0) for these processors...
++ */
++ dword = pci_read_config32(NODE_PCI(node, 0), (fam15h)?0x17c:0x174);
+ dword |= 0x00000001;
+- pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x18c:0x174, dword);
++ pci_write_config32(NODE_PCI(node, 0), (fam15h)?0x17c:0x174, dword);
+ } else {
+ /* Set ConnDly to 1 */
+ dword = pci_read_config32(NODE_PCI(node, 0), 0x16c);
+diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+index 55cdd24..b7c8e83 100644
+--- a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
++++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
+@@ -5430,6 +5430,7 @@ static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc
+ cpu_divisor = (0x1 << cpu_did);
+ pMCTstat->TSCFreq = (100 * (cpu_fid + 0x10)) / cpu_divisor;
+
++ printk(BIOS_DEBUG, "mct_InitialMCT_D: mct_ForceNBPState0_En_Fam15\n");
+ mct_ForceNBPState0_En_Fam15(pMCTstat, pDCTstat);
+ } else {
+ /* K10 BKDG v3.62 section 2.8.9.2 */
+--
+1.7.9.5
+