diff options
author | Zheng Bao <zheng.bao@amd.com> | 2010-04-23 17:32:48 +0000 |
---|---|---|
committer | Stefan Reinauer <stepan@openbios.org> | 2010-04-23 17:32:48 +0000 |
commit | eb75f652d392d2f4f257194e112f3f0db7479145 (patch) | |
tree | aa972907734abcba4ca52f2a3a71f8d81d4bdce0 | |
parent | fe6c2cda6e6977894d9b668af9509b983c850f68 (diff) | |
download | coreboot-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
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; } + |