summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZheng Bao <zheng.bao@amd.com>2010-04-23 17:32:48 +0000
committerStefan Reinauer <stepan@openbios.org>2010-04-23 17:32:48 +0000
commiteb75f652d392d2f4f257194e112f3f0db7479145 (patch)
treeaa972907734abcba4ca52f2a3a71f8d81d4bdce0
parentfe6c2cda6e6977894d9b668af9509b983c850f68 (diff)
downloadcoreboot-eb75f652d392d2f4f257194e112f3f0db7479145.tar.xz
DDR3 support for AMD Fam10.
Signed-off-by: Zheng Bao <zheng.bao@amd.com> Acked-by: Stefan Reinauer <stepan@coresystems.de> git-svn-id: svn://svn.coreboot.org/coreboot/trunk@5481 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
-rw-r--r--src/northbridge/amd/amdfam10/amdfam10.h6
-rw-r--r--src/northbridge/amd/amdfam10/raminit_amdmct.c56
-rw-r--r--src/northbridge/amd/amdmct/amddefs.h7
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d.c3725
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d.h794
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h370
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c87
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c118
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctchi_d.c122
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c143
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c1312
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c268
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c32
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c217
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c252
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c230
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctprob.c37
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctproc.c45
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctrci.c190
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c326
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c1056
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c85
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c129
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c399
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mctwl.c382
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c916
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c270
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mport_d.c39
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c328
-rw-r--r--src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h139
-rw-r--r--src/northbridge/amd/amdmct/wrappers/mcti_d.c19
31 files changed, 12095 insertions, 4 deletions
diff --git a/src/northbridge/amd/amdfam10/amdfam10.h b/src/northbridge/amd/amdfam10/amdfam10.h
index 81dec8858d..49046fb531 100644
--- a/src/northbridge/amd/amdfam10/amdfam10.h
+++ b/src/northbridge/amd/amdfam10/amdfam10.h
@@ -996,7 +996,11 @@ struct mem_info { // pernode
u8 rsv[1];
} __attribute__((packed));
#else
-#include "../amdmct/mct/mct_d.h"
+ #if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+ #include "../amdmct/mct_ddr3/mct_d.h"
+ #else
+ #include "../amdmct/mct/mct_d.h"
+ #endif
#endif
struct link_pair_t {
diff --git a/src/northbridge/amd/amdfam10/raminit_amdmct.c b/src/northbridge/amd/amdfam10/raminit_amdmct.c
index 0a298727b7..09bdd70437 100644
--- a/src/northbridge/amd/amdfam10/raminit_amdmct.c
+++ b/src/northbridge/amd/amdfam10/raminit_amdmct.c
@@ -30,6 +30,54 @@ static void print_t(const char *strval)
printk(BIOS_DEBUG, "%s", strval);
#endif
}
+
+#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+#include "amdfam10.h"
+#include "../amdmct/wrappers/mcti.h"
+#include "../amdmct/amddefs.h"
+#include "../amdmct/mct_ddr3/mwlc_d.h"
+#include "../amdmct/mct_ddr3/mct_d.h"
+#include "../amdmct/mct_ddr3/mct_d_gcc.h"
+
+#include "../amdmct/wrappers/mcti_d.c"
+#include "../amdmct/mct_ddr3/mct_d.c"
+
+#include "../amdmct/mct_ddr3/mctmtr_d.c"
+#include "../amdmct/mct_ddr3/mctcsi_d.c"
+#include "../amdmct/mct_ddr3/mctecc_d.c"
+#include "../amdmct/mct_ddr3/mctdqs_d.c"
+#include "../amdmct/mct_ddr3/mctsrc.c"
+#include "../amdmct/mct_ddr3/mctsdi.c"
+#include "../amdmct/mct_ddr3/mctproc.c"
+#include "../amdmct/mct_ddr3/mctprob.c"
+#include "../amdmct/mct_ddr3/mcthwl.c"
+#include "../amdmct/mct_ddr3/mctwl.c"
+#include "../amdmct/mct_ddr3/mport_d.c"
+#include "../amdmct/mct_ddr3/mutilc_d.c"
+#include "../amdmct/mct_ddr3/modtrdim.c"
+#include "../amdmct/mct_ddr3/mhwlc_d.c"
+#include "../amdmct/mct_ddr3/mctrci.c"
+#include "../amdmct/mct_ddr3/mctsrc1p.c"
+#include "../amdmct/mct_ddr3/mcttmrl.c"
+#include "../amdmct/mct_ddr3/mcthdi.c"
+#include "../amdmct/mct_ddr3/mctndi_d.c"
+#include "../amdmct/mct_ddr3/mctchi_d.c"
+
+#if CONFIG_CPU_SOCKET_TYPE == 0x10
+//TODO: S1G1?
+#elif CONFIG_CPU_SOCKET_TYPE == 0x11
+//AM3
+#include "../amdmct/mct_ddr3/mctardk5.c"
+#elif CONFIG_CPU_SOCKET_TYPE == 0x12
+//F (1207), Fr2, G (1207)
+#include "../amdmct/mct_ddr3/mctardk6.c"
+#elif CONFIG_CPU_SOCKET_TYPE == 0x13
+//ASB2
+#include "../amdmct/mct_ddr3/mctardk5.c"
+#endif
+
+#else /* DDR2 */
+
#include "amdfam10.h"
#include "../amdmct/wrappers/mcti.h"
#include "../amdmct/amddefs.h"
@@ -65,6 +113,8 @@ static void print_t(const char *strval)
#include "../amdmct/mct/mct_fd.c"
+#endif /* DDR2 */
+
int mctRead_SPD(u32 smaddr, u32 reg)
{
return spd_read_byte(smaddr, reg);
@@ -141,9 +191,15 @@ u32 mctGetLogicalCPUID(u32 Node)
case 0x10042:
ret = AMD_RB_C2;
break;
+ case 0x10043:
+ ret = AMD_RB_C3;
+ break;
case 0x10062:
ret = AMD_DA_C2;
break;
+ case 0x10063:
+ ret = AMD_DA_C3;
+ break;
case 0x10080:
ret = AMD_HY_D0;
break;
diff --git a/src/northbridge/amd/amdmct/amddefs.h b/src/northbridge/amd/amdmct/amddefs.h
index 1b75888b12..452de331b1 100644
--- a/src/northbridge/amd/amdmct/amddefs.h
+++ b/src/northbridge/amd/amdmct/amddefs.h
@@ -43,6 +43,8 @@
#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 */
/*
* Groups - Create as many as you wish, from the above public values
@@ -60,8 +62,11 @@
#define AMD_DR_LT_B3 (AMD_DR_B0 | AMD_DR_B1 | AMD_DR_B2 | AMD_DR_BA)
#define AMD_DR_GT_B0 (AMD_DR_ALL & ~(AMD_DR_B0))
#define AMD_DR_ALL (AMD_DR_Bx)
-#define AMD_FAM10_ALL (AMD_DR_ALL | AMD_RB_C2 | AMD_HY_D0)
+#define AMD_FAM10_ALL (AMD_DR_ALL | AMD_RB_C2 | AMD_HY_D0 | AMD_DA_C3 | AMD_DA_C2)
#define AMD_FAM10_GT_B0 (AMD_FAM10_ALL & ~(AMD_DR_B0))
+#define AMD_DR_Cx (AMD_RB_C2 | AMD_DA_C2 | AMD_RB_C3 | AMD_DA_C3)
+#define AMD_DR_Dx (AMD_HY_D0)
+
/*
* Public Platforms - USE THESE VERSIONS TO MAKE COMPARE WITH CPUPLATFORMTYPE RETURN VALUE
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
new file mode 100644
index 0000000000..453a8ba357
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.c
@@ -0,0 +1,3725 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/* Description: Main memory controller system configuration for DDR 3 */
+
+/* KNOWN ISSUES - ERRATA
+ *
+ * Trtp is not calculated correctly when the controller is in 64-bit mode, it
+ * is 1 busclock off. No fix planned. The controller is not ordinarily in
+ * 64-bit mode.
+ *
+ * 32 Byte burst not supported. No fix planned. The controller is not
+ * ordinarily in 64-bit mode.
+ *
+ * Trc precision does not use extra Jedec defined fractional component.
+ * InsteadTrc (course) is rounded up to nearest 1 ns.
+ *
+ * Mini and Micro DIMM not supported. Only RDIMM, UDIMM, SO-DIMM defined types
+ * supported.
+ */
+
+static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static u8 NodePresent_D(u8 Node);
+static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+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_initDCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_DramInit(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat);
+static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,\
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void Get_Twrwr(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void Get_Twrrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+ u32 dev, 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);
+static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct,
+ u32 dev, u32 index_reg, u32 index);
+static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_init(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void SetCSTriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void SetODTTriState(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);
+static void mct_OtherTiming(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+void mct_ClrWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct);
+
+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 u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void SyncSetting(struct DCTStatStruc *pDCTstat);
+static u8 crcCheck(u8 smbaddr);
+
+/*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};
+static const u8 Table_DQSRcvEn_Offset[] = {0x00,0x01,0x10,0x11,0x2};
+
+/****************************************************************************
+ Describe how platform maps MemClk pins to logical DIMMs. The MemClk pins
+ are identified based on BKDG definition of Fn2x88[MemClkDis] bitmap.
+ AGESA will base on this value to disable unused MemClk to save power.
+
+ If MEMCLK_MAPPING or MEMCLK_MAPPING contains all zeroes, AGESA will use
+ default MemClkDis setting based on package type.
+
+ Example:
+ BKDG definition of Fn2x88[MemClkDis] bitmap for AM3 package is like below:
+ Bit AM3/S1g3 pin name
+ 0 M[B,A]_CLK_H/L[0]
+ 1 M[B,A]_CLK_H/L[1]
+ 2 M[B,A]_CLK_H/L[2]
+ 3 M[B,A]_CLK_H/L[3]
+ 4 M[B,A]_CLK_H/L[4]
+ 5 M[B,A]_CLK_H/L[5]
+ 6 M[B,A]_CLK_H/L[6]
+ 7 M[B,A]_CLK_H/L[7]
+
+ And platform has the following routing:
+ CS0 M[B,A]_CLK_H/L[4]
+ CS1 M[B,A]_CLK_H/L[2]
+ CS2 M[B,A]_CLK_H/L[3]
+ CS3 M[B,A]_CLK_H/L[5]
+
+ Then:
+ ; CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7
+ MEMCLK_MAPPING EQU 00010000b, 00000100b, 00001000b, 00100000b, 00000000b, 00000000b, 00000000b, 00000000b
+*/
+
+/* Note: If you are not sure about the pin mappings at initial stage, we dont have to disable MemClk.
+ * Set entries in the tables all 0xFF. */
+static const u8 Tab_L1CLKDis[] = {0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04};
+static const u8 Tab_AM3CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00};
+static const u8 Tab_S1CLKDis[] = {0xA2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+static const u8 Tab_ManualCLKDis[]= {0x10, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00};
+
+static const u8 Table_Comp_Rise_Slew_20x[] = {7, 3, 2, 2, 0xFF};
+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 void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ /*
+ * Memory may be mapped contiguously all the way up to 4GB (depending on setup
+ * options). It is the responsibility of PCI subsystem to create an uncacheable
+ * IO region below 4GB and to adjust TOP_MEM downward prior to any IO mapping or
+ * accesses. It is the same responsibility of the CPU sub-system prior to
+ * accessing LAPIC.
+ *
+ * Slot Number is an external convention, and is determined by OEM with accompanying
+ * silk screening. OEM may choose to use Slot number convention which is consistent
+ * with DIMM number conventions. All AMD engineering platforms do.
+ *
+ * Build Requirements:
+ * 1. MCT_SEG0_START and MCT_SEG0_END macros to begin and end the code segment,
+ * defined in mcti.inc.
+ *
+ * Run-Time Requirements:
+ * 1. Complete Hypertransport Bus Configuration
+ * 2. SMBus Controller Initialized
+ * 1. BSP in Big Real Mode
+ * 2. Stack at SS:SP, located somewhere between A000:0000 and F000:FFFF
+ * 3. Checksummed or Valid NVRAM bits
+ * 4. MCG_CTL=-1, MC4_CTL_EN=0 for all CPUs
+ * 5. MCi_STS from shutdown/warm reset recorded (if desired) prior to entry
+ * 6. All var MTRRs reset to zero
+ * 7. State of NB_CFG.DisDatMsk set properly on all CPUs
+ * 8. All CPUs at 2Ghz Speed (unless DQS training is not installed).
+ * 9. All cHT links at max Speed/Width (unless DQS training is not installed).
+ *
+ *
+ * Global relationship between index values and item values:
+ *
+ * pDCTstat.CASL pDCTstat.Speed
+ * j CL(j) k F(k)
+ * --------------------------
+ * 0 2.0 - -
+ * 1 3.0 1 200 Mhz
+ * 2 4.0 2 266 Mhz
+ * 3 5.0 3 333 Mhz
+ * 4 6.0 4 400 Mhz
+ * 5 7.0 5 533 Mhz
+ * 6 8.0 6 667 Mhz
+ * 7 9.0 7 800 Mhz
+ */
+ u8 Node, NodesWmem;
+ u32 node_sys_base;
+
+restartinit:
+ mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/
+ NodesWmem = 0;
+ node_sys_base = 0;
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ pDCTstat->Node_ID = Node;
+ pDCTstat->dev_host = PA_HOST(Node);
+ pDCTstat->dev_map = PA_MAP(Node);
+ pDCTstat->dev_dct = PA_DCT(Node);
+ pDCTstat->dev_nbmisc = PA_NBMISC(Node);
+ pDCTstat->NodeSysBase = node_sys_base;
+
+ mct_init(pMCTstat, pDCTstat);
+ mctNodeIDDebugPort_D();
+ pDCTstat->NodePresent = NodePresent_D(Node);
+ if (pDCTstat->NodePresent) { /* See if Node is there*/
+ clear_legacy_Mode(pMCTstat, pDCTstat);
+ pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);
+
+ mct_InitialMCT_D(pMCTstat, pDCTstat);
+
+ mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/
+
+ mct_initDCT(pMCTstat, pDCTstat);
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+ goto fatalexit; /* any fatal errors?*/
+ } 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;
+ }
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n");
+ SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n");
+ HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/
+ mctHookAfterHTMap();
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n");
+ CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */
+ mctHookAfterCPU(); /* Setup external northbridge(s) */
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n");
+ DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n");
+ UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */
+
+ 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;
+ }
+
+ InterleaveNodes_D(pMCTstat, pDCTstatA);
+ InterleaveChannels_D(pMCTstat, pDCTstatA);
+
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n");
+ if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/
+ printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n");
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ }
+
+ mct_FinalMCT_D(pMCTstat, (pDCTstatA + 0) ); /* Node 0 */
+ printk(BIOS_DEBUG, "All Done\n");
+ return;
+
+fatalexit:
+ die("mct_d: fatalexit");
+}
+
+static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 ret;
+
+ if (mctGet_NVbits(NV_CS_SpareCTL)) {
+ if (MCT_DIMM_SPARE_NO_WARM) {
+ /* Do no warm-reset DIMM spare */
+ if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+ LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA);
+ ret = 0;
+ } else {
+ mct_ResetDataStruct_D(pMCTstat, pDCTstatA);
+ pMCTstat->GStatus |= 1 << GSB_EnDIMMSpareNW;
+ ret = 1;
+ }
+ } else {
+ /* Do warm-reset DIMM spare */
+ if (mctGet_NVbits(NV_DQSTrainCTL))
+ mctWarmReset_D();
+ ret = 0;
+ }
+ } else {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 nv_DQSTrainCTL;
+
+ if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+ return;
+ }
+
+ nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL);
+ /* FIXME: BOZO- DQS training every time*/
+ nv_DQSTrainCTL = 1;
+
+ mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);
+ phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);
+
+ if (nv_DQSTrainCTL) {
+ mctHookBeforeAnyTraining(pMCTstat, pDCTstatA);
+ /* TODO: should be in mctHookBeforeAnyTraining */
+ _WRMSR(0x26C, 0x04040404, 0x04040404);
+ _WRMSR(0x26D, 0x04040404, 0x04040404);
+ _WRMSR(0x26E, 0x04040404, 0x04040404);
+ _WRMSR(0x26F, 0x04040404, 0x04040404);
+ mct_WriteLevelization_HW(pMCTstat, pDCTstatA);
+
+ TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);
+
+ mct_TrainDQSPos_D(pMCTstat, pDCTstatA);
+
+ /* Second Pass never used for Barcelona! */
+ /* TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); */
+
+ mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);
+
+ /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */
+ mctHookAfterAnyTraining();
+ mctSaveDQSSigTmg_D();
+
+ MCTMemClr_D(pMCTstat, pDCTstatA);
+ } else {
+ mctGetDQSSigTmg_D(); /* get values into data structure */
+ LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA); /* load values into registers.*/
+ /* mctDoWarmResetMemClr_D(); */
+ MCTMemClr_D(pMCTstat, pDCTstatA);
+ }
+}
+
+static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node, Receiver, Channel, Dir, DIMM;
+ u32 dev;
+ u32 index_reg;
+ u32 reg;
+ u32 index;
+ u32 val;
+ u8 ByteLane;
+ u8 txdqs;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->DCTSysLimit) {
+ dev = pDCTstat->dev_dct;
+ for (Channel = 0;Channel < 2; Channel++) {
+ /* there are four receiver pairs,
+ loosely associated with chipselects.*/
+ index_reg = 0x98 + Channel * 0x100;
+ for (Receiver = 0; Receiver < 8; Receiver += 2) {
+ /* Set Receiver Enable Values */
+ mct_SetRcvrEnDly_D(pDCTstat,
+ 0, /* RcvrEnDly */
+ 1, /* FinalValue, From stack */
+ Channel,
+ Receiver,
+ dev, index_reg,
+ (Receiver >> 1) * 3 + 0x10, /* Addl_Index */
+ 2); /* Pass Second Pass ? */
+ /* Restore Write levelization training data */
+ for (ByteLane = 0; ByteLane < 9; ByteLane ++) {
+ 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);
+ if (ByteLane & 1) { /* odd byte lane */
+ val &= ~(0xFF << 16);
+ val |= txdqs << 16;
+ } else {
+ val &= ~0xFF;
+ val |= txdqs;
+ }
+ Set_NB32_index_wait(dev, 0x98 + 0x100*Channel, index, val);
+ }
+ }
+ }
+ for (Channel = 0; Channel<2; Channel++) {
+ SetEccDQSRcvrEn_D(pDCTstat, Channel);
+ }
+
+ for (Channel = 0; Channel < 2; Channel++) {
+ u8 *p;
+ index_reg = 0x98 + Channel * 0x100;
+
+ /* NOTE:
+ * when 400, 533, 667, it will support dimm0/1/2/3,
+ * and set conf for dimm0, hw will copy to dimm1/2/3
+ * set for dimm1, hw will copy to dimm3
+ * Rev A/B only support DIMM0/1 when 800Mhz and above
+ * + 0x100 to next dimm
+ * Rev C support DIMM0/1/2/3 when 800Mhz and above
+ * + 0x100 to next dimm
+ */
+ for (DIMM = 0; DIMM < 4; DIMM++) {
+ if (DIMM == 0) {
+ index = 0; /* CHA Write Data Timing Low */
+ } else {
+ if (pDCTstat->Speed >= 4) {
+ index = 0x100 * DIMM;
+ } else {
+ break;
+ }
+ }
+ 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);
+ val = stream_to_int(p+4); /* CHA Write Data Timing High */
+ Set_NB32_index_wait(dev, index_reg, index+2, val);
+ val = *(p+8); /* CHA Write ECC Timing */
+ Set_NB32_index_wait(dev, index_reg, index+3, val);
+ index += 4;
+ }
+ }
+ }
+
+ for (Channel = 0; Channel<2; Channel++) {
+ reg = 0x78 + Channel * 0x100;
+ val = Get_NB32(dev, 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*/
+ }
+ }
+ }
+}
+
+static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u32 NextBase, BottomIO;
+ u8 _MemHoleRemap, DramHoleBase, DramHoleOffset;
+ u32 HoleSize, DramSelBaseAddr;
+
+ u32 val;
+ u32 base;
+ u32 limit;
+ u32 dev, devx;
+ struct DCTStatStruc *pDCTstat;
+
+ _MemHoleRemap = mctGet_NVbits(NV_MemHole);
+
+ if (pMCTstat->HoleBase == 0) {
+ DramHoleBase = mctGet_NVbits(NV_BottomIO);
+ } else {
+ DramHoleBase = pMCTstat->HoleBase >> (24-8);
+ }
+
+ BottomIO = DramHoleBase << (24-8);
+
+ NextBase = 0;
+ pDCTstat = pDCTstatA + 0;
+ dev = pDCTstat->dev_map;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ devx = pDCTstat->dev_map;
+ DramSelBaseAddr = 0;
+ pDCTstat = pDCTstatA + Node; /* ??? */
+ if (!pDCTstat->GangedMode) {
+ DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
+ /*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
+ val = pDCTstat->NodeSysLimit;
+ if ((val & 0xFF) == 0xFE) {
+ DramSelBaseAddr++;
+ val++;
+ }
+ pDCTstat->DCTSysLimit = val;
+ }
+
+ base = pDCTstat->DCTSysBase;
+ limit = pDCTstat->DCTSysLimit;
+ if (limit > base) {
+ base += NextBase;
+ limit += NextBase;
+ DramSelBaseAddr += NextBase;
+ printk(BIOS_DEBUG, " Node: %02x base: %02x limit: %02x BottomIO: %02x\n", Node, base, limit, BottomIO);
+
+ if (_MemHoleRemap) {
+ if ((base < BottomIO) && (limit >= BottomIO)) {
+ /* HW Dram Remap */
+ pDCTstat->Status |= 1 << SB_HWHole;
+ pMCTstat->GStatus |= 1 << GSB_HWHole;
+ pDCTstat->DCTSysBase = base;
+ pDCTstat->DCTSysLimit = limit;
+ pDCTstat->DCTHoleBase = BottomIO;
+ pMCTstat->HoleBase = BottomIO;
+ HoleSize = _4GB_RJ8 - BottomIO; /* HoleSize[39:8] */
+ if ((DramSelBaseAddr > 0) && (DramSelBaseAddr < BottomIO))
+ base = DramSelBaseAddr;
+ val = ((base + HoleSize) >> (24-8)) & 0xFF;
+ DramHoleOffset = val;
+ val <<= 8; /* shl 16, rol 24 */
+ val |= DramHoleBase << 24;
+ val |= 1 << DramHoleValid;
+ Set_NB32(devx, 0xF0, val); /* Dram Hole Address Reg */
+ pDCTstat->DCTSysLimit += HoleSize;
+ base = pDCTstat->DCTSysBase;
+ limit = pDCTstat->DCTSysLimit;
+ } else if (base == BottomIO) {
+ /* SW Node Hoist */
+ pMCTstat->GStatus |= 1<<GSB_SpIntRemapHole;
+ pDCTstat->Status |= 1<<SB_SWNodeHole;
+ pMCTstat->GStatus |= 1<<GSB_SoftHole;
+ pMCTstat->HoleBase = base;
+ limit -= base;
+ base = _4GB_RJ8;
+ limit += base;
+ pDCTstat->DCTSysBase = base;
+ pDCTstat->DCTSysLimit = limit;
+ } else {
+ /* No Remapping. Normal Contiguous mapping */
+ pDCTstat->DCTSysBase = base;
+ pDCTstat->DCTSysLimit = limit;
+ }
+ } else {
+ /*No Remapping. Normal Contiguous mapping*/
+ pDCTstat->DCTSysBase = base;
+ pDCTstat->DCTSysLimit = limit;
+ }
+ base |= 3; /* set WE,RE fields*/
+ pMCTstat->SysLimit = limit;
+ }
+ Set_NB32(dev, 0x40 + (Node << 3), base); /* [Node] + Dram Base 0 */
+
+ val = limit & 0xFFFF0000;
+ val |= Node;
+ Set_NB32(dev, 0x44 + (Node << 3), val); /* set DstNode */
+
+ printk(BIOS_DEBUG, " Node: %02x base: %02x limit: %02x \n", Node, base, limit);
+ limit = pDCTstat->DCTSysLimit;
+ if (limit) {
+ NextBase = (limit & 0xFFFF0000) + 0x10000;
+ }
+ }
+
+ /* Copy dram map from Node 0 to Node 1-7 */
+ for (Node = 1; Node < MAX_NODES_SUPPORTED; Node++) {
+ u32 reg;
+ pDCTstat = pDCTstatA + Node;
+ devx = pDCTstat->dev_map;
+
+ if (pDCTstat->NodePresent) {
+ reg = 0x40; /*Dram Base 0*/
+ do {
+ val = Get_NB32(dev, reg);
+ Set_NB32(devx, reg, val);
+ reg += 4;
+ } while ( reg < 0x80);
+ } else {
+ break; /* stop at first absent Node */
+ }
+ }
+
+ /*Copy dram map to F1x120/124*/
+ mct_HTMemMapExt(pMCTstat, pDCTstatA);
+}
+
+static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+
+ /* Initiates a memory clear operation for all node. The mem clr
+ * is done in paralel. After the memclr is complete, all processors
+ * status are checked to ensure that memclr has completed.
+ */
+ u8 Node;
+ struct DCTStatStruc *pDCTstat;
+
+ if (!mctGet_NVbits(NV_DQSTrainCTL)){
+ /* FIXME: callback to wrapper: mctDoWarmResetMemClr_D */
+ } else { /* NV_DQSTrainCTL == 1 */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ DCTMemClr_Init_D(pMCTstat, pDCTstat);
+ }
+ }
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+ }
+ }
+ }
+}
+
+static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+ u32 dev;
+ u32 reg;
+
+ /* Initiates a memory clear operation on one node */
+ if (pDCTstat->DCTSysLimit) {
+ dev = pDCTstat->dev_dct;
+ reg = 0x110;
+
+ do {
+ val = Get_NB32(dev, reg);
+ } while (val & (1 << MemClrBusy));
+
+ val |= (1 << MemClrInit);
+ Set_NB32(dev, reg, val);
+ }
+}
+
+static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ /* Ensures that memory clear has completed on all node.*/
+ u8 Node;
+ struct DCTStatStruc *pDCTstat;
+
+ if (!mctGet_NVbits(NV_DQSTrainCTL)){
+ /* callback to wrapper: mctDoWarmResetMemClr_D */
+ } else { /* NV_DQSTrainCTL == 1 */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ DCTMemClr_Sync_D(pMCTstat, pDCTstat);
+ }
+ }
+ }
+}
+
+static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+ u32 dev = pDCTstat->dev_dct;
+ u32 reg;
+
+ /* Ensure that a memory clear operation has completed on one node */
+ if (pDCTstat->DCTSysLimit){
+ reg = 0x110;
+
+ do {
+ val = Get_NB32(dev, reg);
+ } while (val & (1 << MemClrBusy));
+
+ do {
+ val = Get_NB32(dev, reg);
+ } while (!(val & (1 << Dr_MemClrStatus)));
+ }
+
+ val = 0x0FE40FC0; /* BKDG recommended */
+ val |= MCCH_FlushWrOnStpGnt; /* Set for S3 */
+ Set_NB32(dev, 0x11C, val);
+}
+
+static u8 NodePresent_D(u8 Node)
+{
+ /*
+ * Determine if a single Hammer Node exists within the network.
+ */
+ u32 dev;
+ u32 val;
+ u32 dword;
+ u8 ret = 0;
+
+ dev = PA_HOST(Node); /*test device/vendor id at host bridge */
+ val = Get_NB32(dev, 0);
+ dword = mct_NodePresent_D(); /* FIXME: BOZO -11001022h rev for F */
+ if (val == dword) { /* AMD Hammer Family CPU HT Configuration */
+ if (oemNodePresent_D(Node, &ret))
+ goto finish;
+ /* Node ID register */
+ val = Get_NB32(dev, 0x60);
+ val &= 0x07;
+ dword = Node;
+ if (val == dword) /* current nodeID = requested nodeID ? */
+ ret = 1;
+ }
+finish:
+ return ret;
+}
+
+static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /*
+ * Initialize DRAM on single Athlon 64/Opteron Node.
+ */
+ u8 stopDCTflag;
+ u32 val;
+
+ 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);
+ 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! */
+ }
+ }
+ }
+ }
+ }
+ }
+
+ 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);
+ } else {
+ /* mct_EnDllShutdownSR */
+ }
+}
+
+static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ /* Wait (and block further access to dram) for all DCTs to be ready,
+ * by polling all InitDram bits and waiting for possible memory clear
+ * operations to be complete. Read MemClkFreqVal bit to see if
+ * the DIMMs are present in this node.
+ */
+ u8 Node;
+ u32 val;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ 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);
+ }
+ }
+ }
+ /* wait 750us before any memory access can be made. */
+ mct_Wait(15000);
+}
+
+static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Read MemClkFreqVal bit to see if the DIMMs are present in this node.
+ * If the DIMMs are present then set the DRAM Enable bit for this node.
+ *
+ * Setting dram init starts up the DCT state machine, initializes the
+ * dram devices with MRS commands, and kicks off any
+ * HW memory clear process that the chip is capable of. The sooner
+ * that dram init is set for all nodes, the faster the memory system
+ * initialization can complete. Thus, the init loop is unrolled into
+ * two loops so as to start the processeses for non BSP nodes sooner.
+ * This procedure will not wait for the process to finish.
+ * Synchronization is handled elsewhere.
+ */
+ u32 val;
+ u32 dev;
+ u32 reg_off = dct * 0x100;
+
+ dev = pDCTstat->dev_dct;
+ val = Get_NB32(dev, 0x94 + reg_off);
+ if (val & (1<<MemClkFreqVal)) {
+ mctHookBeforeDramInit(); /* generalized Hook */
+ if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)))
+ mct_DramInit(pMCTstat, pDCTstat, dct);
+ AfterDramInit_D(pDCTstat, dct);
+ mctHookAfterDramInit(); /* generalized Hook*/
+ }
+}
+
+static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 reg_end;
+ u32 dev = pDCTstat->dev_dct;
+ u32 reg = 0x40 + 0x100 * dct;
+ u32 val = 0;
+
+ if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+ reg_end = 0x78 + 0x100 * dct;
+ } else {
+ reg_end = 0xA4 + 0x100 * dct;
+ }
+
+ while(reg < reg_end) {
+ Set_NB32(dev, reg, val);
+ reg += 4;
+ }
+
+ val = 0;
+ dev = pDCTstat->dev_map;
+ reg = 0xF0;
+ Set_NB32(dev, reg, val);
+}
+
+static void SPD2ndTiming(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 i;
+ u16 Twr, Trtp;
+ u16 Trp, Trrd, Trcd, Tras, Trc;
+ u8 Trfc[4];
+ u16 Tfaw;
+ u32 DramTimingLo, DramTimingHi;
+ u8 tCK16x;
+ u16 Twtr;
+ u8 LDIMM;
+ u8 MTB16x;
+ u8 byte;
+ u32 dword;
+ u32 dev;
+ u32 reg_off;
+ u32 val;
+ u16 smbaddr;
+
+ /* Gather all DIMM mini-max values for cycle timing data */
+ Trp = 0;
+ Trrd = 0;
+ Trcd = 0;
+ Trtp = 0;
+ Tras = 0;
+ Trc = 0;
+ Twr = 0;
+ Twtr = 0;
+ for (i=0; i < 4; i++)
+ Trfc[i] = 0;
+ Tfaw = 0;
+
+ for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) {
+ LDIMM = i >> 1;
+ if (pDCTstat->DIMMValid & (1 << i)) {
+ smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+
+ val = mctRead_SPD(smbaddr, SPD_MTBDivisor); /* MTB=Dividend/Divisor */
+ MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
+ MTB16x /= val; /* transfer to MTB*16 */
+
+ byte = mctRead_SPD(smbaddr, SPD_tRPmin);
+ val = byte * MTB16x;
+ if (Trp < val)
+ Trp = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_tRRDmin);
+ val = byte * MTB16x;
+ if (Trrd < val)
+ Trrd = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_tRCDmin);
+ val = byte * MTB16x;
+ if (Trcd < val)
+ Trcd = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_tRTPmin);
+ val = byte * MTB16x;
+ if (Trtp < val)
+ Trtp = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_tWRmin);
+ val = byte * MTB16x;
+ if (Twr < val)
+ Twr = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_tWTRmin);
+ val = byte * MTB16x;
+ if (Twtr < val)
+ Twtr = val;
+
+ val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xFF;
+ val >>= 4;
+ val <<= 8;
+ val |= mctRead_SPD(smbaddr, SPD_tRCmin) & 0xFF;
+ val *= MTB16x;
+ if (Trc < val)
+ Trc = val;
+
+ byte = mctRead_SPD(smbaddr, SPD_Density) & 0xF;
+ if (Trfc[LDIMM] < byte)
+ Trfc[LDIMM] = byte;
+
+ val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xF;
+ val <<= 8;
+ val |= (mctRead_SPD(smbaddr, SPD_tRASmin) & 0xFF);
+ val *= MTB16x;
+ if (Tras < val)
+ Tras = val;
+
+ val = mctRead_SPD(smbaddr, SPD_Upper_tFAW) & 0xF;
+ val <<= 8;
+ val |= mctRead_SPD(smbaddr, SPD_tFAWmin) & 0xFF;
+ val *= MTB16x;
+ if (Tfaw < val)
+ Tfaw = val;
+ } /* Dimm Present */
+ }
+
+ /* Convert DRAM CycleTiming values and store into DCT structure */
+ byte = pDCTstat->DIMMAutoSpeed;
+ if (byte == 7)
+ tCK16x = 20;
+ else if (byte == 6)
+ tCK16x = 24;
+ else if (byte == 5)
+ tCK16x = 30;
+ else
+ tCK16x = 40;
+
+ /* Notes:
+ 1. All secondary time values given in SPDs are in binary with units of ns.
+ 2. Some time values are scaled by 16, in order to have least count of 0.25 ns
+ (more accuracy). JEDEC SPD spec. shows which ones are x1 and x4.
+ 3. Internally to this SW, cycle time, tCK16x, is scaled by 16 to match time values
+ */
+
+ /* Tras */
+ pDCTstat->DIMMTras = (u16)Tras;
+ val = Tras / tCK16x;
+ if (Tras % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TrasT)
+ val = Min_TrasT;
+ else if (val > Max_TrasT)
+ val = Max_TrasT;
+ pDCTstat->Tras = val;
+
+ /* Trp */
+ pDCTstat->DIMMTrp = Trp;
+ val = Trp / tCK16x;
+ if (Trp % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TrpT)
+ val = Min_TrpT;
+ else if (val > Max_TrpT)
+ val = Max_TrpT;
+ pDCTstat->Trp = val;
+
+ /*Trrd*/
+ pDCTstat->DIMMTrrd = Trrd;
+ val = Trrd / tCK16x;
+ if (Trrd % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TrrdT)
+ val = Min_TrrdT;
+ else if (val > Max_TrrdT)
+ val = Max_TrrdT;
+ pDCTstat->Trrd = val;
+
+ /* Trcd */
+ pDCTstat->DIMMTrcd = Trcd;
+ val = Trcd / tCK16x;
+ if (Trcd % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TrcdT)
+ val = Min_TrcdT;
+ else if (val > Max_TrcdT)
+ val = Max_TrcdT;
+ pDCTstat->Trcd = val;
+
+ /* Trc */
+ pDCTstat->DIMMTrc = Trc;
+ val = Trc / tCK16x;
+ if (Trc % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TrcT)
+ val = Min_TrcT;
+ else if (val > Max_TrcT)
+ val = Max_TrcT;
+ pDCTstat->Trc = val;
+
+ /* Trtp */
+ pDCTstat->DIMMTrtp = Trtp;
+ val = Trtp / tCK16x;
+ if (Trtp % tCK16x) {
+ val ++;
+ }
+ if (val < Min_TrtpT)
+ val = Min_TrtpT;
+ else if (val > Max_TrtpT)
+ val = Max_TrtpT;
+ pDCTstat->Trtp = val;
+
+ /* Twr */
+ pDCTstat->DIMMTwr = Twr;
+ val = Twr / tCK16x;
+ if (Twr % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TwrT)
+ val = Min_TwrT;
+ else if (val > Max_TwrT)
+ val = Max_TwrT;
+ pDCTstat->Twr = val;
+
+ /* Twtr */
+ pDCTstat->DIMMTwtr = Twtr;
+ val = Twtr / tCK16x;
+ if (Twtr % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TwtrT)
+ val = Min_TwtrT;
+ else if (val > Max_TwtrT)
+ val = Max_TwtrT;
+ pDCTstat->Twtr = val;
+
+ /* Trfc0-Trfc3 */
+ for (i=0; i<4; i++)
+ pDCTstat->Trfc[i] = Trfc[i];
+
+ /* Tfaw */
+ pDCTstat->DIMMTfaw = Tfaw;
+ val = Tfaw / tCK16x;
+ if (Tfaw % tCK16x) { /* round up number of busclocks */
+ val++;
+ }
+ if (val < Min_TfawT)
+ val = Min_TfawT;
+ else if (val > Max_TfawT)
+ val = Max_TfawT;
+ pDCTstat->Tfaw = val;
+
+ mctAdjustAutoCycTmg_D();
+
+ /* Program DRAM Timing values */
+ DramTimingLo = 0; /* Dram Timing Low init */
+ val = pDCTstat->CASL - 2; /* pDCTstat.CASL to reg. definition */
+ DramTimingLo |= val;
+
+ val = pDCTstat->Trcd - Bias_TrcdT;
+ DramTimingLo |= val<<4;
+
+ val = pDCTstat->Trp - Bias_TrpT;
+ val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val);
+ DramTimingLo |= val<<7;
+
+ val = pDCTstat->Trtp - Bias_TrtpT;
+ DramTimingLo |= val<<10;
+
+ val = pDCTstat->Tras - Bias_TrasT;
+ DramTimingLo |= val<<12;
+
+ val = pDCTstat->Trc - Bias_TrcT;
+ DramTimingLo |= val<<16;
+
+ val = pDCTstat->Trrd - Bias_TrrdT;
+ DramTimingLo |= val<<22;
+
+ DramTimingHi = 0; /* Dram Timing High init */
+ val = pDCTstat->Twtr - Bias_TwtrT;
+ DramTimingHi |= val<<8;
+
+ val = 2;
+ DramTimingHi |= val<<16;
+
+ val = 0;
+ for (i=4;i>0;i--) {
+ val <<= 3;
+ val |= Trfc[i-1];
+ }
+ DramTimingHi |= val << 20;
+
+ 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);
+
+ /* 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*/
+
+ if (pDCTstat->Speed > 4) {
+ DramTimingLo |= 1 << DisAutoRefresh;
+ }
+ DramTimingHi |= 0x000018FF;
+ Set_NB32(dev, 0x8c + reg_off, DramTimingHi); /*DCT Timing Hi*/
+
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+}
+
+static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Initialize DCT Timing registers as per DIMM SPD.
+ * For primary timing (T, CL) use best case T value.
+ * For secondary timing params., use most aggressive settings
+ * of slowest DIMM.
+ *
+ * There are three components to determining "maximum frequency":
+ * SPD component, Bus load component, and "Preset" max frequency
+ * component.
+ *
+ * The SPD component is a function of the min cycle time specified
+ * by each DIMM, and the interaction of cycle times from all DIMMs
+ * in conjunction with CAS latency. The SPD component only applies
+ * when user timing mode is 'Auto'.
+ *
+ * The Bus load component is a limiting factor determined by electrical
+ * characteristics on the bus as a result of varying number of device
+ * loads. The Bus load component is specific to each platform but may
+ * also be a function of other factors. The bus load component only
+ * applies when user timing mode is 'Auto'.
+ *
+ * The Preset component is subdivided into three items and is
+ * the minimum of the set: Silicon revision, user limit
+ * setting when user timing mode is 'Auto' and memclock mode
+ * is 'Limit', OEM build specification of the maximum
+ * frequency. The Preset component is only applies when user
+ * timing mode is 'Auto'.
+ */
+
+ /* Get primary timing (CAS Latency and Cycle Time) */
+ if (pDCTstat->Speed == 0) {
+ mctGet_MaxLoadFreq(pDCTstat);
+
+ /* and Factor in presets (setup options, Si cap, etc.) */
+ GetPresetmaxF_D(pMCTstat, pDCTstat);
+
+ /* 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;
+
+ }
+ mct_AfterGetCLT(pMCTstat, pDCTstat, dct);
+
+ SPD2ndTiming(pMCTstat, pDCTstat, dct);
+
+ printk(BIOS_DEBUG, "AutoCycTiming: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "AutoCycTiming: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "AutoCycTiming: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "AutoCycTiming: Done\n\n");
+
+ mctHookAfterAutoCycTmg();
+
+ return pDCTstat->ErrCode;
+}
+
+static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Get max frequency from OEM platform definition, from any user
+ * override (limiting) of max frequency, and from any Si Revision
+ * Specific information. Return the least of these three in
+ * DCTStatStruc.PresetmaxFreq.
+ */
+ u16 proposedFreq;
+ u16 word;
+
+ /* Get CPU Si Revision defined limit (NPT) */
+ proposedFreq = 533; /* Rev F0 programmable max memclock is */
+
+ /*Get User defined limit if "limit" mode */
+ if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) {
+ word = Get_Fk_D(mctGet_NVbits(NV_MemCkVal) + 1);
+ if (word < proposedFreq)
+ proposedFreq = word;
+
+ /* Get Platform defined limit */
+ word = mctGet_NVbits(NV_MAX_MEMCLK);
+ if (word < proposedFreq)
+ proposedFreq = word;
+
+ word = pDCTstat->PresetmaxFreq;
+ if (word > proposedFreq)
+ word = proposedFreq;
+
+ pDCTstat->PresetmaxFreq = word;
+ }
+ /* Check F3xE8[DdrMaxRate] for maximum DRAM data rate support */
+}
+
+static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Find the best T and CL primary timing parameter pair, per Mfg.,
+ * for the given set of DIMMs, and store into DCTStatStruc
+ * (.DIMMAutoSpeed and .DIMMCASL). See "Global relationship between
+ * index values and item values" for definition of CAS latency
+ * index (j) and Frequency index (k).
+ */
+ u8 i, CASLatLow, CASLatHigh;
+ u16 tAAmin16x;
+ u8 MTB16x;
+ u16 tCKmin16x;
+ u16 tCKproposed16x;
+ u8 CLactual, CLdesired, CLT_Fail;
+
+ u8 smbaddr, byte, bytex;
+
+ CASLatLow = 0xFF;
+ CASLatHigh = 0xFF;
+ tAAmin16x = 0;
+ tCKmin16x = 0;
+ CLT_Fail = 0;
+
+ for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
+ if (pDCTstat->DIMMValid & (1 << i)) {
+ smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i));
+ /* Step 1: Determine the common set of supported CAS Latency
+ * values for all modules on the memory channel using the CAS
+ * Latencies Supported in SPD bytes 14 and 15.
+ */
+ byte = mctRead_SPD(smbaddr, SPD_CASLow);
+ CASLatLow &= byte;
+ byte = mctRead_SPD(smbaddr, SPD_CASHigh);
+ CASLatHigh &= byte;
+ /* Step 2: Determine tAAmin(all) which is the largest tAAmin
+ value for all modules on the memory channel (SPD byte 16). */
+ byte = mctRead_SPD(smbaddr, SPD_MTBDivisor);
+
+ MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4);
+ MTB16x /= byte; /* transfer to MTB*16 */
+
+ byte = mctRead_SPD(smbaddr, SPD_tAAmin);
+ if (tAAmin16x < byte * MTB16x)
+ tAAmin16x = byte * MTB16x;
+ /* Step 3: Determine tCKmin(all) which is the largest tCKmin
+ value for all modules on the memory channel (SPD byte 12). */
+ byte = mctRead_SPD(smbaddr, SPD_tCKmin);
+
+ if (tCKmin16x < byte * MTB16x)
+ tCKmin16x = byte * MTB16x;
+ }
+ }
+ /* calculate tCKproposed16x */
+ 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;
+ }
+ else {
+ pDCTstat->TargetFreq = 4;
+ tCKproposed16x = 40;
+ }
+ /* Running through this loop twice:
+ - First time find tCL at target frequency
+ - Second tim find tCL at 400MHz */
+
+ for (;;) {
+ CLT_Fail = 0;
+ /* Step 4: For a proposed tCK value (tCKproposed) between tCKmin(all) and tCKmax,
+ determine the desired CAS Latency. If tCKproposed is not a standard JEDEC
+ value (2.5, 1.875, 1.5, or 1.25 ns) then tCKproposed must be adjusted to the
+ next lower standard tCK value for calculating CLdesired.
+ CLdesired = ceiling ( tAAmin(all) / tCKproposed )
+ where tAAmin is defined in Byte 16. The ceiling function requires that the
+ quotient be rounded up always. */
+ CLdesired = tAAmin16x / tCKproposed16x;
+ if (tAAmin16x % tCKproposed16x)
+ CLdesired ++;
+ /* Step 5: Chose an actual CAS Latency (CLactual) that is greather than or equal
+ to CLdesired and is supported by all modules on the memory channel as
+ determined in step 1. If no such value exists, choose a higher tCKproposed
+ value and repeat steps 4 and 5 until a solution is found. */
+ for (i = 0, CLactual = 4; i < 15; i++, CLactual++) {
+ if ((CASLatHigh << 8 | CASLatLow) & (1 << i)) {
+ if (CLdesired <= CLactual)
+ break;
+ }
+ }
+ if (i == 15)
+ CLT_Fail = 1;
+ /* Step 6: Once the calculation of CLactual is completed, the BIOS must also
+ verify that this CAS Latency value does not exceed tAAmax, which is 20 ns
+ for all DDR3 speed grades, by multiplying CLactual times tCKproposed. If
+ not, choose a lower CL value and repeat steps 5 and 6 until a solution is found. */
+ if (CLactual * tCKproposed16x > 320)
+ CLT_Fail = 1;
+ /* get CL and T */
+ if (!CLT_Fail) {
+ bytex = CLactual - 2;
+ 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 (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
+ pDCTstat->DIMMAutoSpeed = byte;
+ pDCTstat->DIMMCASL = bytex;
+ break;
+ } else {
+ pDCTstat->TargetCASL = bytex;
+ tCKproposed16x = 40;
+ }
+ } else {
+ pDCTstat->DIMMAutoSpeed = byte;
+ pDCTstat->DIMMCASL = bytex;
+ break;
+ }
+ }
+
+ printk(BIOS_DEBUG, "SPDGetTCL_D: DIMMCASL %x\n", pDCTstat->DIMMCASL);
+ printk(BIOS_DEBUG, "SPDGetTCL_D: DIMMAutoSpeed %x\n", pDCTstat->DIMMAutoSpeed);
+
+ printk(BIOS_DEBUG, "SPDGetTCL_D: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "SPDGetTCL_D: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "SPDGetTCL_D: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "SPDGetTCL_D: Done\n\n");
+}
+
+static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 dev;
+ u32 reg;
+ u32 val;
+
+ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);
+
+ if (pDCTstat->GangedMode == 1) {
+ mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
+ }
+
+ 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_PlatformSpec(pMCTstat, pDCTstat, dct);
+ if (pDCTstat->DIMMAutoSpeed == 4)
+ InitPhyCompensation(pMCTstat, pDCTstat, dct);
+ mctHookAfterPSCfg();
+
+ return pDCTstat->ErrCode;
+}
+
+static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 DramControl, DramTimingLo, Status;
+ u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2;
+ u32 val;
+ u32 reg_off;
+ u32 dev;
+ u16 word;
+ u32 dword;
+ u8 byte;
+
+ DramConfigLo = 0;
+ DramConfigHi = 0;
+ DramConfigMisc = 0;
+ DramConfigMisc2 = 0;
+
+ /* set bank addessing and Masks, plus CS pops */
+ SPDSetBanks_D(pMCTstat, pDCTstat, dct);
+ if (pDCTstat->ErrCode == SC_StopError)
+ goto AutoConfig_exit;
+
+ /* map chip-selects into local address space */
+ StitchMemory_D(pMCTstat, pDCTstat, dct);
+ InterleaveBanks_D(pMCTstat, pDCTstat, dct);
+
+ /* temp image of status (for convenience). RO usage! */
+ 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*/
+
+ /* FIXME: Skip mct_checkForDxSupport */
+ /* REV_CALL mct_DoRdPtrInit if not Dx */
+ if (pDCTstat->LogicalCPUID & AMD_DR_Bx)
+ val = 5;
+ else
+ val = 6;
+ DramControl &= ~0xFF;
+ DramControl |= val; /* RdPrtInit = 6 for Cx CPU */
+
+ if (mctGet_NVbits(NV_CLKHZAltVidC3))
+ DramControl |= 1<<16; /* check */
+
+ DramControl |= 0x00002A00;
+
+ /* FIXME: Skip for Ax versions */
+ /* callback not required - if (!mctParityControl_D()) */
+ if (Status & (1 << SB_128bitmode))
+ DramConfigLo |= 1 << Width128; /* 128-bit mode (normal) */
+
+ word = dct;
+ dword = X4Dimm;
+ while (word < 8) {
+ if (pDCTstat->Dimmx4Present & (1 << word))
+ DramConfigLo |= 1 << dword; /* X4Dimm[3:0] */
+ word++;
+ word++;
+ dword++;
+ }
+
+ if (!(Status & (1 << SB_Registered)))
+ DramConfigLo |= 1 << UnBuffDimm; /* Unbufferd DIMMs */
+
+ if (mctGet_NVbits(NV_ECC_CAP))
+ if (Status & (1 << SB_ECCDIMMs))
+ if ( mctGet_NVbits(NV_ECC))
+ DramConfigLo |= 1 << DimmEcEn;
+
+ DramConfigLo = mct_DisDllShutdownSR(pMCTstat, pDCTstat, DramConfigLo, dct);
+
+ /* Build Dram Config Hi Register Value */
+ dword = pDCTstat->Speed;
+ DramConfigHi |= dword - 1; /* get MemClk encoding */
+ DramConfigHi |= 1 << MemClkFreqVal;
+
+ if (Status & (1 << SB_Registered))
+ if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
+ /* set only if x8 Registered DIMMs in System*/
+ DramConfigHi |= 1 << RDqsEn;
+
+ if (mctGet_NVbits(NV_CKE_CTL))
+ /*Chip Select control of CKE*/
+ DramConfigHi |= 1 << 16;
+
+ /* Control Bank Swizzle */
+ if (0) /* call back not needed mctBankSwizzleControl_D()) */
+ DramConfigHi &= ~(1 << BankSwizzleMode);
+ else
+ DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */
+
+ /* Check for Quadrank DIMM presence */
+ if ( pDCTstat->DimmQRPresent != 0) {
+ byte = mctGet_NVbits(NV_4RANKType);
+ if (byte == 2)
+ DramConfigHi |= 1 << 17; /* S4 (4-Rank SO-DIMMs) */
+ else if (byte == 1)
+ DramConfigHi |= 1 << 18; /* R4 (4-Rank Registered DIMMs) */
+ }
+
+ if (0) /* call back not needed mctOverrideDcqBypMax_D ) */
+ val = mctGet_NVbits(NV_BYPMAX);
+ else
+ val = 0x0f; /* recommended setting (default) */
+ DramConfigHi |= val << 24;
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Bx))
+ DramConfigHi |= 1 << DcqArbBypassEn;
+
+ /* Build MemClkDis Value from Dram Timing Lo and
+ Dram Config Misc Registers
+ 1. We will assume that MemClkDis field has been preset prior to this
+ point.
+ 2. We will only set MemClkDis bits if a DIMM is NOT present AND if:
+ NV_AllMemClks <>0 AND SB_DiagClks ==0 */
+
+ /* Dram Timing Low (owns Clock Enable bits) */
+ DramTimingLo = Get_NB32(dev, 0x88 + reg_off);
+ if (mctGet_NVbits(NV_AllMemClks) == 0) {
+ /* Special Jedec SPD diagnostic bit - "enable all clocks" */
+ if (!(pDCTstat->Status & (1<<SB_DiagClks))) {
+ const u8 *p;
+ const u32 *q;
+ p = Tab_ManualCLKDis;
+ q = (u32 *)p;
+
+ byte = mctGet_NVbits(NV_PACK_TYPE);
+ if (byte == PT_L1)
+ p = Tab_L1CLKDis;
+ else if (byte == PT_M2 || byte == PT_AS)
+ p = Tab_AM3CLKDis;
+ else
+ p = Tab_S1CLKDis;
+
+ dword = 0;
+ byte = 0xFF;
+ while(dword < MAX_CS_SUPPORTED) {
+ if (pDCTstat->CSPresent & (1<<dword)){
+ /* re-enable clocks for the enabled CS */
+ val = p[dword];
+ byte &= ~val;
+ }
+ dword++ ;
+ }
+ 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);
+
+ /* 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);
+ DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2);
+ Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2);
+ Set_NB32(dev, 0x90 + reg_off, DramConfigLo);
+ ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
+ dword = Get_NB32(dev, 0x94 + reg_off);
+ DramConfigHi |= dword;
+ mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi);
+ mct_EarlyArbEn_D(pMCTstat, pDCTstat);
+ mctHookAfterAutoCfg();
+
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+
+ printk(BIOS_DEBUG, "AutoConfig: Status %x\n", pDCTstat->Status);
+ 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;
+}
+
+static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Set bank addressing, program Mask values and build a chip-select
+ * population map. This routine programs PCI 0:24N:2x80 config register
+ * and PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3).
+ */
+ u8 ChipSel, Rows, Cols, Ranks, Banks;
+ u32 BankAddrReg, csMask;
+
+ 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) {
+ byte = ChipSel;
+ if ((pDCTstat->Status & (1 << SB_64MuxedMode)) && ChipSel >=4)
+ byte -= 3;
+
+ if (pDCTstat->DIMMValid & (1<<byte)) {
+ smbaddr = Get_DIMMAddress_D(pDCTstat, (ChipSel + dct));
+
+ byte = mctRead_SPD(smbaddr, SPD_Addressing);
+ Rows = (byte >> 3) & 0x7; /* Rows:0b=12-bit,... */
+ Cols = byte & 0x7; /* Cols:0b=9-bit,... */
+
+ byte = mctRead_SPD(smbaddr, SPD_Density);
+ Banks = (byte >> 4) & 7; /* Banks:0b=3-bit,... */
+
+ byte = mctRead_SPD(smbaddr, SPD_Organization);
+ Ranks = ((byte >> 3) & 7) + 1;
+
+ /* Configure Bank encoding
+ * Use a 6-bit key into a lookup table.
+ * Key (index) = RRRBCC, where CC is the number of Columns minus 9,
+ * RRR is the number of Rows minus 12, and B is the number of banks
+ * minus 3.
+ */
+ byte = Cols;
+ if (Banks == 1)
+ byte |= 4;
+
+ byte |= Rows << 3; /* RRRBCC internal encode */
+
+ for (dword=0; dword < 13; dword++) {
+ if (byte == Tab_BankAddr[dword])
+ break;
+ }
+
+ if (dword > 12)
+ continue;
+
+ /* bit no. of CS field in address mapping reg.*/
+ dword <<= (ChipSel<<1);
+ BankAddrReg |= dword;
+
+ /* Mask value=(2pow(rows+cols+banks+3)-1)>>8,
+ or 2pow(rows+cols+banks-5)-1*/
+ csMask = 0;
+
+ byte = Rows + Cols; /* cl=rows+cols*/
+ byte += 21; /* row:12+col:9 */
+ byte -= 2; /* 3 banks - 5 */
+
+ if (pDCTstat->Status & (1 << SB_128bitmode))
+ byte++; /* double mask size if in 128-bit mode*/
+
+ csMask |= 1 << byte;
+ csMask--;
+
+ /*set ChipSelect population indicator even bits*/
+ pDCTstat->CSPresent |= (1<<ChipSel);
+ if (Ranks >= 2)
+ /*set ChipSelect population indicator odd bits*/
+ pDCTstat->CSPresent |= 1 << (ChipSel + 1);
+
+ reg = 0x60+(ChipSel<<1) + reg_off; /*Dram CS Mask Register */
+ val = csMask;
+ val &= 0x1FF83FE0; /* Mask out reserved bits.*/
+ Set_NB32(dev, reg, val);
+ } else {
+ if (pDCTstat->DIMMSPDCSE & (1<<ChipSel))
+ pDCTstat->CSTestFail |= (1<<ChipSel);
+ } /* if DIMMValid*/
+ } /* while ChipSel*/
+
+ SetCSTriState(pMCTstat, pDCTstat, dct);
+ SetCKETriState(pMCTstat, pDCTstat, dct);
+ SetODTTriState(pMCTstat, pDCTstat, dct);
+
+ if (pDCTstat->Status & (1 << SB_128bitmode)) {
+ SetCSTriState(pMCTstat, pDCTstat, 1); /* force dct1) */
+ SetCKETriState(pMCTstat, pDCTstat, 1); /* force dct1) */
+ SetODTTriState(pMCTstat, pDCTstat, 1); /* force dct1) */
+ }
+
+ word = pDCTstat->CSPresent;
+ mctGetCS_ExcludeMap(); /* mask out specified chip-selects */
+ word ^= pDCTstat->CSPresent;
+ pDCTstat->CSTestFail |= word; /* enable ODT to disabled DIMMs */
+ if (!pDCTstat->CSPresent)
+ pDCTstat->ErrCode = SC_StopError;
+
+ reg = 0x80 + reg_off; /* Bank Addressing Register */
+ Set_NB32(dev, reg, BankAddrReg);
+
+ pDCTstat->CSPresent_DCT[dct] = pDCTstat->CSPresent;
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+
+ printk(BIOS_DEBUG, "SPDSetBanks: CSPresent %x\n", pDCTstat->CSPresent_DCT[dct]);
+ printk(BIOS_DEBUG, "SPDSetBanks: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "SPDSetBanks: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "SPDSetBanks: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "SPDSetBanks: Done\n\n");
+}
+
+static void SPDCalcWidth_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Per SPDs, check the symmetry of DIMM pairs (DIMM on Channel A
+ * matching with DIMM on Channel B), the overall DIMM population,
+ * and determine the width mode: 64-bit, 64-bit muxed, 128-bit.
+ */
+ u8 i;
+ u8 smbaddr, smbaddr1;
+ u8 byte, byte1;
+
+ /* Check Symmetry of Channel A and Channel B DIMMs
+ (must be matched for 128-bit mode).*/
+ for (i=0; i < MAX_DIMMS_SUPPORTED; i += 2) {
+ if ((pDCTstat->DIMMValid & (1 << i)) && (pDCTstat->DIMMValid & (1<<(i+1)))) {
+ smbaddr = Get_DIMMAddress_D(pDCTstat, i);
+ smbaddr1 = Get_DIMMAddress_D(pDCTstat, i+1);
+
+ byte = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7;
+ byte1 = mctRead_SPD(smbaddr1, SPD_Addressing) & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+ byte = mctRead_SPD(smbaddr, SPD_Density) & 0x0f;
+ byte1 = mctRead_SPD(smbaddr1, SPD_Density) & 0x0f;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+ byte = mctRead_SPD(smbaddr, SPD_Organization) & 0x7;
+ byte1 = mctRead_SPD(smbaddr1, SPD_Organization) & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+ byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3) & 0x7;
+ byte1 = (mctRead_SPD(smbaddr1, SPD_Organization) >> 3) & 0x7;
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+ byte = mctRead_SPD(smbaddr, SPD_DMBANKS) & 7; /* #ranks-1 */
+ byte1 = mctRead_SPD(smbaddr1, SPD_DMBANKS) & 7; /* #ranks-1 */
+ if (byte != byte1) {
+ pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
+ break;
+ }
+
+ }
+ }
+
+}
+
+static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Requires that Mask values for each bank be programmed first and that
+ * the chip-select population indicator is correctly set.
+ */
+ u8 b = 0;
+ u32 nxtcsBase, curcsBase;
+ u8 p, q;
+ u32 Sizeq, BiggestBank;
+ u8 _DSpareEn;
+
+ u16 word;
+ u32 dev;
+ u32 reg;
+ u32 reg_off;
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+ reg_off = 0x100 * dct;
+
+ _DSpareEn = 0;
+
+ /* CS Sparing 1=enabled, 0=disabled */
+ if (mctGet_NVbits(NV_CS_SpareCTL) & 1) {
+ if (MCT_DIMM_SPARE_NO_WARM) {
+ /* Do no warm-reset DIMM spare */
+ if (pMCTstat->GStatus & 1 << GSB_EnDIMMSpareNW) {
+ word = pDCTstat->CSPresent;
+ val = bsf(word);
+ word &= ~(1<<val);
+ if (word)
+ /* Make sure at least two chip-selects are available */
+ _DSpareEn = 1;
+ else
+ pDCTstat->ErrStatus |= 1 << SB_SpareDis;
+ }
+ } else {
+ if (!mctGet_NVbits(NV_DQSTrainCTL)) { /*DQS Training 1=enabled, 0=disabled */
+ word = pDCTstat->CSPresent;
+ val = bsf(word);
+ word &= ~(1 << val);
+ if (word)
+ /* Make sure at least two chip-selects are available */
+ _DSpareEn = 1;
+ else
+ pDCTstat->ErrStatus |= 1 << SB_SpareDis;
+ }
+ }
+ }
+
+ nxtcsBase = 0; /* Next available cs base ADDR[39:8] */
+ for (p=0; p < MAX_DIMMS_SUPPORTED; p++) {
+ 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);
+ if (!(val & 3)) { /* (CSEnable|Spare==1)bank is enabled already? */
+ reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/
+ val = Get_NB32(dev, reg);
+ val >>= 19;
+ val++;
+ val <<= 19;
+ Sizeq = val; /* never used */
+ if (val > BiggestBank) {
+ /*Bingo! possibly Map this chip-select next! */
+ BiggestBank = val;
+ b = q;
+ }
+ }
+ } /*if bank present */
+ } /* while q */
+ if (BiggestBank !=0) {
+ curcsBase = nxtcsBase; /* curcsBase=nxtcsBase*/
+ /* DRAM CS Base b Address Register offset */
+ reg = 0x40 + (b << 2) + reg_off;
+ if (_DSpareEn) {
+ BiggestBank = 0;
+ val = 1 << Spare; /* Spare Enable*/
+ } else {
+ val = curcsBase;
+ val |= 1 << CSEnable; /* Bank Enable */
+ }
+ if (((reg - 0x40) >> 2) & 1) {
+ if (!(pDCTstat->Status & (1 << SB_Registered))) {
+ u16 dimValid;
+ dimValid = pDCTstat->DIMMValid;
+ if (dct & 1)
+ dimValid <<= 1;
+ if ((dimValid & pDCTstat->MirrPresU_NumRegR) != 0) {
+ val |= 1 << onDimmMirror;
+ }
+ }
+ }
+ Set_NB32(dev, reg, val);
+ if (_DSpareEn)
+ _DSpareEn = 0;
+ else
+ /* let nxtcsBase+=Size[b] */
+ nxtcsBase += BiggestBank;
+ }
+
+ /* bank present but disabled?*/
+ if ( pDCTstat->CSTestFail & (1 << p)) {
+ /* DRAM CS Base b Address Register offset */
+ reg = (p << 2) + 0x40 + reg_off;
+ val = 1 << TestFail;
+ Set_NB32(dev, reg, val);
+ }
+ }
+
+ if (nxtcsBase) {
+ pDCTstat->DCTSysLimit = nxtcsBase - 1;
+ mct_AfterStitchMemory(pMCTstat, pDCTstat, dct);
+ }
+
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+
+ printk(BIOS_DEBUG, "StitchMemory: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "StitchMemory: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "StitchMemory: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "StitchMemory: Done\n\n");
+}
+
+static u16 Get_Fk_D(u8 k)
+{
+ return Table_F_k[k]; /* FIXME: k or k<<1 ? */
+}
+
+static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Check DIMMs present, verify checksum, flag SDRAM type,
+ * build population indicator bitmaps, and preload bus loading
+ * of DIMMs into DCTStatStruc.
+ * MAAload=number of devices on the "A" bus.
+ * MABload=number of devices on the "B" bus.
+ * MAAdimms=number of DIMMs on the "A" bus slots.
+ * MABdimms=number of DIMMs on the "B" bus slots.
+ * DATAAload=number of ranks on the "A" bus slots.
+ * DATABload=number of ranks on the "B" bus slots.
+ */
+ u16 i, j;
+ u8 smbaddr;
+ u8 SPDCtrl;
+ u16 RegDIMMPresent, MaxDimms;
+ u8 devwidth;
+ u16 DimmSlots;
+ u8 byte = 0, bytex;
+
+ /* preload data structure with addrs */
+ mctGet_DIMMAddr(pDCTstat, pDCTstat->Node_ID);
+
+ DimmSlots = MaxDimms = mctGet_NVbits(NV_MAX_DIMMS);
+
+ SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT);
+
+ RegDIMMPresent = 0;
+ pDCTstat->DimmQRPresent = 0;
+
+ for (i = 0; i< MAX_DIMMS_SUPPORTED; i++) {
+ if (i >= MaxDimms)
+ break;
+
+ if ((pDCTstat->DimmQRPresent & (1 << i)) || (i < DimmSlots)) {
+ int status;
+ smbaddr = Get_DIMMAddress_D(pDCTstat, i);
+ status = mctRead_SPD(smbaddr, SPD_ByteUse);
+ if (status >= 0) { /* SPD access is ok */
+ pDCTstat->DIMMPresent |= 1 << i;
+ if (crcCheck(smbaddr)) { /* CRC is OK */
+ byte = mctRead_SPD(smbaddr, SPD_TYPE);
+ if (byte == JED_DDR3SDRAM) {
+ /*Dimm is 'Present'*/
+ pDCTstat->DIMMValid |= 1 << i;
+ }
+ } else {
+ pDCTstat->DIMMSPDCSE = 1 << i;
+ if (SPDCtrl == 0) {
+ pDCTstat->ErrStatus |= 1 << SB_DIMMChkSum;
+ pDCTstat->ErrCode = SC_StopError;
+ } else {
+ /*if NV_SPDCHK_RESTRT is set to 1, ignore faulty SPD checksum*/
+ pDCTstat->ErrStatus |= 1<<SB_DIMMChkSum;
+ byte = mctRead_SPD(smbaddr, SPD_TYPE);
+ if (byte == JED_DDR3SDRAM)
+ pDCTstat->DIMMValid |= 1 << i;
+ }
+ }
+ /* Check module type */
+ byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE) & 0x7;
+ if (byte == JED_RDIMM || byte == JED_MiniRDIMM)
+ RegDIMMPresent |= 1 << i;
+ /* Check ECC capable */
+ byte = mctRead_SPD(smbaddr, SPD_BusWidth);
+ if (byte & JED_ECC) {
+ /* DIMM is ECC capable */
+ pDCTstat->DimmECCPresent |= 1 << i;
+ }
+ /* Check if x4 device */
+ devwidth = mctRead_SPD(smbaddr, SPD_Organization) & 0x7; /* 0:x4,1:x8,2:x16 */
+ if (devwidth == 0) {
+ /* DIMM is made with x4 or x16 drams */
+ pDCTstat->Dimmx4Present |= 1 << i;
+ } else if (devwidth == 1) {
+ pDCTstat->Dimmx8Present |= 1 << i;
+ } else if (devwidth == 2) {
+ pDCTstat->Dimmx16Present |= 1 << i;
+ }
+
+ byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3);
+ byte &= 7;
+ if (byte == 3) { /* 4ranks */
+ /* if any DIMMs are QR, we have to make two passes through DIMMs*/
+ if ( pDCTstat->DimmQRPresent == 0) {
+ MaxDimms <<= 1;
+ }
+ if (i < DimmSlots) {
+ pDCTstat->DimmQRPresent |= (1 << i) | (1 << (i+4));
+ } else {
+ pDCTstat->MAdimms[i & 1] --;
+ }
+ byte = 1; /* upper two ranks of QR DIMM will be counted on another DIMM number iteration*/
+ } else if (byte == 1) { /* 2ranks */
+ pDCTstat->DimmDRPresent |= 1 << i;
+ }
+ bytex = devwidth;
+ if (devwidth == 0)
+ bytex = 16;
+ else if (devwidth == 1)
+ bytex = 8;
+ else if (devwidth == 2)
+ bytex = 4;
+
+ byte++; /* al+1=rank# */
+ if (byte == 2)
+ bytex <<= 1; /*double Addr bus load value for dual rank DIMMs*/
+
+ j = i & (1<<0);
+ pDCTstat->DATAload[j] += byte; /*number of ranks on DATA bus*/
+ pDCTstat->MAload[j] += bytex; /*number of devices on CMD/ADDR bus*/
+ pDCTstat->MAdimms[j]++; /*number of DIMMs on A bus */
+
+ /* check address mirror support for unbuffered dimm */
+ /* check number of registers on a dimm for registered dimm */
+ byte = mctRead_SPD(smbaddr, SPD_AddressMirror);
+ if (RegDIMMPresent & (1 << i)) {
+ if ((byte & 3) > 1)
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+ } else {
+ if ((byte & 1) == 1)
+ pDCTstat->MirrPresU_NumRegR |= 1 << i;
+ }
+ /* Get byte62: Reference Raw Card information. We dont need it now. */
+ /* byte = mctRead_SPD(smbaddr, 62); */
+ /* Get Control word values for RC3. We dont need it. */
+ byte = mctRead_SPD(smbaddr, 70);
+ pDCTstat->CtrlWrd3 |= (byte >> 4) << (i << 2); /* C3 = SPD byte 70 [7:4] */
+ /* Get Control word values for RC4, and RC5 */
+ byte = mctRead_SPD(smbaddr, 71);
+ pDCTstat->CtrlWrd4 |= (byte & 0xFF) << (i << 2); /* RC4 = SPD byte 71 [3:0] */
+ pDCTstat->CtrlWrd5 |= (byte >> 4) << (i << 2); /* RC5 = SPD byte 71 [7:4] */
+ }
+ }
+ }
+ 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: 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);
+ printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx8Present=%x\n", pDCTstat->Dimmx8Present);
+ printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx16Present=%x\n", pDCTstat->Dimmx16Present);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DimmPlPresent=%x\n", pDCTstat->DimmPlPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DimmDRPresent=%x\n", pDCTstat->DimmDRPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DimmQRPresent=%x\n", pDCTstat->DimmQRPresent);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DATAload[0]=%x\n", pDCTstat->DATAload[0]);
+ printk(BIOS_DEBUG, "\t DIMMPresence: MAload[0]=%x\n", pDCTstat->MAload[0]);
+ printk(BIOS_DEBUG, "\t DIMMPresence: MAdimms[0]=%x\n", pDCTstat->MAdimms[0]);
+ printk(BIOS_DEBUG, "\t DIMMPresence: DATAload[1]=%x\n", pDCTstat->DATAload[1]);
+ printk(BIOS_DEBUG, "\t DIMMPresence: MAload[1]=%x\n", pDCTstat->MAload[1]);
+ printk(BIOS_DEBUG, "\t DIMMPresence: MAdimms[1]=%x\n", pDCTstat->MAdimms[1]);
+
+ if (pDCTstat->DIMMValid != 0) { /* If any DIMMs are present...*/
+ if (RegDIMMPresent != 0) {
+ if ((RegDIMMPresent ^ 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_Registered;
+ }
+ }
+ if (pDCTstat->DimmECCPresent != 0) {
+ if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) {
+ /* all DIMMs are ECC capable */
+ pDCTstat->Status |= 1<<SB_ECCDIMMs;
+ }
+ }
+ if (pDCTstat->DimmPARPresent != 0) {
+ if ((pDCTstat->DimmPARPresent ^ pDCTstat->DIMMValid) == 0) {
+ /*all DIMMs are Parity capable */
+ pDCTstat->Status |= 1<<SB_PARDIMMs;
+ }
+ }
+ } else {
+ /* no DIMMs present or no DIMMs that qualified. */
+ pDCTstat->ErrStatus |= 1<<SB_NoDimms;
+ pDCTstat->ErrCode = SC_StopError;
+ }
+
+ printk(BIOS_DEBUG, "\t DIMMPresence: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "\t DIMMPresence: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "\t DIMMPresence: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "\t DIMMPresence: Done\n\n");
+
+ mctHookAfterDIMMpre();
+
+ return pDCTstat->ErrCode;
+}
+
+static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i)
+{
+ u8 *p;
+
+ p = pDCTstat->DIMMAddr;
+ /* mct_BeforeGetDIMMAddress(); */
+ return p[i];
+}
+
+static void mct_initDCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+ u8 err_code;
+
+ /* Config. DCT0 for Ganged or unganged mode */
+ DCTInit_D(pMCTstat, pDCTstat, 0);
+ if (pDCTstat->ErrCode == SC_FatalErr) {
+ /* Do nothing goto exitDCTInit; any fatal errors? */
+ } else {
+ /* Configure DCT1 if unganged and enabled*/
+ if (!pDCTstat->GangedMode) {
+ if ( pDCTstat->DIMMValidDCT[1] > 0) {
+ err_code = pDCTstat->ErrCode; /* save DCT0 errors */
+ pDCTstat->ErrCode = 0;
+ DCTInit_D(pMCTstat, pDCTstat, 1);
+ if (pDCTstat->ErrCode == 2) /* DCT1 is not Running */
+ pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */
+ } else {
+ val = 1 << DisDramInterface;
+ Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val);
+ }
+ }
+ }
+/* exitDCTInit: */
+}
+
+static void mct_DramInit(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat);
+ mct_DramInit_Sw_D(pMCTstat, pDCTstat, dct);
+ /* mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); */
+}
+
+static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 byte;
+ u8 bytex;
+ u32 val;
+ u32 reg;
+
+ byte = bytex = pDCTstat->DIMMValid;
+ bytex &= 0x55; /* CHA DIMM pop */
+ pDCTstat->DIMMValidDCT[0] = bytex;
+
+ byte &= 0xAA; /* CHB DIMM popa */
+ byte >>= 1;
+ pDCTstat->DIMMValidDCT[1] = byte;
+
+ if (byte != bytex) {
+ pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO);
+ } else {
+ byte = mctGet_NVbits(NV_Unganged);
+ if (byte)
+ pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); /* Set temp. to avoid setting of ganged mode */
+
+ if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) {
+ pDCTstat->GangedMode = 1;
+ /* valid 128-bit mode population. */
+ pDCTstat->Status |= 1 << SB_128bitmode;
+ reg = 0x110;
+ val = Get_NB32(pDCTstat->dev_dct, reg);
+ val |= 1 << DctGangEn;
+ Set_NB32(pDCTstat->dev_dct, reg, val);
+ }
+ if (byte) /* NV_Unganged */
+ pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM missmatch error */
+ }
+ return pDCTstat->ErrCode;
+}
+
+u32 Get_NB32(u32 dev, u32 reg)
+{
+ return pci_read_config32(dev, reg);
+}
+
+void Set_NB32(u32 dev, u32 reg, u32 val)
+{
+ pci_write_config32(dev, reg, val);
+}
+
+
+u32 Get_NB32_index(u32 dev, u32 index_reg, u32 index)
+{
+ u32 dword;
+
+ Set_NB32(dev, index_reg, index);
+ dword = Get_NB32(dev, index_reg+0x4);
+
+ return dword;
+}
+
+void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data)
+{
+ Set_NB32(dev, index_reg, index);
+ Set_NB32(dev, index_reg + 0x4, 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 {
+ dword = Get_NB32(dev, index_reg);
+ } while (!(dword & (1 << DctAccessDone)));
+ dword = Get_NB32(dev, index_reg + 0x4);
+
+ return dword;
+}
+
+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);
+ do {
+ dword = Get_NB32(dev, index_reg);
+ } while (!(dword & (1 << DctAccessDone)));
+
+}
+
+static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ /* Get platform specific config/timing values from the interface layer
+ * and program them into DCT.
+ */
+
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg;
+ u8 i, i_start, i_end;
+
+ if (pDCTstat->GangedMode) {
+ SyncSetting(pDCTstat);
+ /* mct_SetupSync_D */
+ i_start = 0;
+ i_end = 2;
+ } else {
+ i_start = dct;
+ 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 */
+ }
+
+ return pDCTstat->ErrCode;
+
+}
+
+static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat)
+{
+ u32 dev;
+ u32 val;
+
+ if (pDCTstat->NodePresent) {
+ dev = pDCTstat->dev_dct;
+
+ if ((pDCTstat->DIMMValidDCT[0] ) || (pDCTstat->DIMMValidDCT[1])) { /* This Node has dram */
+ do {
+ val = Get_NB32(dev, 0x110);
+ } while (!(val & (1 << DramEnabled)));
+ }
+ } /* Node is present */
+}
+
+static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ if (!pDCTstat->GangedMode) {
+ if (dct == 0 ) {
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ if (pDCTstat->DIMMValidDCT[dct] == 0)
+ pDCTstat->ErrCode = SC_StopError;
+ } else {
+ pDCTstat->CSPresent = 0;
+ pDCTstat->CSTestFail = 0;
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ if (pDCTstat->DIMMValidDCT[dct] == 0)
+ pDCTstat->ErrCode = SC_StopError;
+ }
+ }
+}
+
+static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 ret;
+ u32 val;
+
+ if ( dct == 0) {
+ SPDCalcWidth_D(pMCTstat, pDCTstat);
+ ret = mct_setMode(pMCTstat, pDCTstat);
+ } else {
+ ret = pDCTstat->ErrCode;
+ }
+
+ if (pDCTstat->DIMMValidDCT[0] == 0) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val |= 1 << DisDramInterface;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+ }
+ if (pDCTstat->DIMMValidDCT[1] == 0) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val |= 1 << DisDramInterface;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "SPDCalcWidth: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "SPDCalcWidth: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "SPDCalcWidth: Done\n");
+ /* Disable dram interface before DRAM init */
+
+ return ret;
+}
+
+static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 val;
+ u32 dword;
+ u32 dev;
+ u32 reg;
+ u8 _MemHoleRemap;
+ u32 DramHoleBase;
+
+ _MemHoleRemap = mctGet_NVbits(NV_MemHole);
+ DramHoleBase = mctGet_NVbits(NV_BottomIO);
+ DramHoleBase <<= 8;
+ /* Increase hole size so;[31:24]to[31:16]
+ * it has granularity of 128MB shl eax,8
+ * Set 'effective' bottom IOmov DramHoleBase,eax
+ */
+ pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;
+
+ /* In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
+ if (!pDCTstat->GangedMode) {
+ dev = pDCTstat->dev_dct;
+ pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit;
+ /* if DCT0 and DCT1 both exist, set DctSelBaseAddr[47:27] to the top of DCT0 */
+ if (dct == 0) {
+ if (pDCTstat->DIMMValidDCT[1] > 0) {
+ dword = pDCTstat->DCTSysLimit + 1;
+ dword += pDCTstat->NodeSysBase;
+ dword >>= 8; /* scale [39:8] to [47:27],and to F2x110[31:11] */
+ if ((dword >= DramHoleBase) && _MemHoleRemap) {
+ pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;
+ val = pMCTstat->HoleBase;
+ val >>= 16;
+ val = (((~val) & 0xFF) + 1);
+ val <<= 8;
+ dword += val;
+ }
+ reg = 0x110;
+ val = Get_NB32(dev, reg);
+ val &= 0x7F;
+ val |= dword;
+ val |= 3; /* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */
+ Set_NB32(dev, reg, val);
+
+ reg = 0x114;
+ val = dword;
+ Set_NB32(dev, reg, val);
+ }
+ } else {
+ /* Program the DctSelBaseAddr value to 0
+ if DCT 0 is disabled */
+ if (pDCTstat->DIMMValidDCT[0] == 0) {
+ dword = pDCTstat->NodeSysBase;
+ dword >>= 8;
+ if ((dword >= DramHoleBase) && _MemHoleRemap) {
+ pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;
+ val = pMCTstat->HoleBase;
+ val >>= 8;
+ val &= ~(0xFFFF);
+ val |= (((~val) & 0xFFFF) + 1);
+ dword += val;
+ }
+ reg = 0x114;
+ val = dword;
+ Set_NB32(dev, reg, val);
+
+ reg = 0x110;
+ val |= 3; /* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */
+ Set_NB32(dev, reg, val);
+ }
+ }
+ } else {
+ pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit;
+ }
+ printk(BIOS_DEBUG, "AfterStitch pDCTstat->NodeSysBase = %x\n", pDCTstat->NodeSysBase);
+ printk(BIOS_DEBUG, "mct_AfterStitchMemory: pDCTstat->NodeSysLimit = %x\n", pDCTstat->NodeSysLimit);
+}
+
+static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 ret;
+
+ if ( dct == 0)
+ ret = DIMMPresence_D(pMCTstat, pDCTstat);
+ else
+ ret = pDCTstat->ErrCode;
+
+ return ret;
+}
+
+/* mct_BeforeGetDIMMAddress inline in C */
+
+static void mct_OtherTiming(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->NodePresent) {
+ if (pDCTstat->DIMMValidDCT[0]) {
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[0];
+ Set_OtherTiming(pMCTstat, pDCTstat, 0);
+ }
+ if (pDCTstat->DIMMValidDCT[1] && !pDCTstat->GangedMode ) {
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[1];
+ Set_OtherTiming(pMCTstat, pDCTstat, 1);
+ }
+ } /* Node is present*/
+ } /* while Node */
+}
+
+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_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);
+ val &= 0xffff0300;
+ dword = pDCTstat->TrwtTO;
+ val |= dword << 4;
+ dword = pDCTstat->Twrrd & 3;
+ val |= dword << 10;
+ dword = pDCTstat->Twrwr & 3;
+ val |= dword << 12;
+ dword = pDCTstat->Trdrd & 3;
+ val |= dword << 14;
+ dword = pDCTstat->TrwtWB;
+ val |= dword;
+ Set_NB32(dev, reg, val);
+
+ reg = 0x78 + reg_off;
+ val = Get_NB32(dev, reg);
+ val &= 0xFFFFC0FF;
+ dword = pDCTstat->Twrrd >> 2;
+ val |= dword << 8;
+ dword = pDCTstat->Twrwr >> 2;
+ val |= dword << 10;
+ dword = pDCTstat->Trdrd >> 2;
+ val |= dword << 12;
+ Set_NB32(dev, reg, val);
+}
+
+static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ int8_t Trdrd;
+
+ Trdrd = ((int8_t)(pDCTstat->DqsRcvEnGrossMax - pDCTstat->DqsRcvEnGrossMin) >> 1) + 1;
+ if (Trdrd > 8)
+ Trdrd = 8;
+ pDCTstat->Trdrd = Trdrd;
+}
+
+static void Get_Twrwr(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ int8_t Twrwr = 0;
+
+ Twrwr = ((int8_t)(pDCTstat->WrDatGrossMax - pDCTstat->WrDatGrossMin) >> 1) + 2;
+
+ if (Twrwr < 2)
+ Twrwr = 2;
+ else if (Twrwr > 9)
+ Twrwr = 9;
+
+ pDCTstat->Twrwr = Twrwr;
+}
+
+static void Get_Twrrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 LDplus1;
+ int8_t Twrrd;
+
+ LDplus1 = Get_Latency_Diff(pMCTstat, pDCTstat, dct);
+
+ Twrrd = ((int8_t)(pDCTstat->WrDatGrossMax - pDCTstat->DqsRcvEnGrossMin) >> 1) + 4 - LDplus1;
+
+ if (Twrrd < 2)
+ Twrrd = 2;
+ else if (Twrrd > 10)
+ Twrrd = 10;
+ pDCTstat->Twrrd = Twrrd;
+}
+
+static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 LDplus1;
+ int8_t TrwtTO;
+
+ LDplus1 = Get_Latency_Diff(pMCTstat, pDCTstat, dct);
+
+ TrwtTO = ((int8_t)(pDCTstat->DqsRcvEnGrossMax - pDCTstat->WrDatGrossMin) >> 1) + LDplus1;
+
+ pDCTstat->TrwtTO = TrwtTO;
+}
+
+static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* TrwtWB ensures read-to-write data-bus turnaround.
+ This value should be one more than the programmed TrwtTO.*/
+ pDCTstat->TrwtWB = pDCTstat->TrwtTO;
+}
+
+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;
+
+ return val1 - val2;
+}
+
+static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
+ u32 dev, u32 index_reg)
+{
+ u8 Smallest, Largest;
+ u32 val;
+ u8 byte, bytex;
+
+ /* The largest DqsRcvEnGrossDelay of any DIMM minus the
+ 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);
+ Largest = val & 0xFF;
+ Smallest = (val >> 8) & 0xFF;
+
+ /* DqsRcvEn byte 3,2 */
+ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+
+ /* DqsRcvEn byte 5,4 */
+ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+
+ /* DqsRcvEn byte 7,6 */
+ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+
+ if (pDCTstat->DimmECCPresent> 0) {
+ /*DqsRcvEn Ecc */
+ val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12);
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+ }
+
+ pDCTstat->DqsRcvEnGrossMax = Largest;
+ pDCTstat->DqsRcvEnGrossMin = Smallest;
+}
+
+static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat,
+ u8 dct, u32 dev, u32 index_reg)
+{
+ u8 Smallest, Largest;
+ u32 val;
+ u8 byte, bytex;
+
+ /* The largest WrDatGrossDlyByte of any DIMM minus the
+ WrDatGrossDlyByte of any other DIMM is equal to CGDD */
+ if (pDCTstat->DIMMValid & (1 << 0)) {
+ val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x01); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM0 */
+ Largest = val & 0xFF;
+ Smallest = (val >> 8) & 0xFF;
+ }
+ if (pDCTstat->DIMMValid & (1 << 2)) {
+ val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x101); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM1 */
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+ }
+
+ /* If Cx, 2 more dimm need to be checked to find out the largest and smallest */
+ if (pDCTstat->LogicalCPUID & AMD_DR_Cx) {
+ if (pDCTstat->DIMMValid & (1 << 4)) {
+ val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x201); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM2 */
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+ }
+ if (pDCTstat->DIMMValid & (1 << 6)) {
+ val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x301); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM2 */
+ byte = val & 0xFF;
+ bytex = (val >> 8) & 0xFF;
+ if (bytex < Smallest)
+ Smallest = bytex;
+ if (byte > Largest)
+ Largest = byte;
+ }
+ }
+
+ pDCTstat->WrDatGrossMax = Largest;
+ pDCTstat->WrDatGrossMin = Smallest;
+}
+
+static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
+ u32 dev, u32 index_reg,
+ u32 index)
+{
+ u8 Smallest, Largest;
+ u8 i;
+ u8 byte;
+ u32 val;
+ u16 word;
+ u8 ecc_reg = 0;
+
+ Smallest = 7;
+ Largest = 0;
+
+ if (index == 0x12)
+ ecc_reg = 1;
+
+ for (i=0; i < 8; i+=2) {
+ if ( pDCTstat->DIMMValid & (1 << i)) {
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ val &= 0x00E000E0;
+ byte = (val >> 5) & 0xFF;
+ if (byte < Smallest)
+ Smallest = byte;
+ if (byte > Largest)
+ Largest = byte;
+ if (!(ecc_reg)) {
+ byte = (val >> (16 + 5)) & 0xFF;
+ if (byte < Smallest)
+ Smallest = byte;
+ if (byte > Largest)
+ Largest = byte;
+ }
+ }
+ index += 3;
+ } /* while ++i */
+
+ word = Smallest;
+ word <<= 8;
+ word |= Largest;
+
+ return word;
+}
+
+static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
+ u8 dct, u32 dev, u32 index_reg,
+ u32 index)
+{
+ u8 Smallest, Largest;
+ u8 i, j;
+ u32 val;
+ u8 byte;
+ u16 word;
+
+ Smallest = 3;
+ Largest = 0;
+ for (i=0; i < 2; i++) {
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ val &= 0x60606060;
+ val >>= 5;
+ for (j=0; j < 4; j++) {
+ byte = val & 0xFF;
+ if (byte < Smallest)
+ Smallest = byte;
+ if (byte > Largest)
+ Largest = byte;
+ val >>= 8;
+ } /* while ++j */
+ index++;
+ } /*while ++i*/
+
+ if (pDCTstat->DimmECCPresent > 0) {
+ index++;
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ val &= 0x00000060;
+ val >>= 5;
+ byte = val & 0xFF;
+ if (byte < Smallest)
+ Smallest = byte;
+ if (byte > Largest)
+ Largest = byte;
+ }
+
+ word = Smallest;
+ word <<= 8;
+ word |= Largest;
+
+ return word;
+}
+
+static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ mct_ClrClToNB_D(pMCTstat, pDCTstat);
+ mct_ClrWbEnhWsbDis_D(pMCTstat, pDCTstat);
+}
+
+static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ mct_SetClToNB_D(pMCTstat, pDCTstat);
+ mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
+}
+
+static u32 mct_NodePresent_D(void)
+{
+ u32 val;
+ val = 0x12001022;
+ return val;
+}
+
+static void mct_init(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 lo, hi;
+ u32 addr;
+
+ pDCTstat->GangedMode = 0;
+ pDCTstat->DRPresent = 1;
+
+ /* enable extend PCI configuration access */
+ addr = 0xC001001F;
+ _RDMSR(addr, &lo, &hi);
+ if (hi & (1 << (46-32))) {
+ pDCTstat->Status |= 1 << SB_ExtConfig;
+ } else {
+ hi |= 1 << (46-32);
+ _WRMSR(addr, lo, hi);
+ }
+}
+
+static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 reg;
+ u32 val;
+ u32 dev = pDCTstat->dev_dct;
+
+ /* Clear Legacy BIOS Mode bit */
+ reg = 0x94;
+ val = Get_NB32(dev, reg);
+ val &= ~(1<<LegacyBiosMode);
+ Set_NB32(dev, reg, val);
+
+ reg = 0x94 + 0x100;
+ val = Get_NB32(dev, reg);
+ val &= ~(1<<LegacyBiosMode);
+ Set_NB32(dev, reg, val);
+}
+
+static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u32 Drambase, Dramlimit;
+ u32 val;
+ u32 reg;
+ u32 dev;
+ u32 devx;
+ u32 dword;
+ struct DCTStatStruc *pDCTstat;
+
+ pDCTstat = pDCTstatA + 0;
+ dev = pDCTstat->dev_map;
+
+ /* Copy dram map from F1x40/44,F1x48/4c,
+ to F1x120/124(Node0),F1x120/124(Node1),...*/
+ for (Node=0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ devx = pDCTstat->dev_map;
+
+ /* get base/limit from Node0 */
+ reg = 0x40 + (Node << 3); /* Node0/Dram Base 0 */
+ val = Get_NB32(dev, reg);
+ Drambase = val >> ( 16 + 3);
+
+ reg = 0x44 + (Node << 3); /* Node0/Dram Base 0 */
+ val = Get_NB32(dev, reg);
+ Dramlimit = val >> (16 + 3);
+
+ /* set base/limit to F1x120/124 per Node */
+ if (pDCTstat->NodePresent) {
+ reg = 0x120; /* F1x120,DramBase[47:27] */
+ val = Get_NB32(devx, reg);
+ val &= 0xFFE00000;
+ val |= Drambase;
+ Set_NB32(devx, reg, val);
+
+ reg = 0x124;
+ val = Get_NB32(devx, reg);
+ val &= 0xFFE00000;
+ val |= Dramlimit;
+ Set_NB32(devx, reg, val);
+
+ if ( pMCTstat->GStatus & ( 1 << GSB_HWHole)) {
+ reg = 0xF0;
+ val = Get_NB32(devx, reg);
+ val |= (1 << DramMemHoistValid);
+ val &= ~(0xFF << 24);
+ dword = (pMCTstat->HoleBase >> (24 - 8)) & 0xFF;
+ dword <<= 24;
+ val |= dword;
+ Set_NB32(devx, reg, val);
+ }
+
+ }
+ }
+}
+
+static void SetCSTriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 val;
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ u32 index;
+ u16 word;
+
+ /* Tri-state unused chipselects when motherboard
+ termination is available */
+
+ /* FIXME: skip for Ax */
+
+ word = pDCTstat->CSPresent;
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ word |= (word & 0x55) << 1;
+ }
+ word = (~word) & 0xFF;
+ index = 0x0c;
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ val |= word;
+ Set_NB32_index_wait(dev, index_reg, index, val);
+}
+
+static void SetCKETriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 val;
+ u32 dev;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ u32 index;
+ u16 word;
+
+ /* Tri-state unused CKEs when motherboard termination is available */
+
+ /* FIXME: skip for Ax */
+
+ dev = pDCTstat->dev_dct;
+ word = pDCTstat->CSPresent;
+
+ index = 0x0c;
+ val = Get_NB32_index_wait(dev, 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);
+}
+
+static void SetODTTriState(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 val;
+ u32 dev;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ u8 cs;
+ u32 index;
+ u8 odt;
+ u8 max_dimms;
+
+ /* FIXME: skip for Ax */
+
+ dev = pDCTstat->dev_dct;
+
+ /* Tri-state unused ODTs when motherboard termination is available */
+ max_dimms = (u8) mctGet_NVbits(NV_MAX_DIMMS);
+ odt = 0x0F; /* ODT tri-state setting */
+
+ if (pDCTstat->Status & (1 <<SB_Registered)) {
+ for (cs = 0; cs < 8; cs += 2) {
+ if (pDCTstat->CSPresent & (1 << cs)) {
+ odt &= ~(1 << (cs / 2));
+ if (mctGet_NVbits(NV_4RANKType) != 0) { /* quad-rank capable platform */
+ if (pDCTstat->CSPresent & (1 << (cs + 1)))
+ odt &= ~(4 << (cs / 2));
+ }
+ }
+ }
+ } else { /* AM3 package */
+ val = ~(pDCTstat->CSPresent);
+ odt = val & 9; /* swap bits 1 and 2 */
+ if (val & (1 << 1))
+ odt |= 1 << 2;
+ if (val & (1 << 2))
+ odt |= 1 << 1;
+ }
+
+ index = 0x0C;
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ val |= ((odt & 0xFF) << 8); /* set bits 11:8 ODTTriState[3:0] */
+ Set_NB32_index_wait(dev, index_reg, index, val);
+
+}
+
+static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 i;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+ u32 valx = 0;
+ u32 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;
+
+ }
+ 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 */
+ }
+
+ Set_NB32_index_wait(dev, index_reg, 0x0a, dword);
+}
+
+static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ 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(dev, reg);
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Bx | AMD_DR_Cx))
+ val |= (1 << EarlyArbEn);
+ else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
+ val |= (1 << EarlyArbEn);
+
+ Set_NB32(dev, reg, val);
+}
+
+static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 reg;
+ u32 val;
+ u32 tmp;
+ u32 rem;
+ u32 dev = pDCTstat->dev_dct;
+ u32 hi, lo;
+ u8 NbDid = 0;
+
+ /* Check if NB COF >= 4*Memclk, if it is not, return a fatal error
+ */
+
+ /* 3*(Fn2xD4[NBFid]+4)/(2^NbDid)/(3+Fn2x94[MemClkFreq]) */
+ _RDMSR(0xC0010071, &lo, &hi);
+ if (lo & (1 << 22))
+ NbDid |= 1;
+
+ reg = 0x94;
+ val = Get_NB32(dev, reg);
+ if (!(val & (1 << MemClkFreqVal)))
+ val = Get_NB32(dev, reg + 0x100); /* get the DCT1 value */
+
+ val &= 0x07;
+ val += 3;
+ if (NbDid)
+ val <<= 1;
+ tmp = val;
+
+ dev = pDCTstat->dev_nbmisc;
+ reg = 0xD4;
+ val = Get_NB32(dev, reg);
+ val &= 0x1F;
+ val += 3;
+ val *= 3;
+ val = val / tmp;
+ rem = val % tmp;
+ tmp >>= 1;
+
+ /* Yes this could be nicer but this was how the asm was.... */
+ if (val < 3) { /* NClk:MemClk < 3:1 */
+ return 0;
+ } else if (val > 4) { /* NClk:MemClk >= 5:1 */
+ return 0;
+ } else if ((val == 4) && (rem > tmp)) { /* NClk:MemClk > 4.5:1 */
+ return 0;
+ } else {
+ return 1; /* 3:1 <= NClk:MemClk <= 4.5:1*/
+ }
+}
+
+static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u32 i;
+ struct DCTStatStruc *pDCTstat;
+ u32 start, stop;
+ u8 *p;
+ u16 host_serv1, host_serv2;
+
+ /* Initialize Data structures by clearing all entries to 0 */
+ p = (u8 *) pMCTstat;
+ for (i = 0; i < sizeof(struct MCTStatStruc); i++) {
+ p[i] = 0;
+ }
+
+ for (Node = 0; Node < 8; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ host_serv1 = pDCTstat->HostBiosSrvc1;
+ host_serv2 = pDCTstat->HostBiosSrvc2;
+
+ p = (u8 *) pDCTstat;
+ start = 0;
+ stop = ((u32) &((struct DCTStatStruc *)0)->CH_MaxRdLat[2]);
+ for (i = start; i < stop ; i++) {
+ p[i] = 0;
+ }
+
+ start = ((u32) &((struct DCTStatStruc *)0)->CH_D_BC_RCVRDLY[2][4]);
+ stop = sizeof(struct DCTStatStruc);
+ for (i = start; i < stop; i++) {
+ p[i] = 0;
+ }
+ pDCTstat->HostBiosSrvc1 = host_serv1;
+ pDCTstat->HostBiosSrvc2 = host_serv2;
+ }
+}
+
+static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 i;
+ u32 reg_off, dword;
+ u32 dev = pDCTstat->dev_dct;
+
+ 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);
+ }
+ }
+}
+
+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_DA_C2 | AMD_RB_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);
+ }
+
+ return DramConfigLo | /* DisDllShutdownSR */ 1 << 27;
+}
+
+void mct_SetClToNB_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 lo, hi;
+ u32 msr;
+
+ /* FIXME: Maybe check the CPUID? - not for now. */
+ /* pDCTstat->LogicalCPUID; */
+
+ msr = BU_CFG2;
+ _RDMSR(msr, &lo, &hi);
+ lo |= 1 << ClLinesToNbDis;
+ _WRMSR(msr, lo, hi);
+}
+
+void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+
+ u32 lo, hi;
+ u32 msr;
+
+ /* FIXME: Maybe check the CPUID? - not for now. */
+ /* pDCTstat->LogicalCPUID; */
+
+ msr = BU_CFG2;
+ _RDMSR(msr, &lo, &hi);
+ if (!pDCTstat->ClToNB_flag)
+ lo &= ~(1<<ClLinesToNbDis);
+ _WRMSR(msr, lo, hi);
+
+}
+
+void mct_SetWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 lo, hi;
+ u32 msr;
+
+ /* FIXME: Maybe check the CPUID? - not for now. */
+ /* pDCTstat->LogicalCPUID; */
+
+ msr = BU_CFG;
+ _RDMSR(msr, &lo, &hi);
+ hi |= (1 << WbEnhWsbDis_D);
+ _WRMSR(msr, lo, hi);
+}
+
+void mct_ClrWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 lo, hi;
+ u32 msr;
+
+ /* FIXME: Maybe check the CPUID? - not for now. */
+ /* pDCTstat->LogicalCPUID; */
+
+ msr = BU_CFG;
+ _RDMSR(msr, &lo, &hi);
+ hi &= ~(1 << WbEnhWsbDis_D);
+ _WRMSR(msr, lo, hi);
+}
+
+static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dimm)
+{
+ u8 DimmsInstalled = dimm;
+ u32 DramTermDyn = 0;
+ u8 Speed = pDCTstat->Speed;
+
+ if (mctGet_NVbits(NV_MAX_DIMMS) == 4) {
+ if (pDCTstat->CSPresent & 0xF0) {
+ if (DimmsInstalled == 1)
+ if (Speed == 7)
+ DramTermDyn |= 1 << 10;
+ else
+ DramTermDyn |= 1 << 11;
+ else
+ if (Speed == 4)
+ DramTermDyn |= 1 << 11;
+ else
+ DramTermDyn |= 1 << 10;
+ } else {
+ if (DimmsInstalled != 1) {
+ if (Speed == 7)
+ DramTermDyn |= 1 << 10;
+ else
+ DramTermDyn |= 1 << 11;
+ }
+ }
+ } else {
+ if (DimmsInstalled != 1)
+ DramTermDyn |= 1 << 11;
+ }
+ return DramTermDyn;
+}
+
+void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 DramMRS, dword;
+ u8 byte;
+
+ DramMRS = 0;
+
+ /* Set chip select CKE control mode */
+ if (mctGet_NVbits(NV_CKE_CTL)) {
+ if (pDCTstat->CSPresent == 3) {
+ u16 word;
+ word = pDCTstat->DIMMSPDCSE;
+ if (dct == 0)
+ word &= 0b01010100;
+ else
+ word &= 0b10101000;
+ if (word == 0)
+ 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 */
+ }
+ }
+ /* 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);
+ }
+
+ /* 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 |= DramMRS;
+ Set_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84, dword);
+}
+
+void mct_SetDramConfigHi_D(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:
+ * Solution: From the bug report:
+ * 1. A software-initiated frequency change should be wrapped into the
+ * following sequence :
+ * - a) Disable Compensation (F2[1, 0]9C_x08[30] )
+ * b) Reset the Begin Compensation bit (D3CMP->COMP_CONFIG[0]) in all the compensation engines
+ * c) Do frequency change
+ * d) Enable Compensation (F2[1, 0]9C_x08[30] )
+ * 2. A software-initiated Disable Compensation should always be
+ * followed by step b) of the above steps.
+ * Silicon Status: Fixed In Rev B0
+ *
+ * Errata#177: DRAM Phy Automatic Compensation Updates May Be Invalid
+ * Solution: BIOS should disable the phy automatic compensation prior
+ * to initiating a memory clock frequency change as follows:
+ * 1. Disable PhyAutoComp by writing 1'b1 to F2x[1, 0]9C_x08[30]
+ * 2. Reset the Begin Compensation bits by writing 32'h0 to
+ * F2x[1, 0]9C_x4D004F00
+ * 3. Perform frequency change
+ * 4. Enable PhyAutoComp by writing 1'b0 to F2x[1, 0]9C_08[30]
+ * In addition, any time software disables the automatic phy
+ * compensation it should reset the begin compensation bit per step 2.
+ * Silicon Status: Fixed in DR-B0
+ */
+
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ u32 index;
+
+ u32 val;
+
+ 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));
+
+ mct_Wait(100);
+
+ Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi);
+}
+
+static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ 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.
+ * Solutiuon: 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);
+ }
+}
+
+static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 Receiver;
+ u32 dev = pDCTstat->dev_dct;
+ u32 reg_off = 0x100 * dct;
+ u32 addr;
+ u32 lo, hi;
+ u8 wrap32dis = 0;
+ u8 valid = 0;
+
+ /* Skip reset DLL for B3 */
+ if (pDCTstat->LogicalCPUID & AMD_DR_B3) {
+ return;
+ }
+
+ addr = HWCR;
+ _RDMSR(addr, &lo, &hi);
+ if(lo & (1<<17)) { /* save the old value */
+ wrap32dis = 1;
+ }
+ lo |= (1<<17); /* HWCR.wrap32dis */
+ /* Setting wrap32dis allows 64-bit memory references in 32bit mode */
+ _WRMSR(addr, lo, hi);
+
+ pDCTstat->Channel = dct;
+ Receiver = mct_InitReceiver_D(pDCTstat, dct);
+ /* there are four receiver pairs, loosely associated with chipselects.*/
+ for (; Receiver < 8; Receiver += 2) {
+ if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
+ addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, dct, Receiver, &valid);
+ if (valid) {
+ 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, 0x4D080F0C, 0x00008000);
+ mct_Wait(80); /* wait >= 300ns */
+
+ /* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
+ Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00000000);
+ mct_Wait(800); /* wait >= 2us */
+ break;
+ }
+ }
+ }
+
+ if(!wrap32dis) {
+ addr = HWCR;
+ _RDMSR(addr, &lo, &hi);
+ lo &= ~(1<<17); /* restore HWCR.wrap32dis */
+ _WRMSR(addr, lo, hi);
+ }
+}
+
+static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
+ /* Enable F2x110[DctDatIntlv] */
+ /* Call back not required mctHookBeforeDatIntlv_D() */
+ /* FIXME Skip for Ax */
+ if (!pDCTstat->GangedMode) {
+ val = Get_NB32(dev, 0x110);
+ val |= 1 << 5; /* DctDatIntlv */
+ Set_NB32(dev, 0x110, val);
+
+ /* FIXME Skip for Cx */
+ dev = pDCTstat->dev_nbmisc;
+ val = Get_NB32(dev, 0x8C); /* NB Configuration Hi */
+ val |= 1 << (36-32); /* DisDatMask */
+ Set_NB32(dev, 0x8C, val);
+ }
+}
+
+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);
+
+ }
+}
+
+static void SyncSetting(struct DCTStatStruc *pDCTstat)
+{
+ /* set F2x78[ChSetupSync] when F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup,
+ * CkeSetup] setups for one DCT are all 0s and at least one of the setups,
+ * F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup, CkeSetup], of the other
+ * controller is 1
+ */
+ u32 cha, chb;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
+ cha = pDCTstat->CH_ADDR_TMG[0] & 0x0202020;
+ chb = pDCTstat->CH_ADDR_TMG[1] & 0x0202020;
+
+ if ((cha != chb) && ((cha == 0) || (chb == 0))) {
+ val = Get_NB32(dev, 0x78);
+ val |= 1 << ChSetupSync;
+ Set_NB32(dev, 0x78, val);
+ }
+}
+
+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)) {
+ mct_Wait(10000); /* Wait 50 us*/
+ 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 &= ~(1 << Width128); /* Program Width128 = 0 */
+ Set_NB32(dev, 0x90 + reg_off, val);
+
+ val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05); /* Perform dummy CSR read to F2x09C_x05 */
+
+ if (pDCTstat->GangedMode) {
+ val = Get_NB32(dev, 0x90 + reg_off);
+ val |= 1 << Width128; /* Program Width128 = 0 */
+ Set_NB32(dev, 0x90 + reg_off, val);
+ }
+ }
+ }
+}
+
+/* ==========================================================
+ * 6-bit Bank Addressing Table
+ * RR=rows-13 binary
+ * B=Banks-2 binary
+ * CCC=Columns-9 binary
+ * ==========================================================
+ * DCT CCCBRR Rows Banks Columns 64-bit CS Size
+ * Encoding
+ * 0000 000000 13 2 9 128MB
+ * 0001 001000 13 2 10 256MB
+ * 0010 001001 14 2 10 512MB
+ * 0011 010000 13 2 11 512MB
+ * 0100 001100 13 3 10 512MB
+ * 0101 001101 14 3 10 1GB
+ * 0110 010001 14 2 11 1GB
+ * 0111 001110 15 3 10 2GB
+ * 1000 010101 14 3 11 2GB
+ * 1001 010110 15 3 11 4GB
+ * 1010 001111 16 3 10 4GB
+ * 1011 010111 16 3 11 8GB
+ */
+u8 crcCheck(u8 smbaddr)
+{
+ u8 byte_use;
+ u8 Index;
+ u16 CRC;
+ u8 byte, i;
+
+ byte_use = mctRead_SPD(smbaddr, SPD_ByteUse);
+ if (byte_use & 0x80)
+ byte_use = 117;
+ else
+ byte_use = 126;
+
+ CRC = 0;
+ for (Index = 0; Index < byte_use; Index ++) {
+ byte = mctRead_SPD(smbaddr, Index);
+ CRC ^= byte << 8;
+ for (i=0; i<8; i++) {
+ if (CRC & 0x8000) {
+ CRC <<= 1;
+ CRC ^= 0x1021;
+ } else
+ CRC <<= 1;
+ }
+ }
+ return CRC == (mctRead_SPD(smbaddr, SPD_byte_127) << 8 | mctRead_SPD(smbaddr, SPD_byte_126));
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
new file mode 100644
index 0000000000..bef9dfe954
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d.h
@@ -0,0 +1,794 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/*
+ * Description: Include file for all generic DDR 3 MCT files.
+ */
+#ifndef MCT_D_H
+#define MCT_D_H
+
+/*===========================================================================
+ CPU - K8/FAM10
+===========================================================================*/
+#define PT_L1 0 /* CPU Package Type */
+#define PT_M2 1
+#define PT_S1 2
+#define PT_GR 3
+#define PT_AS 4
+#define PT_C3 5
+
+#define J_MIN 0 /* j loop constraint. 1=CL 2.0 T*/
+#define J_MAX 5 /* j loop constraint. 5=CL 7.0 T*/
+#define K_MIN 1 /* k loop constraint. 1=200 Mhz*/
+#define K_MAX 5 /* k loop constraint. 5=533 Mhz*/
+#define CL_DEF 2 /* Default value for failsafe operation. 2=CL 4.0 T*/
+#define T_DEF 1 /* Default value for failsafe operation. 1=5ns (cycle time)*/
+
+#define BSCRate 1 /* reg bit field=rate of dram scrubber for ecc*/
+ /* memory initialization (ecc and check-bits).*/
+ /* 1=40 ns/64 bytes.*/
+#define FirstPass 1 /* First pass through RcvEn training*/
+#define SecondPass 2 /* Second pass through Rcven training*/
+
+#define RCVREN_MARGIN 6 /* number of DLL taps to delay beyond first passing position*/
+#define MAXASYNCLATCTL_2 2 /* Max Async Latency Control value*/
+#define MAXASYNCLATCTL_3 3 /* Max Async Latency Control value*/
+
+#define DQS_FAIL 1
+#define DQS_PASS 0
+#define DQS_WRITEDIR 1
+#define DQS_READDIR 0
+#define MIN_DQS_WNDW 3
+#define secPassOffset 6
+#define Pass1MemClkDly 0x20 /* Add 1/2 Memlock delay */
+#define MAX_RD_LAT 0x3FF
+#define MIN_FENCE 14
+#define MAX_FENCE 20
+#define MIN_DQS_WR_FENCE 14
+#define MAX_DQS_WR_FENCE 20
+#define FenceTrnFinDlySeed 19
+#define EarlyArbEn 19
+
+#define PA_HOST(Node) ((((0x18+Node) << 3)+0) << 12) /* Node 0 Host Bus function PCI Address bits [15:0]*/
+#define PA_MAP(Node) ((((0x18+Node) << 3)+1) << 12) /* Node 0 MAP function PCI Address bits [15:0]*/
+#define PA_DCT(Node) ((((0x18+Node) << 3)+2) << 12) /* Node 0 DCT function PCI Address bits [15:0]*/
+/* #define PA_EXT_DCT (((00 << 3)+4) << 8) */ /*Node 0 DCT extended configuration registers*/
+/* #define PA_DCTADDL (((00 << 3)+2) << 8) */ /*Node x DCT function, Additional Registers PCI Address bits [15:0]*/
+/* #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_NBDEVOP (((00 << 3)+3) << 8) */ /*Node 0 Misc PCI Address bits [15:0]*/
+
+#define DCC_EN 1 /* X:2:0x94[19]*/
+#define ILD_Lmt 3 /* X:2:0x94[18:16]*/
+
+#define EncodedTSPD 0x00191709 /* encodes which SPD byte to get T from*/
+ /* versus CL X, CL X-.5, and CL X-1*/
+
+#define Bias_TrpT 5 /* bias to convert bus clocks to bit field value*/
+#define Bias_TrrdT 4
+#define Bias_TrcdT 5
+#define Bias_TrasT 15
+#define Bias_TrcT 11
+#define Bias_TrtpT 4
+#define Bias_TwrT 4
+#define Bias_TwtrT 4
+#define Bias_TfawT 14
+
+#define Min_TrpT 5 /* min programmable value in busclocks */
+#define Max_TrpT 12 /* max programmable value in busclocks */
+#define Min_TrrdT 4
+#define Max_TrrdT 7
+#define Min_TrcdT 5
+#define Max_TrcdT 12
+#define Min_TrasT 15
+#define Max_TrasT 30
+#define Min_TrcT 11
+#define Max_TrcT 42
+#define Min_TrtpT 4
+#define Max_TrtpT 7
+#define Min_TwrT 5
+#define Max_TwrT 12
+#define Min_TwtrT 4
+#define Max_TwtrT 7
+#define Min_TfawT 16
+#define Max_TfawT 32
+
+/*common register bit names*/
+#define DramHoleValid 0 /* func 1, offset F0h, bit 0*/
+#define DramMemHoistValid 1 /* func 1, offset F0h, bit 1*/
+#define CSEnable 0 /* func 2, offset 40h-5C, bit 0*/
+#define Spare 1 /* func 2, offset 40h-5C, bit 1*/
+#define TestFail 2 /* func 2, offset 40h-5C, bit 2*/
+#define DqsRcvEnTrain 18 /* func 2, offset 78h, bit 18*/
+#define EnDramInit 31 /* func 2, offset 7Ch, bit 31*/
+#define DisAutoRefresh 18 /* func 2, offset 8Ch, bit 18*/
+#define InitDram 0 /* func 2, offset 90h, bit 0*/
+#define BurstLength32 10 /* func 2, offset 90h, bit 10*/
+#define Width128 11 /* func 2, offset 90h, bit 11*/
+#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 RDqsEn 12 /* func 2, offset 94h, bit 12*/
+#define DisDramInterface 14 /* func 2, offset 94h, bit 14*/
+#define DctAccessWrite 30 /* func 2, offset 98h, bit 30*/
+#define DctAccessDone 31 /* func 2, offset 98h, bit 31*/
+#define MemClrStatus 0 /* func 2, offset A0h, bit 0*/
+#define PwrSavingsEn 10 /* func 2, offset A0h, bit 10*/
+#define Mod64BitMux 4 /* func 2, offset A0h, bit 4*/
+#define DisableJitter 1 /* func 2, offset A0h, bit 1*/
+#define MemClrDis 1 /* func 3, offset F8h, FNC 4, bit 1*/
+#define SyncOnUcEccEn 2 /* func 3, offset 44h, bit 2*/
+#define Dr_MemClrStatus 10 /* func 3, offset 110h, bit 10*/
+#define MemClrBusy 9 /* func 3, offset 110h, bit 9*/
+#define DctGangEn 4 /* func 3, offset 110h, bit 4*/
+#define MemClrInit 3 /* func 3, offset 110h, bit 3*/
+#define SendZQCmd 29 /* func 2, offset 7Ch, bit 29 */
+#define AssertCke 28 /* func 2, offset 7Ch, bit 28*/
+#define DeassertMemRstX 27 /* func 2, offset 7Ch, bit 27*/
+#define SendMrsCmd 26 /* func 2, offset 7Ch, bit 26*/
+#define SendAutoRefresh 25 /* func 2, offset 7Ch, bit 25*/
+#define SendPchgAll 24 /* func 2, offset 7Ch, bit 24*/
+#define DisDqsBar 6 /* func 2, offset 90h, bit 6*/
+#define DramEnabled 8 /* func 2, offset 110h, bit 8*/
+#define LegacyBiosMode 9 /* func 2, offset 94h, bit 9*/
+#define PrefDramTrainMode 28 /* func 2, offset 11Ch, bit 28*/
+#define FlushWr 30 /* func 2, offset 11Ch, bit 30*/
+#define DisAutoComp 30 /* func 2, offset 9Ch, Index 8, bit 30*/
+#define DqsRcvTrEn 13 /* func 2, offset 9Ch, Index 8, bit 13*/
+#define ForceAutoPchg 23 /* func 2, offset 90h, bit 23*/
+#define ClLinesToNbDis 15 /* Bu_CFG2, bit 15*/
+#define WbEnhWsbDis_D (48-32)
+#define PhyFenceTrEn 3 /* func 2, offset 9Ch, Index 8, bit 3 */
+#define ParEn 8 /* func 2, offset 90h, bit 8 */
+#define DcqArbBypassEn 19 /* func 2, offset 94h, bit 19 */
+#define ActiveCmdAtRst 1 /* func 2, offset A8H, bit 1 */
+#define FlushWrOnStpGnt 29 /* func 2, offset 11Ch, bit 29 */
+#define BankSwizzleMode 22 /* func 2, offset 94h, bit 22 */
+#define ChSetupSync 15 /* func 2, offset 78h, bit 15 */
+
+#define Ddr3Mode 8 /* func 2, offset 94h, bit 8 */
+#define EnterSelfRef 17 /* func 2, offset 90h, bit 17 */
+#define onDimmMirror 3 /* func 2, offset 5C:40h, bit 3 */
+#define OdtSwizzle 6 /* func 2, offset A8h, bit 6 */
+#define FreqChgInProg 21 /* func 2, offset 94h, bit 21 */
+#define ExitSelfRef 1 /* func 2, offset 90h, bit 1 */
+
+#define SubMemclkRegDly 5 /* func 2, offset A8h, bit 5 */
+#define Ddr3FourSocketCh 2 /* func 2, offset A8h, bit 2 */
+#define SendControlWord 30 /* func 2, offset 7Ch, bit 30 */
+
+/*=============================================================================
+ SW Initialization
+============================================================================*/
+#define DLL_Enable 1
+#define OCD_Default 2
+#define OCD_Exit 3
+
+/*=============================================================================
+ Jedec DDR II
+=============================================================================*/
+#define SPD_ByteUse 0
+#define SPD_TYPE 2 /*SPD byte read location*/
+ #define JED_DDRSDRAM 0x07 /*Jedec defined bit field*/
+ #define JED_DDR2SDRAM 0x08 /*Jedec defined bit field*/
+ #define JED_DDR3SDRAM 0x0B /* Jedec defined bit field*/
+
+#define SPD_DIMMTYPE 3
+#define SPD_ATTRIB 21
+ #define JED_DIFCKMSK 0x20 /*Differential Clock Input*/
+ #define JED_REGADCMSK 0x11 /*Registered Address/Control*/
+ #define JED_PROBEMSK 0x40 /*Analysis Probe installed*/
+ #define JED_RDIMM 0x1 /* RDIMM */
+ #define JED_MiniRDIMM 0x5 /* Mini-RDIMM */
+#define SPD_Density 4 /* Bank address bits,SDRAM capacity */
+#define SPD_Addressing 5 /* Row/Column address bits */
+#define SPD_Organization 7 /* rank#,Device width */
+#define SPD_BusWidth 8 /* ECC, Bus width */
+ #define JED_ECC 8 /* ECC capability */
+
+#define SPD_MTBDividend 10
+#define SPD_MTBDivisor 11
+#define SPD_tCKmin 12
+#define SPD_CASLow 14
+#define SPD_CASHigh 15
+#define SPD_tAAmin 16
+
+#define SPD_DEVATTRIB 22
+#define SPD_EDCTYPE 11
+ #define JED_ADRCPAR 0x04
+
+#define SPD_tWRmin 17
+#define SPD_tRCDmin 18
+#define SPD_tRRDmin 19
+#define SPD_tRPmin 20
+#define SPD_Upper_tRAS_tRC 21
+#define SPD_tRASmin 22
+#define SPD_tRCmin 23
+#define SPD_tWTRmin 26
+#define SPD_tRTPmin 27
+#define SPD_Upper_tFAW 28
+#define SPD_tFAWmin 29
+
+#define SPD_RefRawCard 62
+#define SPD_AddressMirror 63
+#define SPD_RegManufactureID_L 65 /* not used */
+#define SPD_RegManufactureID_H 66 /* not used */
+#define SPD_RegManRevID 67 /* not used */
+
+#define SPD_byte_126 126
+#define SPD_byte_127 127
+
+#define SPD_ROWSZ 3
+#define SPD_COLSZ 4
+#define SPD_LBANKS 17 /*number of [logical] banks on each device*/
+#define SPD_DMBANKS 5 /*number of physical banks on dimm*/
+ #define SPDPLBit 4 /* Dram package bit*/
+#define SPD_BANKSZ 31 /*capacity of physical bank*/
+#define SPD_DEVWIDTH 13
+#define SPD_CASLAT 18
+#define SPD_TRP 27
+#define SPD_TRRD 28
+#define SPD_TRCD 29
+#define SPD_TRAS 30
+#define SPD_TWR 36
+#define SPD_TWTR 37
+#define SPD_TRTP 38
+#define SPD_TRCRFC 40
+#define SPD_TRC 41
+#define SPD_TRFC 42
+
+#define SPD_MANDATEYR 93 /*Module Manufacturing Year (BCD)*/
+
+#define SPD_MANDATEWK 94 /*Module Manufacturing Week (BCD)*/
+
+/*-----------------------------
+ Jdec DDR II related equates
+-----------------------------*/
+#define MYEAR06 6 /* Manufacturing Year BCD encoding of 2006 - 06d*/
+#define MWEEK24 0x24 /* Manufacturing Week BCD encoding of June - 24d*/
+
+/*=============================================================================
+ Macros
+=============================================================================*/
+
+#define _2GB_RJ8 (2<<(30-8))
+#define _4GB_RJ8 (4<<(30-8))
+#define _4GB_RJ4 (4<<(30-4))
+
+#define BigPagex8_RJ8 (1<<(17+3-8)) /*128KB * 8 >> 8 */
+
+/*=============================================================================
+ Global MCT Status Structure
+=============================================================================*/
+struct MCTStatStruc {
+ u32 GStatus; /* Global Status bitfield*/
+ u32 HoleBase; /* If not zero, BASE[39:8] (system address)
+ 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)*/
+};
+
+/*=============================================================================
+ Global MCT Configuration Status Word (GStatus)
+=============================================================================*/
+/*These should begin at bit 0 of GStatus[31:0]*/
+#define GSB_MTRRshort 0 /* Ran out of MTRRs while mapping memory*/
+#define GSB_ECCDIMMs 1 /* All banks of all Nodes are ECC capable*/
+#define GSB_DramECCDis 2 /* Dram ECC requested but not enabled.*/
+#define GSB_SoftHole 3 /* A Node Base gap was created*/
+#define GSB_HWHole 4 /* A HW dram remap was created*/
+#define GSB_NodeIntlv 5 /* Node Memory interleaving was enabled*/
+#define GSB_SpIntRemapHole 16 /* Special condition for Node Interleave and HW remapping*/
+#define GSB_EnDIMMSpareNW 17 /* Indicates that DIMM Spare can be used without a warm reset */
+ /* NOTE: This is a local bit used by memory code */
+
+/*===============================================================================
+ Local DCT Status structure (a structure for each DCT)
+===============================================================================*/
+#include "mwlc_d.h" /* I have to */
+
+struct DCTStatStruc { /* A per Node structure*/
+/* DCTStatStruct_F - start */
+ u8 Node_ID; /* Node ID of current controller*/
+ u8 ErrCode; /* Current error condition of Node
+ 0= no error
+ 1= Variance Error, DCT is running but not in an optimal configuration.
+ 2= Stop Error, DCT is NOT running
+ 3= Fatal Error, DCT/MCT initialization has been halted.*/
+ u32 ErrStatus; /* Error Status bit Field */
+ u32 Status; /* Status bit Field*/
+ u8 DIMMAddr[8]; /* SPD address of DIMM controlled by MA0_CS_L[0,1]*/
+ /* SPD address of..MB0_CS_L[0,1]*/
+ /* SPD address of..MA1_CS_L[0,1]*/
+ /* SPD address of..MB1_CS_L[0,1]*/
+ /* SPD address of..MA2_CS_L[0,1]*/
+ /* SPD address of..MB2_CS_L[0,1]*/
+ /* SPD address of..MA3_CS_L[0,1]*/
+ /* SPD address of..MB3_CS_L[0,1]*/
+ u16 DIMMPresent; /*For each bit n 0..7, 1=DIMM n is present.
+ DIMM# Select Signal
+ 0 MA0_CS_L[0,1]
+ 1 MB0_CS_L[0,1]
+ 2 MA1_CS_L[0,1]
+ 3 MB1_CS_L[0,1]
+ 4 MA2_CS_L[0,1]
+ 5 MB2_CS_L[0,1]
+ 6 MA3_CS_L[0,1]
+ 7 MB3_CS_L[0,1]*/
+ u16 DIMMValid; /* For each bit n 0..7, 1=DIMM n is valid and is/will be configured*/
+ u16 DIMMMismatch; /* For each bit n 0..7, 1=DIMM n is mismatched, channel B is always considered the mismatch */
+ u16 DIMMSPDCSE; /* For each bit n 0..7, 1=DIMM n SPD checksum error*/
+ u16 DimmECCPresent; /* For each bit n 0..7, 1=DIMM n is ECC capable.*/
+ u16 DimmPARPresent; /* For each bit n 0..7, 1=DIMM n is ADR/CMD Parity capable.*/
+ u16 Dimmx4Present; /* For each bit n 0..7, 1=DIMM n contains x4 data devices.*/
+ u16 Dimmx8Present; /* For each bit n 0..7, 1=DIMM n contains x8 data devices.*/
+ u16 Dimmx16Present; /* For each bit n 0..7, 1=DIMM n contains x16 data devices.*/
+ u16 DIMM2Kpage; /* For each bit n 0..7, 1=DIMM n contains 1K page devices.*/
+ u8 MAload[2]; /* Number of devices loading MAA bus*/
+ /* Number of devices loading MAB bus*/
+ u8 MAdimms[2]; /*Number of DIMMs loading CH A*/
+ /* Number of DIMMs loading CH B*/
+ u8 DATAload[2]; /*Number of ranks loading CH A DATA*/
+ /* Number of ranks loading CH B DATA*/
+ u8 DIMMAutoSpeed; /*Max valid Mfg. Speed of DIMMs
+ 1=200Mhz
+ 2=266Mhz
+ 3=333Mhz
+ 4=400Mhz
+ 5=533Mhz*/
+ u8 DIMMCASL; /* Min valid Mfg. CL bitfield
+ 0=2.0
+ 1=3.0
+ 2=4.0
+ 3=5.0
+ 4=6.0 */
+ u16 DIMMTrcd; /* Minimax Trcd*40 (ns) of DIMMs*/
+ u16 DIMMTrp; /* Minimax Trp*40 (ns) of DIMMs*/
+ u16 DIMMTrtp; /* Minimax Trtp*40 (ns) of DIMMs*/
+ u16 DIMMTras; /* Minimax Tras*40 (ns) of DIMMs*/
+ u16 DIMMTrc; /* Minimax Trc*40 (ns) of DIMMs*/
+ u16 DIMMTwr; /* Minimax Twr*40 (ns) of DIMMs*/
+ u16 DIMMTrrd; /* Minimax Trrd*40 (ns) of DIMMs*/
+ u16 DIMMTwtr; /* Minimax Twtr*40 (ns) of DIMMs*/
+ u8 Speed; /* Bus Speed (to set Controller)
+ 1=200Mhz
+ 2=266Mhz
+ 3=333Mhz
+ 4=400Mhz */
+ u8 CASL; /* CAS latency DCT setting
+ 0=2.0
+ 1=3.0
+ 2=4.0
+ 3=5.0
+ 4=6.0 */
+ u8 Trcd; /* DCT Trcd (busclocks) */
+ u8 Trp; /* DCT Trp (busclocks) */
+ u8 Trtp; /* DCT Trtp (busclocks) */
+ u8 Tras; /* DCT Tras (busclocks) */
+ u8 Trc; /* DCT Trc (busclocks) */
+ u8 Twr; /* DCT Twr (busclocks) */
+ u8 Trrd; /* DCT Trrd (busclocks) */
+ u8 Twtr; /* DCT Twtr (busclocks) */
+ u8 Trfc[4]; /* DCT Logical DIMM0 Trfc
+ 0=75ns (for 256Mb devs)
+ 1=105ns (for 512Mb devs)
+ 2=127.5ns (for 1Gb devs)
+ 3=195ns (for 2Gb devs)
+ 4=327.5ns (for 4Gb devs) */
+ /* DCT Logical DIMM1 Trfc (see Trfc0 for format) */
+ /* DCT Logical DIMM2 Trfc (see Trfc0 for format) */
+ /* DCT Logical DIMM3 Trfc (see Trfc0 for format) */
+ u16 CSPresent; /* For each bit n 0..7, 1=Chip-select n is present */
+ u16 CSTestFail; /* For each bit n 0..7, 1=Chip-select n is present but disabled */
+ u32 DCTSysBase; /* BASE[39:8] (system address) of this Node's DCTs. */
+ u32 DCTHoleBase; /* If not zero, BASE[39:8] (system address) of dram hole for HW remapping. Dram hole exists on this Node's DCTs. */
+ u32 DCTSysLimit; /* LIMIT[39:8] (system address) of this Node's DCTs */
+ u16 PresetmaxFreq; /* Maximum OEM defined DDR frequency
+ 200=200Mhz (DDR400)
+ 266=266Mhz (DDR533)
+ 333=333Mhz (DDR667)
+ 400=400Mhz (DDR800) */
+ u8 _2Tmode; /* 1T or 2T CMD mode (slow access mode)
+ 1=1T
+ 2=2T */
+ u8 TrwtTO; /* DCT TrwtTO (busclocks)*/
+ u8 Twrrd; /* DCT Twrrd (busclocks)*/
+ u8 Twrwr; /* DCT Twrwr (busclocks)*/
+ u8 Trdrd; /* DCT Trdrd (busclocks)*/
+ u32 CH_ODC_CTL[2]; /* Output Driver Strength (see BKDG FN2:Offset 9Ch, index 00h*/
+ u32 CH_ADDR_TMG[2]; /* Address Bus Timing (see BKDG FN2:Offset 9Ch, index 04h*/
+ /* Output Driver Strength (see BKDG FN2:Offset 9Ch, index 20h*/
+ /* Address Bus Timing (see BKDG FN2:Offset 9Ch, index 24h*/
+ u16 CH_EccDQSLike[2]; /* CHA DQS ECC byte like...*/
+ u8 CH_EccDQSScale[2]; /* CHA DQS ECC byte scale*/
+ /* CHA DQS ECC byte like...*/
+ /* CHA DQS ECC byte scale*/
+ u8 MaxAsyncLat; /* Max Asynchronous Latency (ns)*/
+ /* NOTE: Not used in Barcelona - u8 CH_D_RCVRDLY[2][4]; */
+ /* CHA DIMM 0 - 4 Receiver Enable Delay*/
+ /* CHB DIMM 0 - 4 Receiver Enable Delay */
+ /* NOTE: Not used in Barcelona - u8 CH_D_B_DQS[2][2][8]; */
+ /* CHA Byte 0-7 Write DQS Delay */
+ /* CHA Byte 0-7 Read DQS Delay */
+ /* CHB Byte 0-7 Write DQS Delay */
+ /* CHB Byte 0-7 Read DQS Delay */
+ u32 PtrPatternBufA; /* Ptr on stack to aligned DQS testing pattern*/
+ u32 PtrPatternBufB; /* Ptr on stack to aligned DQS testing pattern*/
+ u8 Channel; /* Current Channel (0= CH A, 1=CH B)*/
+ u8 ByteLane; /* Current Byte Lane (0..7)*/
+ u8 Direction; /* Current DQS-DQ training write direction (0=read, 1=write)*/
+ u8 Pattern; /* Current pattern*/
+ u8 DQSDelay; /* Current DQS delay value*/
+ u32 TrainErrors; /* Current Training Errors*/
+
+ u32 AMC_TSC_DeltaLo; /* Time Stamp Counter measurement of AMC, Low dword*/
+ u32 AMC_TSC_DeltaHi; /* Time Stamp Counter measurement of AMC, High dword*/
+ /* NOTE: Not used in Barcelona - */
+ u8 CH_D_DIR_MaxMin_B_Dly[2][2][2][8];
+ /* CH A byte lane 0 - 7 minimum filtered window passing DQS delay value*/
+ /* 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*/
+ /* CH A byte lane 0 - 7 minimum filtered window passing DQS delay value*/
+ /* 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*/
+ 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?*/
+ u16 DimmTrainFail; /* Bitmap showing which dimms failed training*/
+ u16 CSTrainFail; /* Bitmap showing which chipselects failed training*/
+ u16 DimmYr06; /* Bitmap indicating which Dimms have a manufactur's year code <= 2006*/
+ u16 DimmWk2406; /* Bitmap indicating which Dimms have a manufactur's week code <= 24 of 2006 (June)*/
+ u16 DimmDRPresent; /* Bitmap indicating that Dual Rank Dimms are present*/
+ u16 DimmPlPresent; /* Bitmap indicating that Planar (1) or Stacked (0) Dimms are present.*/
+ u16 ChannelTrainFai; /* Bitmap showing the chanel informaiton about failed Chip Selects
+ 0 in any bit field indicates Channel 0
+ 1 in any bit field indicates Channel 1 */
+ u16 DIMMTfaw; /* Minimax Tfaw*16 (ns) of DIMMs */
+ u8 Tfaw; /* DCT Tfaw (busclocks) */
+ u16 CSUsrTestFail; /* Chip selects excluded by user */
+/* DCTStatStruct_F - end */
+
+ u16 CH_MaxRdLat[2]; /* Max Read Latency (ns) for DCT 0*/
+ /* Max Read Latency (ns) for DCT 1*/
+ u8 CH_D_DIR_B_DQS[2][4][2][9]; /* [A/B] [DIMM1-4] [R/W] [DQS] */
+ /* CHA DIMM0 Byte 0 - 7 and Check Write DQS Delay*/
+ /* CHA DIMM0 Byte 0 - 7 and Check Read DQS Delay*/
+ /* CHA DIMM1 Byte 0 - 7 and Check Write DQS Delay*/
+ /* CHA DIMM1 Byte 0 - 7 and Check Read DQS Delay*/
+ /* CHB DIMM0 Byte 0 - 7 and Check Write DQS Delay*/
+ /* CHB DIMM0 Byte 0 - 7 and Check Read DQS Delay*/
+ /* CHB DIMM1 Byte 0 - 7 and Check Write DQS Delay*/
+ /* CHB DIMM1 Byte 0 - 7 and Check Read DQS Delay*/
+ u8 CH_D_B_TxDqs[2][4][9]; /* [A/B] [DIMM1-4] [DQS] */
+ /* CHA DIMM0 Byte 0 - 7 TxDqs */
+ /* CHA DIMM0 Byte 0 - 7 TxDqs */
+ /* CHA DIMM1 Byte 0 - 7 TxDqs */
+ /* CHA DIMM1 Byte 0 - 7 TxDqs */
+ /* CHB DIMM0 Byte 0 - 7 TxDqs */
+ /* CHB DIMM0 Byte 0 - 7 TxDqs */
+ /* CHB DIMM1 Byte 0 - 7 TxDqs */
+ /* CHB DIMM1 Byte 0 - 7 TxDqs */
+ u8 CH_D_B_RCVRDLY[2][4][8]; /* [A/B] [DIMM0-3] [DQS] */
+ /* CHA DIMM 0 Receiver Enable Delay*/
+ /* CHA DIMM 1 Receiver Enable Delay*/
+ /* CHA DIMM 2 Receiver Enable Delay*/
+ /* CHA DIMM 3 Receiver Enable Delay*/
+
+ /* CHB DIMM 0 Receiver Enable Delay*/
+ /* CHB DIMM 1 Receiver Enable Delay*/
+ /* CHB DIMM 2 Receiver Enable Delay*/
+ /* CHB DIMM 3 Receiver Enable Delay*/
+ u8 CH_D_BC_RCVRDLY[2][4];
+ /* CHA DIMM 0 - 4 Check Byte Receiver Enable Delay*/
+ /* CHB DIMM 0 - 4 Check Byte Receiver Enable Delay*/
+ u8 DIMMValidDCT[2]; /* DIMM# in DCT0*/
+ /* DIMM# in DCT1*/
+ u16 CSPresent_DCT[2]; /* DCT# CS mapping */
+ u16 MirrPresU_NumRegR; /* Address mapping from edge connect to DIMM present for unbuffered dimm
+ Number of registers on the dimm for registered dimm */
+ u8 MaxDCTs; /* Max number of DCTs in system*/
+ /* NOTE: removed u8 DCT. Use ->dev_ for pci R/W; */ /*DCT pointer*/
+ u8 GangedMode; /* Ganged mode enabled, 0 = disabled, 1 = enabled*/
+ u8 DRPresent; /* Family 10 present flag, 0 = n0t Fam10, 1 = Fam10*/
+ u32 NodeSysLimit; /* BASE[39:8],for DCT0+DCT1 system address*/
+ u8 WrDatGrossH;
+ u8 DqsRcvEnGrossL;
+ /* NOTE: Not used - u8 NodeSpeed */ /* Bus Speed (to set Controller) */
+ /* 1=200Mhz */
+ /* 2=266Mhz */
+ /* 3=333Mhz */
+ /* NOTE: Not used - u8 NodeCASL */ /* CAS latency DCT setting */
+ /* 0=2.0 */
+ /* 1=3.0 */
+ /* 2=4.0 */
+ /* 3=5.0 */
+ /* 4=6.0 */
+ u8 TrwtWB;
+ u8 CurrRcvrCHADelay; /* for keep current RcvrEnDly of chA*/
+ u16 T1000; /* get the T1000 figure (cycle time (ns)*1K)*/
+ u8 DqsRcvEn_Pass; /* for TrainRcvrEn byte lane pass flag*/
+ u8 DqsRcvEn_Saved; /* for TrainRcvrEn byte lane saved flag*/
+ u8 SeedPass1Remainder; /* for Phy assisted DQS receiver enable training*/
+
+ /* for second pass - Second pass should never run for Fam10*/
+ /* NOTE: Not used for Barcelona - u8 CH_D_B_RCVRDLY_1[2][4][8]; */ /* CHA DIMM 0 Receiver Enable Delay */
+ /* CHA DIMM 1 Receiver Enable Delay*/
+ /* CHA DIMM 2 Receiver Enable Delay*/
+ /* CHA DIMM 3 Receiver Enable Delay*/
+
+ /* CHB DIMM 0 Receiver Enable Delay*/
+ /* CHB DIMM 1 Receiver Enable Delay*/
+ /* CHB DIMM 2 Receiver Enable Delay*/
+ /* CHB DIMM 3 Receiver Enable Delay*/
+
+ u8 ClToNB_flag; /* is used to restore ClLinesToNbDis bit after memory */
+ u32 NodeSysBase; /* for channel interleave usage */
+
+/* New for LB Support */
+ u8 NodePresent;
+ u32 dev_host;
+ u32 dev_map;
+ u32 dev_dct;
+ u32 dev_nbmisc;
+ u8 TargetFreq;
+ u8 TargetCASL;
+ u8 CtrlWrd3;
+ u8 CtrlWrd4;
+ u8 CtrlWrd5;
+ u8 DqsRdWrPos_Saved;
+ u8 DqsRcvEnGrossMax;
+ u8 DqsRcvEnGrossMin;
+ u8 WrDatGrossMax;
+ u8 WrDatGrossMin;
+
+ u16 RegMan1Present; /* DIMM present bitmap of Register manufacture 1 */
+ u16 RegMan2Present; /* DIMM present bitmap of Register manufacture 2 */
+
+ struct _sMCTStruct *C_MCTPtr;
+ struct _sDCTStruct *C_DCTPtr[2];
+ /* struct _sDCTStruct *C_DCT1Ptr; */
+
+ struct _sMCTStruct s_C_MCTPtr;
+ struct _sDCTStruct s_C_DCTPtr[2];
+ /* struct _sDCTStruct s_C_DCT1Ptr[8]; */
+};
+
+/*===============================================================================
+ Local Error Status Codes (DCTStatStruc.ErrCode)
+===============================================================================*/
+#define SC_RunningOK 0
+#define SC_VarianceErr 1 /* Running non-optimally*/
+#define SC_StopError 2 /* Not Running*/
+#define SC_FatalErr 3 /* Fatal Error, MCTB has exited immediately*/
+
+/*===============================================================================
+ Local Error Status (DCTStatStruc.ErrStatus[31:0])
+===============================================================================*/
+#define SB_NoDimms 0
+#define SB_DIMMChkSum 1
+#define SB_DimmMismatchM 2 /* dimm module type(buffer) mismatch*/
+#define SB_DimmMismatchT 3 /* dimm CL/T mismatch*/
+#define SB_DimmMismatchO 4 /* dimm organization mismatch (128-bit)*/
+#define SB_NoTrcTrfc 5 /* SPD missing Trc or Trfc info*/
+#define SB_NoCycTime 6 /* SPD missing byte 23 or 25*/
+#define SB_BkIntDis 7 /* Bank interleave requested but not enabled*/
+#define SB_DramECCDis 8 /* Dram ECC requested but not enabled*/
+#define SB_SpareDis 9 /* Online spare requested but not enabled*/
+#define SB_MinimumMode 10 /* Running in Minimum Mode*/
+#define SB_NORCVREN 11 /* No DQS Receiver Enable pass window found*/
+#define SB_CHA2BRCVREN 12 /* DQS Rcvr En pass window CHA to CH B too large*/
+#define SB_SmallRCVR 13 /* DQS Rcvr En pass window too small (far right of dynamic range)*/
+#define SB_NODQSPOS 14 /* No DQS-DQ passing positions*/
+#define SB_SMALLDQS 15 /* DQS-DQ passing window too small*/
+#define SB_DCBKScrubDis 16 /* DCache scrub requested but not enabled */
+
+/*===============================================================================
+ 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*/
+
+
+/*===============================================================================
+ NVRAM/run-time-configurable Items
+===============================================================================*/
+/*Platform Configuration*/
+#define NV_PACK_TYPE 0 /* CPU Package Type (2-bits)
+ 0=NPT L1
+ 1=NPT M2
+ 2=NPT S1*/
+#define NV_MAX_NODES 1 /* Number of Nodes/Sockets (4-bits)*/
+#define NV_MAX_DIMMS 2 /* Number of DIMM slots for the specified Node ID (4-bits)*/
+#define NV_MAX_MEMCLK 3 /* Maximum platform demonstrated Memclock (10-bits)
+ 200=200Mhz (DDR400)
+ 266=266Mhz (DDR533)
+ 333=333Mhz (DDR667)
+ 400=400Mhz (DDR800)*/
+#define NV_ECC_CAP 4 /* Bus ECC capable (1-bits)
+ 0=Platform not capable
+ 1=Platform is capable*/
+#define NV_4RANKType 5 /* 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).
+ 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).
+ 2=8 times (normal for non-UMA systems)
+ 3=16 times (normal for UMA systems)*/
+
+
+/*Dram Timing*/
+#define NV_MCTUSRTMGMODE 10 /* User Memclock Mode (2-bits)
+ 0=Auto, no user limit
+ 1=Auto, user limit provided in NV_MemCkVal
+ 2=Manual, user value provided in NV_MemCkVal*/
+#define NV_MemCkVal 11 /* Memory Clock Value (2-bits)
+ 0=200Mhz
+ 1=266Mhz
+ 2=333Mhz
+ 3=400Mhz*/
+
+/*Dram Configuration*/
+#define NV_BankIntlv 20 /* Dram Bank (chip-select) Interleaving (1-bits)
+ 0=disable
+ 1=enable*/
+#define NV_AllMemClks 21 /* Turn on All DIMM clocks (1-bits)
+ 0=normal
+ 1=enable all memclocks*/
+#define NV_SPDCHK_RESTRT 22 /* SPD Check control bitmap (1-bits)
+ 0=Exit current node init if any DIMM has SPD checksum error
+ 1=Ignore faulty SPD checksums (Note: DIMM cannot be enabled)*/
+#define NV_DQSTrainCTL 23 /* DQS Signal Timing Training Control
+ 0=skip DQS training
+ 1=perform DQS training*/
+#define NV_NodeIntlv 24 /* Node Memory Interleaving (1-bits)
+ 0=disable
+ 1=enable*/
+#define NV_BurstLen32 25 /* BurstLength32 for 64-bit mode (1-bits)
+ 0=disable (normal)
+ 1=enable (4 beat burst when width is 64-bits)*/
+
+/*Dram Power*/
+#define NV_CKE_PDEN 30 /* CKE based power down mode (1-bits)
+ 0=disable
+ 1=enable*/
+#define NV_CKE_CTL 31 /* CKE based power down control (1-bits)
+ 0=per Channel control
+ 1=per Chip select control*/
+#define NV_CLKHZAltVidC3 32 /* Memclock tri-stating during C3 and Alt VID (1-bits)
+ 0=disable
+ 1=enable*/
+
+/*Memory Map/Mgt.*/
+#define NV_BottomIO 40 /* Bottom of 32-bit IO space (8-bits)
+ NV_BottomIO[7:0]=Addr[31:24]*/
+#define NV_BottomUMA 41 /* Bottom of shared graphics dram (8-bits)
+ NV_BottomUMA[7:0]=Addr[31:24]*/
+#define NV_MemHole 42 /* Memory Hole Remapping (1-bits)
+ 0=disable
+ 1=enable */
+
+/*ECC*/
+#define NV_ECC 50 /* Dram ECC enable*/
+#define NV_NBECC 52 /* ECC MCE enable*/
+#define NV_ChipKill 53 /* Chip-Kill ECC Mode enable*/
+#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:
+ 0=disable Spare
+ 1=enable Spare */
+ /* Chip Select Spare Control bit 1-4:
+ Reserved, must be zero*/
+#define NV_SyncOnUnEccEn 61 /* SyncOnUnEccEn control
+ 0=disable
+ 1=enable*/
+#define NV_Unganged 62
+
+#define NV_ChannelIntlv 63 /* Channel Interleaving (3-bits)
+ xx0b = disable
+ yy1b = enable with DctSelIntLvAddr set to yyb */
+
+
+#ifndef MAX_NODES_SUPPORTED
+#define MAX_NODES_SUPPORTED 8
+#endif
+
+#ifndef MAX_DIMMS_SUPPORTED
+#define MAX_DIMMS_SUPPORTED 8
+#endif
+
+#ifndef MAX_CS_SUPPORTED
+#define MAX_CS_SUPPORTED 8
+#endif
+
+#ifndef MCT_DIMM_SPARE_NO_WARM
+#define MCT_DIMM_SPARE_NO_WARM 0
+#endif
+
+u32 Get_NB32(u32 dev, u32 reg);
+void Set_NB32(u32 dev, u32 reg, u32 val);
+u32 Get_NB32_index(u32 dev, u32 index_reg, u32 index);
+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);
+void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data);
+u32 OtherTiming_A_D(struct DCTStatStruc *pDCTstat, u32 val);
+void mct_ForceAutoPrecharge_D(struct DCTStatStruc *pDCTstat, u32 dct);
+u32 Modify_D3CMP(struct DCTStatStruc *pDCTstat, u32 dct, u32 value);
+u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass);
+u32 SetupDqsPattern_1PassA(u8 Pass);
+u32 SetupDqsPattern_1PassB(u8 Pass);
+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);
+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);
+void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+void TrainMaxReadLatency_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+void mct_EndDQSTraining_D(struct MCTStatStruc *pMCTstat,struct DCTStatStruc *pDCTstatA);
+void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 FinalValue, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass);
+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_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_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);
+void mct_BeforeDramInit_D(struct DCTStatStruc *pDCTstat, u32 dct);
+void mctGet_DIMMAddr(struct DCTStatStruc *pDCTstat, u32 node);
+void mctSMBhub_Init(u32 node);
+int mctRead_SPD(u32 smaddr, u32 reg);
+void InterleaveNodes_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+void InterleaveChannels_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+void mct_BeforeDQSTrain_Samp_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA);
+u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass);
+u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct);
+void mct_Wait(u32 cycles);
+u8 mct_RcvrRankEnabled_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 Channel, u8 ChipSel);
+u32 mct_GetRcvrSysAddr_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 channel, u8 receiver, u8 *valid);
+void mct_Read1LTestPattern_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 addr);
+
+#endif
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
new file mode 100644
index 0000000000..4b36207a2b
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mct_d_gcc.h
@@ -0,0 +1,370 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static inline void _WRMSR(u32 addr, u32 lo, u32 hi)
+{
+ __asm__ volatile (
+ "wrmsr"
+ :
+ :"c"(addr),"a"(lo), "d" (hi)
+ );
+}
+
+static inline void _RDMSR(u32 addr, u32 *lo, u32 *hi)
+{
+ __asm__ volatile (
+ "rdmsr"
+ :"=a"(*lo), "=d" (*hi)
+ :"c"(addr)
+ );
+}
+
+static inline void _RDTSC(u32 *lo, u32 *hi)
+{
+ __asm__ volatile (
+ "rdtsc"
+ : "=a" (*lo), "=d"(*hi)
+ );
+}
+
+static inline void _cpu_id(u32 addr, u32 *val)
+{
+ __asm__ volatile(
+ "cpuid"
+ : "=a" (val[0]),
+ "=b" (val[1]),
+ "=c" (val[2]),
+ "=d" (val[3])
+ : "0" (addr));
+
+}
+
+static u32 bsr(u32 x)
+{
+ u8 i;
+ u32 ret = 0;
+
+ for(i=31; i>0; i--) {
+ if(x & (1<<i)) {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+
+}
+
+static u32 bsf(u32 x)
+{
+ u8 i;
+ u32 ret = 32;
+
+ for(i=0; i<32; i++) {
+ if(x & (1<<i)) {
+ ret = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#define _MFENCE asm volatile ( "mfence")
+
+#define _SFENCE asm volatile ( "sfence" )
+
+/* prevent speculative execution of following instructions */
+#define _EXECFENCE asm volatile ("outb %al, $0xed")
+
+static inline u32 read_cr4(void)
+{
+ u32 cr4;
+ __asm__ volatile ("movl %%cr4, %0" : "=r" (cr4));
+ return cr4;
+}
+
+static inline void write_cr4(u32 cr4)
+{
+ __asm__ volatile ("movl %0, %%cr4" : : "r" (cr4));
+}
+
+u32 SetUpperFSbase(u32 addr_hi);
+
+static void proc_CLFLUSH(u32 addr_hi)
+{
+ SetUpperFSbase(addr_hi);
+
+ __asm__ volatile (
+ /* clflush fs:[eax] */
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "clflush %%fs:(%0)\n\t"
+ "mfence\n\t"
+ ::"a" (addr_hi<<8)
+ );
+}
+
+
+static void WriteLNTestPattern(u32 addr_lo, u8 *buf_a, u32 line_num)
+{
+ __asm__ volatile (
+ /*prevent speculative execution of following instructions*/
+ /* FIXME: needed ? */
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "1:\n\t"
+ "movdqa (%3), %%xmm0\n\t"
+ "movntdq %%xmm0, %%fs:(%0)\n\t" /* xmm0 is 128 bit */
+ "addl %1, %0\n\t"
+ "addl %1, %3\n\t"
+ "loop 1b\n\t"
+ "mfence\n\t"
+
+ :: "a" (addr_lo), "d" (16), "c" (line_num * 4), "b"(buf_a)
+ );
+
+}
+
+static u32 read32_fs(u32 addr_lo)
+{
+ u32 value;
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "movl %%fs:(%1), %0\n\t"
+ :"=b"(value): "a" (addr_lo)
+ );
+ return value;
+}
+
+#ifdef UNUSED_CODE
+static u8 read8_fs(u32 addr_lo)
+{
+ u8 byte;
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "movb %%fs:(%1), %b0\n\t"
+ "mfence\n\t"
+ :"=b"(byte): "a" (addr_lo)
+ );
+ return byte;
+}
+#endif
+
+static void FlushDQSTestPattern_L9(u32 addr_lo)
+{
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "clflush %%fs:-128(%%ecx)\n\t"
+ "clflush %%fs:-64(%%ecx)\n\t"
+ "clflush %%fs:(%%ecx)\n\t"
+ "clflush %%fs:64(%%ecx)\n\t"
+
+ "clflush %%fs:-128(%%eax)\n\t"
+ "clflush %%fs:-64(%%eax)\n\t"
+ "clflush %%fs:(%%eax)\n\t"
+ "clflush %%fs:64(%%eax)\n\t"
+
+ "clflush %%fs:-128(%%ebx)\n\t"
+
+ :: "b" (addr_lo+128+8*64), "c"(addr_lo+128),
+ "a"(addr_lo+128+4*64)
+ );
+
+}
+
+static __attribute__((noinline)) void FlushDQSTestPattern_L18(u32 addr_lo)
+{
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "clflush %%fs:-128(%%eax)\n\t"
+ "clflush %%fs:-64(%%eax)\n\t"
+ "clflush %%fs:(%%eax)\n\t"
+ "clflush %%fs:64(%%eax)\n\t"
+
+ "clflush %%fs:-128(%%edi)\n\t"
+ "clflush %%fs:-64(%%edi)\n\t"
+ "clflush %%fs:(%%edi)\n\t"
+ "clflush %%fs:64(%%edi)\n\t"
+
+ "clflush %%fs:-128(%%ebx)\n\t"
+ "clflush %%fs:-64(%%ebx)\n\t"
+ "clflush %%fs:(%%ebx)\n\t"
+ "clflush %%fs:64(%%ebx)\n\t"
+
+ "clflush %%fs:-128(%%ecx)\n\t"
+ "clflush %%fs:-64(%%ecx)\n\t"
+ "clflush %%fs:(%%ecx)\n\t"
+ "clflush %%fs:64(%%ecx)\n\t"
+
+ "clflush %%fs:-128(%%edx)\n\t"
+ "clflush %%fs:-64(%%edx)\n\t"
+
+ :: "b" (addr_lo+128+8*64), "c" (addr_lo+128+12*64),
+ "d" (addr_lo +128+16*64), "a"(addr_lo+128),
+ "D"(addr_lo+128+4*64)
+ );
+}
+
+static void ReadL18TestPattern(u32 addr_lo)
+{
+ /* set fs and use fs prefix to access the mem */
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "movl %%fs:-128(%%esi), %%eax\n\t" /* TestAddr cache line */
+ "movl %%fs:-64(%%esi), %%eax\n\t" /* +1 */
+ "movl %%fs:(%%esi), %%eax\n\t" /* +2 */
+ "movl %%fs:64(%%esi), %%eax\n\t" /* +3 */
+
+ "movl %%fs:-128(%%edi), %%eax\n\t" /* +4 */
+ "movl %%fs:-64(%%edi), %%eax\n\t" /* +5 */
+ "movl %%fs:(%%edi), %%eax\n\t" /* +6 */
+ "movl %%fs:64(%%edi), %%eax\n\t" /* +7 */
+
+ "movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
+ "movl %%fs:-64(%%ebx), %%eax\n\t" /* +9 */
+ "movl %%fs:(%%ebx), %%eax\n\t" /* +10 */
+ "movl %%fs:64(%%ebx), %%eax\n\t" /* +11 */
+
+ "movl %%fs:-128(%%ecx), %%eax\n\t" /* +12 */
+ "movl %%fs:-64(%%ecx), %%eax\n\t" /* +13 */
+ "movl %%fs:(%%ecx), %%eax\n\t" /* +14 */
+ "movl %%fs:64(%%ecx), %%eax\n\t" /* +15 */
+
+ "movl %%fs:-128(%%edx), %%eax\n\t" /* +16 */
+ "movl %%fs:-64(%%edx), %%eax\n\t" /* +17 */
+ "mfence\n\t"
+
+ :: "a"(0), "b" (addr_lo+128+8*64), "c" (addr_lo+128+12*64),
+ "d" (addr_lo +128+16*64), "S"(addr_lo+128),
+ "D"(addr_lo+128+4*64)
+ );
+
+}
+
+static void ReadL9TestPattern(u32 addr_lo)
+{
+
+ /* set fs and use fs prefix to access the mem */
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+
+ "movl %%fs:-128(%%ecx), %%eax\n\t" /* TestAddr cache line */
+ "movl %%fs:-64(%%ecx), %%eax\n\t" /* +1 */
+ "movl %%fs:(%%ecx), %%eax\n\t" /* +2 */
+ "movl %%fs:64(%%ecx), %%eax\n\t" /* +3 */
+
+ "movl %%fs:-128(%%edx), %%eax\n\t" /* +4 */
+ "movl %%fs:-64(%%edx), %%eax\n\t" /* +5 */
+ "movl %%fs:(%%edx), %%eax\n\t" /* +6 */
+ "movl %%fs:64(%%edx), %%eax\n\t" /* +7 */
+
+ "movl %%fs:-128(%%ebx), %%eax\n\t" /* +8 */
+ "mfence\n\t"
+
+ :: "a"(0), "b" (addr_lo+128+8*64), "c"(addr_lo+128),
+ "d"(addr_lo+128+4*64)
+ );
+
+}
+
+static void ReadMaxRdLat1CLTestPattern_D(u32 addr)
+{
+ SetUpperFSbase(addr);
+
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "movl %%fs:-128(%%esi), %%eax\n\t" /* TestAddr cache line */
+ "movl %%fs:-64(%%esi), %%eax\n\t" /* +1 */
+ "movl %%fs:(%%esi), %%eax\n\t" /* +2 */
+ "mfence\n\t"
+ :: "a"(0), "S"((addr<<8)+128)
+ );
+
+}
+
+static void WriteMaxRdLat1CLTestPattern_D(u32 buf, u32 addr)
+{
+ SetUpperFSbase(addr);
+
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "1:\n\t"
+ "movdqa (%3), %%xmm0\n\t"
+ "movntdq %%xmm0, %%fs:(%0)\n\t" /* xmm0 is 128 bit */
+ "addl %1, %0\n\t"
+ "addl %1, %3\n\t"
+ "loop 1b\n\t"
+ "mfence\n\t"
+
+ :: "a" (addr<<8), "d" (16), "c" (3 * 4), "b"(buf)
+ );
+}
+
+static void FlushMaxRdLatTestPattern_D(u32 addr)
+{
+ /* Flush a pattern of 72 bit times (per DQ) from cache.
+ * This procedure is used to ensure cache miss on the next read training.
+ */
+
+ SetUpperFSbase(addr);
+
+ __asm__ volatile (
+ "outb %%al, $0xed\n\t" /* _EXECFENCE */
+ "clflush %%fs:-128(%%esi)\n\t" /* TestAddr cache line */
+ "clflush %%fs:-64(%%esi)\n\t" /* +1 */
+ "clflush %%fs:(%%esi)\n\t" /* +2 */
+ "mfence\n\t"
+
+ :: "S"((addr<<8)+128)
+ );
+}
+
+static u32 stream_to_int(u8 *p)
+{
+ int i;
+ u32 val;
+ u32 valx;
+
+ val = 0;
+
+ for(i=3; i>=0; i--) {
+ val <<= 8;
+ valx = *(p+i);
+ val |= valx;
+ }
+
+ return val;
+}
+
+#ifdef UNUSED_CODE
+static void oemSet_NB32(u32 addr, u32 val, u8 *valid)
+{
+}
+
+static u32 oemGet_NB32(u32 addr, u8 *valid)
+{
+ *valid = 0;
+ return 0xffffffff;
+}
+#endif
+
+static u8 oemNodePresent_D(u8 Node, u8 *ret)
+{
+ *ret = 0;
+ return 0;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
new file mode 100644
index 0000000000..72502c0c16
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk5.c
@@ -0,0 +1,87 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+ u32 *AddrTmgCTL, u32 *ODC_CTL,
+ u8 *CMDmode);
+
+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);
+
+ 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 */
+}
+
+/*
+ * In: MAAdimms - number of DIMMs on the channel
+ * : Speed - Speed (see DCTStatstruc.Speed for definition)
+ * : MAAload - number of address bus loads on the channel
+ * Out: AddrTmgCTL - Address Timing Control Register Value
+ * : ODC_CTL - Output Driver Compensation Control Register Value
+ * : CMDmode - CMD mode
+ */
+static void Get_ChannelPS_Cfg0_D( u8 MAAdimms, u8 Speed, u8 MAAload,
+ u32 *AddrTmgCTL, u32 *ODC_CTL,
+ u8 *CMDmode)
+{
+ *AddrTmgCTL = 0;
+ *ODC_CTL = 0;
+ *CMDmode = 1;
+
+ if(MAAdimms == 1) {
+ if(MAAload >= 16) {
+ if(Speed == 4)
+ *AddrTmgCTL = 0x003B0000;
+ else if (Speed == 5)
+ *AddrTmgCTL = 0x00380000;
+ else if (Speed == 6)
+ *AddrTmgCTL = 0x00360000;
+ else
+ *AddrTmgCTL = 0x00340000;
+ } else {
+ *AddrTmgCTL = 0x00000000;
+ }
+ *ODC_CTL = 0x00113222;
+ *CMDmode = 1;
+ } else /* if(MAAdimms == 0) */ {
+ if(Speed == 4) {
+ *CMDmode = 1;
+ *AddrTmgCTL = 0x00390039;
+ } else if(Speed == 5) {
+ *CMDmode = 1;
+ *AddrTmgCTL = 0x00350037;
+ } else if(Speed == 6) {
+ *CMDmode = 2;
+ *AddrTmgCTL = 0x00000035;
+ } else {
+ *CMDmode = 2;
+ *AddrTmgCTL = 0x00000033;
+ }
+ *ODC_CTL = 0x00223323;
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c b/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c
new file mode 100644
index 0000000000..8a843e698c
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctardk6.c
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/* The socket type F (1207), Fr2, G (1207) are not tested.
+ */
+
+static void Get_ChannelPS_Cfg0_D(u8 MAAdimms, u8 Speed, u8 MAAload,
+ u8 DATAAload, u32 *AddrTmgCTL, u32 *ODC_CTL,
+ u8 *CMDmode);
+
+
+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->DATAload[dct],
+ &(pDCTstat->CH_ADDR_TMG[dct]), &(pDCTstat->CH_ODC_CTL[dct]),
+ &pDCTstat->_2Tmode);
+
+ if (pDCTstat->GangedMode == 1 && dct == 0)
+ Get_ChannelPS_Cfg0_D(pDCTstat->MAdimms[1], pDCTstat->Speed,
+ pDCTstat->MAload[1], pDCTstat->DATAload[1],
+ &(pDCTstat->CH_ADDR_TMG[1]), &(pDCTstat->CH_ODC_CTL[1]),
+ &pDCTstat->_2Tmode);
+
+ pDCTstat->CH_EccDQSLike[0] = 0x0302;
+ pDCTstat->CH_EccDQSLike[1] = 0x0302;
+
+}
+
+/*
+ * In: MAAdimms - number of DIMMs on the channel
+ * : Speed - Speed (see DCTStatstruc.Speed for definition)
+ * : MAAload - number of address bus loads on the channel
+ * : DATAAload - number of ranks on the channel
+ * Out: AddrTmgCTL - Address Timing Control Register Value
+ * : ODC_CTL - Output Driver Compensation Control Register Value
+ * : CMDmode - CMD mode
+ */
+static void Get_ChannelPS_Cfg0_D( u8 MAAdimms, u8 Speed, u8 MAAload,
+ u8 DATAAload, u32 *AddrTmgCTL, u32 *ODC_CTL,
+ u8 *CMDmode)
+{
+ *AddrTmgCTL = 0;
+ *ODC_CTL = 0;
+ *CMDmode = 1;
+
+ if (mctGet_NVbits(NV_MAX_DIMMS) == 4) {
+ if(Speed == 4) {
+ *AddrTmgCTL = 0x00000000;
+ } else if (Speed == 5) {
+ *AddrTmgCTL = 0x003C3C3C;
+ if (MAAdimms > 1)
+ *AddrTmgCTL = 0x003A3C3A;
+ } else if (Speed == 6) {
+ if (MAAdimms == 1)
+ *AddrTmgCTL = 0x003A3A3A;
+ else
+ *AddrTmgCTL = 0x00383A38;
+ } else {
+ if (MAAdimms == 1)
+ *AddrTmgCTL = 0x00373937;
+ else
+ *AddrTmgCTL = 0x00353935;
+ }
+ }
+ else {
+ if(Speed == 4) {
+ *AddrTmgCTL = 0x00000000;
+ if (MAAdimms == 3)
+ *AddrTmgCTL = 0x00380038;
+ } else if (Speed == 5) {
+ if (MAAdimms == 1)
+ *AddrTmgCTL = 0x003C3C3C;
+ else if (MAAdimms == 2)
+ *AddrTmgCTL = 0x003A3C3A;
+ else
+ *AddrTmgCTL = 0x00373C37;
+ } else if (Speed == 6) {
+ if (MAAdimms == 1)
+ *AddrTmgCTL = 0x003A3A3A;
+ else if (MAAdimms == 2)
+ *AddrTmgCTL = 0x00383A38;
+ else
+ *AddrTmgCTL = 0x00343A34;
+ } else {
+ if (MAAdimms == 1)
+ *AddrTmgCTL = 0x00393939;
+ else if (MAAdimms == 2)
+ *AddrTmgCTL = 0x00363936;
+ else
+ *AddrTmgCTL = 0x00303930;
+ }
+ }
+
+ if ((MAAdimms == 1) && (MAAload < 4))
+ *ODC_CTL = 0x20113222;
+ else
+ *ODC_CTL = 0x20223222;
+
+ *CMDmode = 1;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctchi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctchi_d.c
new file mode 100644
index 0000000000..05b01d7d5b
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctchi_d.c
@@ -0,0 +1,122 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+void InterleaveChannels_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+
+ u8 Node;
+ u32 DramBase, DctSelBase;
+ u8 DctSelIntLvAddr, DctSelHi;
+ u8 HoleValid = 0;
+ u32 HoleSize, HoleBase = 0;
+ u32 val, tmp;
+ u32 dct0_size, dct1_size;
+ struct DCTStatStruc *pDCTstat;
+
+ /* HoleValid - indicates whether the current Node contains hole.
+ * HoleSize - indicates whether there is IO hole in the whole system
+ * memory.
+ */
+
+ /* call back to wrapper not needed ManualChannelInterleave_D(); */
+ /* call back - DctSelIntLvAddr = mctGet_NVbits(NV_ChannelIntlv);*/ /* override interleave */
+ /* Manually set: typ=5, otherwise typ=7. */
+ DctSelIntLvAddr = mctGet_NVbits(NV_ChannelIntlv); /* typ=5: Hash*: exclusive OR of address bits[20:16, 6]. */
+
+ if (DctSelIntLvAddr & 1) {
+ DctSelIntLvAddr >>= 1;
+ HoleSize = 0;
+ if ((pMCTstat->GStatus & (1 << GSB_SoftHole)) ||
+ (pMCTstat->GStatus & (1 << GSB_HWHole))) {
+ if (pMCTstat->HoleBase) {
+ HoleBase = pMCTstat->HoleBase >> 8;
+ HoleSize = HoleBase & 0xFFFF0000;
+ HoleSize |= ((~HoleBase) + 1) & 0xFFFF;
+ }
+ }
+ Node = 0;
+ while (Node < MAX_NODES_SUPPORTED) {
+ pDCTstat = pDCTstatA + Node;
+ val = Get_NB32(pDCTstat->dev_map, 0xF0);
+ if (val & (1 << DramHoleValid))
+ HoleValid = 1;
+ if (!pDCTstat->GangedMode && pDCTstat->DIMMValidDCT[0] && pDCTstat->DIMMValidDCT[1]) {
+ DramBase = pDCTstat->NodeSysBase >> 8;
+ dct1_size = ((pDCTstat->NodeSysLimit) + 2) >> 8;
+ dct0_size = Get_NB32(pDCTstat->dev_dct, 0x114);
+ if (dct0_size >= 0x10000) {
+ dct0_size -= HoleSize;
+ }
+
+ dct0_size -= DramBase;
+ dct1_size -= dct0_size;
+ DctSelHi = 0x05; /* DctSelHiRngEn = 1, DctSelHi = 0 */
+ if (dct1_size == dct0_size) {
+ dct1_size = 0;
+ DctSelHi = 0x04; /* DctSelHiRngEn = 0 */
+ } else if (dct1_size > dct0_size ) {
+ dct1_size = dct0_size;
+ DctSelHi = 0x07; /* DctSelHiRngEn = 1, DctSelHi = 1 */
+ }
+ dct0_size = dct1_size;
+ dct0_size += DramBase;
+ dct0_size += dct1_size;
+ if (dct0_size >= HoleBase) /* if DctSelBaseAddr > HoleBase */
+ dct0_size += HoleSize;
+ DctSelBase = dct0_size;
+
+ if (dct1_size == 0)
+ dct0_size = 0;
+ dct0_size -= dct1_size; /* DctSelBaseOffset = DctSelBaseAddr - Interleaved region */
+ Set_NB32(pDCTstat->dev_dct, 0x114, dct0_size);
+
+ if (dct1_size == 0)
+ dct1_size = DctSelBase;
+ val = Get_NB32(pDCTstat->dev_dct, 0x110);
+ val &= 0x7F8;
+ val |= dct1_size;
+ val |= DctSelHi;
+ val |= (DctSelIntLvAddr << 6) & 0xFF;
+ Set_NB32(pDCTstat->dev_dct, 0x110, val);
+
+ if (HoleValid) {
+ tmp = DramBase;
+ val = DctSelBase;
+ if (val < HoleBase) { /* DctSelBaseAddr < DramHoleBase */
+ val -= DramBase;
+ val >>= 1;
+ tmp += val;
+ }
+ tmp += HoleSize;
+ val = Get_NB32(pDCTstat->dev_map, 0xF0); /* DramHoleOffset */
+ val &= 0xFFFF007F;
+ val |= (tmp & ~0xFFFF007F);
+ Set_NB32(pDCTstat->dev_map, 0xF0, val);
+ }
+ }
+ printk(BIOS_DEBUG, "InterleaveChannels_D: Node %x\n", Node);
+ printk(BIOS_DEBUG, "InterleaveChannels_D: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "InterleaveChannels_D: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "InterleaveChannels_D: ErrCode %x\n", pDCTstat->ErrCode);
+ Node++;
+ }
+ }
+ printk(BIOS_DEBUG, "InterleaveChannels_D: Done\n\n");
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
new file mode 100644
index 0000000000..4cf0fa8b30
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctcsi_d.c
@@ -0,0 +1,143 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/* Low swap bit vs bank size encoding (physical, not logical address bit)
+ * ;To calculate the number by hand, add the number of Bank address bits
+ * ;(2 or 3) to the number of column address bits, plus 3 (the logical
+ * ;page size), and subtract 8.
+ */
+static const u8 Tab_int_D[] = {6,7,7,8,8,8,8,8,9,9,8,9};
+
+void InterleaveBanks_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 ChipSel, EnChipSels;
+ u32 AddrLoMask, AddrHiMask;
+ u32 AddrLoMaskN, AddrHiMaskN, MemSize = 0;
+ u8 DoIntlv, _CsIntCap;
+ u32 BitDelta, BankEncd = 0;
+
+ u32 dev;
+ u32 reg;
+ u32 reg_off;
+ u32 val;
+ u32 val_lo, val_hi;
+
+ DoIntlv = mctGet_NVbits(NV_BankIntlv);
+ _CsIntCap = 0;
+ 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);
+ if ( val & (1<<CSEnable)) {
+ EnChipSels++;
+ reg = 0x60+((ChipSel>>1)<<2)+reg_off; /*Dram CS Mask 0 */
+ val = Get_NB32(dev, reg);
+ val >>= 19;
+ val &= 0x3ff;
+ val++;
+ if (EnChipSels == 1)
+ MemSize = val;
+ else
+ /*If mask sizes not same then skip */
+ if (val != MemSize)
+ break;
+ reg = 0x80 + reg_off; /*Dram Bank Addressing */
+ val = Get_NB32(dev, reg);
+ val >>= (ChipSel>>1)<<2;
+ val &= 0x0f;
+ if(EnChipSels == 1)
+ BankEncd = val;
+ else
+ /*If number of Rows/Columns not equal, skip */
+ if (val != BankEncd)
+ break;
+ }
+ ChipSel++;
+ }
+ if (ChipSel == MAX_CS_SUPPORTED) {
+ if ((EnChipSels == 2) || (EnChipSels == 4) || (EnChipSels == 8))
+ _CsIntCap = 1;
+ }
+
+ if (DoIntlv) {
+ if(!_CsIntCap) {
+ pDCTstat->ErrStatus |= 1<<SB_BkIntDis;
+ DoIntlv = 0;
+ }
+ }
+
+ if(DoIntlv) {
+ val = Tab_int_D[BankEncd];
+ if (pDCTstat->Status & (1<<SB_128bitmode))
+ val++;
+
+ AddrLoMask = (EnChipSels - 1) << val;
+ AddrLoMaskN = ~AddrLoMask;
+
+ val = bsf(MemSize) + 19;
+ AddrHiMask = (EnChipSels -1) << val;
+ AddrHiMaskN = ~AddrHiMask;
+
+ 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);
+ if (val & 3) {
+ val_lo = val & AddrLoMask;
+ val_hi = val & AddrHiMask;
+ val &= AddrLoMaskN;
+ val &= AddrHiMaskN;
+ val_lo <<= BitDelta;
+ val_hi >>= BitDelta;
+ val |= val_lo;
+ val |= val_hi;
+ Set_NB32(dev, reg, val);
+
+ if(ChipSel & 1)
+ continue;
+
+ reg = 0x60 + ((ChipSel>>1)<<2) + reg_off; /*Dram CS Mask 0 */
+ val = Get_NB32(dev, reg);
+ val_lo = val & AddrLoMask;
+ val_hi = val & AddrHiMask;
+ val &= AddrLoMaskN;
+ val &= AddrHiMaskN;
+ val_lo <<= BitDelta;
+ val_hi >>= BitDelta;
+ val |= val_lo;
+ val |= val_hi;
+ Set_NB32(dev, reg, val);
+ }
+ }
+ } /* DoIntlv */
+
+ /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */
+
+ printk(BIOS_DEBUG, "InterleaveBanks_D: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "InterleaveBanks_D: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "InterleaveBanks_D: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "InterleaveBanks_D: Done\n\n");
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
new file mode 100644
index 0000000000..db930eff9f
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctdqs_d.c
@@ -0,0 +1,1312 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u16 like,
+ u8 scale, u8 ChipSel);
+static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel);
+static u8 MiddleDQS_D(u8 min, u8 max);
+static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start);
+static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start);
+static void WriteDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo);
+static void WriteL18TestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo);
+static void WriteL9TestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo);
+static u16 CompareDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 addr_lo);
+static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 addr_lo);
+static void SetTargetWTIO_D(u32 TestAddr);
+static void ResetTargetWTIO_D(void);
+static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo);
+static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel,
+ u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
+void ResetDCTWrPtr_D(u32 dev, u32 index_reg, u32 index);
+u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 ChipSel);
+static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start);
+u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel,
+ u8 receiver, u8 *valid);
+static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 *buffer);
+
+static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel,
+ u8 RnkDlyFilterMin, u8 RnkDlyFilterMax);
+
+static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 ChipSel);
+
+#define DQS_TRAIN_DEBUG 0
+
+static void print_debug_dqs(const char *str, u32 val, u8 level)
+{
+#if DQS_TRAIN_DEBUG > 0
+ if (DQS_TRAIN_DEBUG >= level) {
+ printk(BIOS_DEBUG, "%s%x\n", str, val);
+ }
+#endif
+}
+
+static void print_debug_dqs_pair(const char *str, u32 val, const char *str2, u32 val2, u8 level)
+{
+#if DQS_TRAIN_DEBUG > 0
+ if (DQS_TRAIN_DEBUG >= level) {
+ printk(BIOS_DEBUG, "%s%08x%s%08x\n", str, val, str2, val2);
+ }
+#endif
+}
+
+/*Warning: These must be located so they do not cross a logical 16-bit segment boundary!*/
+const static u32 TestPatternJD1a_D[] = {
+ 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF, /* QW0-1, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW2-3, ALL-EVEN */
+ 0x00000000,0x00000000,0xFFFFFFFF,0xFFFFFFFF, /* QW4-5, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW6-7, ALL-EVEN */
+ 0xFeFeFeFe,0xFeFeFeFe,0x01010101,0x01010101, /* QW0-1, DQ0-ODD */
+ 0xFeFeFeFe,0xFeFeFeFe,0x01010101,0x01010101, /* QW2-3, DQ0-ODD */
+ 0x01010101,0x01010101,0xFeFeFeFe,0xFeFeFeFe, /* QW4-5, DQ0-ODD */
+ 0xFeFeFeFe,0xFeFeFeFe,0x01010101,0x01010101, /* QW6-7, DQ0-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW0-1, DQ1-ODD */
+ 0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd, /* QW2-3, DQ1-ODD */
+ 0xFdFdFdFd,0xFdFdFdFd,0x02020202,0x02020202, /* QW4-5, DQ1-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW6-7, DQ1-ODD */
+ 0x04040404,0x04040404,0xfBfBfBfB,0xfBfBfBfB, /* QW0-1, DQ2-ODD */
+ 0x04040404,0x04040404,0x04040404,0x04040404, /* QW2-3, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW4-5, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW6-7, DQ2-ODD */
+ 0x08080808,0x08080808,0xF7F7F7F7,0xF7F7F7F7, /* QW0-1, DQ3-ODD */
+ 0x08080808,0x08080808,0x08080808,0x08080808, /* QW2-3, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0x08080808,0x08080808, /* QW4-5, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7, /* QW6-7, DQ3-ODD */
+ 0x10101010,0x10101010,0x10101010,0x10101010, /* QW0-1, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0x10101010,0x10101010, /* QW2-3, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF, /* QW4-5, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0x10101010,0x10101010, /* QW6-7, DQ4-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW0-1, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0x20202020,0x20202020, /* QW2-3, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW4-5, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW6-7, DQ5-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW0-1, DQ6-ODD */
+ 0x40404040,0x40404040,0xBfBfBfBf,0xBfBfBfBf, /* QW2-3, DQ6-ODD */
+ 0x40404040,0x40404040,0xBfBfBfBf,0xBfBfBfBf, /* QW4-5, DQ6-ODD */
+ 0x40404040,0x40404040,0xBfBfBfBf,0xBfBfBfBf, /* QW6-7, DQ6-ODD */
+ 0x80808080,0x80808080,0x7F7F7F7F,0x7F7F7F7F, /* QW0-1, DQ7-ODD */
+ 0x80808080,0x80808080,0x7F7F7F7F,0x7F7F7F7F, /* QW2-3, DQ7-ODD */
+ 0x80808080,0x80808080,0x7F7F7F7F,0x7F7F7F7F, /* QW4-5, DQ7-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080 /* QW6-7, DQ7-ODD */
+};
+const static u32 TestPatternJD1b_D[] = {
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW0,CHA-B, ALL-EVEN */
+ 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, /* QW1,CHA-B, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW2,CHA-B, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW3,CHA-B, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW4,CHA-B, ALL-EVEN */
+ 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, /* QW5,CHA-B, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW6,CHA-B, ALL-EVEN */
+ 0x00000000,0x00000000,0x00000000,0x00000000, /* QW7,CHA-B, ALL-EVEN */
+ 0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe, /* QW0,CHA-B, DQ0-ODD */
+ 0x01010101,0x01010101,0x01010101,0x01010101, /* QW1,CHA-B, DQ0-ODD */
+ 0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe, /* QW2,CHA-B, DQ0-ODD */
+ 0x01010101,0x01010101,0x01010101,0x01010101, /* QW3,CHA-B, DQ0-ODD */
+ 0x01010101,0x01010101,0x01010101,0x01010101, /* QW4,CHA-B, DQ0-ODD */
+ 0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe, /* QW5,CHA-B, DQ0-ODD */
+ 0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe,0xFeFeFeFe, /* QW6,CHA-B, DQ0-ODD */
+ 0x01010101,0x01010101,0x01010101,0x01010101, /* QW7,CHA-B, DQ0-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW0,CHA-B, DQ1-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW1,CHA-B, DQ1-ODD */
+ 0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd, /* QW2,CHA-B, DQ1-ODD */
+ 0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd, /* QW3,CHA-B, DQ1-ODD */
+ 0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd,0xFdFdFdFd, /* QW4,CHA-B, DQ1-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW5,CHA-B, DQ1-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW6,CHA-B, DQ1-ODD */
+ 0x02020202,0x02020202,0x02020202,0x02020202, /* QW7,CHA-B, DQ1-ODD */
+ 0x04040404,0x04040404,0x04040404,0x04040404, /* QW0,CHA-B, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW1,CHA-B, DQ2-ODD */
+ 0x04040404,0x04040404,0x04040404,0x04040404, /* QW2,CHA-B, DQ2-ODD */
+ 0x04040404,0x04040404,0x04040404,0x04040404, /* QW3,CHA-B, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW4,CHA-B, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW5,CHA-B, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW6,CHA-B, DQ2-ODD */
+ 0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB,0xfBfBfBfB, /* QW7,CHA-B, DQ2-ODD */
+ 0x08080808,0x08080808,0x08080808,0x08080808, /* QW0,CHA-B, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7, /* QW1,CHA-B, DQ3-ODD */
+ 0x08080808,0x08080808,0x08080808,0x08080808, /* QW2,CHA-B, DQ3-ODD */
+ 0x08080808,0x08080808,0x08080808,0x08080808, /* QW3,CHA-B, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7, /* QW4,CHA-B, DQ3-ODD */
+ 0x08080808,0x08080808,0x08080808,0x08080808, /* QW5,CHA-B, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7, /* QW6,CHA-B, DQ3-ODD */
+ 0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7,0xF7F7F7F7, /* QW7,CHA-B, DQ3-ODD */
+ 0x10101010,0x10101010,0x10101010,0x10101010, /* QW0,CHA-B, DQ4-ODD */
+ 0x10101010,0x10101010,0x10101010,0x10101010, /* QW1,CHA-B, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF, /* QW2,CHA-B, DQ4-ODD */
+ 0x10101010,0x10101010,0x10101010,0x10101010, /* QW3,CHA-B, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF, /* QW4,CHA-B, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF, /* QW5,CHA-B, DQ4-ODD */
+ 0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF,0xeFeFeFeF, /* QW6,CHA-B, DQ4-ODD */
+ 0x10101010,0x10101010,0x10101010,0x10101010, /* QW7,CHA-B, DQ4-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW0,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW1,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW2,CHA-B, DQ5-ODD */
+ 0x20202020,0x20202020,0x20202020,0x20202020, /* QW3,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW4,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW5,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW6,CHA-B, DQ5-ODD */
+ 0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF,0xdFdFdFdF, /* QW7,CHA-B, DQ5-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW0,CHA-B, DQ6-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW1,CHA-B, DQ6-ODD */
+ 0x40404040,0x40404040,0x40404040,0x40404040, /* QW2,CHA-B, DQ6-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW3,CHA-B, DQ6-ODD */
+ 0x40404040,0x40404040,0x40404040,0x40404040, /* QW4,CHA-B, DQ6-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW5,CHA-B, DQ6-ODD */
+ 0x40404040,0x40404040,0x40404040,0x40404040, /* QW6,CHA-B, DQ6-ODD */
+ 0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf,0xBfBfBfBf, /* QW7,CHA-B, DQ6-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080, /* QW0,CHA-B, DQ7-ODD */
+ 0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F, /* QW1,CHA-B, DQ7-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080, /* QW2,CHA-B, DQ7-ODD */
+ 0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F, /* QW3,CHA-B, DQ7-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080, /* QW4,CHA-B, DQ7-ODD */
+ 0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F,0x7F7F7F7F, /* QW5,CHA-B, DQ7-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080, /* QW6,CHA-B, DQ7-ODD */
+ 0x80808080,0x80808080,0x80808080,0x80808080 /* QW7,CHA-B, DQ7-ODD */
+};
+
+void TrainReceiverEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA, u8 Pass)
+{
+ u8 Node;
+ struct DCTStatStruc *pDCTstat;
+ u32 val;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ 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);
+ mct_TrainRcvrEn_D(pMCTstat, pDCTstat, Pass);
+ }
+ }
+}
+
+static void SetEccDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+{
+ u8 channel;
+ u8 direction;
+
+ for (channel = 0; channel < 2; channel++){
+ for (direction = 0; direction < 2; direction++) {
+ pDCTstat->Channel = channel; /* Channel A or B */
+ pDCTstat->Direction = direction; /* Read or write */
+ CalcEccDQSPos_D(pMCTstat, pDCTstat, pDCTstat->CH_EccDQSLike[channel], pDCTstat->CH_EccDQSScale[channel], ChipSel);
+ print_debug_dqs_pair("\t\tSetEccDQSRdWrPos: channel ", channel, direction==DQS_READDIR? " R dqs_delay":" W dqs_delay", pDCTstat->DQSDelay, 2);
+ pDCTstat->ByteLane = 8;
+ StoreDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+ mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, ChipSel);
+ }
+ }
+}
+
+static void CalcEccDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u16 like, u8 scale, u8 ChipSel)
+{
+ u8 DQSDelay0, DQSDelay1;
+ u16 DQSDelay;
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ return;
+ }
+
+ pDCTstat->ByteLane = like & 0xff;
+ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+ DQSDelay0 = pDCTstat->DQSDelay;
+
+ pDCTstat->ByteLane = (like >> 8) & 0xff;
+ GetDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+ DQSDelay1 = pDCTstat->DQSDelay;
+
+ if (DQSDelay0>DQSDelay1) {
+ DQSDelay = DQSDelay0 - DQSDelay1;
+ } else {
+ DQSDelay = DQSDelay1 - DQSDelay0;
+ }
+
+ DQSDelay = DQSDelay * (~scale);
+
+ DQSDelay += 0x80; /* round it */
+
+ DQSDelay >>= 8; /* 256 */
+
+ if (DQSDelay0>DQSDelay1) {
+ DQSDelay = DQSDelay1 - DQSDelay;
+ } else {
+ DQSDelay += DQSDelay1;
+ }
+
+ pDCTstat->DQSDelay = (u8)DQSDelay;
+}
+
+static void TrainDQSRdWrPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start)
+{
+ u32 Errors;
+ u8 Channel, DQSWrDelay;
+ u8 _DisableDramECC = 0;
+ u32 PatternBuffer[292];
+ u8 _Wrap32Dis = 0, _SSE2 = 0;
+ u8 dqsWrDelay_end;
+
+ u32 addr;
+ u32 cr4;
+ u32 lo, hi;
+
+ print_debug_dqs("\nTrainDQSRdWrPos: 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);
+
+ SetupDqsPattern_D(pMCTstat, pDCTstat, PatternBuffer);
+
+ /* mct_BeforeTrainDQSRdWrPos_D */
+ dqsWrDelay_end = 0x20;
+
+ Errors = 0;
+ for (Channel = 0; Channel < 2; Channel++) {
+ print_debug_dqs("\tTrainDQSRdWrPos: 1 Channel ",Channel, 1);
+ pDCTstat->Channel = Channel;
+
+ if (pDCTstat->DIMMValidDCT[Channel] == 0) /* mct_BeforeTrainDQSRdWrPos_D */
+ continue;
+
+ pDCTstat->DqsRdWrPos_Saved = 0;
+ for ( DQSWrDelay = 0; DQSWrDelay < dqsWrDelay_end; DQSWrDelay++) {
+ pDCTstat->DQSDelay = DQSWrDelay;
+ pDCTstat->Direction = DQS_WRITEDIR;
+ mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
+
+ print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DQSWrDelay ", DQSWrDelay, 2);
+ TrainReadDQS_D(pMCTstat, pDCTstat, cs_start);
+ print_debug_dqs("\t\tTrainDQSRdWrPos: 21 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 2);
+ if (pDCTstat->DqsRdWrPos_Saved == 0xFF)
+ break;
+
+ print_debug_dqs("\t\tTrainDQSRdWrPos: 22 TrainErrors ",pDCTstat->TrainErrors, 2);
+ if (pDCTstat->TrainErrors == 0) {
+ break;
+ }
+ Errors |= pDCTstat->TrainErrors;
+ }
+
+ pDCTstat->DqsRdWrPos_Saved = 0;
+ if (DQSWrDelay < dqsWrDelay_end) {
+ Errors = 0;
+
+ print_debug_dqs("\tTrainDQSRdWrPos: 231 DQSWrDelay ", DQSWrDelay, 1);
+ TrainWriteDQS_D(pMCTstat, pDCTstat, cs_start);
+ }
+ print_debug_dqs("\tTrainDQSRdWrPos: 232 Errors ", Errors, 1);
+ pDCTstat->ErrStatus |= Errors;
+ }
+
+#if DQS_TRAIN_DEBUG > 0
+ {
+ u8 val;
+ u8 i;
+ u8 Channel, Receiver, Dir;
+ u8 *p;
+
+ for (Dir = 0; Dir < 2; Dir++) {
+ if (Dir == 1) {
+ print_debug("TrainDQSRdWrPos: CH_D_DIR_B_DQS WR:\n");
+ } else {
+ print_debug("TrainDQSRdWrPos: CH_D_DIR_B_DQS RD:\n");
+ }
+ for (Channel = 0; Channel < 2; Channel++) {
+ print_debug("Channel:"); print_debug_hex8(Channel); print_debug("\n");
+ for (Receiver = cs_start; Receiver < (cs_start + 2); Receiver += 2) {
+ print_debug("\t\tReceiver:"); print_debug_hex8(Receiver);
+ p = pDCTstat->CH_D_DIR_B_DQS[Channel][Receiver >> 1][Dir];
+ print_debug(": ");
+ for (i=0;i<8; i++) {
+ val = p[i];
+ print_debug_hex8(val);
+ print_debug(" ");
+ }
+ print_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, "TrainDQSRdWrPos: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: TrainErrors %x\n", pDCTstat->TrainErrors);
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "TrainDQSRdWrPos: Done\n\n");
+}
+
+static void SetupDqsPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 *buffer)
+{
+ /* 1. Set the Pattern type (0 or 1) in DCTStatstruc.Pattern
+ * 2. Copy the pattern from ROM to Cache, aligning on 16 byte boundary
+ * 3. Set the ptr to Cacheable copy in DCTStatstruc.PtrPatternBufA
+ */
+
+ u32 *buf;
+ u16 i;
+
+ buf = (u32 *)(((u32)buffer + 0x10) & (0xfffffff0));
+ if (pDCTstat->Status & (1<<SB_128bitmode)) {
+ pDCTstat->Pattern = 1; /* 18 cache lines, alternating qwords */
+ for (i=0; i<16*18; i++)
+ buf[i] = TestPatternJD1b_D[i];
+ } else {
+ pDCTstat->Pattern = 0; /* 9 cache lines, sequential qwords */
+ for (i=0; i<16*9; i++)
+ buf[i] = TestPatternJD1a_D[i];
+ }
+ pDCTstat->PtrPatternBufA = (u32)buf;
+}
+
+static void TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start)
+{
+ u32 Errors;
+ u8 ChipSel, DQSDelay;
+ u8 RnkDlySeqPassMin=0, RnkDlySeqPassMax=0xFF, RnkDlyFilterMin=0, RnkDlyFilterMax=0xFF;
+ u8 RnkDlySeqPassMinTot=0, RnkDlySeqPassMaxTot=0xFF, RnkDlyFilterMinTot=0, RnkDlyFilterMaxTot=0xFF;
+ u8 LastTest ,LastTestTot;
+ u32 TestAddr;
+ u8 ByteLane;
+ u8 MutualCSPassW[128];
+ u8 BanksPresent;
+ u8 dqsDelay_end;
+ u8 tmp, valid, tmp1;
+ u16 word;
+
+ /* MutualCSPassW: each byte represents a bitmap of pass/fail per
+ * ByteLane. The indext within MutualCSPassW is the delay value
+ * given the results.
+ */
+ print_debug_dqs("\t\t\tTrainDQSPos begin ", 0, 3);
+
+ Errors = 0;
+ BanksPresent = 0;
+
+ dqsDelay_end = 32;
+ /* Bitmapped status per delay setting, 0xff=All positions
+ * passing (1= PASS). Set the entire array.
+ */
+ for (DQSDelay=0; DQSDelay<128; DQSDelay++) {
+ MutualCSPassW[DQSDelay] = 0xFF;
+ }
+
+ for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) { /* logical register chipselects 0..7 */
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 11 ChipSel ", ChipSel, 4);
+
+ if (!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
+ print_debug_dqs("\t\t\t\tmct_RcvrRankEnabled_D CS not enabled ", ChipSel, 4);
+ continue;
+ }
+
+ BanksPresent = 1; /* flag for atleast one bank is present */
+ TestAddr = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel, &valid);
+ if (!valid) {
+ print_debug_dqs("\t\t\t\tAddress not supported on current CS ", TestAddr, 4);
+ continue;
+ }
+
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 12 TestAddr ", TestAddr, 4);
+ SetUpperFSbase(TestAddr); /* fs:eax=far ptr to target */
+
+ if (pDCTstat->Direction==DQS_READDIR) {
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 13 for read ", 0, 4);
+ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr<<8);
+ }
+
+ for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 141 DQSDelay ", DQSDelay, 5);
+
+ tmp = 0xFF;
+ tmp1 = DQSDelay;
+ if (pDCTstat->Direction == DQS_READDIR) {
+ tmp &= MutualCSPassW[DQSDelay];
+ tmp1 += dqsDelay_end;
+ }
+ tmp &= MutualCSPassW[tmp1];
+
+ if (tmp == 0) {
+ continue;/* skip current delay value if other chipselects have failed all 8 bytelanes */
+ }
+
+ pDCTstat->DQSDelay = DQSDelay;
+ mct_SetDQSDelayAllCSR_D(pMCTstat, pDCTstat, cs_start);
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 142 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+
+ if (pDCTstat->Direction == DQS_WRITEDIR) {
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 143 for write", 0, 5);
+ WriteDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr<<8);
+ }
+
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 Pattern ", pDCTstat->Pattern, 5);
+ ReadDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr<<8);
+/* print_debug_dqs("\t\t\t\t\tTrainDQSPos: 145 MutualCSPassW ", MutualCSPassW[DQSDelay], 5); */
+ word = CompareDQSTestPattern_D(pMCTstat, pDCTstat, TestAddr << 8); /* 0=fail, 1=pass */
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 1 ", word, 3);
+
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 DqsRdWrPos_Saved ", pDCTstat->DqsRdWrPos_Saved, 3);
+ word &= ~(pDCTstat->DqsRdWrPos_Saved); /* mask out bytelanes that already passed */
+ word &= ~(pDCTstat->DqsRdWrPos_Saved << 8);
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 144 compare 2 ", word, 3);
+
+ tmp = DQSDelay;
+ if (pDCTstat->Direction == DQS_READDIR) {
+ MutualCSPassW[tmp] &= word >> 8;
+ tmp += dqsDelay_end;
+ }
+ MutualCSPassW[tmp] &= word & 0xFF;
+
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 146 \tMutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+
+ SetTargetWTIO_D(TestAddr);
+ FlushDQSTestPattern_D(pDCTstat, TestAddr<<8);
+ ResetTargetWTIO_D();
+ }
+
+ }
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ dqsDelay_end <<= 1;
+ }
+
+ if (BanksPresent) {
+ u8 mask_pass = 0;
+ for (ByteLane = 0; ByteLane < 8; ByteLane++) {
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 31 ByteLane ",ByteLane, 4);
+ if (!(pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane))) {
+ pDCTstat->ByteLane = ByteLane;
+ LastTest = DQS_FAIL; /* Analyze the results */
+ LastTestTot = DQS_FAIL;
+ /* RnkDlySeqPassMin = 0; */
+ /* RnkDlySeqPassMax = 0; */
+ RnkDlyFilterMax = 0;
+ RnkDlyFilterMin = 0;
+ RnkDlyFilterMaxTot = 0;
+ RnkDlyFilterMinTot = 0;
+ for (DQSDelay = 0; DQSDelay < dqsDelay_end; DQSDelay++) {
+ if (MutualCSPassW[DQSDelay] & (1 << ByteLane)) {
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 321 DQSDelay ", DQSDelay, 5);
+ print_debug_dqs("\t\t\t\t\tTrainDQSPos: 322 MutualCSPassW ", MutualCSPassW[DQSDelay], 5);
+ if (pDCTstat->Direction == DQS_READDIR)
+ tmp = 0x20;
+ else
+ tmp = 0;
+ if (DQSDelay >= tmp) {
+ RnkDlySeqPassMax = DQSDelay;
+ if (LastTest == DQS_FAIL) {
+ RnkDlySeqPassMin = DQSDelay; /* start sequential run */
+ }
+ if ((RnkDlySeqPassMax - RnkDlySeqPassMin)>(RnkDlyFilterMax-RnkDlyFilterMin)){
+ RnkDlyFilterMin = RnkDlySeqPassMin;
+ RnkDlyFilterMax = RnkDlySeqPassMax;
+ }
+ LastTest = DQS_PASS;
+ }
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ RnkDlySeqPassMaxTot = DQSDelay;
+ if (LastTestTot == DQS_FAIL)
+ RnkDlySeqPassMinTot = DQSDelay;
+ if ((RnkDlySeqPassMaxTot - RnkDlySeqPassMinTot)>(RnkDlyFilterMaxTot-RnkDlyFilterMinTot)){
+ RnkDlyFilterMinTot = RnkDlySeqPassMinTot;
+ RnkDlyFilterMaxTot = RnkDlySeqPassMaxTot;
+ }
+ LastTestTot = DQS_PASS;
+ }
+ } else {
+ LastTest = DQS_FAIL;
+ LastTestTot = DQS_FAIL;
+ }
+ }
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 33 RnkDlySeqPassMax ", RnkDlySeqPassMax, 4);
+ if (RnkDlySeqPassMax == 0) {
+ Errors |= 1<<SB_NODQSPOS; /* no passing window */
+ } else {
+ print_debug_dqs_pair("\t\t\t\tTrainDQSPos: 34 RnkDlyFilter: ", RnkDlyFilterMin, " ", RnkDlyFilterMax, 4);
+ if (((RnkDlyFilterMax - RnkDlyFilterMin) < MIN_DQS_WNDW)){
+ Errors |= 1 << SB_SMALLDQS;
+ } else {
+ u8 middle_dqs;
+ /* mctEngDQSwindow_Save_D Not required for arrays */
+ if (pDCTstat->Direction == DQS_READDIR)
+ middle_dqs = MiddleDQS_D(RnkDlyFilterMinTot, RnkDlyFilterMaxTot);
+ else
+ middle_dqs = MiddleDQS_D(RnkDlyFilterMin, RnkDlyFilterMax);
+ pDCTstat->DQSDelay = middle_dqs;
+ mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, cs_start); /* load the register with the value */
+ if (pDCTstat->Direction == DQS_READDIR)
+ StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMinTot, RnkDlyFilterMaxTot); /* store the value into the data structure */
+ else
+ StoreWrRdDQSDatStrucVal_D(pMCTstat, pDCTstat, cs_start, RnkDlyFilterMin, RnkDlyFilterMax); /* store the value into the data structure */
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 42 middle_dqs : ",middle_dqs, 4);
+ pDCTstat->DqsRdWrPos_Saved |= 1 << ByteLane;
+ }
+ }
+ } /* if (pDCTstat->DqsRdWrPos_Saved &(1 << ByteLane)) */
+ }
+ print_debug_dqs("\t\t\t\tTrainDQSPos: 41 mask_pass ",mask_pass, 3);
+ }
+/* skipLocMiddle: */
+ pDCTstat->TrainErrors = Errors;
+
+ print_debug_dqs("\t\t\tTrainDQSPos: Errors ", Errors, 3);
+}
+
+static void mctEngDQSwindow_Save_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel,
+ u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
+{
+ pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
+ [pDCTstat->Direction]
+ [0]
+ [pDCTstat->ByteLane] = RnkDlyFilterMin;
+ pDCTstat->CH_D_DIR_MaxMin_B_Dly[pDCTstat->Channel]
+ [pDCTstat->Direction]
+ [1]
+ [pDCTstat->ByteLane] = RnkDlyFilterMax;
+}
+
+static void StoreDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+{
+ /* Store the DQSDelay value, found during a training sweep, into the DCT
+ * status structure for this node
+ */
+
+ /* When 400, 533, 667, it will support dimm0/1/2/3,
+ * and set conf for dimm0, hw will copy to dimm1/2/3
+ * set for dimm1, hw will copy to dimm3
+ * Rev A/B only support DIMM0/1 when 800Mhz and above + 0x100 to next dimm
+ * Rev C support DIMM0/1/2/3 when 800Mhz and above + 0x100 to next dimm
+ */
+
+ /* FindDQSDatDimmVal_D is not required since we use an array */
+ u8 dn = 0;
+
+ dn = ChipSel>>1; /* if odd or even logical DIMM */
+
+ pDCTstat->CH_D_DIR_B_DQS[pDCTstat->Channel][dn][pDCTstat->Direction][pDCTstat->ByteLane] =
+ pDCTstat->DQSDelay;
+}
+
+static void StoreWrRdDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel,
+ u8 RnkDlyFilterMin, u8 RnkDlyFilterMax)
+{
+ u8 dn;
+
+ if (pDCTstat->Direction == DQS_WRITEDIR) {
+ dn = ChipSel >> 1;
+ RnkDlyFilterMin += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+ RnkDlyFilterMax += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+ pDCTstat->DQSDelay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][dn][pDCTstat->ByteLane];
+ } else {
+ RnkDlyFilterMin <<= 1;
+ RnkDlyFilterMax <<= 1;
+ pDCTstat->DQSDelay <<= 1;
+ }
+ mctEngDQSwindow_Save_D(pMCTstat, pDCTstat, ChipSel, RnkDlyFilterMin, RnkDlyFilterMax);
+ StoreDQSDatStrucVal_D(pMCTstat, pDCTstat, ChipSel);
+}
+
+static void GetDQSDatStrucVal_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+{
+ u8 dn = 0;
+
+ /* When 400, 533, 667, it will support dimm0/1/2/3,
+ * and set conf for dimm0, hw will copy to dimm1/2/3
+ * set for dimm1, hw will copy to dimm3
+ * Rev A/B only support DIMM0/1 when 800Mhz and above + 0x100 to next dimm
+ * Rev C support DIMM0/1/2/3 when 800Mhz and above + 0x100 to next dimm
+ */
+
+ /* FindDQSDatDimmVal_D is not required since we use an array */
+ dn = ChipSel >> 1; /*if odd or even logical DIMM */
+
+ pDCTstat->DQSDelay =
+ pDCTstat->CH_D_DIR_B_DQS[pDCTstat->Channel][dn][pDCTstat->Direction][pDCTstat->ByteLane];
+}
+
+/* FindDQSDatDimmVal_D is not required since we use an array */
+
+static u8 MiddleDQS_D(u8 min, u8 max)
+{
+ u8 size;
+ size = max-min;
+ if (size % 2)
+ size++; /* round up if the size isn't even. */
+ return ( min + (size >> 1));
+}
+
+static void TrainReadDQS_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start)
+{
+ print_debug_dqs("\t\tTrainReadPos ", 0, 2);
+ pDCTstat->Direction = DQS_READDIR;
+ TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
+}
+
+static void TrainWriteDQS_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start)
+{
+ pDCTstat->Direction = DQS_WRITEDIR;
+ print_debug_dqs("\t\tTrainWritePos", 0, 2);
+ TrainDQSPos_D(pMCTstat, pDCTstat, cs_start);
+}
+
+static void proc_IOCLFLUSH_D(u32 addr_hi)
+{
+ SetTargetWTIO_D(addr_hi);
+ proc_CLFLUSH(addr_hi);
+ ResetTargetWTIO_D();
+}
+
+static u8 ChipSelPresent_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 ChipSel)
+{
+ u32 val;
+ u32 reg;
+ u32 dev = pDCTstat->dev_dct;
+ u32 reg_off;
+ u8 ret = 0;
+
+ if (!pDCTstat->GangedMode) {
+ reg_off = 0x100 * Channel;
+ } else {
+ reg_off = 0;
+ }
+
+ if (ChipSel < MAX_CS_SUPPORTED){
+ reg = 0x40 + (ChipSel << 2) + reg_off;
+ val = Get_NB32(dev, reg);
+ if (val & ( 1 << 0))
+ ret = 1;
+ }
+
+ return ret;
+}
+
+/* proc_CLFLUSH_D located in mct_gcc.h */
+
+static void WriteDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo)
+{
+ /* Write a pattern of 72 bit times (per DQ), to test dram functionality.
+ * The pattern is a stress pattern which exercises both ISI and
+ * crosstalk. The number of cache lines to fill is dependent on DCT
+ * width mode and burstlength.
+ * Mode BL Lines Pattern no.
+ * ----+---+-------------------
+ * 64 4 9 0
+ * 64 8 9 0
+ * 64M 4 9 0
+ * 64M 8 9 0
+ * 128 4 18 1
+ * 128 8 N/A -
+ */
+ if (pDCTstat->Pattern == 0)
+ WriteL9TestPattern_D(pDCTstat, TestAddr_lo);
+ else
+ WriteL18TestPattern_D(pDCTstat, TestAddr_lo);
+}
+
+static void WriteL18TestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo)
+{
+ u8 *buf;
+
+ buf = (u8 *)pDCTstat->PtrPatternBufA;
+ WriteLNTestPattern(TestAddr_lo, buf, 18);
+
+}
+
+static void WriteL9TestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo)
+{
+ u8 *buf;
+
+ buf = (u8 *)pDCTstat->PtrPatternBufA;
+ WriteLNTestPattern(TestAddr_lo, buf, 9);
+}
+
+static u16 CompareDQSTestPattern_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u32 addr_lo)
+{
+ /* Compare a pattern of 72 bit times (per DQ), to test dram functionality.
+ * The pattern is a stress pattern which exercises both ISI and
+ * crosstalk. The number of cache lines to fill is dependent on DCT
+ * width mode and burstlength.
+ * Mode BL Lines Pattern no.
+ * ----+---+-------------------
+ * 64 4 9 0
+ * 64 8 9 0
+ * 64M 4 9 0
+ * 64M 8 9 0
+ * 128 4 18 1
+ * 128 8 N/A -
+ */
+
+ u32 *test_buf;
+ u16 MEn1Results, bitmap;
+ u8 bytelane;
+ u8 i;
+ u32 value;
+ u8 j;
+ u32 value_test;
+ u32 value_r, value_r_test;
+ u8 pattern, channel, BeatCnt;
+ struct DCTStatStruc *ptrAddr;
+
+ ptrAddr = pDCTstat;
+ pattern = pDCTstat->Pattern;
+ channel = pDCTstat->Channel;
+ test_buf = (u32 *)pDCTstat->PtrPatternBufA;
+
+ if (pattern && channel) {
+ addr_lo += 8; /* second channel */
+ test_buf+= 2;
+ }
+
+ bytelane = 0;
+ bitmap = 0xFFFF;
+ MEn1Results = 0xFFFF;
+ BeatCnt = 0;
+ for (i=0; i < (9 * 64 / 4); i++) { /* /4 due to next loop */
+ value = read32_fs(addr_lo);
+ value_test = *test_buf;
+
+ print_debug_dqs_pair("\t\t\t\t\t\ttest_buf = ", (u32)test_buf, " value = ", value_test, 7);
+ print_debug_dqs_pair("\t\t\t\t\t\ttaddr_lo = ", addr_lo, " value = ", value, 7);
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ if (BeatCnt != 0) {
+ value_r = *test_buf;
+ if (pattern)
+ value_r_test = read32_fs(addr_lo - 16);
+ else
+ value_r_test = read32_fs(addr_lo - 8);
+ }
+ print_debug_dqs_pair("\t\t\t\t\t\t\ttest_buf = ", (u32)test_buf, " value_r_test = ", value_r, 7);
+ print_debug_dqs_pair("\t\t\t\t\t\t\ttaddr_lo = ", addr_lo, " value_r = ", value_r_test, 7);
+ }
+
+ for (j = 0; j < (4 * 8); j += 8) {
+ if (((value >> j) & 0xff) != ((value_test >> j) & 0xff)) {
+ bitmap &= ~(1 << bytelane);
+ }
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ if (BeatCnt != 0) {
+ if (((value_r >> j) & 0xff) != ((value_r_test >> j) & 0xff)) {
+ MEn1Results &= ~(1 << bytelane);
+ }
+ }
+ }
+ bytelane++;
+ bytelane &= 0x7;
+ }
+
+ print_debug_dqs("\t\t\t\t\t\tbitmap = ", bitmap, 7);
+ print_debug_dqs("\t\t\t\t\t\tMEn1Results = ", MEn1Results, 7);
+
+ if (!bitmap)
+ break;
+
+ if (bytelane == 0){
+ BeatCnt += 4;
+ if (!(pDCTstat->Status & (1 <<SB_128bitmode))) {
+ if (BeatCnt == 8) BeatCnt = 0; /* 8 beat burst */
+ } else {
+ if (BeatCnt == 4) BeatCnt = 0; /* 4 beat burst */
+ }
+ if (pattern == 1) { /* dual channel */
+ addr_lo += 8; /* skip over other channel's data */
+ test_buf += 2;
+ }
+ }
+ addr_lo += 4;
+ test_buf += 1;
+ }
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ bitmap &= 0xFF;
+ bitmap |= MEn1Results << 8;
+ }
+
+ print_debug_dqs("\t\t\t\t\t\tbitmap = ", bitmap, 6);
+
+ return bitmap;
+}
+
+static void FlushDQSTestPattern_D(struct DCTStatStruc *pDCTstat,
+ u32 addr_lo)
+{
+ /* Flush functions in mct_gcc.h */
+ if (pDCTstat->Pattern == 0){
+ FlushDQSTestPattern_L9(addr_lo);
+ } else {
+ FlushDQSTestPattern_L18(addr_lo);
+ }
+}
+
+static void SetTargetWTIO_D(u32 TestAddr)
+{
+ u32 lo, hi;
+ hi = TestAddr >> 24;
+ lo = TestAddr << 8;
+ _WRMSR(0xC0010016, lo, hi); /* IORR0 Base */
+ hi = 0xFF;
+ lo = 0xFC000800; /* 64MB Mask */
+ _WRMSR(0xC0010017, lo, hi); /* IORR0 Mask */
+}
+
+static void ResetTargetWTIO_D(void)
+{
+ u32 lo, hi;
+
+ hi = 0;
+ lo = 0;
+ _WRMSR(0xc0010017, lo, hi); /* IORR0 Mask */
+}
+
+static void ReadDQSTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr_lo)
+{
+ /* Read a pattern of 72 bit times (per DQ), to test dram functionality.
+ * The pattern is a stress pattern which exercises both ISI and
+ * crosstalk. The number of cache lines to fill is dependent on DCT
+ * width mode and burstlength.
+ * Mode BL Lines Pattern no.
+ * ----+---+-------------------
+ * 64 4 9 0
+ * 64 8 9 0
+ * 64M 4 9 0
+ * 64M 8 9 0
+ * 128 4 18 1
+ * 128 8 N/A -
+ */
+ if (pDCTstat->Pattern == 0)
+ ReadL9TestPattern(TestAddr_lo);
+ else
+ ReadL18TestPattern(TestAddr_lo);
+ _MFENCE;
+}
+
+u32 SetUpperFSbase(u32 addr_hi)
+{
+ /* Set the upper 32-bits of the Base address, 4GB aligned) for the
+ * FS selector.
+ */
+ u32 lo, hi;
+ u32 addr;
+ lo = 0;
+ hi = addr_hi>>24;
+ addr = FS_Base;
+ _WRMSR(addr, lo, hi);
+ return addr_hi<<8;
+}
+
+void ResetDCTWrPtr_D(u32 dev, 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);
+}
+
+/* mctEngDQSwindow_Save_D not required with arrays */
+
+void mct_TrainDQSPos_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u8 ChipSel;
+ struct DCTStatStruc *pDCTstat;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ pDCTstat = pDCTstatA + Node;
+ if (pDCTstat->DCTSysLimit) {
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+ TrainDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
+ SetEccDQSRdWrPos_D(pMCTstat, pDCTstat, ChipSel);
+ }
+ }
+ }
+}
+
+/* mct_BeforeTrainDQSRdWrPos_D
+ * Function is inline.
+ */
+u8 mct_DisableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 _DisableDramECC = 0;
+ u32 val;
+ u32 reg;
+ u32 dev;
+
+ /*Disable ECC correction of reads on the dram bus. */
+
+ dev = pDCTstat->dev_dct;
+ reg = 0x90;
+ val = Get_NB32(dev, reg);
+ if (val & (1<<DimmEcEn)) {
+ _DisableDramECC |= 0x01;
+ val &= ~(1<<DimmEcEn);
+ Set_NB32(dev, reg, val);
+ }
+ if (!pDCTstat->GangedMode) {
+ reg = 0x190;
+ val = Get_NB32(dev, reg);
+ if (val & (1<<DimmEcEn)) {
+ _DisableDramECC |= 0x02;
+ val &= ~(1<<DimmEcEn);
+ Set_NB32(dev, reg, val);
+ }
+ }
+ return _DisableDramECC;
+}
+
+void mct_EnableDimmEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 _DisableDramECC)
+{
+ u32 val;
+ u32 reg;
+ u32 dev;
+
+ /* Enable ECC correction if it was previously disabled */
+
+ dev = pDCTstat->dev_dct;
+
+ if ((_DisableDramECC & 0x01) == 0x01) {
+ reg = 0x90;
+ val = Get_NB32(dev, reg);
+ val |= (1<<DimmEcEn);
+ Set_NB32(dev, reg, val);
+ }
+ if ((_DisableDramECC & 0x02) == 0x02) {
+ reg = 0x190;
+ val = Get_NB32(dev, reg);
+ val |= (1<<DimmEcEn);
+ Set_NB32(dev, reg, val);
+ }
+}
+
+static void mct_SetDQSDelayCSR_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 ChipSel)
+{
+ u8 ByteLane;
+ u32 val;
+ u32 index_reg = 0x98 + 0x100 * pDCTstat->Channel;
+ u8 shift;
+ u32 dqs_delay = (u32)pDCTstat->DQSDelay;
+ u32 dev = pDCTstat->dev_dct;
+ u32 index;
+
+ ByteLane = pDCTstat->ByteLane;
+
+ if (!(pDCTstat->DqsRdWrPos_Saved & (1 << ByteLane))) {
+ /* Channel is offset */
+ if (ByteLane < 4) {
+ index = 1;
+ } else if (ByteLane <8) {
+ index = 2;
+ } else {
+ index = 3;
+ }
+
+ if (pDCTstat->Direction == DQS_READDIR) {
+ index += 4;
+ }
+
+ /* get the proper register index */
+ shift = ByteLane%4;
+ shift <<= 3; /* get bit position of bytelane, 8 bit */
+
+ index += (ChipSel>>1) << 8;
+
+ val = Get_NB32_index_wait(dev, index_reg, index);
+ if (ByteLane < 8) {
+ if (pDCTstat->Direction == DQS_WRITEDIR) {
+ dqs_delay += pDCTstat->CH_D_B_TxDqs[pDCTstat->Channel][ChipSel>>1][ByteLane];
+ } else {
+ dqs_delay <<= 1;
+ }
+ }
+ val &= ~(0x7f << shift);
+ val |= (dqs_delay << shift);
+ Set_NB32_index_wait(dev, index_reg, index, val);
+ }
+}
+
+static void mct_SetDQSDelayAllCSR_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 cs_start)
+{
+ u8 ByteLane;
+ u8 ChipSel = cs_start;
+
+ for (ChipSel = cs_start; ChipSel < (cs_start + 2); ChipSel++) {
+ if ( mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, pDCTstat->Channel, ChipSel)) {
+ for (ByteLane = 0; ByteLane < 8; ByteLane++) {
+ pDCTstat->ByteLane = ByteLane;
+ mct_SetDQSDelayCSR_D(pMCTstat, pDCTstat, ChipSel);
+ }
+ }
+ }
+}
+
+u8 mct_RcvrRankEnabled_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 ChipSel)
+{
+ u8 ret;
+
+ ret = ChipSelPresent_D(pMCTstat, pDCTstat, Channel, ChipSel);
+ return ret;
+}
+
+u32 mct_GetRcvrSysAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 channel, u8 receiver, u8 *valid)
+{
+ return mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, channel, receiver, valid);
+}
+
+u32 mct_GetMCTSysAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 receiver, u8 *valid)
+{
+ u32 val;
+ u32 reg_off = 0;
+ u32 reg;
+ u32 dword;
+ u32 dev = pDCTstat->dev_dct;
+
+ *valid = 0;
+
+
+ if (!pDCTstat->GangedMode) { /* FIXME: not used. */
+ reg_off = 0x100 * Channel;
+ }
+
+ /* get the local base addr of the chipselect */
+ reg = 0x40 + (receiver << 2);
+ val = Get_NB32(dev, reg);
+
+ val &= ~0x0F;
+
+ /* unganged mode DCT0+DCT1, sys addr of DCT1=node
+ * base+DctSelBaseAddr+local ca base*/
+ if ((Channel) && (pDCTstat->GangedMode == 0) && ( pDCTstat->DIMMValidDCT[0] > 0)) {
+ reg = 0x110;
+ dword = Get_NB32(dev, reg);
+ dword &= 0xfffff800;
+ dword <<= 8; /* scale [47:27] of F2x110[31:11] to [39:8]*/
+ val += dword;
+
+ /* if DCTSelBaseAddr < Hole, and eax > HoleBase, then add Hole size to test address */
+ if ((val >= pDCTstat->DCTHoleBase) && (pDCTstat->DCTHoleBase > dword)) {
+ dword = (~(pDCTstat->DCTHoleBase >> (24 - 8)) + 1) & 0xFF;
+ dword <<= (24 - 8);
+ val += dword;
+ }
+ } else {
+ /* sys addr=node base+local cs base */
+ val += pDCTstat->DCTSysBase;
+
+ /* New stuff */
+ if (pDCTstat->DCTHoleBase && (val >= pDCTstat->DCTHoleBase)) {
+ val -= pDCTstat->DCTSysBase;
+ dword = Get_NB32(pDCTstat->dev_map, 0xF0); /* get Hole Offset */
+ val += (dword & 0x0000ff00) << (24-8-8);
+ }
+ }
+
+ /* New stuff */
+ val += ((1 << 21) >> 8); /* Add 2MB offset to avoid compat area */
+ if (val >= MCT_TRNG_KEEPOUT_START) {
+ while(val < MCT_TRNG_KEEPOUT_END)
+ val += (1 << (15-8)); /* add 32K */
+ }
+
+ /* Add a node seed */
+ val += (((1 * pDCTstat->Node_ID) << 20) >> 8); /* Add 1MB per node to avoid aliases */
+
+ /* HW remap disabled? */
+ if (!(pDCTstat->Status & (1 << SB_HWHole))) {
+ if (!(pDCTstat->Status & (1 << SB_SWNodeHole))) {
+ /* SW memhole disabled */
+ u32 lo, hi;
+ _RDMSR(TOP_MEM, &lo, &hi);
+ lo >>= 8;
+ if ((val >= lo) && (val < _4GB_RJ8)) {
+ val = 0;
+ *valid = 0;
+ goto exitGetAddr;
+ } else {
+ *valid = 1;
+ goto exitGetAddrWNoError;
+ }
+ } else {
+ *valid = 1;
+ goto exitGetAddrWNoError;
+ }
+ } else {
+ *valid = 1;
+ goto exitGetAddrWNoError;
+ }
+
+exitGetAddrWNoError:
+
+ /* Skip if Address is in UMA region */
+ dword = pMCTstat->Sub4GCacheTop;
+ dword >>= 8;
+ if (dword != 0) {
+ if ((val >= dword) && (val < _4GB_RJ8)) {
+ val = 0;
+ *valid = 0;
+ } else {
+ *valid = 1;
+ }
+ }
+ print_debug_dqs("mct_GetMCTSysAddr_D: receiver ", receiver, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: Channel ", Channel, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: base_addr ", val, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: valid ", *valid, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: status ", pDCTstat->Status, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: HoleBase ", pDCTstat->DCTHoleBase, 2);
+ print_debug_dqs("mct_GetMCTSysAddr_D: Cachetop ", pMCTstat->Sub4GCacheTop, 2);
+
+exitGetAddr:
+ return val;
+}
+
+static void mct_Write1LTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 TestAddr, u8 pattern)
+{
+
+ u8 *buf;
+
+ /* Issue the stream of writes. When F2x11C[MctWrLimit] is reached
+ * (or when F2x11C[FlushWr] is set again), all the writes are written
+ * to DRAM.
+ */
+
+ SetUpperFSbase(TestAddr);
+
+ if (pattern)
+ buf = (u8 *)pDCTstat->PtrPatternBufB;
+ else
+ buf = (u8 *)pDCTstat->PtrPatternBufA;
+
+ WriteLNTestPattern(TestAddr << 8, buf, 1);
+}
+
+void mct_Read1LTestPattern_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 addr)
+{
+ u32 value;
+
+ /* BIOS issues the remaining (Ntrain - 2) reads after checking that
+ * F2x11C[PrefDramTrainMode] is cleared. These reads must be to
+ * consecutive cache lines (i.e., 64 bytes apart) and must not cross
+ * a naturally aligned 4KB boundary. These reads hit the prefetches and
+ * read the data from the prefetch buffer.
+ */
+
+ /* get data from DIMM */
+ SetUpperFSbase(addr);
+
+ /* 1st move causes read fill (to exclusive or shared)*/
+ value = read32_fs(addr<<8);
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
new file mode 100644
index 0000000000..288093c73b
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctecc_d.c
@@ -0,0 +1,268 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+#include "mct_d.h"
+
+static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA);
+static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat);
+
+/* Initialize ECC modes of Integrated Dram+Memory Controllers of a network of
+ * Hammer processors. Use Dram background scrubber to fast initialize ECC bits
+ * of all dram.
+ *
+ * Notes:
+ *
+ * Order that items are set:
+ * 1. eccen bit in NB
+ * 2. Scrub Base
+ * 3. Temp Node Base
+ * 4. Temp Node Limit
+ * 5. Redir bit in NB
+ * 6. Scrub CTL
+ *
+ * Conditions for setting background scrubber.
+ * 1. node is present
+ * 2. node has dram functioning (WE=RE=1)
+ * 3. all eccdimms (or bit 17 of offset 90,fn 2)
+ * 4. no chip-select gap exists
+ *
+ * The dram background scrubber is used under very controlled circumstances to
+ * initialize all the ECC bits on the DIMMs of the entire dram address map
+ * (including hidden or lost dram and dram above 4GB). We will turn the scrub
+ * rate up to maximum, which should clear 4GB of dram in about 2.7 seconds.
+ * We will activate the scrubbers of all nodes with ecc dram and let them run in
+ * parallel, thereby reducing even further the time required to condition dram.
+ * Finally, we will go through each node and either disable background scrubber,
+ * or set the scrub rate to the user setup specified rate.
+ *
+ * To allow the NB to scrub, we need to wait a time period long enough to
+ * guarantee that the NB scrubs the entire dram on its node. Do do this, we
+ * simply sample the scrub ADDR once, for an initial value, then we sample and poll until the polled value of scrub ADDR
+ * has wrapped around at least once: Scrub ADDRi+1 < Scrub ADDRi. Since we let all
+ * Nodes run in parallel, we need to gaurantee that all nodes have wrapped. To do
+ * this efficiently, we need only to sample one of the nodes, the node with the
+ * largest ammount of dram populated is the one which will take the longest amount
+ * of time (the scrub rate is set to max, the same rate, on all nodes). So,
+ * during setup of scrub Base, we determine how much memory and which node has
+ * the largest memory installed.
+ *
+ * Scrubbing should not ordinarily be enabled on a Node with a chip-select gap
+ * (aka SW memhole, cs hoisting, etc..).To init ECC memory on this node, the
+ * scrubber is used in two steps. First, the Dram Limit for the node is adjusted
+ * down to the bottom of the gap, and that ECC dram is initialized. Second, the
+ * orignal Limit is restored, the Scrub base is set to 4GB, and scrubber is
+ * allowed to run until the Scrub Addr wraps around to zero.
+ */
+u8 ECCInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u8 AllECC;
+ u16 OB_NBECC;
+ u32 curBase;
+ u16 OB_ECCRedir;
+ u32 LDramECC;
+ u32 OF_ScrubCTL;
+ u16 OB_ChipKill;
+ u8 MemClrECC;
+
+ u32 dev;
+ u32 reg;
+ u32 val;
+ u16 nvbits;
+
+ mctHookBeforeECC();
+
+ /* 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_ECCRedir = mctGet_NVbits(NV_ECCRedir); /* ECC Redirection */
+
+ OB_ChipKill = mctGet_NVbits(NV_ChipKill); /* ECC Chip-kill mode */
+
+ 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;
+
+ nvbits = mctGet_NVbits(NV_L2BKScrub);
+ OF_ScrubCTL |= (u32) nvbits << 8;
+
+ nvbits = mctGet_NVbits(NV_DramBKScrub);
+ OF_ScrubCTL |= nvbits;
+
+ AllECC = 1;
+ MemClrECC = 0;
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ LDramECC = 0;
+ if (NodePresent_D(Node)) { /*If Node is present */
+ dev = pDCTstat->dev_map;
+ reg = 0x40+(Node << 3); /* Dram Base Node 0 + index */
+ val = Get_NB32(dev, reg);
+
+ /* WE/RE is checked */
+ if((val & 3)==3) { /* Node has dram populated */
+ /* Negate 'all nodes/dimms ECC' flag if non ecc
+ memory populated */
+ if( pDCTstat->Status & (1<<SB_ECCDIMMs)) {
+ LDramECC = isDramECCEn_D(pDCTstat);
+ if(pDCTstat->ErrCode != SC_RunningOK) {
+ pDCTstat->Status &= ~(1 << SB_ECCDIMMs);
+ if (OB_NBECC) {
+ pDCTstat->ErrStatus |= (1 << SB_DramECCDis);
+ }
+ AllECC = 0;
+ LDramECC =0;
+ }
+ } else {
+ AllECC = 0;
+ }
+ if(LDramECC) { /* if ECC is enabled on this dram */
+ if (OB_NBECC) {
+ mct_EnableDatIntlv_D(pMCTstat, pDCTstat);
+ dev = pDCTstat->dev_nbmisc;
+ reg =0x44; /* MCA NB Configuration */
+ val = Get_NB32(dev, reg);
+ val |= 1 << 22; /* EccEn */
+ Set_NB32(dev, reg, val);
+ DCTMemClr_Init_D(pMCTstat, pDCTstat);
+ MemClrECC = 1;
+ }
+ } /* this node has ECC enabled dram */
+ } else {
+ LDramECC = 0;
+ } /* Node has Dram */
+
+ if (MemClrECC) {
+ MCTMemClrSync_D(pMCTstat, pDCTstatA);
+ }
+ } /* if Node present */
+ }
+
+ if(AllECC)
+ pMCTstat->GStatus |= 1<<GSB_ECCDIMMs;
+ else
+ pMCTstat->GStatus &= ~(1<<GSB_ECCDIMMs);
+
+ /* Program the Dram BKScrub CTL to the proper (user selected) value.*/
+ /* Reset MC4_STS. */
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ LDramECC = 0;
+ if (NodePresent_D(Node)) { /* If Node is present */
+ reg = 0x40+(Node<<3); /* Dram Base Node 0 + index */
+ val = Get_NB32(pDCTstat->dev_map, reg);
+ curBase = val & 0xffff0000;
+ /*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 */
+ dev = pDCTstat->dev_nbmisc;
+ val = curBase << 8;
+ if(OB_ECCRedir) {
+ val |= (1<<0); /* enable redirection */
+ }
+ Set_NB32(dev, 0x5C, val); /* Dram Scrub Addr Low */
+ val = curBase>>24;
+ 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);
+ }
+ }
+ } /* this node has ECC enabled dram */
+ } /*Node has Dram */
+ } /*if Node present */
+ }
+
+ if(mctGet_NVbits(NV_SyncOnUnEccEn))
+ setSyncOnUnEccEn_D(pMCTstat, pDCTstatA);
+
+ mctHookAfterECC();
+ return MemClrECC;
+}
+
+static void setSyncOnUnEccEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u32 Node;
+ u32 reg;
+ u32 dev;
+ u32 val;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ if (NodePresent_D(Node)) { /* If Node is present*/
+ reg = 0x40+(Node<<3); /* Dram Base Node 0 + index*/
+ val = Get_NB32(pDCTstat->dev_map, reg);
+ /*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*/
+ dev = pDCTstat->dev_nbmisc;
+ reg = 0x44; /* MCA NB Configuration*/
+ val = Get_NB32(dev, reg);
+ val |= (1<<SyncOnUcEccEn);
+ Set_NB32(dev, reg, val);
+ }
+ } /* Node has Dram*/
+ } /* if Node present*/
+ }
+}
+
+static u8 isDramECCEn_D(struct DCTStatStruc *pDCTstat)
+{
+ u32 reg;
+ u32 val;
+ u8 i;
+ u32 dev = pDCTstat->dev_dct;
+ u8 ch_end;
+ u8 isDimmECCEn = 0;
+
+ if(pDCTstat->GangedMode) {
+ ch_end = 1;
+ } else {
+ ch_end = 2;
+ }
+ for(i=0; i<ch_end; i++) {
+ if(pDCTstat->DIMMValidDCT[i] > 0){
+ reg = 0x90 + i * 0x100; /* Dram Config Low */
+ val = Get_NB32(dev, reg);
+ if(val & (1<<DimmEcEn)) {
+ /* set local flag 'dram ecc capable' */
+ isDimmECCEn = 1;
+ break;
+ }
+ }
+ }
+ return isDimmECCEn;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
new file mode 100644
index 0000000000..9b03b75420
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthdi.c
@@ -0,0 +1,32 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+void mct_DramInit_Hw_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u32 val;
+ u32 reg;
+ 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);
+ val |= (1<<InitDram);
+ Set_NB32(dev, reg, val);
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
new file mode 100644
index 0000000000..bf9222339d
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcthwl.c
@@ -0,0 +1,217 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void AgesaHwWlPhase1(sMCTStruct *pMCTData,
+ sDCTStruct *pDCTData, 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);
+static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+static void MultiplyDelay(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
+static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
+
+static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u8 ByteLane, DimmNum, OddByte, Addl_Index, Channel;
+ u8 EccRef1, EccRef2, EccDQSScale;
+ u32 val;
+ u16 word;
+
+ for (Channel = 0; Channel < 2; Channel ++) {
+ for (DimmNum = 0; DimmNum < C_MAX_DIMMS; DimmNum ++) { /* we use DimmNum instead of DimmNumx3 */
+ for (ByteLane = 0; ByteLane < 9; ByteLane ++) {
+ /* Get RxEn initial value from WrDqs */
+ if (ByteLane & 1)
+ OddByte = 1;
+ else
+ OddByte = 0;
+ if (ByteLane < 2)
+ Addl_Index = 0x30;
+ else if (ByteLane < 4)
+ Addl_Index = 0x31;
+ else if (ByteLane < 6)
+ Addl_Index = 0x40;
+ else if (ByteLane < 8)
+ Addl_Index = 0x41;
+ else
+ Addl_Index = 0x32;
+ Addl_Index += DimmNum * 3;
+
+ val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index);
+ if (OddByte)
+ val >>= 16;
+ /* Save WrDqs to stack for later usage */
+ pDCTstat->CH_D_B_TxDqs[Channel][DimmNum][ByteLane] = val & 0xFF;
+ EccDQSScale = pDCTstat->CH_EccDQSScale[Channel];
+ word = pDCTstat->CH_EccDQSLike[Channel];
+ if ((word & 0xFF) == ByteLane) EccRef1 = val & 0xFF;
+ if (((word >> 8) & 0xFF) == ByteLane) EccRef2 = val & 0xFF;
+ }
+ }
+ }
+}
+
+static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+ val &= ~(1 << DisAutoRefresh);
+ Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+ val &= ~(1 << DisAutoRefresh);
+ Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+}
+
+static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C);
+ val |= 1 << DisAutoRefresh;
+ Set_NB32(pDCTstat->dev_dct, 0x8C, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
+ val |= 1 << DisAutoRefresh;
+ Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
+}
+
+
+static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DIMMValid;
+ void *DCTPtr;
+
+ dct &= 1;
+
+ DCTPtr = (void *)(pDCTstat->C_DCTPtr[dct]);
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
+
+ if (pDCTstat->GangedMode & 1)
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->DIMMValid) {
+ 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);
+ }
+ }
+}
+
+static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DIMMValid;
+ void *DCTPtr;
+
+ dct &= 1;
+
+ DCTPtr = (void *)&(pDCTstat->C_DCTPtr[dct]); /* todo: */
+ pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
+
+ if (pDCTstat->GangedMode & 1)
+ pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->DIMMValid) {
+ DIMMValid = pDCTstat->DIMMValid;
+ PrepareC_DCT(pMCTstat, pDCTstat, dct);
+ 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);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ StartupDCT_D(pMCTstat, pDCTstat, dct);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+ MultiplyDelay(pMCTstat, pDCTstat, dct);
+ for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
+ if (DIMMValid & (1 << (dimm << 1)))
+ AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
+ }
+ }
+}
+
+static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ 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]);
+
+ /* Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1 */
+ DisableAutoRefresh_D(pMCTstat, pDCTstat);
+
+ /* Disable ZQ calibration short command by F2x[1,0]94[ZqcsInterval]=00b */
+ DisableZQcalibration(pMCTstat, pDCTstat);
+ PrepareC_MCT(pMCTstat, pDCTstat);
+
+ if (pDCTstat->GangedMode & (1 << 0)) {
+ pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0];
+ }
+
+ PhyWLPass1(pMCTstat, pDCTstat, 0);
+ PhyWLPass1(pMCTstat, pDCTstat, 1);
+
+ 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);
+
+ }
+
+ SetEccWrDQS_D(pMCTstat, pDCTstat);
+ EnableAutoRefresh_D(pMCTstat, pDCTstat);
+ EnableZQcalibration(pMCTstat, pDCTstat);
+}
+
+void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
+ if (pDCTstat->NodePresent) {
+ mctSMBhub_Init(Node);
+ Clear_OnDimmMirror(pMCTstat, pDCTstat);
+ WriteLevelization_HW(pMCTstat, pDCTstat);
+ Restore_OnDimmMirror(pMCTstat, pDCTstat);
+ }
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
new file mode 100644
index 0000000000..947da43ef2
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctmtr_d.c
@@ -0,0 +1,252 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+#include "mct_d.h"
+
+static void SetMTRRrangeWB_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr);
+static void SetMTRRrange_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr, u16 MtrrType);
+
+void CPUMemTyping_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ /* BSP only. Set the fixed MTRRs for common legacy ranges.
+ * Set TOP_MEM and TOM2.
+ * Set some variable MTRRs with WB Uncacheable type.
+ */
+
+ u32 Bottom32bIO, Bottom40bIO, Cache32bTOP;
+ u32 val;
+ u32 addr;
+ u32 lo, hi;
+
+ /* Set temporary top of memory from Node structure data.
+ * Adjust temp top of memory down to accomodate 32-bit IO space.
+ * Bottom40bIO=top of memory, right justified 8 bits
+ * (defines dram versus IO space type)
+ * Bottom32bIO=sub 4GB top of memory, right justified 8 bits
+ * (defines dram versus IO space type)
+ * Cache32bTOP=sub 4GB top of WB cacheable memory,
+ * right justified 8 bits
+ */
+
+ val = mctGet_NVbits(NV_BottomIO);
+ if(val == 0)
+ val++;
+
+ Bottom32bIO = val << (24-8);
+
+ val = pMCTstat->SysLimit + 1;
+ if(val <= _4GB_RJ8) {
+ Bottom40bIO = 0;
+ if(Bottom32bIO >= val)
+ Bottom32bIO = val;
+ } else {
+ Bottom40bIO = val;
+ }
+
+ Cache32bTOP = Bottom32bIO;
+
+ /*======================================================================
+ Set default values for CPU registers
+ ======================================================================*/
+
+ /* NOTE : For coreboot, we don't need to set mtrr enables here because
+ they are still enable from cache_as_ram.inc */
+
+ addr = 0x250;
+ lo = 0x1E1E1E1E;
+ hi = lo;
+ _WRMSR(addr, lo, hi); /* 0 - 512K = WB Mem */
+ addr = 0x258;
+ _WRMSR(addr, lo, hi); /* 512K - 640K = WB Mem */
+
+ /*======================================================================
+ Set variable MTRR values
+ ======================================================================*/
+ /* NOTE: for coreboot change from 0x200 to 0x204: coreboot is using
+ 0x200, 0x201 for [1M, CONFIG_TOP_MEM)
+ 0x202, 0x203 for ROM Caching
+ */
+ addr = 0x204; /* MTRR phys base 2*/
+ /* use TOP_MEM as limit*/
+ /* Limit=TOP_MEM|TOM2*/
+ /* Base=0*/
+ printk(BIOS_DEBUG, "\t CPUMemTyping: Cache32bTOP:%x\n", Cache32bTOP);
+ SetMTRRrangeWB_D(0, &Cache32bTOP, &addr);
+ /* Base */
+ /* Limit */
+ /* MtrrAddr */
+ if(addr == -1) /* ran out of MTRRs?*/
+ pMCTstat->GStatus |= 1<<GSB_MTRRshort;
+
+ pMCTstat->Sub4GCacheTop = Cache32bTOP<<8;
+
+ /*======================================================================
+ Set TOP_MEM and TOM2 CPU registers
+ ======================================================================*/
+ addr = TOP_MEM;
+ lo = Bottom32bIO<<8;
+ hi = Bottom32bIO>>24;
+ _WRMSR(addr, lo, hi);
+ printk(BIOS_DEBUG, "\t CPUMemTyping: Bottom32bIO:%x\n", Bottom32bIO);
+ printk(BIOS_DEBUG, "\t CPUMemTyping: Bottom40bIO:%x\n", Bottom40bIO);
+ if(Bottom40bIO) {
+ hi = Bottom40bIO >> 24;
+ lo = Bottom40bIO << 8;
+ addr += 3; /* TOM2 */
+ _WRMSR(addr, lo, hi);
+ }
+ addr = 0xC0010010; /* SYS_CFG */
+ _RDMSR(addr, &lo, &hi);
+ if(Bottom40bIO) {
+ lo |= (1<<21); /* MtrrTom2En=1 */
+ lo |= (1<<22); /* Tom2ForceMemTypeWB */
+ } else {
+ lo &= ~(1<<21); /* MtrrTom2En=0 */
+ lo &= ~(1<<22); /* Tom2ForceMemTypeWB */
+ }
+ _WRMSR(addr, lo, hi);
+}
+
+static void SetMTRRrangeWB_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr)
+{
+ /*set WB type*/
+ SetMTRRrange_D(Base, pLimit, pMtrrAddr, 6);
+}
+
+static void SetMTRRrange_D(u32 Base, u32 *pLimit, u32 *pMtrrAddr, u16 MtrrType)
+{
+ /* Program MTRRs to describe given range as given cache type.
+ * Use MTRR pairs starting with the given MTRRphys Base address,
+ * and use as many as is required up to (excluding) MSR 020C, which
+ * is reserved for OS.
+ *
+ * "Limit" in the context of this procedure is not the numerically
+ * correct limit, but rather the Last address+1, for purposes of coding
+ * efficiency and readability. Size of a region is then Limit-Base.
+ *
+ * 1. Size of each range must be a power of two
+ * 2. Each range must be naturally aligned (Base is same as size)
+ *
+ * There are two code paths: the ascending path and descending path
+ * (analogous to bsf and bsr), where the next limit is a funtion of the
+ * next set bit in a forward or backward sequence of bits (as a function
+ * of the Limit). We start with the ascending path, to ensure that
+ * regions are naturally aligned, then we switch to the descending path
+ * to maximize MTRR usage efficiency. Base=0 is a special case where we
+ * start with the descending path. Correct Mask for region is
+ * 2comp(Size-1)-1, which is 2comp(Limit-Base-1)-1
+ */
+
+ u32 curBase, curLimit, curSize;
+ u32 val, valx;
+ u32 addr;
+
+ val = curBase = Base;
+ curLimit = *pLimit;
+ addr = *pMtrrAddr;
+ while((addr >= 0x200) && (addr < 0x20C) && (val < *pLimit)) {
+ /* start with "ascending" code path */
+ /* alignment (largest block size)*/
+ valx = 1 << bsf(curBase);
+ curSize = valx;
+
+ /* largest legal limit, given current non-zero range Base*/
+ valx += curBase;
+ if((curBase == 0) || (*pLimit < valx)) {
+ /* flop direction to "descending" code path*/
+ valx = 1<<bsr(*pLimit - curBase);
+ curSize = valx;
+ valx += curBase;
+ }
+ curLimit = valx; /*eax=curBase, edx=curLimit*/
+ valx = val>>24;
+ val <<= 8;
+
+ /* now program the MTRR */
+ val |= MtrrType; /* set cache type (UC or WB)*/
+ _WRMSR(addr, val, valx); /* prog. MTRR with current region Base*/
+ val = ((~(curSize - 1))+1) - 1; /* Size-1*/ /*Mask=2comp(Size-1)-1*/
+ valx = (val >> 24) | (0xff00); /* GH have 48 bits addr */
+ val <<= 8;
+ val |= ( 1 << 11); /* set MTRR valid*/
+ addr++;
+ _WRMSR(addr, val, valx); /* prog. MTRR with current region Mask*/
+ val = curLimit;
+ curBase = val; /* next Base = current Limit (loop exit)*/
+ addr++; /* next MTRR pair addr */
+ }
+ if(val < *pLimit) {
+ *pLimit = val;
+ addr = -1;
+ }
+ *pMtrrAddr = addr;
+}
+
+void UMAMemTyping_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
+{
+/* UMA memory size may need splitting the MTRR configuration into two
+ Before training use NB_BottomIO or the physical memory size to set the MTRRs.
+ After training, add UMAMemTyping function to reconfigure the MTRRs based on
+ NV_BottomUMA (for UMA systems only).
+ This two-step process allows all memory to be cached for training
+*/
+ u32 Bottom32bIO, Cache32bTOP;
+ u32 val;
+ u32 addr;
+ u32 lo, hi;
+
+ /*======================================================================
+ * Adjust temp top of memory down to accomodate UMA memory start
+ *======================================================================*/
+ /* Bottom32bIO=sub 4GB top of memory, right justified 8 bits
+ * (defines dram versus IO space type)
+ * Cache32bTOP=sub 4GB top of WB cacheable memory, right justified 8 bits */
+
+ Bottom32bIO = pMCTstat->Sub4GCacheTop >> 8;
+
+ val = mctGet_NVbits(NV_BottomUMA);
+ if (val == 0)
+ val++;
+
+ val <<= (24-8);
+ if (val < Bottom32bIO) {
+ Cache32bTOP = val;
+ pMCTstat->Sub4GCacheTop = val;
+
+ /*======================================================================
+ * Clear variable MTRR values
+ *======================================================================*/
+ addr = 0x200;
+ lo = 0;
+ hi = lo;
+ while( addr < 0x20C) {
+ _WRMSR(addr, lo, hi); /* prog. MTRR with current region Mask */
+ addr++; /* next MTRR pair addr */
+ }
+
+ /*======================================================================
+ * Set variable MTRR values
+ *======================================================================*/
+ printk(BIOS_DEBUG, "\t UMAMemTyping_D: Cache32bTOP:%x\n", Cache32bTOP);
+ SetMTRRrangeWB_D(0, &Cache32bTOP, &addr);
+ if(addr == -1) /* ran out of MTRRs?*/
+ pMCTstat->GStatus |= 1<<GSB_MTRRshort;
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c
new file mode 100644
index 0000000000..cc59561976
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctndi_d.c
@@ -0,0 +1,230 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+void InterleaveNodes_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ /* Applies Node memory interleaving if enabled and if all criteria are met. */
+ u8 Node;
+ u32 Base;
+ u32 MemSize, MemSize0 = 0;
+ u32 Dct0MemSize = 0, DctSelBase, DctSelBaseOffset;
+ u8 Nodes;
+ u8 NodesWmem;
+ u8 DoIntlv;
+ u8 _NdIntCap;
+ u8 _SWHole;
+ u32 HWHoleSz;
+ u32 DramHoleAddrReg;
+ u32 HoleBase;
+ u32 dev0;
+ u32 reg0;
+ u32 val;
+ u8 i;
+ struct DCTStatStruc *pDCTstat;
+
+ DoIntlv = mctGet_NVbits(NV_NodeIntlv);
+
+ _NdIntCap = 0;
+ HWHoleSz = 0; /*For HW remapping, NOT Node hoisting. */
+
+ pDCTstat = pDCTstatA + 0;
+ dev0 = pDCTstat->dev_host;
+ Nodes = ((Get_NB32(dev0, 0x60) >> 4) & 0x7) + 1;
+
+ dev0 = pDCTstat->dev_map;
+ reg0 = 0x40;
+
+ NodesWmem = 0;
+ Node = 0;
+
+ while (DoIntlv && (Node < Nodes)) {
+ pDCTstat = pDCTstatA + Node;
+ if (pMCTstat->GStatus & (1 << GSB_SpIntRemapHole)) {
+ pMCTstat->GStatus |= 1 << GSB_HWHole;
+ _SWHole = 0;
+ } else if (pDCTstat->Status & (1 << SB_SWNodeHole)) {
+ _SWHole = 1;
+ } else {
+ _SWHole = 0;
+ }
+
+ if(!_SWHole) {
+ Base = Get_NB32(dev0, reg0);
+ if (Base & 1) {
+ NodesWmem++;
+ Base &= 0xFFFF0000; /* Base[39:8] */
+
+ if (pDCTstat->Status & (1 << SB_HWHole )) {
+
+ /* to get true amount of dram,
+ * subtract out memory hole if HW dram remapping */
+ DramHoleAddrReg = Get_NB32(pDCTstat->dev_map, 0xF0);
+ HWHoleSz = DramHoleAddrReg >> 16;
+ HWHoleSz = (((~HWHoleSz) + 1) & 0xFF);
+ HWHoleSz <<= 24-8;
+ }
+ /* check to see if the amount of memory on each channel
+ * are the same on all nodes */
+
+ DctSelBase = Get_NB32(pDCTstat->dev_dct, 0x114);
+ if(DctSelBase) {
+ DctSelBase <<= 8;
+ if ( pDCTstat->Status & (1 << SB_HWHole)) {
+ if (DctSelBase >= 0x1000000) {
+ DctSelBase -= HWHoleSz;
+ }
+ }
+ DctSelBaseOffset -= Base;
+ if (Node == 0) {
+ Dct0MemSize = DctSelBase;
+ } else if (DctSelBase != Dct0MemSize) {
+ break;
+ }
+ }
+
+ MemSize = Get_NB32(dev0, reg0 + 4);
+ MemSize &= 0xFFFF0000;
+ MemSize += 0x00010000;
+ MemSize -= Base;
+ if ( pDCTstat->Status & (1 << SB_HWHole)) {
+ MemSize -= HWHoleSz;
+ }
+ if (Node == 0) {
+ MemSize0 = MemSize;
+ } else if (MemSize0 != MemSize) {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ Node++;
+ reg0 += 8;
+ }
+
+ if (Node == Nodes) {
+ /* if all nodes have memory and no Node had SW memhole */
+ if (Nodes == 2 || Nodes == 4 || Nodes == 8)
+ _NdIntCap = 1;
+ }
+
+ if (!_NdIntCap)
+ DoIntlv = 0;
+
+ if (pMCTstat->GStatus & 1 << (GSB_SpIntRemapHole)) {
+ HWHoleSz = pMCTstat->HoleBase;
+ if (HWHoleSz == 0) {
+ HWHoleSz = mctGet_NVbits(NV_BottomIO) & 0xFF;
+ HWHoleSz <<= 24-8;
+ }
+ HWHoleSz = ((~HWHoleSz) + 1) & 0x00FF0000;
+ }
+
+ if (DoIntlv) {
+ MCTMemClr_D(pMCTstat,pDCTstatA);
+ /* Program Interleaving enabled on Node 0 map only.*/
+ MemSize0 <<= bsf(Nodes); /* MemSize=MemSize*2 (or 4, or 8) */
+ Dct0MemSize <<= bsf(Nodes);
+ MemSize0 += HWHoleSz;
+ Base = ((Nodes - 1) << 8) | 3;
+ reg0 = 0x40;
+ Node = 0;
+ while(Node < Nodes) {
+ Set_NB32(dev0, reg0, Base);
+ MemSize = MemSize0;
+ MemSize--;
+ MemSize &= 0xFFFF0000;
+ MemSize |= Node << 8; /* set IntlvSel[2:0] field */
+ MemSize |= Node; /* set DstNode[2:0] field */
+ Set_NB32(dev0, reg0 + 4, MemSize0);
+ reg0 += 8;
+ Node++;
+ }
+
+ /* set base/limit to F1x120/124 per Node */
+ Node = 0;
+ while(Node < Nodes) {
+ pDCTstat = pDCTstatA + Node;
+ pDCTstat->NodeSysBase = 0;
+ MemSize = MemSize0;
+ MemSize -= HWHoleSz;
+ MemSize--;
+ pDCTstat->NodeSysLimit = MemSize;
+ Set_NB32(pDCTstat->dev_map, 0x120, Node << 21);
+ MemSize = MemSize0;
+ MemSize--;
+ MemSize >>= 19;
+ val = Base;
+ val &= 0x700;
+ val <<= 13;
+ val |= MemSize;
+ Set_NB32(pDCTstat->dev_map, 0x124, val);
+
+ if (pMCTstat->GStatus & (1 << GSB_HWHole)) {
+ HoleBase = pMCTstat->HoleBase;
+ if (Dct0MemSize >= HoleBase) {
+ val = HWHoleSz;
+ if( Node == 0) {
+ val += Dct0MemSize;
+ }
+ } else {
+ val = HWHoleSz + Dct0MemSize;
+ }
+
+ val >>= 8; /* DramHoleOffset */
+ HoleBase <<= 8; /* DramHoleBase */
+ val |= HoleBase;
+ val |= 1 << DramMemHoistValid;
+ val |= 1 << DramHoleValid;
+ Set_NB32(pDCTstat->dev_map, 0xF0, val);
+ }
+
+ Set_NB32(pDCTstat->dev_dct, 0x114, Dct0MemSize >> 8); /* DctSelBaseOffset */
+ val = Get_NB32(pDCTstat->dev_dct, 0x110);
+ val &= 0x7FF;
+ val |= Dct0MemSize >> 8;
+ Set_NB32(pDCTstat->dev_dct, 0x110, val); /* DctSelBaseAddr */
+ printk(BIOS_DEBUG, "InterleaveNodes: DRAM Controller Select Low Register = %x\n", val);
+ Node++;
+ }
+
+ /* Copy Node 0 into other Nodes' CSRs */
+ Node = 1;
+ while (Node < Nodes) {
+ pDCTstat = pDCTstatA + Node;
+
+ for (i = 0x40; i <= 0x80; i++) {
+ val = Get_NB32(dev0, i);
+ Set_NB32(pDCTstat->dev_map, i, val);
+ }
+
+ val = Get_NB32(dev0, 0xF0);
+ Set_NB32(pDCTstat->dev_map, 0xF0, val);
+ Node++;
+ }
+ pMCTstat->GStatus = (1 << GSB_NodeIntlv);
+ }
+ printk(BIOS_DEBUG, "InterleaveNodes_D: Status %x\n", pDCTstat->Status);
+ printk(BIOS_DEBUG, "InterleaveNodes_D: ErrStatus %x\n", pDCTstat->ErrStatus);
+ printk(BIOS_DEBUG, "InterleaveNodes_D: ErrCode %x\n", pDCTstat->ErrCode);
+ printk(BIOS_DEBUG, "InterleaveNodes_D: Done\n\n");
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctprob.c b/src/northbridge/amd/amdmct/mct_ddr3/mctprob.c
new file mode 100644
index 0000000000..cfd4adfa6f
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctprob.c
@@ -0,0 +1,37 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ if (pDCTstat->LogicalCPUID & AMD_DR_Bx) {
+ Set_NB32(pDCTstat->dev_dct, 0x98, 0x0D004007);
+ val = Get_NB32(pDCTstat->dev_dct, 0x9C);
+ val |= 0x3FF;
+ Set_NB32(pDCTstat->dev_dct, 0x9C, val);
+ Set_NB32(pDCTstat->dev_dct, 0x98, 0x4D0F4F07);
+
+ Set_NB32(pDCTstat->dev_dct, 0x198, 0x0D004007);
+ val = Get_NB32(pDCTstat->dev_dct, 0x19C);
+ val |= 0x3FF;
+ Set_NB32(pDCTstat->dev_dct, 0x19C, val);
+ Set_NB32(pDCTstat->dev_dct, 0x198, 0x4D0F4F07);
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
new file mode 100644
index 0000000000..a40dda42c7
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctproc.c
@@ -0,0 +1,45 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2)
+{
+ u32 val;
+
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Dx | AMD_DR_Cx)) {
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ misc2 |= 1 << SubMemclkRegDly;
+ if (mctGet_NVbits(NV_MAX_DIMMS) == 8)
+ misc2 |= 1 << Ddr3FourSocketCh;
+ else
+ misc2 &= ~(1 << Ddr3FourSocketCh);
+ }
+
+ if (pDCTstat->LogicalCPUID & AMD_DR_Cx)
+ misc2 |= 1 << OdtSwizzle;
+ val = Get_NB32(pDCTstat->dev_dct, dct * 0x100 + 0x78);
+
+ val &= 7;
+ val = ((~val) & 0xFF) + 1;
+ val += 6;
+ val &= 0xFF;
+ misc2 &= 0xFFF8FFFF;
+ misc2 |= val << 16; /* DataTxFifoWrDly */
+ }
+ return misc2;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
new file mode 100644
index 0000000000..647e63719c
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctrci.c
@@ -0,0 +1,190 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static u32 mct_ControlRC(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 MrsChipSel, u32 CtrlWordNum)
+{
+ u8 Dimms, DimmNum, MaxDimm, Speed;
+ u32 val;
+
+ DimmNum = MrsChipSel >> 20;
+
+ /* assume dct=0; */
+ /* if (dct == 1) */
+ /* DimmNum ++; */
+ /* cl +=8; */
+
+ MaxDimm = mctGet_NVbits(NV_MAX_DIMMS);
+ Speed = pDCTstat->DIMMAutoSpeed;
+ /* if (dct == 0) */
+ Dimms = pDCTstat->MAdimms[0];
+
+ val = 0;
+ if (CtrlWordNum == 0)
+ val |= 1 << 1;
+ else if (CtrlWordNum == 1) {
+ if (!((pDCTstat->DimmDRPresent | pDCTstat->DimmQRPresent) & (1 << DimmNum)))
+ val |= 0xC; /* if single rank, set DBA1 and DBA0 */
+ }
+ else if (CtrlWordNum == 2) {
+ if (MaxDimm == 4) {
+ if (Speed == 4) {
+ if (((pDCTstat->DimmQRPresent & (1 << DimmNum)) && (Dimms == 1)) || Dimms == 2)
+ if (!(pDCTstat->MirrPresU_NumRegR & (1 << DimmNum)))
+ val |= 1 << 2;
+ } else {
+ if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum))
+ val |= 2;
+ }
+ } else {
+ if (Dimms > 1)
+ val |= 2;
+ }
+ } else if (CtrlWordNum == 3) {
+ val = pDCTstat->CtrlWrd3 >> (DimmNum << 2);
+ } else if (CtrlWordNum == 4) {
+ val = pDCTstat->CtrlWrd4 >> (DimmNum << 2);
+ } else if (CtrlWordNum == 5) {
+ val = pDCTstat->CtrlWrd5 >> (DimmNum << 2);
+ } else if (CtrlWordNum == 8) {
+ if (MaxDimm == 4)
+ if (Speed == 4)
+ if (pDCTstat->MirrPresU_NumRegR & (1 << DimmNum))
+ val |= 1 << 2;
+ } else if (CtrlWordNum == 9) {
+ val |= 0xD; /* DBA1, DBA0, DA3 = 0 */
+ }
+ val &= 0xf;
+
+ val = MrsChipSel | ((val >> 2) & 3) << 16 | MrsChipSel | ((val >> 2) & 3);
+
+ /* transfer Control word number to address [BA2,A2,A1,A0] */
+ if (CtrlWordNum > 7) {
+ val |= 1 << 18;
+ CtrlWordNum &= 7;
+ }
+ val |= CtrlWordNum;
+
+ return val;
+}
+
+static void mct_SendCtrlWrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 val)
+{
+ u32 dev = pDCTstat->dev_dct;
+
+ val |= Get_NB32(dev, 0x7C) & ~0xFFFFFF;
+ val |= 1 << SendControlWord;
+ Set_NB32(dev, 0x7C, val);
+
+ do {
+ val = Get_NB32(dev, 0x7C);
+ } while (val & (1 << SendControlWord));
+}
+
+void mct_DramControlReg_Init_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 MrsChipSel;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val, cw;
+
+ mct_Wait(1600);
+
+ mct_Wait(1200);
+
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel ++, MrsChipSel ++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ val = Get_NB32(dev, 0xA8);
+ val &= ~(0xF << 8);
+
+ switch (MrsChipSel) {
+ case 0:
+ case 1:
+ val |= 3 << 8;
+ case 2:
+ case 3:
+ val |= (3 << 2) << 8;
+ case 4:
+ case 5:
+ val |= (3 << 4) << 8;
+ case 6:
+ case 7:
+ val |= (3 << 6) << 8;
+ }
+ Set_NB32(dev, 0xA8, val);
+
+ for (cw=0; cw <=15; cw ++) {
+ mct_Wait(1600);
+ if (!(cw==6 || cw==7)) {
+ val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, cw);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+ }
+ }
+ }
+ }
+
+ mct_Wait(1200);
+}
+
+void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 SaveSpeed = pDCTstat->DIMMAutoSpeed;
+ u32 MrsChipSel;
+ u32 dev = pDCTstat->dev_dct;
+ u32 val;
+
+ pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
+ for (MrsChipSel=0; MrsChipSel < 8; MrsChipSel++, MrsChipSel++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ val = Get_NB32(dev, 0xA8);
+ val &= ~(0xFF << 8);
+ val |= (0x3 << MrsChipSel) << 8;
+ Set_NB32(dev, 0xA8, val);
+
+ mct_Wait(1600);
+ switch (pDCTstat->TargetFreq) {
+ case 6:
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4000A);
+ break;
+ case 5:
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x40012);
+ break;
+ case 7:
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, MrsChipSel << 20 | 0x4001A);
+ break;
+ }
+
+ mct_Wait(1600);
+
+ val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 2);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+
+ mct_Wait(1600);
+
+ /* Resend control word 8 */
+ val = mct_ControlRC(pMCTstat, pDCTstat, MrsChipSel << 20, 8);
+ mct_SendCtrlWrd(pMCTstat, pDCTstat, val);
+
+ mct_Wait(1600);
+ }
+ }
+ pDCTstat->DIMMAutoSpeed = SaveSpeed;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
new file mode 100644
index 0000000000..18ef4770e2
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsdi.c
@@ -0,0 +1,326 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+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);
+ } while (!(val & (1 << DctAccessDone)));
+}
+
+static u32 swapAddrBits(struct DCTStatStruc *pDCTstat, u32 MR_register_setting, u8 MrsChipSel, u8 dct)
+{
+ u16 word;
+ u32 ret;
+
+ if (!(pDCTstat->Status & (1 << SB_Registered))) {
+ word = pDCTstat->MirrPresU_NumRegR;
+ if (dct == 0) {
+ word &= 0x55;
+ word <<= 1;
+ } else
+ word &= 0xAA;
+
+ if (word & (1 << MrsChipSel)) {
+ /* A3<->A4,A5<->A6,A7<->A8,BA0<->BA1 */
+ ret = 0;
+ if (MR_register_setting & (1 << 3)) ret |= 1 << 4;
+ if (MR_register_setting & (1 << 4)) ret |= 1 << 3;
+ if (MR_register_setting & (1 << 5)) ret |= 1 << 6;
+ 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;
+ MR_register_setting |= ret;
+ }
+ }
+ return 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 |= EMRS;
+ val |= 1 << SendMrsCmd;
+ Set_NB32(dev, reg_off + 0x7C, val);
+
+ do {
+ val = Get_NB32(dev, reg_off + 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;
+
+ /* 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);
+
+ 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;
+}
+
+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;
+
+ /* 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;
+
+ return ret;
+}
+
+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;
+ } else {
+ /* TODO: mct_MR1Odt_RDimm */
+ }
+
+ /* 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;
+ }
+
+ if (dword & (1 << 13))
+ ret |= 1 << 12;
+
+ return ret;
+}
+
+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 & 0xF) << 4; /* F2x88[3:0] to MrsAddress[6:4,2]=xxx0b */
+
+ /* 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 &= ~0xFFFFFF;
+ dword |= 1 << 10;
+ dword |= 1 << SendZQCmd;
+ Set_NB32(dev, reg_off + 0x7C, dword);
+
+ /* Wait for SendZQCmd=0 */
+ do {
+ dword = Get_NB32(dev, reg_off + 0x7C);
+ } while (dword & (1 << SendZQCmd));
+
+ /* 4.Wait 512 MEMCLKs */
+ mct_Wait(300);
+}
+
+void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 MrsChipSel;
+ u32 dword;
+ u32 reg_off = 0x100 * dct;
+ u32 dev = pDCTstat->dev_dct;
+
+ if (pDCTstat->DIMMAutoSpeed == 4) {
+ /* 3.Program F2x[1,0]7C[EnDramInit]=1 */
+ dword = Get_NB32(dev, reg_off + 0x7C);
+ dword |= 1 << EnDramInit;
+ Set_NB32(dev, reg_off + 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);
+ dword |= 1 << DeassertMemRstX;
+ Set_NB32(dev, reg_off + 0x7C, dword);
+
+ /* 6.wait 500us */
+ mct_Wait(200000);
+
+ /* 7.Program F2x[1,0]7C[AssertCke]=1 */
+ dword = Get_NB32(dev, reg_off + 0x7C);
+ dword |= 1 << AssertCke;
+ Set_NB32(dev, reg_off + 0x7C, dword);
+
+ /* 8.wait 360ns */
+ mct_Wait(80);
+
+ /* The following steps are performed with registered DIMMs only and
+ * 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 once for unbuffered DIMMs and once for each
+ * chip select on registered DIMMs: */
+ for (MrsChipSel = 0; MrsChipSel < 8; MrsChipSel++) {
+ if (pDCTstat->CSPresent & (1 << MrsChipSel)) {
+ u32 EMRS;
+ /* 13.Send EMRS(2) */
+ EMRS = mct_MR2(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+ 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 = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+ /* 15.Send EMRS(1) */
+ EMRS= mct_MR1(pMCTstat, pDCTstat, dct, MrsChipSel << 20);
+ 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 = swapAddrBits(pDCTstat, EMRS, MrsChipSel, dct);
+ mct_SendMrsCmd(pDCTstat, dct, EMRS);
+
+ if (pDCTstat->DIMMAutoSpeed == 4)
+ if (!(pDCTstat->Status & (1 << SB_Registered)))
+ break; /* For UDIMM, only send MR commands once per channel */
+ }
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Cx/* | AMD_RB_C0 */)) /* We dont support RB_C0 now. need to be added and tested. */
+ if (!(pDCTstat->Status & (1 << SB_Registered)))
+ MrsChipSel ++;
+ }
+
+ mct_Wait(100000);
+
+ if (pDCTstat->DIMMAutoSpeed == 4) {
+ /* 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 &= ~(1 << EnDramInit);
+ Set_NB32(dev, reg_off + 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
new file mode 100644
index 0000000000..86d07423f3
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc.c
@@ -0,0 +1,1056 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ */
+
+/******************************************************************************
+ Description: Receiver En and DQS Timing Training feature for DDR 3 MCT
+******************************************************************************/
+
+static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass);
+static u8 mct_SavePassRcvEnDly_D(struct DCTStatStruc *pDCTstat,
+ u8 rcvrEnDly, u8 Channel,
+ u8 receiver, u8 Pass);
+static u8 mct_CompareTestPatternQW0_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 addr, u8 channel,
+ u8 pattern, u8 Pass);
+static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel);
+static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel);
+static void mct_SetFinalRcvrEnDly_D(struct DCTStatStruc *pDCTstat,
+ u8 RcvrEnDly, u8 where,
+ u8 Channel, u8 Receiver,
+ u32 dev, u32 index_reg,
+ u8 Addl_Index, u8 Pass);
+static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQSRcvEnDly);
+static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct);
+static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat);
+
+/* Warning: These must be located so they do not cross a logical 16-bit
+ segment boundary! */
+const static u32 TestPattern0_D[] = {
+ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+ 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa, 0xaaaaaaaa,
+};
+const static u32 TestPattern1_D[] = {
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+ 0x55555555, 0x55555555, 0x55555555, 0x55555555,
+};
+const static u32 TestPattern2_D[] = {
+ 0x12345678, 0x87654321, 0x23456789, 0x98765432,
+ 0x59385824, 0x30496724, 0x24490795, 0x99938733,
+ 0x40385642, 0x38465245, 0x29432163, 0x05067894,
+ 0x12349045, 0x98723467, 0x12387634, 0x34587623,
+};
+
+static void SetupRcvrPattern(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u32 *buffer, u8 pass)
+{
+ /*
+ * 1. Copy the alpha and Beta patterns from ROM to Cache,
+ * aligning on 16 byte boundary
+ * 2. Set the ptr to DCTStatstruc.PtrPatternBufA for Alpha
+ * 3. Set the ptr to DCTStatstruc.PtrPatternBufB for Beta
+ */
+ u32 *buf_a;
+ u32 *buf_b;
+ u32 *p_A;
+ u32 *p_B;
+ u8 i;
+
+ buf_a = (u32 *)(((u32)buffer + 0x10) & (0xfffffff0));
+ buf_b = buf_a + 32; /* ?? */
+ p_A = (u32 *)SetupDqsPattern_1PassB(pass);
+ p_B = (u32 *)SetupDqsPattern_1PassA(pass);
+
+ for(i=0;i<16;i++) {
+ buf_a[i] = p_A[i];
+ buf_b[i] = p_B[i];
+ }
+
+ pDCTstat->PtrPatternBufA = (u32)buf_a;
+ pDCTstat->PtrPatternBufB = (u32)buf_b;
+}
+
+void mct_TrainRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass)
+{
+ if(mct_checkNumberOfDqsRcvEn_1Pass(Pass))
+ dqsTrainRcvrEn_SW(pMCTstat, pDCTstat, Pass);
+}
+
+static void dqsTrainRcvrEn_SW(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Pass)
+{
+ u8 Channel, RcvrEnDly, RcvrEnDlyRmin;
+ u8 Test0, Test1, CurrTest, CurrTestSide0, CurrTestSide1;
+ u8 CTLRMaxDelay, _2Ranks, PatternA, PatternB;
+ u8 Addl_Index = 0;
+ u8 Receiver;
+ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
+ u8 RcvrEnDlyLimit, Final_Value, MaxDelay_CH[2];
+ u32 TestAddr0, TestAddr1, TestAddr0B, TestAddr1B;
+ u32 PatternBuffer[64+4]; /* FIXME: need increase 8? */
+ u32 Errors;
+
+ u32 val;
+ u32 reg;
+ u32 dev;
+ u32 index_reg;
+ u32 ch_start, ch_end, ch;
+ u32 msr;
+ u32 cr4;
+ u32 lo, hi;
+
+ u8 valid;
+ u32 tmp;
+ u8 LastTest;
+
+ print_debug_dqs("\nTrainRcvEn: Node", pDCTstat->Node_ID, 0);
+ print_debug_dqs("TrainRcvEn: Pass", Pass, 0);
+
+ dev = pDCTstat->dev_dct;
+ ch_start = 0;
+ if(!pDCTstat->GangedMode) {
+ ch_end = 2;
+ } else {
+ ch_end = 1;
+ }
+
+ for (ch = ch_start; ch < ch_end; ch++) {
+ reg = 0x78 + (0x100 * ch);
+ val = Get_NB32(dev, reg);
+ val &= ~(0x3ff << 22);
+ val |= (0x0c8 << 22); /* Max Rd Lat */
+ Set_NB32(dev, reg, val);
+ }
+
+ Final_Value = 1;
+ if (Pass == FirstPass) {
+ mct_InitDQSPos4RcvrEn_D(pMCTstat, pDCTstat);
+ } else {
+ 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);
+
+ SetupRcvrPattern(pMCTstat, pDCTstat, PatternBuffer, Pass);
+
+ Errors = 0;
+ dev = pDCTstat->dev_dct;
+ CTLRMaxDelay = 0;
+
+ 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;
+
+ MaxDelay_CH[Channel] = 0;
+ index_reg = 0x98 + 0x100 * Channel;
+
+ Receiver = mct_InitReceiver_D(pDCTstat, Channel);
+ /* There are four receiver pairs, loosely associated with chipselects. */
+ for (; Receiver < 8; Receiver += 2) {
+ Addl_Index = (Receiver >> 1) * 3 + 0x10;
+ LastTest = DQS_FAIL;
+
+ /* mct_ModifyIndex_D */
+ RcvrEnDlyRmin = RcvrEnDlyLimit = 0xff;
+
+ print_debug_dqs("\t\tTrainRcvEnd52: index ", Addl_Index, 2);
+
+ if(!mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver)) {
+ continue;
+ }
+
+ TestAddr0 = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, Channel, Receiver, &valid);
+ if(!valid) { /* Address not supported on current CS */
+ continue;
+ }
+
+ TestAddr0B = TestAddr0 + (BigPagex8_RJ8 << 3);
+
+ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, Receiver+1)) {
+ TestAddr1 = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, Channel, Receiver+1, &valid);
+ if(!valid) { /* Address not supported on current CS */
+ continue;
+ }
+ TestAddr1B = TestAddr1 + (BigPagex8_RJ8 << 3);
+ _2Ranks = 1;
+ } else {
+ _2Ranks = TestAddr1 = TestAddr1B = 0;
+ }
+
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr0 ", TestAddr0, 2);
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr0B ", TestAddr0B, 2);
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr1 ", TestAddr1, 2);
+ print_debug_dqs("\t\tTrainRcvEn53: TestAddr1B ", TestAddr1B, 2);
+
+ /*
+ * Get starting RcvrEnDly value
+ */
+ RcvrEnDly = mct_Get_Start_RcvrEnDly_1Pass(Pass);
+
+ /* mct_GetInitFlag_D*/
+ if (Pass == FirstPass) {
+ pDCTstat->DqsRcvEn_Pass = 0;
+ } else {
+ pDCTstat->DqsRcvEn_Pass=0xFF;
+ }
+ pDCTstat->DqsRcvEn_Saved = 0;
+
+
+ while(RcvrEnDly < RcvrEnDlyLimit) { /* sweep Delay value here */
+ print_debug_dqs("\t\t\tTrainRcvEn541: RcvrEnDly ", RcvrEnDly, 3);
+
+ /* callback not required
+ if(mct_AdjustDelay_D(pDCTstat, RcvrEnDly))
+ goto skipDly;
+ */
+
+ /* Odd steps get another pattern such that even
+ and odd steps alternate. The pointers to the
+ patterns will be swaped at the end of the loop
+ so that they correspond. */
+ if(RcvrEnDly & 1) {
+ PatternA = 1;
+ PatternB = 0;
+ } else {
+ /* Even step */
+ PatternA = 0;
+ PatternB = 1;
+ }
+
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0, PatternA); /* rank 0 of DIMM, testpattern 0 */
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B, PatternB); /* rank 0 of DIMM, testpattern 1 */
+ if(_2Ranks) {
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1, PatternA); /*rank 1 of DIMM, testpattern 0 */
+ mct_Write1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1B, PatternB); /*rank 1 of DIMM, testpattern 1 */
+ }
+
+ mct_SetRcvrEnDly_D(pDCTstat, RcvrEnDly, 0, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
+
+ CurrTest = DQS_FAIL;
+ CurrTestSide0 = DQS_FAIL;
+ CurrTestSide1 = DQS_FAIL;
+
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0); /*cache fills */
+ Test0 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr0, Channel, PatternA, Pass);/* ROM vs cache compare */
+ proc_IOCLFLUSH_D(TestAddr0);
+ ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+
+ print_debug_dqs("\t\t\tTrainRcvEn542: Test0 result ", Test0, 3);
+
+ /* != 0x00 mean pass */
+
+ if(Test0 == DQS_PASS) {
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr0B); /*cache fills */
+ /* ROM vs cache compare */
+ Test1 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr0B, Channel, PatternB, Pass);
+ proc_IOCLFLUSH_D(TestAddr0B);
+ ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+
+ print_debug_dqs("\t\t\tTrainRcvEn543: Test1 result ", Test1, 3);
+
+ if(Test1 == DQS_PASS) {
+ CurrTestSide0 = DQS_PASS;
+ }
+ }
+ if(_2Ranks) {
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1); /*cache fills */
+ /* ROM vs cache compare */
+ Test0 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr1, Channel, PatternA, Pass);
+ proc_IOCLFLUSH_D(TestAddr1);
+ ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+
+ print_debug_dqs("\t\t\tTrainRcvEn544: Test0 result ", Test0, 3);
+
+ if(Test0 == DQS_PASS) {
+ mct_Read1LTestPattern_D(pMCTstat, pDCTstat, TestAddr1B); /*cache fills */
+ /* ROM vs cache compare */
+ Test1 = mct_CompareTestPatternQW0_D(pMCTstat, pDCTstat, TestAddr1B, Channel, PatternB, Pass);
+ proc_IOCLFLUSH_D(TestAddr1B);
+ ResetDCTWrPtr_D(dev, index_reg, Addl_Index);
+
+ print_debug_dqs("\t\t\tTrainRcvEn545: Test1 result ", Test1, 3);
+ if(Test1 == DQS_PASS) {
+ CurrTestSide1 = DQS_PASS;
+ }
+ }
+ }
+
+ if(_2Ranks) {
+ if ((CurrTestSide0 == DQS_PASS) && (CurrTestSide1 == DQS_PASS)) {
+ CurrTest = DQS_PASS;
+ }
+ } else if (CurrTestSide0 == DQS_PASS) {
+ CurrTest = DQS_PASS;
+ }
+
+ /* record first pass DqsRcvEn to stack */
+ valid = mct_SavePassRcvEnDly_D(pDCTstat, RcvrEnDly, Channel, Receiver, Pass);
+
+ /* Break(1:RevF,2:DR) or not(0) FIXME: This comment deosn't make sense */
+ if(valid == 2 || (LastTest == DQS_FAIL && valid == 1)) {
+ RcvrEnDlyRmin = RcvrEnDly;
+ break;
+ }
+
+ LastTest = CurrTest;
+
+ /* swap the rank 0 pointers */
+ tmp = TestAddr0;
+ TestAddr0 = TestAddr0B;
+ TestAddr0B = tmp;
+
+ /* swap the rank 1 pointers */
+ tmp = TestAddr1;
+ TestAddr1 = TestAddr1B;
+ TestAddr1B = tmp;
+
+ print_debug_dqs("\t\t\tTrainRcvEn56: RcvrEnDly ", RcvrEnDly, 3);
+
+ RcvrEnDly++;
+
+ } /* while RcvrEnDly */
+
+ print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDly ", RcvrEnDly, 2);
+ print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDlyRmin ", RcvrEnDlyRmin, 3);
+ print_debug_dqs("\t\tTrainRcvEn61: RcvrEnDlyLimit ", RcvrEnDlyLimit, 3);
+ if(RcvrEnDlyRmin == RcvrEnDlyLimit) {
+ /* no passing window */
+ pDCTstat->ErrStatus |= 1 << SB_NORCVREN;
+ Errors |= 1 << SB_NORCVREN;
+ pDCTstat->ErrCode = SC_FatalErr;
+ }
+
+ if(RcvrEnDly > (RcvrEnDlyLimit - 1)) {
+ /* passing window too narrow, too far delayed*/
+ pDCTstat->ErrStatus |= 1 << SB_SmallRCVR;
+ Errors |= 1 << SB_SmallRCVR;
+ pDCTstat->ErrCode = SC_FatalErr;
+ RcvrEnDly = RcvrEnDlyLimit - 1;
+ pDCTstat->CSTrainFail |= 1 << Receiver;
+ pDCTstat->DimmTrainFail |= 1 << (Receiver + Channel);
+ }
+
+ /* CHB_D0_B0_RCVRDLY set in mct_Average_RcvrEnDly_Pass */
+ mct_Average_RcvrEnDly_Pass(pDCTstat, RcvrEnDly, RcvrEnDlyLimit, Channel, Receiver, Pass);
+
+ mct_SetFinalRcvrEnDly_D(pDCTstat, RcvrEnDly, Final_Value, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
+
+ if(pDCTstat->ErrStatus & (1 << SB_SmallRCVR)) {
+ Errors |= 1 << SB_SmallRCVR;
+ }
+
+ RcvrEnDly += Pass1MemClkDly;
+ if(RcvrEnDly > CTLRMaxDelay) {
+ CTLRMaxDelay = RcvrEnDly;
+ }
+
+ } /* while Receiver */
+ MaxDelay_CH[Channel] = CTLRMaxDelay;
+ } /* for Channel */
+
+ CTLRMaxDelay = MaxDelay_CH[0];
+ if (MaxDelay_CH[1] > CTLRMaxDelay)
+ CTLRMaxDelay = MaxDelay_CH[1];
+
+ for (Channel = 0; Channel < 2; Channel++) {
+ mct_SetMaxLatency_D(pDCTstat, Channel, CTLRMaxDelay); /* program Ch A/B MaxAsyncLat to correspond with max delay */
+ }
+
+ ResetDCTWrPtr_D(dev, 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 Channel;
+ printk(BIOS_DEBUG, "TrainRcvrEn: CH_MaxRdLat:\n");
+ for(Channel = 0; Channel<2; Channel++) {
+ printk(BIOS_DEBUG, "Channel:%x: %x\n",
+ Channel, pDCTstat->CH_MaxRdLat[Channel]);
+ }
+ }
+#endif
+
+#if DQS_TRAIN_DEBUG > 0
+ {
+ u8 val;
+ u8 Channel, Receiver;
+ u8 i;
+ u8 *p;
+
+ printk(BIOS_DEBUG, "TrainRcvrEn: CH_D_B_RCVRDLY:\n");
+ for(Channel = 0; Channel < 2; Channel++) {
+ printk(BIOS_DEBUG, "Channel:%x\n");
+ for(Receiver = 0; Receiver<8; Receiver+=2) {
+ printk(BIOS_DEBUG, "\t\tReceiver:%x:");
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver>>1];
+ for (i=0;i<8; i++) {
+ val = p[i];
+ printk(BIOS_DEBUG, "%x ", val);
+ }
+ 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");
+}
+
+u8 mct_InitReceiver_D(struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ if (pDCTstat->DIMMValidDCT[dct] == 0 ) {
+ return 8;
+ } else {
+ return 0;
+ }
+}
+
+static void mct_SetFinalRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly, u8 where, u8 Channel, u8 Receiver, u32 dev, u32 index_reg, u8 Addl_Index, u8 Pass/*, u8 *p*/)
+{
+ /*
+ * Program final DqsRcvEnDly to additional index for DQS receiver
+ * enabled delay
+ */
+ mct_SetRcvrEnDly_D(pDCTstat, RcvrEnDly, where, Channel, Receiver, dev, index_reg, Addl_Index, Pass);
+}
+
+static void mct_DisableDQSRcvEn_D(struct DCTStatStruc *pDCTstat)
+{
+ u8 ch_end, ch;
+ u32 reg;
+ u32 dev;
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+ if (pDCTstat->GangedMode) {
+ ch_end = 1;
+ } else {
+ ch_end = 2;
+ }
+
+ for (ch=0; ch<ch_end; ch++) {
+ reg = 0x78 + 0x100 * ch;
+ val = Get_NB32(dev, reg);
+ val &= ~(1 << DqsRcvEnTrain);
+ Set_NB32(dev, reg, val);
+ }
+}
+
+/* mct_ModifyIndex_D
+ * Function only used once so it was inlined.
+ */
+
+/* mct_GetInitFlag_D
+ * Function only used once so it was inlined.
+ */
+
+void mct_SetRcvrEnDly_D(struct DCTStatStruc *pDCTstat, u8 RcvrEnDly,
+ u8 FinalValue, u8 Channel, u8 Receiver, u32 dev,
+ u32 index_reg, u8 Addl_Index, u8 Pass)
+{
+ u32 index;
+ u8 i;
+ u8 *p;
+ u32 val;
+
+ if(RcvrEnDly == 0xFE) {
+ /*set the boudary flag */
+ pDCTstat->Status |= 1 << SB_DQSRcvLimit;
+ }
+
+ /* DimmOffset not needed for CH_D_B_RCVRDLY array */
+ for(i=0; i < 8; i++) {
+ if(FinalValue) {
+ /*calculate dimm offset */
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver >> 1];
+ RcvrEnDly = p[i];
+ }
+
+ /* if flag=0, set DqsRcvEn value to reg. */
+ /* 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);
+ if(i & 1) {
+ /* odd byte lane */
+ val &= ~(0xFF << 16);
+ val |= (RcvrEnDly << 16);
+ } else {
+ /* even byte lane */
+ val &= ~0xFF;
+ val |= RcvrEnDly;
+ }
+ Set_NB32_index_wait(dev, index_reg, index, val);
+ }
+
+}
+
+static void mct_SetMaxLatency_D(struct DCTStatStruc *pDCTstat, u8 Channel, u8 DQSRcvEnDly)
+{
+ u32 dev;
+ u32 reg;
+ u16 SubTotal;
+ u32 index_reg;
+ u32 reg_off;
+ u32 val;
+ u32 valx;
+
+ if(pDCTstat->GangedMode)
+ Channel = 0;
+
+ dev = pDCTstat->dev_dct;
+ reg_off = 0x100 * Channel;
+ index_reg = 0x98 + reg_off;
+
+ /* Multiply the CAS Latency by two to get a number of 1/2 MEMCLKs units.*/
+ val = Get_NB32(dev, 0x88 + reg_off);
+ 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);
+ 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);
+ if(!(val & 0x00202020))
+ SubTotal += 1;
+ else
+ SubTotal += 2;
+
+ /* 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);
+ SubTotal += 8 - (val & 0x0f);
+
+ /* Convert bits 7-5 (also referred to as the course delay) of
+ * the current (or worst case) DQS receiver enable delay to
+ * 1/2 MEMCLKs units, rounding up, and add this to the sub-total.
+ */
+ SubTotal += DQSRcvEnDly >> 5; /*BOZO-no rounding up */
+
+ /* Add 5.5 to the sub-total. 5.5 represents part of the
+ * processor specific constant delay value in the DRAM
+ * clock domain.
+ */
+ SubTotal <<= 1; /*scale 1/2 MemClk to 1/4 MemClk */
+ SubTotal += 11; /*add 5.5 1/2MemClk */
+
+ /* Convert the sub-total (in 1/2 MEMCLKs) to northbridge
+ * clocks (NCLKs) as follows (assuming DDR400 and assuming
+ * that no P-state or link speed changes have occurred).
+ */
+
+ /* New formula:
+ * SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */
+ val = Get_NB32(dev, 0x94 + reg_off);
+
+ /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
+ val &= 7;
+ if (val >= 3) {
+ val <<= 1;
+ } else
+ val += 3;
+ valx = val << 2;
+
+ val = Get_NB32(pDCTstat->dev_nbmisc, 0xD4);
+ SubTotal *= ((val & 0x1f) + 4 ) * 3;
+
+ SubTotal /= valx;
+ if (SubTotal % valx) { /* round up */
+ SubTotal++;
+ }
+
+ /* Add 5 NCLKs to the sub-total. 5 represents part of the
+ * processor specific constant value in the northbridge
+ * clock domain.
+ */
+ SubTotal += 5;
+
+ pDCTstat->CH_MaxRdLat[Channel] = SubTotal;
+ if(pDCTstat->GangedMode) {
+ pDCTstat->CH_MaxRdLat[1] = SubTotal;
+ }
+
+ /* Program the F2x[1, 0]78[MaxRdLatency] register with
+ * the total delay value (in NCLKs).
+ */
+ reg = 0x78 + reg_off;
+ val = Get_NB32(dev, reg);
+ val &= ~(0x3ff << 22);
+ val |= (SubTotal & 0x3ff) << 22;
+
+ /* program MaxRdLatency to correspond with current delay */
+ Set_NB32(dev, reg, val);
+}
+
+static u8 mct_SavePassRcvEnDly_D(struct DCTStatStruc *pDCTstat,
+ u8 rcvrEnDly, u8 Channel,
+ u8 receiver, u8 Pass)
+{
+ u8 i;
+ u8 mask_Saved, mask_Pass;
+ u8 *p;
+
+ /* calculate dimm offset
+ * not needed for CH_D_B_RCVRDLY array
+ */
+
+ /* cmp if there has new DqsRcvEnDly to be recorded */
+ mask_Pass = pDCTstat->DqsRcvEn_Pass;
+
+ if(Pass == SecondPass) {
+ mask_Pass = ~mask_Pass;
+ }
+
+ mask_Saved = pDCTstat->DqsRcvEn_Saved;
+ if(mask_Pass != mask_Saved) {
+
+ /* find desired stack offset according to channel/dimm/byte */
+ if(Pass == SecondPass) {
+ /* FIXME: SecondPass is never used for Barcelona p = pDCTstat->CH_D_B_RCVRDLY_1[Channel][receiver>>1]; */
+ p = 0; /* Keep the compiler happy. */
+ } else {
+ mask_Saved &= mask_Pass;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][receiver>>1];
+ }
+ for(i=0; i < 8; i++) {
+ /* cmp per byte lane */
+ if(mask_Pass & (1 << i)) {
+ if(!(mask_Saved & (1 << i))) {
+ /* save RcvEnDly to stack, according to
+ the related Dimm/byte lane */
+ p[i] = (u8)rcvrEnDly;
+ mask_Saved |= 1 << i;
+ }
+ }
+ }
+ pDCTstat->DqsRcvEn_Saved = mask_Saved;
+ }
+ return mct_SaveRcvEnDly_D_1Pass(pDCTstat, Pass);
+}
+
+static u8 mct_CompareTestPatternQW0_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 addr, u8 channel,
+ u8 pattern, u8 Pass)
+{
+ /* Compare only the first beat of data. Since target addrs are cache
+ * line aligned, the Channel parameter is used to determine which
+ * cache QW to compare.
+ */
+
+ u8 *test_buf;
+ u8 i;
+ u8 result;
+ u8 value;
+
+ if(Pass == FirstPass) {
+ if(pattern==1) {
+ test_buf = (u8 *)TestPattern1_D;
+ } else {
+ test_buf = (u8 *)TestPattern0_D;
+ }
+ } else { /* Second Pass */
+ test_buf = (u8 *)TestPattern2_D;
+ }
+
+ SetUpperFSbase(addr);
+ addr <<= 8;
+
+ if((pDCTstat->Status & (1<<SB_128bitmode)) && channel ) {
+ addr += 8; /* second channel */
+ test_buf += 8;
+ }
+
+ print_debug_dqs_pair("\t\t\t\t\t\t test_buf = ", (u32)test_buf, " | addr_lo = ", addr, 4);
+ for (i=0; i<8; i++, addr ++) {
+ value = read32_fs(addr);
+ print_debug_dqs_pair("\t\t\t\t\t\t\t\t ", test_buf[i], " | ", value, 4);
+
+ if (value == test_buf[i]) {
+ pDCTstat->DqsRcvEn_Pass |= (1<<i);
+ } else {
+ pDCTstat->DqsRcvEn_Pass &= ~(1<<i);
+ }
+ }
+
+ result = DQS_FAIL;
+
+ if (Pass == FirstPass) {
+ /* if first pass, at least one byte lane pass
+ * ,then DQS_PASS=1 and will set to related reg.
+ */
+ if(pDCTstat->DqsRcvEn_Pass != 0) {
+ result = DQS_PASS;
+ } else {
+ result = DQS_FAIL;
+ }
+
+ } else {
+ /* if second pass, at least one byte lane fail
+ * ,then DQS_FAIL=1 and will set to related reg.
+ */
+ if(pDCTstat->DqsRcvEn_Pass != 0xFF) {
+ result = DQS_FAIL;
+ } else {
+ result = DQS_PASS;
+ }
+ }
+
+ /* if second pass, we can't find the fail until FFh,
+ * then let it fail to save the final delay
+ */
+ if((Pass == SecondPass) && (pDCTstat->Status & (1 << SB_DQSRcvLimit))) {
+ result = DQS_FAIL;
+ pDCTstat->DqsRcvEn_Pass = 0;
+ }
+
+ /* second pass needs to be inverted
+ * FIXME? this could be inverted in the above code to start with...
+ */
+ if(Pass == SecondPass) {
+ if (result == DQS_PASS) {
+ result = DQS_FAIL;
+ } else if (result == DQS_FAIL) { /* FIXME: doesn't need to be else if */
+ result = DQS_PASS;
+ }
+ }
+
+
+ return result;
+}
+
+static void mct_InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Initialize the DQS Positions in preparation for
+ * Reciever Enable Training.
+ * Write Position is 1/2 Memclock Delay
+ * Read Position is 1/2 Memclock Delay
+ */
+ u8 i;
+ for(i=0;i<2; i++){
+ InitDQSPos4RcvrEn_D(pMCTstat, pDCTstat, i);
+ }
+}
+
+static void InitDQSPos4RcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel)
+{
+ /* Initialize the DQS Positions in preparation for
+ * Reciever Enable Training.
+ * Write Position is no Delay
+ * Read Position is 1/2 Memclock Delay
+ */
+
+ u8 i, j;
+ u32 dword;
+ u8 dn = 4; /* TODO: Rev C could be 4 */
+ u32 dev = pDCTstat->dev_dct;
+ u32 index_reg = 0x98 + 0x100 * Channel;
+
+ /* FIXME: add Cx support */
+ dword = 0x00000000;
+ for(i=1; i<=3; i++) {
+ 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);
+ }
+
+ /* errata #180 */
+ dword = 0x2f2f2f2f;
+ 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);
+ }
+
+ 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);
+}
+
+void SetEccDQSRcvrEn_D(struct DCTStatStruc *pDCTstat, u8 Channel)
+{
+ u32 dev;
+ u32 index_reg;
+ u32 index;
+ u8 ChipSel;
+ u8 *p;
+ u32 val;
+
+ dev = pDCTstat->dev_dct;
+ index_reg = 0x98 + Channel * 0x100;
+ 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);
+ print_debug_dqs_pair("\t\tSetEccDQSRcvrPos: ChipSel ",
+ ChipSel, " rcvr_delay ", val, 2);
+ index += 3;
+ }
+}
+
+static void CalcEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel)
+{
+ u8 ChipSel;
+ u16 EccDQSLike;
+ u8 EccDQSScale;
+ u32 val, val0, val1;
+
+ EccDQSLike = pDCTstat->CH_EccDQSLike[Channel];
+ EccDQSScale = pDCTstat->CH_EccDQSScale[Channel];
+
+ for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel += 2) {
+ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel, ChipSel)) {
+ u8 *p;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][ChipSel>>1];
+
+ /* DQS Delay Value of Data Bytelane
+ * most like ECC byte lane */
+ val0 = p[EccDQSLike & 0x07];
+ /* DQS Delay Value of Data Bytelane
+ * 2nd most like ECC byte lane */
+ val1 = p[(EccDQSLike>>8) & 0x07];
+
+ if (!(pDCTstat->Status & (1 << SB_Registered))) {
+ if(val0 > val1) {
+ val = val0 - val1;
+ } else {
+ val = val1 - val0;
+ }
+
+ val *= ~EccDQSScale;
+ val >>= 8; /* /256 */
+
+ if(val0 > val1) {
+ val -= val1;
+ } else {
+ val += val0;
+ }
+ } else {
+ val = val1 - val0;
+ val += val1;
+ }
+
+ pDCTstat->CH_D_BC_RCVRDLY[Channel][ChipSel>>1] = val;
+ }
+ }
+ SetEccDQSRcvrEn_D(pDCTstat, Channel);
+}
+
+void mctSetEccDQSRcvrEn_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+ u8 i;
+
+ for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+ if (!pDCTstat->NodePresent)
+ break;
+ if (pDCTstat->DCTSysLimit) {
+ for(i=0; i<2; i++)
+ CalcEccDQSRcvrEn_D(pMCTstat, pDCTstat, i);
+ }
+ }
+}
+
+void phyAssistedMemFnceTraining(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node = 0;
+ struct DCTStatStruc *pDCTstat;
+
+ /* FIXME: skip for Ax */
+ while (Node < MAX_NODES_SUPPORTED) {
+ pDCTstat = pDCTstatA + Node;
+
+ if(pDCTstat->DCTSysLimit) {
+ fenceDynTraining_D(pMCTstat, pDCTstat, 0);
+ fenceDynTraining_D(pMCTstat, pDCTstat, 1);
+ }
+ Node++;
+ }
+}
+
+static void fenceDynTraining_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u16 avRecValue;
+ u32 val;
+ u32 dev;
+ u32 index_reg = 0x98 + 0x100 * dct;
+ 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 = Get_NB32_index_wait(dev, index_reg, index) & ~0xFF;
+ val |= (FenceTrnFinDlySeed & 0x1F);
+ if (index != 0x52) {
+ val &= ~(0xFF << 8);
+ val |= (val & 0xFF) << 8;
+ val &= 0xFFFF;
+ val |= val << 16;
+ }
+ 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;
+
+ /* Write the (averaged value -8) to F2x[1,0]9C_x0C[PhyFence]. */
+ /* inlined mct_AdjustFenceValue() */
+ /* The RBC0 is not supported. */
+ /* if (pDCTstat->LogicalCPUID & AMD_RB_C0)
+ avRecValue -= 3;
+ 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);
+}
+
+void mct_Wait(u32 cycles)
+{
+ u32 saved;
+ u32 hi, lo, msr;
+
+ /* Wait # of 50ns cycles
+ This seems like a hack to me... */
+
+ cycles <<= 3; /* x8 (number of 1.25ns ticks) */
+
+ msr = 0x10; /* TSC */
+ _RDMSR(msr, &lo, &hi);
+ saved = lo;
+ do {
+ _RDMSR(msr, &lo, &hi);
+ } while (lo - saved < cycles );
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
new file mode 100644
index 0000000000..008705c613
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc1p.c
@@ -0,0 +1,85 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+u8 mct_checkNumberOfDqsRcvEn_1Pass(u8 pass)
+{
+ u8 ret = 1;
+ if (pass == SecondPass)
+ ret = 0;
+
+ return ret;
+}
+
+u32 SetupDqsPattern_1PassA(u8 pass)
+{
+ return (u32) TestPattern1_D;
+}
+
+u32 SetupDqsPattern_1PassB(u8 pass)
+{
+ return (u32) TestPattern0_D;
+}
+
+u8 mct_Get_Start_RcvrEnDly_1Pass(u8 pass)
+{
+ return 0;
+}
+
+static u8 mct_Average_RcvrEnDly_1Pass(struct DCTStatStruc *pDCTstat, u8 Channel, u8 Receiver,
+ u8 Pass)
+{
+ u8 i, MaxValue;
+ u8 *p;
+ u8 val;
+
+ MaxValue = 0;
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver >> 1];
+
+ for(i=0; i < 8; i++) {
+ /* get left value from DCTStatStruc.CHA_D0_B0_RCVRDLY*/
+ val = p[i];
+ /* get right value from DCTStatStruc.CHA_D0_B0_RCVRDLY_1*/
+ val += Pass1MemClkDly;
+ /* write back the value to stack */
+ if (val > MaxValue)
+ MaxValue = val;
+
+ p[i] = val;
+ }
+ /* pDCTstat->DimmTrainFail &= ~(1<<Receiver+Channel); */
+
+ return MaxValue;
+}
+
+u8 mct_SaveRcvEnDly_D_1Pass(struct DCTStatStruc *pDCTstat, u8 pass)
+{
+ u8 ret;
+ ret = 0;
+ if((pDCTstat->DqsRcvEn_Pass == 0xff) && (pass== FirstPass))
+ ret = 2;
+ return ret;
+}
+
+u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+ u8 RcvrEnDly, u8 RcvrEnDlyLimit,
+ u8 Channel, u8 Receiver, u8 Pass)
+
+{
+ return mct_Average_RcvrEnDly_1Pass(pDCTstat, Channel, Receiver, Pass);
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c
new file mode 100644
index 0000000000..7fe6867e93
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctsrc2p.c
@@ -0,0 +1,129 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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
+ */
+
+u8 mct_checkNumberOfDqsRcvEn_Pass(u8 pass)
+{
+ return 1;
+}
+
+u32 SetupDqsPattern_PassA(u8 Pass)
+{
+ u32 ret;
+ if(Pass == FirstPass)
+ ret = (u32) TestPattern1_D;
+ else
+ ret = (u32) TestPattern2_D;
+
+ return ret;
+}
+
+u32 SetupDqsPattern_PassB(u8 Pass)
+{
+ u32 ret;
+ if(Pass == FirstPass)
+ ret = (u32) TestPattern0_D;
+ else
+ ret = (u32) TestPattern2_D;
+
+ return ret;
+}
+
+u8 mct_Get_Start_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 Receiver,
+ u8 Pass)
+{
+ u8 RcvrEnDly;
+
+ if (Pass == FirstPass)
+ RcvrEnDly = 0;
+ else {
+ u8 max = 0;
+ u8 val;
+ u8 i;
+ u8 *p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver>>1];
+ u8 bn;
+ bn = 8;
+
+ for ( i=0;i<bn; i++) {
+ val = p[i];
+
+ if(val > max) {
+ max = val;
+ }
+ }
+ RcvrEnDly = max;
+ }
+
+ return RcvrEnDly;
+}
+
+u8 mct_Average_RcvrEnDly_Pass(struct DCTStatStruc *pDCTstat,
+ u8 RcvrEnDly, u8 RcvrEnDlyLimit,
+ u8 Channel, u8 Receiver, u8 Pass)
+{
+ u8 i;
+ u8 *p;
+ u8 *p_1;
+ u8 val;
+ u8 val_1;
+ u8 valid = 1;
+ u8 bn;
+
+ bn = 8;
+
+ p = pDCTstat->CH_D_B_RCVRDLY[Channel][Receiver>>1];
+
+ if (Pass == SecondPass) { /* second pass must average values */
+ /* FIXME: which byte? */
+ p_1 = pDCTstat->B_RCVRDLY_1;
+ /* p_1 = pDCTstat->CH_D_B_RCVRDLY_1[Channel][Receiver>>1]; */
+ for(i=0; i<bn; i++) {
+ val = p[i];
+ /* left edge */
+ if (val != (RcvrEnDlyLimit - 1)) {
+ val -= Pass1MemClkDly;
+ val_1 = p_1[i];
+ val += val_1;
+ val >>= 1;
+ p[i] = val;
+ } else {
+ valid = 0;
+ break;
+ }
+ }
+ if (!valid) {
+ pDCTstat->ErrStatus |= 1<<SB_NORCVREN;
+ } else {
+ pDCTstat->DimmTrainFail &= ~(1<<(Receiver + Channel));
+ }
+ } else {
+ for(i=0; i < bn; i++) {
+ val = p[i];
+ /* Add 1/2 Memlock delay */
+ /* val += Pass1MemClkDly; */
+ val += 0x5; /* NOTE: middle value with DQSRCVEN_SAVED_GOOD_TIMES */
+ /* val += 0x02; */
+ p[i] = val;
+ pDCTstat->DimmTrainFail &= ~(1<<(Receiver + Channel));
+ }
+ }
+
+ return RcvrEnDly;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
new file mode 100644
index 0000000000..355e92617d
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mcttmrl.c
@@ -0,0 +1,399 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/*
+ * Description: Max Read Latency Training feature for DDR 3 MCT
+ */
+
+static u8 CompareMaxRdLatTestPattern_D(u32 pattern_buf, u32 addr);
+static u32 GetMaxRdLatTestAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel,
+ u8 *MaxRcvrEnDly, u8 *valid);
+u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 Channel,
+ u8 DQSRcvEnDly, u32 *Margin);
+static void maxRdLatencyTrain_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+static void mct_setMaxRdLatTrnVal_D(struct DCTStatStruc *pDCTstat, u8 Channel,
+ u16 MaxRdLatVal);
+
+/*Warning: These must be located so they do not cross a logical 16-bit
+ segment boundary!*/
+static const u32 TestMaxRdLAtPattern_D[] = {
+ 0x6E0E3FAC, 0x0C3CFF52,
+ 0x4A688181, 0x49C5B613,
+ 0x7C780BA6, 0x5C1650E3,
+ 0x0C4F9D76, 0x0C6753E6,
+ 0x205535A5, 0xBABFB6CA,
+ 0x610E6E5F, 0x0C5F1C87,
+ 0x488493CE, 0x14C9C383,
+ 0xF5B9A5CD, 0x9CE8F615,
+
+ 0xAAD714B5, 0xC38F1B4C,
+ 0x72ED647C, 0x669F7562,
+ 0x5233F802, 0x4A898B30,
+ 0x10A40617, 0x3326B465,
+ 0x55386E04, 0xC807E3D3,
+ 0xAB49E193, 0x14B4E63A,
+ 0x67DF2495, 0xEA517C45,
+ 0x7624CE51, 0xF8140C51,
+
+ 0x4824BD23, 0xB61DD0C9,
+ 0x072BCFBE, 0xE8F3807D,
+ 0x919EA373, 0x25E30C47,
+ 0xFEB12958, 0x4DA80A5A,
+ 0xE9A0DDF8, 0x792B0076,
+ 0xE81C73DC, 0xF025B496,
+ 0x1DB7E627, 0x808594FE,
+ 0x82668268, 0x655C7783,
+};
+
+static u32 SetupMaxRdPattern(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u32 *buffer)
+{
+ /* 1. Copy the alpha and Beta patterns from ROM to Cache,
+ * aligning on 16 byte boundary
+ * 2. Set the ptr to Cacheable copy in DCTStatstruc.PtrPatternBufA
+ * for Alpha
+ * 3. Set the ptr to Cacheable copy in DCTStatstruc.PtrPatternBufB
+ * for Beta
+ */
+ u32 *buf;
+ u8 i;
+
+ buf = (u32 *)(((u32)buffer + 0x10) & (0xfffffff0));
+
+ for(i = 0; i < (16 * 3); i++) {
+ buf[i] = TestMaxRdLAtPattern_D[i];
+ }
+
+ return (u32)buf;
+}
+
+void TrainMaxReadLatency_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstatA)
+{
+ u8 Node;
+
+ for(Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
+ struct DCTStatStruc *pDCTstat;
+ pDCTstat = pDCTstatA + Node;
+
+ if(!pDCTstat->NodePresent)
+ break;
+
+ if(pDCTstat->DCTSysLimit)
+ maxRdLatencyTrain_D(pMCTstat, pDCTstat);
+ }
+}
+
+static void maxRdLatencyTrain_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 Channel;
+ u32 TestAddr0;
+ u8 _DisableDramECC = 0, _Wrap32Dis = 0, _SSE2 = 0;
+ u16 MaxRdLatDly;
+ u8 RcvrEnDly = 0;
+ u32 PatternBuffer[60]; /* FIXME: why not 48 + 4 */
+ u32 Margin;
+ u32 addr;
+ u32 cr4;
+ u32 lo, hi;
+
+ u8 valid;
+ u32 pattern_buf;
+
+ cr4 = read_cr4();
+ if(cr4 & (1<<9)) { /* save the old value */
+ _SSE2 = 1;
+ }
+ cr4 |= (1<<9); /* OSFXSR enable SSE2 */
+ write_cr4(cr4);
+
+ addr = HWCR;
+ _RDMSR(addr, &lo, &hi);
+ if(lo & (1<<17)) { /* save the old value */
+ _Wrap32Dis = 1;
+ }
+ lo |= (1<<17); /* HWCR.wrap32dis */
+ lo &= ~(1<<15); /* SSEDIS */
+ /* Setting wrap32dis allows 64-bit memory references in
+ real mode */
+ _WRMSR(addr, lo, hi);
+
+ _DisableDramECC = mct_DisableDimmEccEn_D(pMCTstat, pDCTstat);
+
+ pattern_buf = SetupMaxRdPattern(pMCTstat, pDCTstat, PatternBuffer);
+
+ for (Channel = 0; Channel < 2; Channel++) {
+ print_debug_dqs("\tMaxRdLatencyTrain51: Channel ",Channel, 1);
+ pDCTstat->Channel = Channel;
+
+ if( (pDCTstat->Status & (1 << SB_128bitmode)) && Channel)
+ break; /*if ganged mode, skip DCT 1 */
+
+ TestAddr0 = GetMaxRdLatTestAddr_D(pMCTstat, pDCTstat, Channel, &RcvrEnDly, &valid);
+ if(!valid) /* Address not supported on current CS */
+ continue;
+ /* rank 1 of DIMM, testpattern 0 */
+ WriteMaxRdLat1CLTestPattern_D(pattern_buf, TestAddr0);
+
+ MaxRdLatDly = mct_GetStartMaxRdLat_D(pMCTstat, pDCTstat, Channel, RcvrEnDly, &Margin);
+ print_debug_dqs("\tMaxRdLatencyTrain52: MaxRdLatDly start ", MaxRdLatDly, 2);
+ print_debug_dqs("\tMaxRdLatencyTrain52: MaxRdLatDly Margin ", Margin, 2);
+ while(MaxRdLatDly < MAX_RD_LAT) { /* sweep Delay value here */
+ mct_setMaxRdLatTrnVal_D(pDCTstat, Channel, MaxRdLatDly);
+ ReadMaxRdLat1CLTestPattern_D(TestAddr0);
+ if( CompareMaxRdLatTestPattern_D(pattern_buf, TestAddr0) == DQS_PASS)
+ break;
+ SetTargetWTIO_D(TestAddr0);
+ FlushMaxRdLatTestPattern_D(TestAddr0);
+ ResetTargetWTIO_D();
+ MaxRdLatDly++;
+ }
+ print_debug_dqs("\tMaxRdLatencyTrain53: MaxRdLatDly end ", MaxRdLatDly, 2);
+ mct_setMaxRdLatTrnVal_D(pDCTstat, Channel, MaxRdLatDly + Margin);
+ }
+
+ 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);
+ }
+
+#if DQS_TRAIN_DEBUG > 0
+ {
+ u8 Channel;
+ print_debug("maxRdLatencyTrain: CH_MaxRdLat:\n");
+ for(Channel = 0; Channel<2; Channel++) {
+ print_debug("Channel:"); print_debug_hex8(Channel);
+ print_debug(": ");
+ print_debug_hex8( pDCTstat->CH_MaxRdLat[Channel] );
+ print_debug("\n");
+ }
+ }
+#endif
+}
+
+static void mct_setMaxRdLatTrnVal_D(struct DCTStatStruc *pDCTstat,
+ u8 Channel, u16 MaxRdLatVal)
+{
+ u8 i;
+ u32 reg;
+ u32 dev;
+ u32 val;
+
+ if (pDCTstat->GangedMode) {
+ Channel = 0; /* for safe */
+ for (i=0; i<2; i++)
+ pDCTstat->CH_MaxRdLat[i] = MaxRdLatVal;
+ } else {
+ pDCTstat->CH_MaxRdLat[Channel] = MaxRdLatVal;
+ }
+
+ dev = pDCTstat->dev_dct;
+ reg = 0x78 + Channel * 0x100;
+ val = Get_NB32(dev, reg);
+ val &= ~(0x3ff<<22);
+ val |= MaxRdLatVal<<22;
+ /* program MaxRdLatency to correspond with current delay */
+ Set_NB32(dev, reg, val);
+}
+
+static u8 CompareMaxRdLatTestPattern_D(u32 pattern_buf, u32 addr)
+{
+ /* Compare only the first beat of data. Since target addrs are cache
+ * line aligned, the Channel parameter is used to determine which cache
+ * QW to compare.
+ */
+
+ u32 *test_buf = (u32 *)pattern_buf;
+ u32 addr_lo;
+ u32 val, val_test;
+ int i;
+ u8 ret = DQS_PASS;
+
+ SetUpperFSbase(addr);
+ addr_lo = addr<<8;
+
+ _EXECFENCE;
+ for (i=0; i<(16*3); i++) {
+ val = read32_fs(addr_lo);
+ val_test = test_buf[i];
+
+ print_debug_dqs_pair("\t\t\t\t\t\ttest_buf = ", (u32)test_buf, " value = ", val_test, 5);
+ print_debug_dqs_pair("\t\t\t\t\t\ttaddr_lo = ", addr_lo, " value = ", val, 5);
+ if(val != val_test) {
+ ret = DQS_FAIL;
+ break;
+ }
+ addr_lo += 4;
+ }
+
+ return ret;
+}
+
+static u32 GetMaxRdLatTestAddr_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 *MaxRcvrEnDly,
+ u8 *valid)
+{
+ u8 Max = 0;
+
+ u8 Channel_Max = 0;
+ u8 d;
+ u8 d_Max = 0;
+
+ u8 Byte;
+ u32 TestAddr0 = 0;
+ u8 ch, ch_start, ch_end;
+ u8 bn;
+
+ bn = 8;
+
+ if(pDCTstat->Status & (1 << SB_128bitmode)) {
+ ch_start = 0;
+ ch_end = 2;
+ } else {
+ ch_start = Channel;
+ ch_end = Channel + 1;
+ }
+
+ *valid = 0;
+
+ for(ch = ch_start; ch < ch_end; ch++) {
+ for(d=0; d<4; d++) {
+ for(Byte = 0; Byte<bn; Byte++) {
+ u8 tmp;
+ tmp = pDCTstat->CH_D_B_RCVRDLY[ch][d][Byte];
+ if(tmp>Max) {
+ Max = tmp;
+ Channel_Max = Channel;
+ d_Max = d;
+ }
+ }
+ }
+ }
+
+ if(mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, Channel_Max, d_Max << 1)) {
+ TestAddr0 = mct_GetMCTSysAddr_D(pMCTstat, pDCTstat, Channel_Max, d_Max << 1, valid);
+ }
+
+ if(*valid)
+ *MaxRcvrEnDly = Max;
+
+ return TestAddr0;
+}
+
+u8 mct_GetStartMaxRdLat_D(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat,
+ u8 Channel, u8 DQSRcvEnDly, u32 *Margin)
+{
+ u32 SubTotal;
+ u32 val;
+ u32 valx;
+ u32 valxx;
+ u32 index_reg;
+ u32 reg_off;
+ u32 dev;
+
+ if(pDCTstat->GangedMode)
+ Channel = 0;
+
+ index_reg = 0x98 + 0x100 * Channel;
+
+ 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);
+ 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);
+ 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);
+ if(!(val & 0x00202020))
+ SubTotal += 1;
+ else
+ SubTotal += 2;
+
+ /* 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);
+ SubTotal += 8 - (val & 0x0f);
+
+ /* Convert bits 7-5 (also referred to as the course delay) of the current
+ * (or worst case) DQS receiver enable delay to 1/2 MEMCLKs units,
+ * rounding up, and add this to the sub-total. */
+ SubTotal += DQSRcvEnDly >> 5; /*BOZO-no rounding up */
+
+ SubTotal <<= 1; /*scale 1/2 MemClk to 1/4 MemClk */
+
+ /* Convert the sub-total (in 1/2 MEMCLKs) to northbridge clocks (NCLKs)
+ * as follows (assuming DDR400 and assuming that no P-state or link speed
+ * changes have occurred). */
+
+ /*New formula:
+ SubTotal *= 3*(Fn2xD4[NBFid]+4)/(3+Fn2x94[MemClkFreq])/2 */
+ val = Get_NB32(dev, 0x94 + reg_off);
+ /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
+ val &= 7;
+ if (val >= 3) {
+ val <<= 1;
+ } else
+ val += 3;
+ valx = (val) << 2; /* SubTotal div 4 to scale 1/4 MemClk back to MemClk */
+
+ val = Get_NB32(pDCTstat->dev_nbmisc, 0xD4);
+ val = ((val & 0x1f) + 4 ) * 3;
+
+ /* Calculate 1 MemClk + 1 NCLK delay in NCLKs for margin */
+ valxx = val << 2;
+ valxx /= valx;
+ if (valxx % valx)
+ valxx++; /* round up */
+ valxx++; /* add 1NCLK */
+ *Margin = valxx; /* one MemClk delay in NCLKs and one additional NCLK */
+
+ val *= SubTotal;
+
+ val /= valx;
+ if (val % valx)
+ val++; /* round up */
+
+ return val;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
new file mode 100644
index 0000000000..d14bc0dd58
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mctwl.c
@@ -0,0 +1,382 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static void FreqChgCtrlWrd(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat);
+
+
+static void AgesaDelay(u32 msec)
+{
+ mct_Wait(msec*10);
+}
+
+void PrepareC_MCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ pDCTstat->C_MCTPtr->AgesaDelay = AgesaDelay;
+ pDCTstat->C_MCTPtr->PlatMaxTotalDimms = mctGet_NVbits(NV_MAX_DIMMS);
+ pDCTstat->C_MCTPtr->PlatMaxDimmsDct = pDCTstat->C_MCTPtr->PlatMaxTotalDimms >> 1;
+}
+
+void PrepareC_DCT(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u8 dimm;
+ u16 DimmValid;
+ u16 Dimmx8Present;
+
+ dct &= 1;
+
+ pDCTstat->C_DCTPtr[dct]->DctTrain = dct;
+
+ if (dct == 1) {
+ Dimmx8Present = pDCTstat->Dimmx8Present >> 1;
+ } else
+ Dimmx8Present = pDCTstat->Dimmx8Present;
+ Dimmx8Present &= 0x55;
+
+ pDCTstat->C_DCTPtr[dct]->MaxDimmsInstalled = pDCTstat->MAdimms[dct];
+ DimmValid = pDCTstat->DIMMValidDCT[dct];
+
+ pDCTstat->C_DCTPtr[dct]->NodeId = pDCTstat->Node_ID;
+ pDCTstat->C_DCTPtr[dct]->LogicalCPUID = pDCTstat->LogicalCPUID;
+
+ for (dimm = 0; dimm < MAX_DIMMS; dimm++) {
+ if (DimmValid & (1 << dimm))
+ pDCTstat->C_DCTPtr[dct]->DimmPresent[dimm] = 1;
+ if (Dimmx8Present & (1 << dimm))
+ pDCTstat->C_DCTPtr[dct]->DimmX8Present[dimm] = 1;
+ }
+
+ if (pDCTstat->GangedMode & (1 << 0))
+ pDCTstat->C_DCTPtr[dct]->CurrDct = 0;
+ else
+ pDCTstat->C_DCTPtr[dct]->CurrDct = dct;
+
+ pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[dct];
+ if (!(pDCTstat->GangedMode & (1 << 0)) && (dct == 1))
+ pDCTstat->C_DCTPtr[dct]->DctCSPresent = pDCTstat->CSPresent_DCT[0];
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 1;
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 0;
+ } else {
+ if (pDCTstat->MirrPresU_NumRegR > 0)
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_OnDimmMirror] = 1;
+ pDCTstat->C_DCTPtr[dct]->Status[DCT_STATUS_REGISTERED] = 0;
+ }
+
+ pDCTstat->C_DCTPtr[dct]->RegMan1Present = pDCTstat->RegMan1Present;
+
+ for (dimm = 0; dimm < MAX_TOTAL_DIMMS; dimm++) {
+ u8 DimmRanks;
+ if (DimmValid & (1 << dimm)) {
+ DimmRanks = 1;
+ if (pDCTstat->DimmDRPresent & (1 << (dimm+dct)))
+ DimmRanks = 2;
+ else if (pDCTstat->DimmQRPresent & (1 << (dimm+dct)))
+ DimmRanks = 4;
+ } else
+ DimmRanks = 0;
+ pDCTstat->C_DCTPtr[dct]->DimmRanks[dimm] = DimmRanks;
+ }
+}
+
+void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val |= 1 << 11;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val |= 1 << 11;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+}
+
+void DisableZQcalibration(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u32 val;
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= ~(1 << 11);
+ val &= ~(1 << 10);
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+}
+
+static void EnterSelfRefresh(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+ /* Program F2x[1, 0]90[EnterSelfRefresh]=1. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ val |= 1 << EnterSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ val |= 1 << EnterSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[EnterSelfRefresh]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ } while (val & (1 <<EnterSelfRef));
+ if (DCT1Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ } while (val & (1 <<EnterSelfRef));
+}
+
+/*
+ * Change memclk for write levelization pass 2
+ */
+static void ChangeMemClk(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ 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);
+ }
+
+ /* 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);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= ~(1 << MemClkFreqVal);
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* Program F2x[1, 0]94[MemClkFreq] to specify the target MEMCLK frequency. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ val &= 0xFFFFFFF8;
+ val |= pDCTstat->TargetFreq - 1;
+ Set_NB32(pDCTstat->dev_dct, 0x94, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val &= 0xFFFFFFF8;
+ val |= pDCTstat->TargetFreq - 1;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* 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);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100);
+ val |= 1 << MemClkFreqVal;
+ Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val);
+ }
+
+ /* Wait until F2x[1, 0]94[FreqChgInProg]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x94);
+ } while (val & (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);
+ }
+}
+
+/* Multiply the previously saved delay values in Pass 1, step #5 by
+ (target frequency)/400 to find the gross and fine delay initialization
+ values at the target frequency.
+ */
+void MultiplyDelay(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat, u8 dct)
+{
+ u16 index;
+ u8 Multiplier;
+ u8 gross, fine;
+ u16 total;
+
+ Multiplier = pDCTstat->TargetFreq;
+
+ for (index=0; index < MAX_BYTE_LANES*MAX_LDIMMS; index ++) {
+ gross = pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index];
+ fine = pDCTstat->C_DCTPtr[dct]->WLFineDelay[index];
+
+ total = gross << 5 | fine;
+ total *= Multiplier;
+ if (total % 3)
+ total = total / 3 + 1;
+ else
+ total = total / 3;
+ pDCTstat->C_DCTPtr[dct]->WLGrossDelay[index] = (total & 0xFF) >> 5;
+ pDCTstat->C_DCTPtr[dct]->WLFineDelay[index] = total & 0x1F;
+ }
+}
+
+/*
+ * the DRAM controller to bring the DRAMs out of self refresh mode.
+ */
+static void ExitSelfRefresh(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ u8 DCT0Present, DCT1Present;
+ u32 val;
+
+ DCT0Present = pDCTstat->DIMMValidDCT[0];
+ if (pDCTstat->GangedMode)
+ DCT1Present = 0;
+ else
+ DCT1Present = pDCTstat->DIMMValidDCT[1];
+
+ /* Program F2x[1, 0]90[ExitSelfRef]=1 for both DCTs. */
+ if (DCT0Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ val |= 1 << ExitSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90, val);
+ }
+ if (DCT1Present) {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ val |= 1 << ExitSelfRef;
+ Set_NB32(pDCTstat->dev_dct, 0x90 + 0x100, val);
+ }
+ /* Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0. */
+ if (DCT0Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90);
+ } while (val & (1 << ExitSelfRef));
+ if (DCT1Present)
+ do {
+ val = Get_NB32(pDCTstat->dev_dct, 0x90 + 0x100);
+ } while (val & (1 << ExitSelfRef));
+}
+
+void SetTargetFreq(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ /* Program F2x[1,0]90[EnterSelfRefresh]=1.
+ * Wait until the hardware resets F2x[1,0]90[EnterSelfRefresh]=0.
+ */
+ EnterSelfRefresh(pMCTstat, pDCTstat);
+
+ /*
+ * Program F2x[1,0]9C_x08[DisAutoComp]=1
+ * Program F2x[1,0]94[MemClkFreqVal] = 0.
+ * Program F2x[1,0]94[MemClkFreq] to specify the target MEMCLK frequency.
+ * Program F2x[1,0]94[MemClkFreqVal] = 1.
+ * Wait until F2x[1,0]94[FreqChgInProg]=0.
+ * Program F2x[1,0]9C_x08[DisAutoComp]=0
+ */
+ ChangeMemClk(pMCTstat, pDCTstat);
+
+ /* Program F2x[1,0]90[ExitSelfRef]=1 for both DCTs.
+ * Wait until the hardware resets F2x[1, 0]90[ExitSelfRef]=0.
+ */
+ ExitSelfRefresh(pMCTstat, pDCTstat);
+
+ /* wait for 500 MCLKs after ExitSelfRef, 500*2.5ns=1250ns */
+ mct_Wait(250);
+
+ if (pDCTstat->Status & (1 << SB_Registered)) {
+ /* TODO: Assuming the dct==0. The agesa here is confusing. */
+ FreqChgCtrlWrd(pMCTstat, pDCTstat);
+ }
+}
+
+static void Modify_OnDimmMirror(struct DCTStatStruc *pDCTstat, u8 dct, u8 set)
+{
+ u32 val;
+ u32 reg_off = dct * 0x100 + 0x44;
+ while (reg_off < 0x60) {
+ val = Get_NB32(pDCTstat->dev_dct, reg_off);
+ if (val & (1 << CSEnable))
+ set ? (val |= 1 << onDimmMirror) : (val &= ~(1<<onDimmMirror));
+ Set_NB32(pDCTstat->dev_dct, reg_off, val);
+ reg_off += 8;
+ }
+}
+
+void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
+ if (pDCTstat->MirrPresU_NumRegR & 0x55)
+ Modify_OnDimmMirror(pDCTstat, 0, 1); /* dct=0, set */
+ if (pDCTstat->MirrPresU_NumRegR & 0xAA)
+ Modify_OnDimmMirror(pDCTstat, 1, 1); /* dct=1, set */
+ }
+}
+void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat,
+ struct DCTStatStruc *pDCTstat)
+{
+ if (pDCTstat->LogicalCPUID & (AMD_DR_Bx /* | AMD_RB_C0 */)) { /* We dont support RB-C0 now */
+ if (pDCTstat->MirrPresU_NumRegR & 0x55)
+ Modify_OnDimmMirror(pDCTstat, 0, 0); /* dct=0, clear */
+ if (pDCTstat->MirrPresU_NumRegR & 0xAA)
+ Modify_OnDimmMirror(pDCTstat, 1, 0); /* dct=1, clear */
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
new file mode 100644
index 0000000000..fdc35e3bfb
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mhwlc_d.c
@@ -0,0 +1,916 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * MODULES USED
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+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);
+/*
+ *-----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+/*-----------------------------------------------------------------------------
+ * void AgesaHwWlPhase1(SPDStruct *SPDData,MCTStruct *MCTData, DCTStruct *DCTData,
+ * u8 Dimm, u8 Pass)
+ *
+ * Description:
+ * This function initialized Hardware based write levelization phase 1
+ *
+ * Parameters:
+ * IN OUT *SPDData - Pointer to buffer with information about each DIMMs
+ * SPD information
+ * *MCTData - Pointer to buffer with runtime parameters,
+ * *DCTData - Pointer to buffer with information about each DCT
+ *
+ * IN DIMM - Logical DIMM number
+ * Pass - First or Second Pass
+ * OUT
+ *-----------------------------------------------------------------------------
+ */
+void AgesaHwWlPhase1(sMCTStruct *pMCTData, sDCTStruct *pDCTData,
+ u8 dimm, u8 pass)
+{
+ u8 ByteLane;
+ u32 Value, Addr;
+ u16 Addl_Data_Offset, Addl_Data_Port;
+
+ 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,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, TrDimmSelStart,
+ TrDimmSelEnd,(u32)dimm);
+ /* 2. Prepare the DIMMs for write levelization using DDR3-defined
+ * MR commands. */
+ prepareDimms(pMCTData, pDCTData,dimm, TRUE);
+ /* 3. After the DIMMs are configured, BIOS waits 40 MEMCLKs to
+ * satisfy DDR3-defined internal DRAM timing.
+ */
+ pMCTData->AgesaDelay(40);
+ /* 4. Configure the processor's DDR phy for write levelization training: */
+ procConifg(pMCTData,pDCTData, dimm, pass);
+ /* 5. Begin write levelization training:
+ * Program F2x[1, 0]9C_x08[WrtLevelTrEn]=1. */
+ if (pDCTData->LogicalCPUID & AMD_DR_Cx)
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrtLvTrEn, WrtLvTrEn, 1);
+ else
+ {
+ /* Broadcast write to all D3Dbyte chiplet register offset 0xc
+ * Set bit 0 (wrTrain)
+ * Program bit 4 to nibble being trained (only matters for x4dimms)
+ * retain value of 3:2 (Trdimmsel)
+ * reset bit 5 (FrzPR)
+ */
+ if (pDCTData->DctTrain)
+ {
+ Addl_Data_Offset=0x198;
+ Addl_Data_Port=0x19C;
+ }
+ else
+ {
+ Addl_Data_Offset=0x98;
+ Addl_Data_Port=0x9C;
+ }
+ Addr=0x0D00000C;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Value = bitTestSet(Value, 0); /* enable WL training */
+ Value = bitTestReset(Value, 4); /* for x8 only */
+ Value = bitTestReset(Value, 5); /* for harward WL training */
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Addr=0x4D030F0C;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
+ }
+
+ /* Wait 200 MEMCLKs. If executing pass 2, wait 32 MEMCLKs. */
+ pMCTData->AgesaDelay(140);
+ /* Program F2x[1, 0]9C_x08[WrtLevelTrEn]=0. */
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, 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++;
+ }
+
+ /* 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,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, WrLvOdtEn, WrLvOdtEn, 0);
+
+ /* Wait 10 MEMCLKs to allow for ODT signal settling. */
+ pMCTData->AgesaDelay(10);
+
+ /* 7. Program the target DIMM back to normal operation by configuring
+ * the following (See section 2.8.5.4.1.1
+ * [Phy Assisted Write Levelization] on page 97 pass 1, step #2):
+ * Configure all ranks of the target DIMM for normal operation.
+ * Enable the output drivers of all ranks of the target DIMM.
+ * For a two DIMM system, program the Rtt value for the target DIMM
+ * to the normal operating termination:
+ */
+ prepareDimms(pMCTData, pDCTData,dimm,FALSE);
+}
+
+/*----------------------------------------------------------------------------
+ * LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*-----------------------------------------------------------------------------
+ * u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+ *
+ * Description:
+ * This function swaps the bits in MSR register value
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN u32: MRS value
+ * OUT u32: sWAPPED BANK BITS
+ *
+ * ----------------------------------------------------------------------------
+ */
+u32 swapAddrBits_wl(sDCTStruct *pDCTData, u32 MRSValue)
+{
+ u32 tempW, tempW1;
+
+ tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
+ if (tempW1 & 1)
+ {
+ if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
+ {
+ /* swap A3/A4,A5/A6,A7/A8 */
+ tempW = MRSValue;
+ tempW1 = MRSValue;
+ tempW &= 0x0A8;
+ tempW1 &= 0x0150;
+ MRSValue &= 0xFE07;
+ MRSValue |= (tempW<<1);
+ MRSValue |= (tempW1>>1);
+ }
+ }
+ return MRSValue;
+}
+
+/*-----------------------------------------------------------------------------
+ * u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
+ *
+ * Description:
+ * This function swaps the bits in MSR register value
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN u32: MRS value
+ * OUT u32: sWAPPED BANK BITS
+ *
+ * ----------------------------------------------------------------------------
+ */
+u32 swapBankBits(sDCTStruct *pDCTData, u32 MRSValue)
+{
+ u32 tempW, tempW1;
+
+ tempW1 = get_Bits(pDCTData, pDCTData->CurrDct, pDCTData->NodeId,
+ FUN_DCT, DRAM_INIT, MrsChipSelStart, MrsChipSelEnd);
+ if (tempW1 & 1)
+ {
+ if ((pDCTData->Status[DCT_STATUS_OnDimmMirror]))
+ {
+ /* swap BA0/BA1 */
+ tempW = MRSValue;
+ tempW1 = MRSValue;
+ tempW &= 0x01;
+ tempW1 &= 0x02;
+ MRSValue = 0;
+ MRSValue |= (tempW<<1);
+ MRSValue |= (tempW1>>1);
+ }
+ }
+ return MRSValue;
+}
+
+/*-----------------------------------------------------------------------------
+ * void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *DCTData, u8 Dimm, BOOL WL)
+ *
+ * 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
+ *
+ * ----------------------------------------------------------------------------
+ */
+void prepareDimms(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl)
+{
+ u32 tempW, tempW1, tempW2, MrsBank;
+ u8 rank, currDimm, MemClkFreq;
+
+ MemClkFreq = get_Bits(pDCTData, pDCTData->CurrDct, 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.
+ */
+ rank = 0;
+ 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);
+ /* 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);
+ /* 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;
+ }
+
+ /* determine Rtt_Nom for WL & Normal mode */
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ tempW1 = RttNomTargetRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+ } else {
+ if (wl)
+ {
+ if (pDCTData->MaxDimmsInstalled == 1)
+ {
+ if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0))
+ {
+ tempW1 = 0x00; /* Rtt_Nom=OFF */
+ }
+ else
+ {
+ tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+ }
+ }
+ else /* 2 Dimms or more per channel */
+ {
+ if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1))
+ {
+ tempW1 = 0x00; /* Rtt_Nom=OFF */
+ }
+ else
+ {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+ } else {
+ tempW1 = 0x40;/* Rtt_Nom=RZQ/2=120 Ohm */
+ }
+ }
+ }
+ }
+ else { /* 1 or 4 Dimms per channel */
+ if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->MaxDimmsInstalled == 4))
+ {
+ tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+ }
+ else /* 2 or 3 Dimms per channel */
+ {
+ if (MemClkFreq < 5) {
+ tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
+ } else {
+ tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
+ }
+ }
+ }
+ }
+ tempW=tempW|tempW1;
+
+ /* All ranks of the target DIMM are set to write levelization mode. */
+ if (wl)
+ {
+ tempW1 = bitTestSet(tempW, MRS_Level);
+ if (rank == 0)
+ {
+ /* ?Enable the output driver of the first rank of the target DIMM. */
+ tempW = tempW1;
+ }
+ else
+ {
+ /* Disable the output drivers of all other ranks for
+ * the target DIMM. */
+ 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);
+ 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);
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command to
+ * ;the specified DIMM.
+ */
+ set_Bits(pDCTData, pDCTData->CurrDct, 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,
+ 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);
+ /* 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;
+ /* determine Rtt_WR for WL & Normal mode */
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ tempW1 = RttWrRegDimm(pMCTData, pDCTData, dimm, wl, MemClkFreq, rank);
+ } else {
+ if (wl)
+ {
+ tempW1 = 0x00; /* Rtt_WR=off */
+ }
+ else
+ {
+ if (pDCTData->MaxDimmsInstalled == 1)
+ {
+ tempW1 = 0x00; /* Rtt_WR=off */
+ }
+ else
+ {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
+ } else {
+ tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
+ }
+ }
+ }
+ }
+ tempW=tempW|tempW1;
+ tempW = swapAddrBits_wl(pDCTData,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,
+ 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,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
+ {
+ }
+
+ rank++;
+ }
+
+ /* Configure the non-target DIMM normally. */
+ currDimm = 0;
+ while (currDimm < MAX_LDIMMS)
+ {
+ if (pDCTData->DimmPresent[currDimm])
+ {
+ if (currDimm != dimm)
+ {
+ 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);
+ /* 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);
+ /* 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;
+ }
+
+ /* determine Rtt_Nom for WL & Normal mode */
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ tempW1 = RttNomNonTargetRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+ } else {
+ if (wl)
+ {
+ if ((pDCTData->DimmRanks[currDimm] == 2) && (rank == 1))
+ {
+ tempW1 = 0x00; /* Rtt_Nom=OFF */
+ }
+ else
+ {
+ if (MemClkFreq < 5) {
+ tempW1 = 0x0044;/* Rtt_Nom=RZQ/6=40 Ohm */
+ } else {
+ tempW1 = 0x0204;/* Rtt_Nom=RZQ/8=30 Ohm */
+ }
+ }
+ }
+ else { /* 1 or 4 Dimms per channel */
+ if ((pDCTData->MaxDimmsInstalled == 4))
+ {
+ tempW1 = 0x04; /* Rtt_Nom=RZQ/4=60 Ohm */
+ }
+ else { /* 2 or 3 Dimms per channel */
+ if (MemClkFreq < 5) {
+ tempW1 = 0x0044; /* Rtt_Nom=RZQ/6=40 Ohm */
+ } else {
+ tempW1 = 0x0204; /* Rtt_Nom=RZQ/8=30 Ohm */
+ }
+ }
+ }
+ }
+ 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);
+ 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);
+ /* Program F2x[1, 0]7C[SendMrsCmd]=1 to initiate the command
+ * ; to the specified DIMM.
+ */
+ set_Bits(pDCTData, pDCTData->CurrDct, 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, 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);
+ /* 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;
+ /* determine Rtt_WR for WL & Normal mode */
+ if (pDCTData->Status[DCT_STATUS_REGISTERED]) {
+ tempW1 = RttWrRegDimm(pMCTData, pDCTData, currDimm, wl, MemClkFreq, rank);
+ } else {
+ if (wl)
+ {
+ tempW1 = 0x00; /* Rtt_WR=off */
+ }
+ else
+ {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x200; /* Rtt_WR=RZQ/4=60 Ohm */
+ } else {
+ tempW1 = 0x400; /* Rtt_WR=RZQ/2 */
+ }
+ }
+ }
+ tempW=tempW|tempW1;
+ tempW = swapAddrBits_wl(pDCTData,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,
+ 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,
+ FUN_DCT, DRAM_INIT, SendMrsCmd, SendMrsCmd)) == 0x1)
+ {
+ }
+ rank++;
+ }
+ }
+ }
+ currDimm++;
+ }
+}
+
+/*-----------------------------------------------------------------------------
+ * void programODT(sMCTStruct *pMCTData, DCTStruct *DCTData, u8 dimm)
+ *
+ * Description:
+ * This function programs the ODT values for the NB
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+void programODT(sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+{
+ 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);
+ } else {
+ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, dimm);
+ }
+ } else {
+ WrLvOdt1 = WrLvOdtRegDimm(pMCTData, pDCTData, dimm);
+ }
+
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ DRAM_ADD_DCT_PHY_CONTROL_REG, 8, 11, (u32)WrLvOdt1);
+
+}
+
+/*-----------------------------------------------------------------------------
+ * void procConifg(MCTStruct *MCTData,DCTStruct *DCTData, u8 Dimm, u8 Pass)
+ *
+ * Description:
+ * This function programs the ODT values for the NB
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * *MCTData - Pointer to buffer with runtime parameters,
+ * IN Dimm - Logical DIMM
+ * Pass - First of Second Pass
+ * OUT
+ * ----------------------------------------------------------------------------
+ */
+void procConifg(sMCTStruct *pMCTData,sDCTStruct *pDCTData, u8 dimm, u8 pass)
+{
+ u8 ByteLane, Seed_Gross, Seed_Fine;
+ u32 Value, Addr;
+ u16 Addl_Data_Offset, Addl_Data_Port;
+
+ /* Program F2x[1, 0]9C_x08[WrLvOdt[3:0]] to the proper ODT settings for the
+ * ;current memory subsystem configuration.
+ */
+ programODT(pMCTData, pDCTData, dimm);
+
+ /* Program F2x[1,0]9C_x08[WrLvOdtEn]=1 */
+ if (pDCTData->LogicalCPUID & AMD_DR_Cx)
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, 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)
+ {
+ Addl_Data_Offset=0x198;
+ Addl_Data_Port=0x19C;
+ }
+ else
+ {
+ Addl_Data_Offset=0x98;
+ Addl_Data_Port=0x9C;
+ }
+ Addr=0x0D008000;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Value = bitTestSet(Value, 12);
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Port), 31, 0, &Value);
+ Addr=0x4D088F00;
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+(pDCTData->NodeId),FUN_DCT,Addl_Data_Offset), 31, 0, &Addr);
+ while ((get_Bits(pDCTData,FUN_DCT,pDCTData->NodeId, FUN_DCT, Addl_Data_Offset,
+ DctAccessDone, DctAccessDone)) == 0);
+ }
+
+ /* Wait 10 MEMCLKs to allow for ODT signal settling. */
+ pMCTData->AgesaDelay(10);
+ ByteLane = 0;
+ if (pass == 1)
+ {
+ if (pDCTData->Status[DCT_STATUS_REGISTERED])
+ {
+ if(pDCTData->RegMan1Present & ((1<<(dimm*2+pDCTData->DctTrain))))
+ {
+ Seed_Gross = 0x02;
+ Seed_Fine = 0x16;
+ }
+ else
+ {
+ Seed_Gross = 0x02;
+ Seed_Fine = 0x00;
+ }
+ }
+ else
+ {
+ Seed_Gross = 0x00;
+ Seed_Fine = 0x1A;
+ }
+ while(ByteLane < MAX_BYTE_LANES)
+ {
+ /* Program an initialization value to registers F2x[1, 0]9C_x[51:50] and
+ * ;F2x[1, 0]9C_x52 to set the gross and fine delay for all the byte lane fields
+ * ; If the target frequency is different than 400MHz, BIOS must
+ * execute two training passes for each DIMM.
+ * For pass 1 at a 400MHz MEMCLK frequency, use an initial total delay value
+ * ; of 01Fh. This represents a 1UI (UI=.5MEMCLK) delay and is determined
+ * ;by design.
+ */
+ pDCTData->WLGrossDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Gross;
+ pDCTData->WLFineDelay[MAX_BYTE_LANES*dimm+ByteLane] = Seed_Fine;
+ ByteLane++;
+ }
+ }
+ setWLByteDelay(pDCTData, ByteLane, dimm, 0);
+}
+
+/*-----------------------------------------------------------------------------
+ * void setWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm){
+ *
+ * Description:
+ * This function writes the write levelization byte delay for the Phase
+ * Recovery control registers
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN Dimm - Dimm Number
+ * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for each
+ * logical DIMM
+ * DCTData->WLFineDelay[index+ByteLane] - fine write delay for each
+ * logical DIMM
+ * ByteLane - target byte lane to write
+ * targetAddr - 0: write to DRAM phase recovery control register
+ * 1: write to DQS write register
+ * OUT
+ *
+ *-----------------------------------------------------------------------------
+ */
+void setWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm, u8 targetAddr)
+{
+ u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, index, offsetAddr;
+ u32 addr, fineDelayValue, grossDelayValue, ValueLow, ValueHigh, EccValue, tempW;
+
+ if (targetAddr == 0)
+ {
+ index = (u8)(MAX_BYTE_LANES * dimm);
+ ValueLow = 0;
+ ValueHigh = 0;
+ ByteLane = 0;
+ 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;
+ }
+ fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
+ if (ByteLane < 4)
+ ValueLow |= ((grossDelayValue << 5) | fineDelayValue) << 8*ByteLane;
+ else if(ByteLane < 8)
+ ValueHigh |= ((grossDelayValue << 5) | fineDelayValue) << 8*(ByteLane-4);
+ else
+ EccValue = ((grossDelayValue << 5) | fineDelayValue);
+ ByteLane++;
+ }
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, 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,
+ DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH, 0, 31, (u32)ValueHigh);
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ DRAM_CONT_ADD_ECC_PHASE_REC_CTRL, 0, 31, (u32)EccValue);
+ }
+ else
+ {
+ index = (u8)(MAX_BYTE_LANES * dimm);
+ grossDelayValue = pDCTData->WLGrossDelay[index+ByteLane];
+ fineDelayValue = pDCTData->WLFineDelay[index+ByteLane];
+
+ tempB = 0;
+ offsetAddr = (u8)(3 * dimm);
+ if (ByteLane < 2)
+ {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01;
+ }
+ else if (ByteLane <4)
+ {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 1;
+ }
+ else if (ByteLane <6)
+ {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45;
+ }
+ else if (ByteLane <8)
+ {
+ tempB = (u8)(16 * ByteLane);
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 + 1;
+ }
+ else
+ {
+ tempB = 0;
+ addr = DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 + 2;
+ }
+ addr += offsetAddr;
+
+ fineStartLoc = (u8)(tempB % 32);
+ fineEndLoc = (u8)(fineStartLoc + 4);
+ grossStartLoc = (u8)(fineEndLoc + 1);
+ grossEndLoc = (u8)(grossStartLoc + 1);
+
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ (u16)addr, fineStartLoc, fineEndLoc,(u32)fineDelayValue);
+ set_DCT_ADDR_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId, FUN_DCT,
+ (u16)addr, grossStartLoc, grossEndLoc, (u32)grossDelayValue);
+ }
+
+}
+
+/*-----------------------------------------------------------------------------
+ * void getWLByteDelay(DCTStruct *DCTData, u8 ByteLane, u8 Dimm)
+ *
+ * Description:
+ * This function reads the write levelization byte delay from the Phase
+ * Recovery control registers
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN Dimm - Dimm Number
+ * ByteLane - target byte lane to read
+ * OUT
+ * DCTData->WLGrossDelay[index+ByteLane] - gross write delay for current
+ * byte for logical DIMM
+ * DCTData->WLFineDelay[index+ByteLane] - fine write delay for current
+ * byte for logical DIMM
+ *
+ *-----------------------------------------------------------------------------
+ */
+void getWLByteDelay(sDCTStruct *pDCTData, u8 ByteLane, u8 dimm)
+{
+ u8 fineStartLoc, fineEndLoc, grossStartLoc, grossEndLoc, tempB, tempB1, index;
+ u32 addr, fine, gross;
+ tempB = 0;
+ index = (u8)(MAX_BYTE_LANES*dimm);
+ if (ByteLane < 4)
+ {
+ tempB = (u8)(8 * ByteLane);
+ addr = DRAM_CONT_ADD_PHASE_REC_CTRL_LOW;
+ }
+ else if (ByteLane < 8)
+ {
+ tempB1 = (u8)(ByteLane - 4);
+ tempB = (u8)(8 * tempB1);
+ addr = DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH;
+ }
+ else
+ {
+ tempB = 0;
+ addr = DRAM_CONT_ADD_ECC_PHASE_REC_CTRL;
+ }
+ fineStartLoc = tempB;
+ fineEndLoc = (u8)(fineStartLoc + 4);
+ grossStartLoc = (u8)(fineEndLoc + 1);
+ grossEndLoc = (u8)(grossStartLoc + 1);
+
+ fine = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, pDCTData->NodeId,
+ FUN_DCT, (u16)addr, fineStartLoc, fineEndLoc);
+ gross = get_ADD_DCT_Bits(pDCTData, pDCTData->DctTrain, 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.
+ */
+ 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/modtrdim.c b/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c
new file mode 100644
index 0000000000..e75b549fa9
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/modtrdim.c
@@ -0,0 +1,270 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/* This file contains functions for odt setting on registered DDR3 dimms */
+
+/*
+ *-----------------------------------------------------------------------------
+ * MODULES USED
+ *
+ *-----------------------------------------------------------------------------
+ */
+/*----------------------------------------------------------------------------
+ * PROTOTYPES OF LOCAL FUNCTIONS
+ *
+ *----------------------------------------------------------------------------
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *-----------------------------------------------------------------------------
+ */
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function set Rtt_Nom for registered DDR3 dimms on targeted dimm.
+ *
+ * @param[in] *pDCTData - Pointer to buffer with information about each DCT
+ * dimm - targeted dimm
+ * wl - current mode, either write levelization mode or normal mode
+ * MemClkFreq - current frequency
+ *
+ * @return tempW1 - Rtt_Nom
+ */
+static u32 RttNomTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl, u8 MemClkFreq, u8 rank)
+{
+ u32 tempW1;
+ tempW1 = 0;
+ if (wl) {
+ switch (pMCTData->PlatMaxDimmsDct) {
+ case 2:
+ /* 2 dimms per channel */
+ if (pDCTData->MaxDimmsInstalled == 1) {
+ if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 0)) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF */
+ } else if (pDCTData->DimmRanks[dimm] == 4) {
+ if (rank == 1) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF on second and forth rank of QR dimm */
+ } else {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ } else {
+ tempW1 = 0x40; /* Rtt_Nom = 120 ohms */
+ }
+ }
+ } else {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ }
+ } else if (pDCTData->MaxDimmsInstalled == 2) {
+ if (((pDCTData->DimmRanks[dimm] == 2) || (pDCTData->DimmRanks[dimm] == 4)) && (rank == 1)) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF */
+ } else if ((pDCTData->DimmRanks[dimm] == 4) || (pDCTData->DctCSPresent & 0xF0)) {
+ if (MemClkFreq == 3) {
+ tempW1 = 0x40; /* Rtt_Nom = 120 ohms */
+ } else {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ }
+ } else {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ } else {
+ tempW1 = 0x40; /* Rtt_Nom = 120 ohms */
+ }
+ }
+ }
+ break;
+ case 3:
+ /* 3 dimms per channel */
+ /* QR not supported in this case on L1 package. */
+ if (pDCTData->MaxDimmsInstalled == 1) {
+ if ((pDCTData->DimmRanks[dimm] == 2) && (rank == 1)) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF */
+ } else {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ }
+ } else {
+ tempW1 = 0x40; /* Rtt_Nom = 120 ohms */
+ }
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ } else {
+ switch (pMCTData->PlatMaxDimmsDct) {
+ case 2:
+ /* 2 dimms per channel */
+ if ((pDCTData->DimmRanks[dimm] == 4) && (rank == 1)) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF */
+ } else if ((pDCTData->MaxDimmsInstalled == 1) || (pDCTData->DimmRanks[dimm] == 4)) {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ } else {
+ if (pDCTData->DctCSPresent & 0xF0) {
+ tempW1 = 0x0204; /* Rtt_Nom = 30 ohms */
+ } else {
+ if (MemClkFreq < 5) {
+ tempW1 = 0x44; /* Rtt_Nom = 40 ohms */
+ } else {
+ tempW1 = 0x0204; /* Rtt_Nom = 30 ohms */
+ }
+ }
+ }
+ break;
+ case 3:
+ /* 3 dimms per channel */
+ /* L1 package does not support QR dimms this case. */
+ if (rank == 1) {
+ tempW1 = 0x00; /* Rtt_Nom = OFF */
+ } else if (pDCTData->MaxDimmsInstalled == 1) {
+ tempW1 = 0x04; /* Rtt_Nom = 60 ohms */
+ } else if ((MemClkFreq < 5) || (pDCTData->MaxDimmsInstalled == 3)) {
+ tempW1 = 0x44; /* Rtt_Nom = 40 ohms */
+ } else {
+ tempW1 = 0x0204; /* Rtt_Nom = 30 ohms */
+ }
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ }
+ return tempW1;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function set Rtt_Nom for registered DDR3 dimms on non-targeted dimm.
+ *
+ * @param[in] *pDCTData - Pointer to buffer with information about each DCT
+ * dimm - non-targeted dimm
+ * wl - current mode, either write levelization mode or normal mode
+ * MemClkFreq - current frequency
+ *
+ * @return tempW1 - Rtt_Nom
+ */
+static u32 RttNomNonTargetRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl, u8 MemClkFreq, u8 rank)
+{
+ if ((wl) && (pMCTData->PlatMaxDimmsDct == 2) && (pDCTData->DimmRanks[dimm] == 2) && (rank == 1)) {
+ return 0x00; /* for non-target dimm during WL, the second rank of a DR dimm need to have Rtt_Nom = OFF */
+ } else {
+ return RttNomTargetRegDimm (pMCTData, pDCTData, dimm, FALSE, MemClkFreq, rank); /* otherwise, the same as target dimm in normal mode. */
+ }
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function set Rtt_Wr for registered DDR3 dimms.
+ *
+ * @param[in] *pDCTData - Pointer to buffer with information about each DCT
+ * dimm - targeted dimm
+ * wl - current mode, either write levelization mode or normal mode
+ * MemClkFreq - current frequency
+ *
+ * @return tempW1 - Rtt_Wr
+ */
+static u32 RttWrRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm, BOOL wl, u8 MemClkFreq, u8 rank)
+{
+ u32 tempW1;
+ tempW1 = 0;
+ if (wl) {
+ tempW1 = 0x00; /* Rtt_WR = OFF */
+ } else {
+ switch (pMCTData->PlatMaxDimmsDct) {
+ case 2:
+ if (pDCTData->MaxDimmsInstalled == 1) {
+ if (pDCTData->DimmRanks[dimm] != 4) {
+ tempW1 = 0x00;
+ } else {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x200; /* Rtt_WR = 60 ohms */
+ } else {
+ tempW1 = 0x400; /* Rtt_WR = 120 ohms */
+ }
+ }
+ } else {
+ if ((pDCTData->DimmRanks[dimm] == 4) || (pDCTData->DctCSPresent & 0xF0)) {
+ if (MemClkFreq == 3) {
+ tempW1 = 0x400; /* Rtt_WR = 120 ohms */
+ } else {
+ tempW1 = 0x200; /* Rtt_WR = 60 ohms */
+ }
+ } else {
+ if (MemClkFreq == 6) {
+ tempW1 = 0x200; /* Rtt_WR = 60 ohms */
+ } else {
+ tempW1 = 0x400; /* Rtt_Nom = 120 ohms */
+ }
+ }
+ }
+ break;
+ case 3:
+ if (pDCTData->MaxDimmsInstalled == 1) {
+ tempW1 = 0x00; /* Rtt_WR = OFF */
+ } else {
+ tempW1 = 0x400; /* Rtt_Nom = 120 ohms */
+ }
+ break;
+ default:
+ ASSERT (FALSE);
+ }
+ }
+ return tempW1;
+}
+
+/* -----------------------------------------------------------------------------*/
+/**
+ *
+ *
+ * This function set WrLvOdt for registered DDR3 dimms.
+ *
+ * @param[in] *pDCTData - Pointer to buffer with information about each DCT
+ * dimm - targeted dimm
+ *
+ * @return WrLvOdt
+ */
+static u8 WrLvOdtRegDimm (sMCTStruct *pMCTData, sDCTStruct *pDCTData, u8 dimm)
+{
+ u8 WrLvOdt1, i;
+ WrLvOdt1 = 0;
+ i = 0;
+ while (i < 8) {
+ if (pDCTData->DctCSPresent & (1 << i)) {
+ WrLvOdt1 = (u8)bitTestSet(WrLvOdt1, i/2);
+ }
+ i += 2;
+ }
+ if (pMCTData->PlatMaxDimmsDct == 2) {
+ if ((pDCTData->DimmRanks[dimm] == 4) && (pDCTData->MaxDimmsInstalled != 1)) {
+ if (dimm >= 2) {
+ WrLvOdt1 = (u8)bitTestReset (WrLvOdt1, (dimm - 2));
+ } else {
+ WrLvOdt1 = (u8)bitTestReset (WrLvOdt1, (dimm + 2));
+ }
+ } else if ((pDCTData->DimmRanks[dimm] == 2) && (pDCTData->MaxDimmsInstalled == 1)) {
+ /* the case for one DR on a 2 dimms per channel is special */
+ WrLvOdt1 = 0x8;
+ }
+ }
+ return WrLvOdt1;
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mport_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mport_d.c
new file mode 100644
index 0000000000..a1132ff7bb
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mport_d.c
@@ -0,0 +1,39 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+static void AmdMemPCIRead(SBDFO loc, u32 *Value)
+{
+ /* Convert SBDFO into a CF8 Address */
+ loc = (loc >> 4 & 0xFFFFFF00) | (loc & 0xFF) | ((loc & 0xF00) << 16) ;
+ loc |= 0x80000000;
+
+ outl(loc, 0xCF8);
+
+ *Value = inl(0xCFC);
+}
+
+static void AmdMemPCIWrite(SBDFO loc, u32 *Value)
+{
+ /* Convert SBDFO into a CF8 Address */
+ loc = (loc >> 4 & 0xFFFFFF00) | (loc & 0xFF) | ((loc & 0xF00) << 16) ;
+ loc |= 0x80000000;
+
+ outl(loc, 0xCF8);
+ outl(*Value, 0xCFC);
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
new file mode 100644
index 0000000000..b153eef71e
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mutilc_d.c
@@ -0,0 +1,328 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+
+/* This file contains functions for common utility functions */
+
+/*
+ *-----------------------------------------------------------------------------
+ * MODULES USED
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+/*
+ *-----------------------------------------------------------------------------
+ * EXPORTED FUNCTIONS
+ *
+ *-----------------------------------------------------------------------------
+ */
+
+static void AmdMemPCIReadBits(SBDFO loc, u8 highbit, u8 lowbit, u32 *pValue)
+{
+ /* ASSERT(highbit < 32 && lowbit < 32 && highbit >= lowbit && (loc & 3) == 0); */
+
+ AmdMemPCIRead(loc, pValue);
+ *pValue = *pValue >> lowbit; /* Shift */
+
+ /* A 1<<32 == 1<<0 due to x86 SHL instruction, so skip if that is the case */
+ if ((highbit-lowbit) != 31)
+ *pValue &= (((u32)1 << (highbit-lowbit+1))-1);
+}
+
+static void AmdMemPCIWriteBits(SBDFO loc, u8 highbit, u8 lowbit, u32 *pValue)
+{
+ u32 temp, mask;
+
+ /* ASSERT(highbit < 32 && lowbit < 32 && highbit >= lowbit && (loc & 3) == 0); */
+
+ /* A 1<<32 == 1<<0 due to x86 SHL instruction, so skip if that is the case */
+ if ((highbit-lowbit) != 31)
+ mask = (((u32)1 << (highbit-lowbit+1))-1);
+ else
+ mask = (u32)0xFFFFFFFF;
+
+ AmdMemPCIRead(loc, &temp);
+ temp &= ~(mask << lowbit);
+ temp |= (*pValue & mask) << lowbit;
+ AmdMemPCIWrite(loc, &temp);
+}
+
+/*-----------------------------------------------------------------------------
+ * uint 32
+ * u32 bitTestSet(u32 csMask,u32 tempD)
+ *
+ * Description:
+ * This routine sets a bit in a u32
+ *
+ * Parameters:
+ * IN csMask = Target value in which the bit will be set
+ * IN tempD = Bit that will be set
+ * OUT value = Target value with the bit set
+ *-----------------------------------------------------------------------------
+ */
+static u32 bitTestSet(u32 csMask,u32 tempD)
+{
+ u32 localTemp;
+ /* ASSERT(tempD < 32); */
+ localTemp = 1;
+ csMask |= localTemp << tempD;
+ return csMask;
+}
+
+/*-----------------------------------------------------------------------------
+ * uint 32
+ * u32 bitTestReset(u32 csMask,u32 tempD)
+ *
+ * Description:
+ * This routine re-sets a bit in a u32
+ *
+ * Parameters:
+ * IN csMask = Target value in which the bit will be re-set
+ * IN tempD = Bit that will be re-set
+ * OUT value = Target value with the bit re-set
+ *-----------------------------------------------------------------------------
+ */
+static u32 bitTestReset(u32 csMask,u32 tempD)
+{
+ u32 temp, localTemp;
+ /* ASSERT(tempD < 32); */
+ localTemp = 1;
+ temp = localTemp << tempD;
+ temp = ~temp;
+ csMask &= temp;
+ return csMask;
+}
+
+/*-----------------------------------------------------------------------------
+ * uint 32
+ * u32 get_Bits(DCTStruct *DCTData, u8 DCT, u8 Node, u8 func, u16 offset,
+ * u8 low, u8 high)
+ *
+ * Description:
+ * This routine Gets the PCT bits from the specidfied Node, DCT and PCI address
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN DCT - DCT number
+ * - 1 indicates DCT 1
+ * - 0 indicates DCT 0
+ * - 2 both DCTs
+ * Node - Node number
+ * Func - PCI Function number
+ * Offset - PCI register number
+ * Low - Low bit of the bit field
+ * High - High bit of the bit field
+ *
+ * OUT value = Value read from PCI space
+ *-----------------------------------------------------------------------------
+ */
+static u32 get_Bits(sDCTStruct *pDCTData,
+ u8 dct, u8 node, u8 func,
+ u16 offset, u8 low, u8 high)
+{
+ u32 temp;
+ /* ASSERT(node < MAX_NODES); */
+ if (dct == BOTH_DCTS)
+ {
+ /* Registers exist on DCT0 only */
+ AmdMemPCIReadBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+ if (dct == 1)
+ {
+ /* Write to dct 1 */
+ offset += 0x100;
+ 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);
+ }
+ }
+ return temp;
+}
+
+/*-----------------------------------------------------------------------------
+ * uint 32
+ * void set_Bits(DCTStruct *DCTData,u8 DCT,u8 Node,u8 func, u16 offset,
+ * u8 low, u8 high, u32 value)
+ *
+ * Description:
+ * This routine Sets the PCT bits from the specidfied Node, DCT and PCI address
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN DCT - DCT number
+ * - 1 indicates DCT 1
+ * - 0 indicates DCT 0
+ * - 2 both DCTs
+ * Node - Node number
+ * Func - PCI Function number
+ * Offset - PCI register number
+ * Low - Low bit of the bit field
+ * High - High bit of the bit field
+ *
+ * OUT
+ *-----------------------------------------------------------------------------
+ */
+static void set_Bits(sDCTStruct *pDCTData,
+ u8 dct, u8 node, u8 func,
+ u16 offset, u8 low, u8 high, u32 value)
+{
+ u32 temp;
+ temp = value;
+
+ if (dct == BOTH_DCTS)
+ {
+ /* Registers exist on DCT0 only */
+ AmdMemPCIWriteBits(MAKE_SBDFO(0,0,24+node,func,offset), high, low, &temp);
+ }
+ else
+ {
+ 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);
+ }
+ }
+}
+
+/*-------------------------------------------------
+ * uint 32
+ * u32 get_ADD_DCT_Bits(DCTStruct *DCTData,u8 DCT,u8 Node,u8 func,
+ * u16 offset,u8 low, u8 high)
+ *
+ * Description:
+ * This routine gets the Additional PCT register from Function 2 by specidfied
+ * Node, DCT and PCI address
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN DCT - DCT number
+ * - 1 indicates DCT 1
+ * - 0 indicates DCT 0
+ * - 2 both DCTs
+ * Node - Node number
+ * Func - PCI Function number
+ * Offset - Additional PCI register number
+ * Low - Low bit of the bit field
+ * High - High bit of the bit field
+ *
+ * OUT
+ *-------------------------------------------------
+ */
+static u32 get_ADD_DCT_Bits(sDCTStruct *pDCTData,
+ u8 dct, u8 node, u8 func,
+ u16 offset, u8 low, u8 high)
+{
+ u32 tempD;
+ tempD = offset;
+ tempD = bitTestReset(tempD,DctAccessWrite);
+ set_Bits(pDCTData, dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_OFFSET_REG,
+ PCI_MIN_LOW, PCI_MAX_HIGH, offset);
+ while ((get_Bits(pDCTData,dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_OFFSET_REG,
+ DctAccessDone, DctAccessDone)) == 0);
+ return (get_Bits(pDCTData, dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_PORT_REG,
+ low, high));
+}
+
+/*-------------------------------------------------
+ * uint 32
+ * void set_DCT_ADDR_Bits(DCTStruct *DCTData, u8 DCT,u8 Node,u8 func,
+ * u16 offset,u8 low, u8 high, u32 value)
+ *
+ * Description:
+ * This routine sets the Additional PCT register from Function 2 by specidfied
+ * Node, DCT and PCI address
+ *
+ * Parameters:
+ * IN OUT *DCTData - Pointer to buffer with information about each DCT
+ * IN DCT - DCT number
+ * - 1 indicates DCT 1
+ * - 0 indicates DCT 0
+ * - 2 both DCTs
+ * Node - Node number
+ * Func - PCI Function number
+ * Offset - Additional PCI register number
+ * Low - Low bit of the bit field
+ * High - High bit of the bit field
+ *
+ * OUT
+ *-------------------------------------------------
+ */
+static void set_DCT_ADDR_Bits(sDCTStruct *pDCTData,
+ u8 dct, u8 node, u8 func,
+ u16 offset, u8 low, u8 high, u32 value)
+{
+ u32 tempD;
+
+ set_Bits(pDCTData, dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_OFFSET_REG,
+ PCI_MIN_LOW, PCI_MAX_HIGH, offset);
+ while ((get_Bits(pDCTData,dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_OFFSET_REG,
+ DctAccessDone, DctAccessDone)) == 0);
+
+ set_Bits(pDCTData, dct, node, FUN_DCT, DRAM_CONTROLLER_ADD_DATA_PORT_REG,
+ low, high, value);
+ tempD = offset;
+ tempD = bitTestSet(tempD,DctAccessWrite);
+ set_Bits(pDCTData, dct, node, FUN_DCT,DRAM_CONTROLLER_ADD_DATA_OFFSET_REG,
+ PCI_MIN_LOW, PCI_MAX_HIGH, tempD);
+ while ((get_Bits(pDCTData,dct, pDCTData->NodeId, FUN_DCT,
+ DRAM_CONTROLLER_ADD_DATA_OFFSET_REG, DctAccessDone,
+ DctAccessDone)) == 0);
+}
+
+/*-------------------------------------------------
+ * uint 32
+ * BOOL bitTest(u32 value, u8 bitLoc)
+ *
+ * Description:
+ * This routine tests the value to determine if the bitLoc is set
+ *
+ * Parameters:
+ * IN Value - value to be tested
+ * bitLoc - bit location to be tested
+ * OUT TRUE - bit is set
+ * FALSE - bit is clear
+ *-------------------------------------------------
+ */
+static BOOL bitTest(u32 value, u8 bitLoc)
+{
+ u32 tempD, compD;
+ tempD = value;
+ compD = 0;
+ compD = bitTestSet(compD,bitLoc);
+ tempD &= compD;
+ if (compD == tempD)
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
diff --git a/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
new file mode 100644
index 0000000000..9b20699224
--- /dev/null
+++ b/src/northbridge/amd/amdmct/mct_ddr3/mwlc_d.h
@@ -0,0 +1,139 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2010 Advanced Micro Devices, Inc.
+ *
+ * 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
+ */
+/* IBV defined Structure */ /* IBV Specific Options */
+#ifndef MWLC_D_H
+#define MWLC_D_H
+
+#define MAX_TOTAL_DIMMS 8 /* Maximum Number of DIMMs in systems */
+ /* (DCT0 + DCT1) */
+#define MAX_DIMMS 4 /* Maximum Number of DIMMs on each DCT */
+#define MAX_LDIMMS 4 /* Maximum number of Logial DIMMs per DCT */
+
+/*MCT Max variables */
+#define MAX_ERRORS 32 /* Maximum number of Errors Reported */
+#define MAX_STATUS 32 /* Maximum number of Status variables*/
+#define MAX_BYTE_LANES (8+1) /* Maximum number of Byte Lanes - include ECC */
+
+#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_OnDimmMirror 24 /* OnDimmMirror support */
+
+/* PCI Defintions */
+#define FUN_HT 0 /* Funtion 0 Access */
+#define FUN_MAP 1 /* Funtion 1 Access */
+#define FUN_DCT 2 /* Funtion 2 Access */
+#define FUN_MISC 3 /* Funtion 3 Access */
+#define FUN_ADD_DCT 0xF /* Funtion 2 Additional Register Access */
+#define BOTH_DCTS 2 /* The access is independent of DCTs */
+#define PCI_MIN_LOW 0 /* Lowest possible PCI register location */
+#define PCI_MAX_HIGH 31 /* Highest possible PCI register location */
+
+/*Function 2 */
+/* #define DRAM_INIT 0x7C */
+#define DRAM_MRS_REGISTER 0x84
+#define DRAM_CONFIG_HIGH 0x94
+#define DRAM_CONTROLLER_ADD_DATA_OFFSET_REG 0x98
+#define DRAM_CONTROLLER_ADD_DATA_PORT_REG 0x9C
+
+/*Function 2 Additional DRAM control registers */
+#define DRAM_ADD_DCT_PHY_CONTROL_REG 0x8
+#define DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_01 0x30
+#define DRAM_CONT_ADD_DQS_TIMING_CTRL_BL_45 0x40
+#define DRAM_CONT_ADD_PHASE_REC_CTRL_LOW 0x50
+#define DRAM_CONT_ADD_PHASE_REC_CTRL_HIGH 0x51
+#define DRAM_CONT_ADD_ECC_PHASE_REC_CTRL 0x52
+#define DRAM_CONT_ADD_WRITE_LEV_ERROR_REG 0x53
+
+/* CPU Register defintions */
+
+/* Register Bit Location */
+#define DctAccessDone 31
+#define DctAccessWrite 30
+#define RDqsEn 12
+#define TrDimmSelStart 4
+#define TrDimmSelEnd 5
+#define WrLvTrMode 1
+#define TrNibbleSel 2
+#define WrLvOdtEn 12
+#define WrLvErrStart 0
+#define WrLvErrEnd 8
+#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 ASR 18
+#define SRT 19
+#define DramTermDynStart 10
+#define DramTermDynEnd 11
+#define WrtLvTrMode 1
+#define TrNibbleSel 2
+#define TrDimmSelStart 4
+#define TrDimmSelEnd 5
+#define WrtLvTrEn 0
+#define DrvImpCtrlStart 2
+#define DrvImpCtrlEnd 3
+#define DramTermNbStart 7
+#define DramTermNbEnd 9
+#define onDimmMirror 3
+
+typedef struct _sMCTStruct
+{
+ u8 PlatMaxTotalDimms; /* IBV defined total number of DIMMs */
+ /* on a particular node */
+ u8 PlatMaxDimmsDct; /* IBV defined maximum number of */
+ /* DIMMs on a DCT */
+ void (*AgesaDelay)(u32 delayval); /* IBV defined Delay Function */
+} sMCTStruct;
+
+/* DCT 0 and DCT 1 Data structure */
+typedef struct _sDCTStruct
+{
+ u8 NodeId; /* Node ID */
+ u8 DctTrain; /* Current DCT being trained */
+ u8 CurrDct; /* Current DCT number (0 or 1) */
+ u8 DctCSPresent; /* Current DCT CS mapping */
+ 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*/
+ u16 RegMan1Present;
+ u8 DimmPresent[MAX_TOTAL_DIMMS];/* Indicates which DIMMs are present */
+ /* from Total Number of DIMMs(per Node)*/
+ u8 DimmX8Present[MAX_TOTAL_DIMMS]; /* Which DIMMs x8 devices */
+ u8 Status[MAX_STATUS]; /* Status for DCT0 and 1 */
+ u8 ErrCode[MAX_ERRORS]; /* Major Error codes for DCT0 and 1 */
+ u8 ErrStatus[MAX_ERRORS]; /* Minor Error codes for DCT0 and 1 */
+ u8 DimmValid[MAX_TOTAL_DIMMS]; /* Indicates which DIMMs are valid for */
+ /* Total Number of DIMMs(per Node) */
+ u8 WLTotalDelay[MAX_BYTE_LANES];/* Write Levelization Toral Delay */
+ /* per byte lane */
+ u8 MaxDimmsInstalled; /* Max Dimms Installed for current DCT */
+ u8 DimmRanks[MAX_TOTAL_DIMMS]; /* Total Number of Ranks(per Dimm) */
+ u32 LogicalCPUID;
+ u8 WLPass;
+} sDCTStruct;
+
+#endif
diff --git a/src/northbridge/amd/amdmct/wrappers/mcti_d.c b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
index 632b581289..a4a87fca01 100644
--- a/src/northbridge/amd/amdmct/wrappers/mcti_d.c
+++ b/src/northbridge/amd/amdmct/wrappers/mcti_d.c
@@ -27,8 +27,10 @@ static u16 mctGet_NVbits(u8 index)
case NV_PACK_TYPE:
#if CONFIG_CPU_SOCKET_TYPE == 0x10 /* Socket F */
val = 0;
-#elif CONFIG_CPU_SOCKET_TYPE == 0x11 /* AM2r2 */
+#elif CONFIG_CPU_SOCKET_TYPE == 0x11 /* AM3 */
val = 1;
+#elif CONFIG_CPU_SOCKET_TYPE == 0x13 /* ASB2 */
+ val = 4;
//#elif SYSTEM_TYPE == MOBILE
// val = 2;
#endif
@@ -400,9 +402,21 @@ static void vErrata350(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTs
static void mctHookBeforeAnyTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
{
- if (pDCTstatA->LogicalCPUID & (AMD_RB_C2 | AMD_DA_C2)) {
+#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
+ if (pDCTstatA->LogicalCPUID & (AMD_RB_C2 | AMD_DA_C2 | AMD_DA_C3)) {
vErrata350(pMCTstat, pDCTstatA);
}
+#endif
+}
+
+static u32 mct_AdjustSPDTimings(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u32 val)
+{
+ if (pDCTstatA->LogicalCPUID & AMD_DR_Bx) {
+ if (pDCTstatA->Status & (1 << SB_Registered)) {
+ val ++;
+ }
+ }
+ return val;
}
static void mctHookAfterAnyTraining(void)
@@ -418,3 +432,4 @@ static u8 mctSetNodeBoundary_D(void)
{
return 0;
}
+