diff options
author | Marc Jones <marc.jones@amd.com> | 2007-12-19 01:32:08 +0000 |
---|---|---|
committer | Marc Jones <marc.jones@amd.com> | 2007-12-19 01:32:08 +0000 |
commit | 8ae8c8822068ef1722c08073ffa4ecc25633cbee (patch) | |
tree | 8c7bbf2f7b791081e486439a9b7ffb2fd6e649ac /src/northbridge/amd/amdmct/mct/mct_d.c | |
parent | 2006b38fed2f5f3680de1736f7fc878823f2f93b (diff) |
Initial AMD Barcelona support for rev Bx.
These are the core files for HyperTransport, DDR2 Memory, and multi-core initialization.
Signed-off-by: Marc Jones <marc.jones@amd.com>
Reviewed-by: Jordan Crouse <jordan.crouse@amd.com>
Acked-by: Myles Watson <myles@pel.cs.byu.edu>
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@3014 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge/amd/amdmct/mct/mct_d.c')
-rw-r--r-- | src/northbridge/amd/amdmct/mct/mct_d.c | 3862 |
1 files changed, 3862 insertions, 0 deletions
diff --git a/src/northbridge/amd/amdmct/mct/mct_d.c b/src/northbridge/amd/amdmct/mct/mct_d.c new file mode 100644 index 0000000000..b4a5fdcf1f --- /dev/null +++ b/src/northbridge/amd/amdmct/mct/mct_d.c @@ -0,0 +1,3862 @@ +/* + * This file is part of the LinuxBIOS project. + * + * Copyright (C) 2007 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 2 */ + + +/* 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 ResetNBECCstat_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 u8 Get_DefTrc_k_D(u8 k); +static u16 Get_40Tk_D(u8 k); +static u16 Get_Fk_D(u8 k); +static u8 Dimm_Supports_D(struct DCTStatStruc *pDCTstat, u8 i, u8 j, u8 k); +static u8 Sys_Capability_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, int j, int 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 u8 Check_DqsRcvEn_Diff(struct DCTStatStruc *pDCTstat, u8 dct, + u32 dev, u32 index_reg, u32 index); +static u8 Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, + u32 dev, u32 index_reg); +static u8 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 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 WaitRoutine_D(u32 time); +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); + + +/*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 Table_T_k[] = {0x00,0x50,0x3D,0x30,0x25, 0x18 }; +static const u8 Table_CL2_j[] = {0x04,0x08,0x10,0x20,0x40, 0x80 }; +static const u8 Tab_defTrc_k[] = {0x0,0x41,0x3C,0x3C,0x3A, 0x3A }; +static const u16 Tab_40T_k[] = {00,200,150,120,100,75 }; +static const u8 Tab_TrefT_k[] = {00,0,1,1,2,2,3,4,5,6,0,0}; +static const u8 Tab_BankAddr[] = {0x0,0x08,0x09,0x10,0x0C,0x0D,0x11,0x0E,0x15,0x16,0x0F,0x17}; +static const u8 Tab_tCL_j[] = {0,2,3,4,5}; +static const u8 Tab_1KTfawT_k[] = {00,8,10,13,14,20}; +static const u8 Tab_2KTfawT_k[] = {00,10,14,17,18,24}; +static const u8 Tab_L1CLKDis[] = {8,8,6,4,2,0,8,8}; +static const u8 Tab_M2CLKDis[] = {2,0,8,8,2,0,2,0}; +static const u8 Tab_S1CLKDis[] = {8,0,8,8,8,0,8,0}; +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}; + +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 toaccessing 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. + * + * Run-Time Requirements: + * 1. Complete Hypertransport Bus Configuration + * 2. SMBus Controller Initialized + * 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: + * 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 + */ + 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; + + print_tx("mctAutoInitMCT_D: mct_init Node ", Node); + mct_init(pMCTstat, pDCTstat); + mctNodeIDDebugPort_D(); + pDCTstat->NodePresent = NodePresent_D(Node); + if (pDCTstat->NodePresent) { /* See if Node is there*/ + print_t("mctAutoInitMCT_D: clear_legacy_Mode\n"); + clear_legacy_Mode(pMCTstat, pDCTstat); + pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node); + + print_t("mctAutoInitMCT_D: mct_InitialMCT_D\n"); + mct_InitialMCT_D(pMCTstat, pDCTstat); + + print_t("mctAutoInitMCT_D: mctSMBhub_Init\n"); + mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ + + print_t("mctAutoInitMCT_D: mct_initDCT\n"); + 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) { + print_debug("No Nodes?!\n"); + goto fatalexit; + } + + print_t("mctAutoInitMCT_D: SyncDCTsReady_D\n"); + SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/ + + print_t("mctAutoInitMCT_D: HTMemMapInit_D\n"); + HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/ + mctHookAfterHTMap(); + + print_t("mctAutoInitMCT_D: CPUMemTyping_D\n"); + CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */ + mctHookAfterCPU(); /* Setup external northbridge(s) */ + + print_t("mctAutoInitMCT_D: DQSTiming_D\n"); + DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/ + + print_t("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); + + print_t("mctAutoInitMCT_D: ECCInit_D\n"); + if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/ + print_t("mctAutoInitMCT_D: MCTMemClr_D\n"); + MCTMemClr_D(pMCTstat,pDCTstatA); + } + + mct_FinalMCT_D(pMCTstat, (pDCTstatA + 0) ); // Node 0 + print_t("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; + + if (nv_DQSTrainCTL) { + print_t("DQSTiming_D: mct_BeforeDQSTrain_D:\n"); + mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);; + phyAssistedMemFnceTraining(pMCTstat, pDCTstatA); + mctHookBeforeAnyTraining(); + + print_t("DQSTiming_D: TrainReceiverEn_D FirstPass:\n"); + TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); + + print_t("DQSTiming_D: mct_TrainDQSPos_D\n"); + mct_TrainDQSPos_D(pMCTstat, pDCTstatA); + + // Second Pass never used for Barcelona! + //print_t("DQSTiming_D: TrainReceiverEn_D SecondPass:\n"); + //TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); + + print_t("DQSTiming_D: mctSetEccDQSRcvrEn_D\n"); + mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA); + + print_t("DQSTiming_D: TrainMaxReadLatency_D\n"); +//FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); + mctHookAfterAnyTraining(); + mctSaveDQSSigTmg_D(); + + print_t("DQSTiming_D: mct_EndDQSTraining_D\n"); + mct_EndDQSTraining_D(pMCTstat, pDCTstatA); + + print_t("DQSTiming_D: MCTMemClr_D\n"); + 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; + + + 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 ? */ + + } + } + 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 < 2; 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 ResetNBECCstat_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstatA) +{ + /* Clear MC4_STS for all Nodes in the system. This is required in some + * circumstances to clear left over garbage from cold reset, shutdown, + * or normal ECC memory conditioning. + */ + + //FIXME: this function depends on pDCTstat Array ( with Node id ) - Is this really a problem? + + u32 dev; + u8 Node; + + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + + if (pDCTstat->NodePresent) { + dev = pDCTstat->dev_nbmisc; + /*MCA NB Status Low (alias to MC4_STS[31:0] */ + Set_NB32(dev, 0x48, 0); + /* MCA NB Status High (alias to MC4_STS[63:32] */ + Set_NB32(dev, 0x4C, 0); + } + } +} + + +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; + 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++) { + 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_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(dev, 0xF0, val); /*Dram Hole Address Register*/ + 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 DstNode*/ + Set_NB32(dev, 0x44 + (Node << 3), val); /* set DstNode */ + + 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++) { + pDCTstat = pDCTstatA + Node; + u32 reg; + u32 devx = pDCTstat->dev_map; + + if (pDCTstat->NodePresent) { + printk_debug(" Copy dram map from Node 0 to Node %02x \n", Node); + 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' */ + if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) { + print_t("\t\tDCTInit_D: mct_DIMMPresence Done\n"); + if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) { + print_t("\t\tDCTInit_D: mct_SPDCalcWidth Done\n"); + if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) { + print_t("\t\tDCTInit_D: AutoCycTiming_D Done\n"); + if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) { + print_t("\t\tDCTInit_D: AutoConfig_D Done\n"); + if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) { + print_t("\t\tDCTInit_D: PlatformSpec_D Done\n"); + stopDCTflag = 0; + if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) { + print_t("\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); + } +} + + +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; + + for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { + struct DCTStatStruc *pDCTstat; + pDCTstat = pDCTstatA + Node; + mct_SyncDCTsReady(pDCTstat); + } +} + + +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; + u8 byte; + u32 reg; + u32 reg_off = dct * 0x100; + + dev = pDCTstat->dev_dct; + val = Get_NB32(dev, 0x94 + reg_off); + if (val & (1<<MemClkFreqVal)) { + print_t("\t\t\tStartupDCT_D: MemClkFreqVal\n"); + byte = mctGet_NVbits(NV_DQSTrainCTL); + if (byte == 1) { + /* Enable DQSRcvEn training mode */ + print_t("\t\t\tStartupDCT_D: DqsRcvEnTrain set \n"); + reg = 0x78 + reg_off; + val = Get_NB32(dev, reg); + /* Setting this bit forces a 1T window with hard left + * pass/fail edge and a probabalistic right pass/fail + * edge. LEFT edge is referenced for final + * receiver enable position.*/ + val |= 1 << DqsRcvEnTrain; + Set_NB32(dev, reg, val); + } + mctHookBeforeDramInit(); /* generalized Hook */ + print_t("\t\t\tStartupDCT_D: DramInit \n"); + 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 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'. + */ + + u8 i; + u8 Twr, Trtp; + u8 Trp, Trrd, Trcd, Tras, Trc, Trfc[4], Rows; + u32 DramTimingLo, DramTimingHi; + u16 Tk10, Tk40; + u8 Twtr; + u8 LDIMM; + u8 DDR2_1066; + u8 byte; + u32 dword; + u32 dev; + u32 reg; + u32 reg_off; + u32 val; + u16 smbaddr; + + /* 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; + + /* if "manual" memclock mode */ + if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 2) + pDCTstat->Speed = mctGet_NVbits(NV_MemCkVal) + 1; + + mct_AfterGetCLT(pMCTstat, pDCTstat, dct); + } + + /* Gather all DIMM mini-max values for cycle timing data */ + Rows = 0; + 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; + + for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) { + LDIMM = i >> 1; + if (pDCTstat->DIMMValid & (1 << i)) { + smbaddr = Get_DIMMAddress_D(pDCTstat, i); + byte = mctRead_SPD(smbaddr, SPD_ROWSZ); + if (Rows < byte) + Rows = byte; /* keep track of largest row sz */ + + byte = mctRead_SPD(smbaddr, SPD_TRP); + if (Trp < byte) + Trp = byte; + + byte = mctRead_SPD(smbaddr, SPD_TRRD); + if (Trrd < byte) + Trrd = byte; + + byte = mctRead_SPD(smbaddr, SPD_TRCD); + if (Trcd < byte) + Trcd = byte; + + byte = mctRead_SPD(smbaddr, SPD_TRTP); + if (Trtp < byte) + Trtp = byte; + + byte = mctRead_SPD(smbaddr, SPD_TWR); + if (Twr < byte) + Twr = byte; + + byte = mctRead_SPD(smbaddr, SPD_TWTR); + if (Twtr < byte) + Twtr = byte; + + val = mctRead_SPD(smbaddr, SPD_TRC); + if ((val == 0) || (val == 0xFF)) { + pDCTstat->ErrStatus |= 1<<SB_NoTrcTrfc; + pDCTstat->ErrCode = SC_VarianceErr; + val = Get_DefTrc_k_D(pDCTstat->DIMMAutoSpeed); + } else { + byte = mctRead_SPD(smbaddr, SPD_TRCRFC); + if (byte & 0xF0) { + val++; /* round up in case fractional extention is non-zero.*/ + } + } + if (Trc < val) + Trc = val; + + /* dev density=rank size/#devs per rank */ + byte = mctRead_SPD(smbaddr, SPD_BANKSZ); + + val = ((byte >> 5) | (byte << 3)) & 0xFF; + val <<= 2; + + byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0xFE; /* dev density=2^(rows+columns+banks) */ + if (byte == 4) { + val >>= 4; + } else if (byte == 8) { + val >>= 3; + } else if (byte == 16) { + val >>= 2; + } + + byte = bsr(val); + + if (Trfc[LDIMM] < byte) + Trfc[LDIMM] = byte; + + byte = mctRead_SPD(smbaddr, SPD_TRAS); + if (Tras < byte) + Tras = byte; + } /* Dimm Present */ + } + + /* Convert DRAM CycleTiming values and store into DCT structure */ + DDR2_1066 = 0; + byte = pDCTstat->DIMMAutoSpeed; + if (byte == 5) + DDR2_1066 = 1; + Tk40 = Get_40Tk_D(byte); + Tk10 = Tk40>>2; + + /* Notes: + 1. All secondary time values given in SPDs are in binary with units of ns. + 2. Some time values are scaled by four, 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, Tk, is scaled by 10 to affect a + least count of 0.1 ns (more accuracy). + 4. SPD values not scaled are multiplied by 10 and then divided by 10T to find + equivalent minimum number of bus clocks (a remainder causes round-up of clocks). + 5. SPD values that are prescaled by 4 are multiplied by 10 and then divided by 40T to find + equivalent minimum number of bus clocks (a remainder causes round-up of clocks).*/ + + /* Tras */ + dword = Tras * 40; + pDCTstat->DIMMTras = (u16)dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TrasT_1066) + val = Min_TrasT_1066; + else if (val > Max_TrasT_1066) + val = Max_TrasT_1066; + } else { + if (val < Min_TrasT) + val = Min_TrasT; + else if (val > Max_TrasT) + val = Max_TrasT; + } + pDCTstat->Tras = val; + + /* Trp */ + dword = Trp * 10; + pDCTstat->DIMMTrp = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TrasT_1066) + val = Min_TrpT_1066; + else if (val > Max_TrpT_1066) + val = Max_TrpT_1066; + } else { + if (val < Min_TrpT) + val = Min_TrpT; + else if (val > Max_TrpT) + val = Max_TrpT; + } + pDCTstat->Trp = val; + + /*Trrd*/ + dword = Trrd * 10; + pDCTstat->DIMMTrrd = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TrrdT_1066) + val = Min_TrrdT_1066; + else if (val > Max_TrrdT_1066) + val = Max_TrrdT_1066; + } else { + if (val < Min_TrrdT) + val = Min_TrrdT; + else if (val > Max_TrrdT) + val = Max_TrrdT; + } + pDCTstat->Trrd = val; + + /* Trcd */ + dword = Trcd * 10; + pDCTstat->DIMMTrcd = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TrcdT_1066) + val = Min_TrcdT_1066; + else if (val > Max_TrcdT_1066) + val = Max_TrcdT_1066; + } else { + if (val < Min_TrcdT) + val = Min_TrcdT; + else if (val > Max_TrcdT) + val = Max_TrcdT; + } + pDCTstat->Trcd = val; + + /* Trc */ + dword = Trc * 40; + pDCTstat->DIMMTrc = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TrcT_1066) + val = Min_TrcT_1066; + else if (val > Max_TrcT_1066) + val = Max_TrcT_1066; + } else { + if (val < Min_TrcT) + val = Min_TrcT; + else if (val > Max_TrcT) + val = Max_TrcT; + } + pDCTstat->Trc = val; + + /* Trtp */ + dword = Trtp * 10; + pDCTstat->DIMMTrtp = dword; + val = pDCTstat->Speed; + if (val <= 2) { + val = 2; /* Calculate by 7.75ns / Speed in ns to get clock # */ + } else if (val == 4) { /* Note a speed of 3 will be a Trtp of 3 */ + val = 3; + } else if (val == 5){ + val = 2; + } + pDCTstat->Trtp = val; + + /* Twr */ + dword = Twr * 10; + pDCTstat->DIMMTwr = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TwrT_1066) + val = Min_TwrT_1066; + else if (val > Max_TwrT_1066) + val = Max_TwrT_1066; + } else { + if (val < Min_TwrT) + val = Min_TwrT; + else if (val > Max_TwrT) + val = Max_TwrT; + } + pDCTstat->Twr = val; + + /* Twtr */ + dword = Twtr * 10; + pDCTstat->DIMMTwtr = dword; + val = dword / Tk40; + if (dword % Tk40) { /* round up number of busclocks */ + val++; + } + if (DDR2_1066) { + if (val < Min_TwrT_1066) + val = Min_TwtrT_1066; + else if (val > Max_TwtrT_1066) + val = Max_TwtrT_1066; + } else { + 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]; + + mctAdjustAutoCycTmg_D(); + + /* Program DRAM Timing values */ + DramTimingLo = 0; /* Dram Timing Low init */ + val = pDCTstat->CASL; + val = Tab_tCL_j[val]; + DramTimingLo |= val; + + val = pDCTstat->Trcd; + if (DDR2_1066) + val -= Bias_TrcdT_1066; + else + val -= Bias_TrcdT; + + DramTimingLo |= val<<4; + + val = pDCTstat->Trp; + if (DDR2_1066) + val -= Bias_TrpT_1066; + else { + val -= Bias_TrpT; + val <<= 1; + } + DramTimingLo |= val<<7; + + val = pDCTstat->Trtp; + val -= Bias_TrtpT; + DramTimingLo |= val<<11; + + val = pDCTstat->Tras; + if (DDR2_1066) + val -= Bias_TrasT_1066; + else + val -= Bias_TrasT; + DramTimingLo |= val<<12; + + val = pDCTstat->Trc; + val -= Bias_TrcT; + DramTimingLo |= val<<16; + + if (!DDR2_1066) { + val = pDCTstat->Twr; + val -= Bias_TwrT; + DramTimingLo |= val<<20; + } + + val = pDCTstat->Trrd; + if (DDR2_1066) + val -= Bias_TrrdT_1066; + else + val -= Bias_TrrdT; + DramTimingLo |= val<<22; + + + DramTimingHi = 0; /* Dram Timing Low init */ + val = pDCTstat->Twtr; + if (DDR2_1066) + val -= Bias_TwtrT_1066; + else + val -= 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; + print_tx("AutoCycTiming: DramTimingLo ", DramTimingLo); + print_tx("AutoCycTiming: DramTimingHi ", DramTimingHi); + + Set_NB32(dev, 0x88 + reg_off, DramTimingLo); /*DCT Timing Low*/ + DramTimingHi |=0x0000FC77; + Set_NB32(dev, 0x8c + reg_off, DramTimingHi); /*DCT Timing Hi*/ + + if (DDR2_1066) { + /* Twr */ + dword = pDCTstat->Twr; + dword -= Bias_TwrT_1066; + dword <<= 4; + reg = 0x84 + reg_off; + val = Get_NB32(dev, reg); + val &= 0x8F; + val |= dword; + Set_NB32(dev, reg, val); + } +// dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); + + print_tx("AutoCycTiming: Status ", pDCTstat->Status); + print_tx("AutoCycTiming: ErrStatus ", pDCTstat->ErrStatus); + print_tx("AutoCycTiming: ErrCode ", pDCTstat->ErrCode); + print_t("AutoCycTiming: Done\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; + } +} + + + +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). + */ + int i, j, k; + u8 T1min, CL1min; + + /* i={0..7} (std. physical DIMM number) + * j is an integer which enumerates increasing CAS latency. + * k is an integer which enumerates decreasing cycle time. + * CL no. {0,1,2} corresponds to CL X, CL X-.5, or CL X-1 (per individual DIMM) + * Max timing values are per parameter, of all DIMMs, spec'd in ns like the SPD. + */ + + CL1min = 0xFF; + T1min = 0xFF; + for (k=K_MAX; k >= K_MIN; k--) { + for (j = J_MIN; j <= J_MAX; j++) { + if (Sys_Capability_D(pMCTstat, pDCTstat, j, k) ) { + /* 1. check to see if DIMMi is populated. + 2. check if DIMMi supports CLj and Tjk */ + for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) { + if (pDCTstat->DIMMValid & (1 << i)) { + if (Dimm_Supports_D(pDCTstat, i, j, k)) + break; + } + } /* while ++i */ + if (i == MAX_DIMMS_SUPPORTED) { + T1min = k; + CL1min = j; + goto got_TCL; + } + } + } /* while ++j */ + } /* while --k */ + +got_TCL: + if (T1min != 0xFF) { + pDCTstat->DIMMCASL = CL1min; /*mfg. optimized */ + pDCTstat->DIMMAutoSpeed = T1min; + print_tx("SPDGetTCL_D: DIMMCASL ", pDCTstat->DIMMCASL); + print_tx("SPDGetTCL_D: DIMMAutoSpeed ", pDCTstat->DIMMAutoSpeed); + + } else { + pDCTstat->DIMMCASL = CL_DEF; /* failsafe values (running in min. mode) */ + pDCTstat->DIMMAutoSpeed = T_DEF; + pDCTstat->ErrStatus |= 1 << SB_DimmMismatchT; + pDCTstat->ErrStatus |= 1 << SB_MinimumMode; + pDCTstat->ErrCode = SC_VarianceErr; + } + print_tx("SPDGetTCL_D: Status ", pDCTstat->Status); + print_tx("SPDGetTCL_D: ErrStatus ", pDCTstat->ErrStatus); + print_tx("SPDGetTCL_D: ErrCode ", pDCTstat->ErrCode); + print_t("SPDGetTCL_D: Done\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) { + 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); + 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; + + print_tx("AutoConfig_D: DCT: ", dct); + + 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*/ + + if (mctGet_NVbits(NV_CLKHZAltVidC3)) + DramControl |= 1<<16; + + // FIXME: Add support(skip) for Ax and Cx versions + DramControl |= 5; /* RdPtrInit */ + + + /* Build Dram Config Lo Register Value */ + DramConfigLo |= 1 << 4; /* 75 Ohms ODT */ + if (mctGet_NVbits(NV_MAX_DIMMS) == 8) { + if (pDCTstat->Speed == 3) { + if ((pDCTstat->MAdimms[dct] == 4)) + DramConfigLo |= 1 << 5; /* 50 Ohms ODT */ + } else if (pDCTstat->Speed == 4){ + if ((pDCTstat->MAdimms[dct] != 1)) + DramConfigLo |= 1 << 5; /* 50 Ohms ODT */ + } + } else { + // FIXME: Skip for Ax versions + if ((pDCTstat->MAdimms[dct] == 4)) { + if ( pDCTstat->DimmQRPresent != 0) { + if ((pDCTstat->Speed == 3) || (pDCTstat->Speed == 4)) { + DramConfigLo |= 1 << 5; /* 50 Ohms ODT */ + } + } else if ((pDCTstat->MAdimms[dct] == 4)) { + if (pDCTstat->Speed == 4) { + if ( pDCTstat->DimmQRPresent != 0) { + DramConfigLo |= 1 << 5; /* 50 Ohms ODT */ + } + } + } + } else if ((pDCTstat->MAdimms[dct] == 2)) { + DramConfigLo |= 1 << 5; /* 50 Ohms ODT */ + } + + } + + // FIXME: Skip for Ax versions + /* callback not required - if (!mctParityControl_D()) */ + if (Status & (1 << SB_PARDIMMs)) { + DramConfigLo |= 1 << ParEn; + DramConfigMisc2 |= 1 << ActiveCmdAtRst; + } else { + DramConfigLo &= ~(1 << ParEn); + DramConfigMisc2 &= ~(1 << ActiveCmdAtRst); + } + + if (mctGet_NVbits(NV_BurstLen32)) { + if (!pDCTstat->GangedMode) + DramConfigLo |= 1 << BurstLength32; + } + + 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; + + + + /* 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_PDEN)) { + DramConfigHi |= 1 << 15; /* PowerDownEn */ + 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; + + val = pDCTstat->DIMM2Kpage; + if (pDCTstat->GangedMode != 0) { + if (dct != 0) { + val &= 0x55; + } else { + val &= 0xAA; + } + } + if (val) + val = Tab_2KTfawT_k[pDCTstat->Speed]; + else + val = Tab_1KTfawT_k[pDCTstat->Speed]; + + if (pDCTstat->Speed == 5) + val >>= 1; + + val -= Bias_TfawT; + val <<= 28; + DramConfigHi |= val; /* Tfaw for 1K or 2K paged drams */ + + // FIXME: Skip for Ax versions + 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; + byte = mctGet_NVbits(NV_PACK_TYPE); + if (byte == PT_L1) + p = Tab_L1CLKDis; + else if (byte == PT_M2) + p = Tab_M2CLKDis; + else + p = Tab_S1CLKDis; + + dword = 0; + while(dword < MAX_DIMMS_SUPPORTED) { + val = p[dword]; + print_tx("DramTimingLo: val=", val); + if (!(pDCTstat->DIMMValid & (1<<val))) + /*disable memclk*/ + DramTimingLo |= 1<<(dword+24); + dword++ ; + } + } + } + + print_tx("AutoConfig_D: DramControl: ", DramControl); + print_tx("AutoConfig_D: DramTimingLo: ", DramTimingLo); + print_tx("AutoConfig_D: DramConfigMisc: ", DramConfigMisc); + print_tx("AutoConfig_D: DramConfigMisc2: ", DramConfigMisc2); + print_tx("AutoConfig_D: DramConfigLo: ", DramConfigLo); + print_tx("AutoConfig_D: DramConfigHi: ", DramConfigHi); + + /* Write Values to the registers */ + Set_NB32(dev, 0x78 + reg_off, DramControl); + Set_NB32(dev, 0x88 + reg_off, DramTimingLo); + Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc); + Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2); + Set_NB32(dev, 0x90 + reg_off, DramConfigLo); + mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi); + mct_ForceAutoPrecharge_D(pDCTstat, dct); + mct_EarlyArbEn_D(pMCTstat, pDCTstat); + mctHookAfterAutoCfg(); + +// dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); + + print_tx("AutoConfig: Status ", pDCTstat->Status); + print_tx("AutoConfig: ErrStatus ", pDCTstat->ErrStatus); + print_tx("AutoConfig: ErrCode ", pDCTstat->ErrCode); + print_t("AutoConfig: Done\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, DevWidth; + 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_ROWSZ); + Rows = byte & 0x1f; + + byte = mctRead_SPD(smbaddr, SPD_COLSZ); + Cols = byte & 0x1f; + + Banks = mctRead_SPD(smbaddr, SPD_LBANKS); + + byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH); + DevWidth = byte & 0x7f; /* bits 0-6 = bank 0 width */ + + byte = mctRead_SPD(smbaddr, SPD_DMBANKS); + Ranks = (byte & 7) + 1; + + /* Configure Bank encoding + * Use a 6-bit key into a lookup table. + * Key (index) = CCCBRR, where CCC is the number of + * Columns minus 9,RR is the number of Rows minus 13, + * and B is the number of banks minus 2. + * See "6-bit Bank Addressing Table" at the end of + * this file.*/ + byte = Cols - 9; /* 9 Cols is smallest dev size */ + byte <<= 3; /* make room for row and bank bits*/ + if (Banks == 8) + byte |= 4; + + /* 13 Rows is smallest dev size */ + byte |= Rows - 13; /* CCCBRR internal encode */ + + for (dword=0; dword < 12; dword++) { + if (byte == Tab_BankAddr[dword]) + break; + } + + if (dword < 12) { + + /* 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*/ + if (Banks == 8) + byte -= 2; /* 3 banks - 5 */ + else + byte -= 3; /* 2 banks - 5 */ + /* mask size (64-bit rank only) */ + + 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 */ + SetODTTriState(pMCTstat, pDCTstat, dct); + + if ( pDCTstat->Status & 1<<SB_128bitmode) { + SetCSTriState(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); + +// dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); + + print_tx("SPDSetBanks: Status ", pDCTstat->Status); + print_tx("SPDSetBanks: ErrStatus ", pDCTstat->ErrStatus); + print_tx("SPDSetBanks: ErrCode ", pDCTstat->ErrCode); + print_t("SPDSetBanks: Done\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_ROWSZ) & 0x1f; + byte1 = mctRead_SPD(smbaddr1, SPD_ROWSZ) & 0x1f; + if (byte != byte1) { + pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); + break; + } + + byte = mctRead_SPD(smbaddr, SPD_COLSZ) & 0x1f; + byte1 = mctRead_SPD(smbaddr1, SPD_COLSZ) & 0x1f; + if (byte != byte1) { + pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); + break; + } + + byte = mctRead_SPD(smbaddr, SPD_BANKSZ); + byte1 = mctRead_SPD(smbaddr1, SPD_BANKSZ); + if (byte != byte1) { + pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); + break; + } + + byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0x7f; + byte1 = mctRead_SPD(smbaddr1, SPD_DEVWIDTH) & 0x7f; + 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 */ + } + 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)); + + print_tx("StitchMemory: Status ", pDCTstat->Status); + print_tx("StitchMemory: ErrStatus ", pDCTstat->ErrStatus); + print_tx("StitchMemory: ErrCode ", pDCTstat->ErrCode); + print_t("StitchMemory: Done\n"); +} + + +static u8 Get_Tk_D(u8 k) +{ + return Table_T_k[k]; +} + + +static u8 Get_CLj_D(u8 j) +{ + return Table_CL2_j[j]; +} + +static u8 Get_DefTrc_k_D(u8 k) +{ + return Tab_defTrc_k[k]; +} + + +static u16 Get_40Tk_D(u8 k) +{ + return Tab_40T_k[k]; /* FIXME: k or k<<1 ?*/ +} + + +static u16 Get_Fk_D(u8 k) +{ + return Table_F_k[k]; /* FIXME: k or k<<1 ? */ +} + + +static u8 Dimm_Supports_D(struct DCTStatStruc *pDCTstat, + u8 i, u8 j, u8 k) +{ + u8 Tk, CLj, CL_i; + u8 ret = 0; + + u32 DIMMi; + u8 byte; + u16 word, wordx; + + DIMMi = Get_DIMMAddress_D(pDCTstat, i); + + CLj = Get_CLj_D(j); + + /* check if DIMMi supports CLj */ + CL_i = mctRead_SPD(DIMMi, SPD_CASLAT); + byte = CL_i & CLj; + if (byte) { + /*find out if its CL X, CLX-1, or CLX-2 */ + word = bsr(byte); /* bit position of CLj */ + wordx = bsr(CL_i); /* bit position of CLX of CLi */ + wordx -= word; /* CL number (CL no. = 0,1, 2, or 3) */ + wordx <<= 3; /* 8 bits per SPD byte index */ + /*get T from SPD byte 9, 23, 25*/ + word = (EncodedTSPD >> wordx) & 0xFF; + Tk = Get_Tk_D(k); + byte = mctRead_SPD(DIMMi, word); /* DIMMi speed */ + if (Tk < byte) { + ret = 1; + } else if (byte == 0){ + pDCTstat->ErrStatus |= 1<<SB_NoCycTime; + ret = 1; + } else { + ret = 0; /* DIMM is capable! */ + } + } else { + ret = 1; + } + return ret; +} + + +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, Index; + u16 Checksum; + u8 SPDCtrl; + u16 RegDIMMPresent, MaxDimms; + u8 devwidth; + u16 DimmSlots; + u8 byte = 0, bytex; + u16 word; + + /* 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)) { + print_tx("\t DIMMPresence: i=", i); + smbaddr = Get_DIMMAddress_D(pDCTstat, i); + print_tx("\t DIMMPresence: smbaddr=", smbaddr); + if (smbaddr) { + Checksum = 0; + for (Index=0; Index < 64; Index++){ + int status; + status = mctRead_SPD(smbaddr, Index); + if (status < 0) + break; + byte = status & 0xFF; + if (Index < 63) + Checksum += byte; + } + + if (Index == 64) { + pDCTstat->DIMMPresent |= 1 << i; + if ((Checksum & 0xFF) == byte) { + byte = mctRead_SPD(smbaddr, SPD_TYPE); + if (byte == JED_DDR2SDRAM) { + /*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_DDR2SDRAM) + pDCTstat->DIMMValid |= 1 << i; + } + } + /* Check module type */ + byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE); + if (byte & JED_REGADCMSK) + RegDIMMPresent |= 1 << i; + /* Check ECC capable */ + byte = mctRead_SPD(smbaddr, SPD_EDCTYPE); + if (byte & JED_ECC) { + /* DIMM is ECC capable */ + pDCTstat->DimmECCPresent |= 1 << i; + } + if (byte & JED_ADRCPAR) { + /* DIMM is ECC capable */ + pDCTstat->DimmPARPresent |= 1 << i; + } + /* Check if x4 device */ + devwidth = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0xFE; + if (devwidth == 4) { + /* DIMM is made with x4 or x16 drams */ + pDCTstat->Dimmx4Present |= 1 << i; + } else if (devwidth == 8) { + pDCTstat->Dimmx8Present |= 1 << i; + } else if (devwidth == 16) { + pDCTstat->Dimmx16Present |= 1 << i; + } + /* check page size */ + byte = mctRead_SPD(smbaddr, SPD_COLSZ); + byte &= 0x0F; + word = 1 << byte; + word >>= 3; + word *= devwidth; /* (((2^COLBITS) / 8) * ORG) / 2048 */ + word >>= 11; + if (word) + pDCTstat->DIMM2Kpage |= 1 << i; + + /*Check if SPD diag bit 'analysis probe installed' is set */ + byte = mctRead_SPD(smbaddr, SPD_ATTRIB); + if ( byte & JED_PROBEMSK ) + pDCTstat->Status |= 1<<SB_DiagClks; + + byte = mctRead_SPD(smbaddr, SPD_DMBANKS); + if (!(byte & (1<< SPDPLBit))) + pDCTstat->DimmPlPresent |= 1 << i; + byte &= 7; + byte++; /* ranks */ + if (byte > 2) { + /* 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)); + } + byte = 2; /* upper two ranks of QR DIMM will be counted on another DIMM number iteration*/ + } else if (byte == 2) { + pDCTstat->DimmDRPresent |= 1 << i; + } + bytex = devwidth; + if (devwidth == 16) + bytex = 4; + else if (devwidth == 4) + bytex=16; + + 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 for DRAM package Year <= 06*/ + byte = mctRead_SPD(smbaddr, SPD_MANDATEYR); + if (byte < MYEAR06) { + /*Year < 06 and hence Week < 24 of 06 */ + pDCTstat->DimmYr06 |= 1 << i; + pDCTstat->DimmWk2406 |= 1 << i; + } else if (byte == MYEAR06) { + /*Year = 06, check if Week <= 24 */ + pDCTstat->DimmYr06 |= 1 << i; + byte = mctRead_SPD(smbaddr, SPD_MANDATEWK); + if (byte <= MWEEK24) + pDCTstat->DimmWk2406 |= 1 << i; + } + } + } + } + } + print_tx("\t DIMMPresence: DIMMValid=", pDCTstat->DIMMValid); + print_tx("\t DIMMPresence: DIMMPresent=", pDCTstat->DIMMPresent); + print_tx("\t DIMMPresence: RegDIMMPresent=", RegDIMMPresent); + print_tx("\t DIMMPresence: DimmECCPresent=", pDCTstat->DimmECCPresent); + print_tx("\t DIMMPresence: DimmPARPresent=", pDCTstat->DimmPARPresent); + print_tx("\t DIMMPresence: Dimmx4Present=", pDCTstat->Dimmx4Present); + print_tx("\t DIMMPresence: Dimmx8Present=", pDCTstat->Dimmx8Present); + print_tx("\t DIMMPresence: Dimmx16Present=", pDCTstat->Dimmx16Present); + print_tx("\t DIMMPresence: DimmPlPresent=", pDCTstat->DimmPlPresent); + print_tx("\t DIMMPresence: DimmDRPresent=", pDCTstat->DimmDRPresent); + print_tx("\t DIMMPresence: DimmQRPresent=", pDCTstat->DimmQRPresent); + print_tx("\t DIMMPresence: DATAload[0]=", pDCTstat->DATAload[0]); + print_tx("\t DIMMPresence: MAload[0]=", pDCTstat->MAload[0]); + print_tx("\t DIMMPresence: MAdimms[0]=", pDCTstat->MAdimms[0]); + print_tx("\t DIMMPresence: DATAload[1]=", pDCTstat->DATAload[1]); + print_tx("\t DIMMPresence: MAload[1]=", pDCTstat->MAload[1]); + print_tx("\t DIMMPresence: MAdimms[1]=", 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; + } + + print_tx("\t DIMMPresence: Status ", pDCTstat->Status); + print_tx("\t DIMMPresence: ErrStatus ", pDCTstat->ErrStatus); + print_tx("\t DIMMPresence: ErrCode ", pDCTstat->ErrCode); + print_t("\t DIMMPresence: Done\n"); + + mctHookAfterDIMMpre(); + + return pDCTstat->ErrCode; +} + + +static u8 Sys_Capability_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, int j, int k) +{ + /* Determine if system is capable of operating at given input + * parameters for CL, and T. There are three components to + * determining "maximum frequency" in AUTO mode: SPD component, + * Bus load component, and "Preset" max frequency component. + * This procedure is used to help find the SPD component and relies + * on pre-determination of the bus load component and the Preset + * components. The generalized algorithm for finding maximum + * frequency is structured this way so as to optimize for CAS + * latency (which might get better as a result of reduced frequency). + * See "Global relationship between index values and item values" + * for definition of CAS latency index (j) and Frequency index (k). + */ + u8 freqOK, ClOK; + u8 ret = 0; + + if (Get_Fk_D(k) > pDCTstat->PresetmaxFreq) + freqOK = 0; + else + freqOK = 1; + + /* compare proposed CAS latency with AMD Si capabilities */ + if ((j < J_MIN) || (j > J_MAX)) + ClOK = 0; + else + ClOK = 1; + + if (freqOK && ClOK) + ret = 1; + + return ret; +} + + +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 */ + print_t("\tmct_initDCT: DCTInit_D 0\n"); + 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) { + print_t("\tmct_initDCT: DCTInit_D 1\n"); + 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) +{ + u32 val; + + mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat); + // FIXME: for rev A: mct_BeforeDramInit_D(pDCTstat, dct); + + /* Disable auto refresh before Dram init when in ganged mode */ + if (pDCTstat->GangedMode) { + val = Get_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct)); + val |= 1 << DisAutoRefresh; + Set_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct), val); + } + + mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); + + /* Re-enable auto refresh after Dram init when in ganged mode + * to ensure both DCTs are in sync + */ + + if (pDCTstat->GangedMode) { + do { + val = Get_NB32(pDCTstat->dev_dct, 0x90 + (0x100 * dct)); + } while (!(val & (1 << InitDram))); + + WaitRoutine_D(50); + + val = Get_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct)); + val &= ~(1 << DisAutoRefresh); + val |= 1 << DisAutoRefresh; + val &= ~(1 << DisAutoRefresh); + } +} + + +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 { + if ( mctGet_NVbits(NV_Unganged) ) + pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); + + 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); + print_tx("setMode: DRAM Controller Select Low Register = ", val); + } + } + return pDCTstat->ErrCode; +} + + +u32 Get_NB32(u32 dev, u32 reg) +{ + u32 addr; + + addr = (dev>>4) | (reg & 0xFF) | ((reg & 0xf00)<<16); + outl((1<<31) | (addr & ~3), 0xcf8); + + return inl(0xcfc); +} + + +void Set_NB32(u32 dev, u32 reg, u32 val) +{ + u32 addr; + + addr = (dev>>4) | (reg & 0xFF) | ((reg & 0xf00)<<16); + outl((1<<31) | (addr & ~3), 0xcf8); + outl(val, 0xcfc); +} + + +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); + 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) { + print_tx("mct_SyncDCTsReady: Node ", pDCTstat->Node_ID); + 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))); + print_t("mct_SyncDCTsReady: DramEnabled\n"); + } + } /* 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; + + if ( dct == 0) { + SPDCalcWidth_D(pMCTstat, pDCTstat); + ret = mct_setMode(pMCTstat, pDCTstat); + } else { + ret = pDCTstat->ErrCode; + } + + print_tx("SPDCalcWidth: Status ", pDCTstat->Status); + print_tx("SPDCalcWidth: ErrStatus ", pDCTstat->ErrStatus); + print_tx("SPDCalcWidth: ErrCode ", pDCTstat->ErrCode); + print_t("SPDCalcWidth: Done\n"); + + 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 exist both, set DctSelBaseAddr[47:27] */ + 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 &= ~(0xFF); + 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); + print_tx("AfterStitch DCT0 and DCT1: DRAM Controller Select Low Register = ", 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) { + 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); + print_tx("AfterStitch DCT1 only: DRAM Controller Select Low Register = ", val); + } + } + } else { + pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit; + } + print_tx("AfterStitch pDCTstat->NodeSysBase = ", pDCTstat->NodeSysBase); + print_tx("mct_AfterStitchMemory: pDCTstat->NodeSysLimit ", 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_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; //0x07 + val |= dword << 4; + dword = pDCTstat->Twrrd; //0x03 + val |= dword << 10; + dword = pDCTstat->Twrwr; //0x03 + val |= dword << 12; + dword = pDCTstat->Trdrd; //0x03 + val |= dword << 14; + dword = pDCTstat->TrwtWB; //0x07 + val |= dword; + val = OtherTiming_A_D(pDCTstat, val); + Set_NB32(dev, reg, val); + +} + + +static void Get_Trdrd(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) +{ + u8 Trdrd; + u8 byte; + u32 dword; + u32 val; + u32 index_reg = 0x98 + 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + + if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0)) { + /* mixed (x4 or x8) DIMM types + the largest DqsRcvEnGrossDelay of any DIMM minus the DqsRcvEnGrossDelay + of any other DIMM is equal to the Critical Gross Delay Difference (CGDD) for Trdrd.*/ + byte = Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg); + if (byte == 0) + Trdrd = 1; + else + Trdrd = 2; + + } else { + /* + Trdrd with non-mixed DIMM types + RdDqsTime are the same for all DIMMs and DqsRcvEn difference between + any two DIMMs is less than half of a MEMCLK, BIOS should program Trdrd to 0000b, + else BIOS should program Trdrd to 0001b. + + RdDqsTime are the same for all DIMMs + DDR400~DDR667 only use one set register + DDR800 have two set register for DIMM0 and DIMM1 */ + Trdrd = 1; + if (pDCTstat->Speed > 3) { + /* DIMM0+DIMM1 exist */ //NOTE it should be 5 + val = bsf(pDCTstat->DIMMValid); + dword = bsr(pDCTstat->DIMMValid); + if (dword != val && dword != 0) { + /* DCT Read DQS Timing Control - DIMM0 - Low */ + dword = Get_NB32_index_wait(dev, index_reg, 0x05); + /* DCT Read DQS Timing Control - DIMM1 - Low */ + val = Get_NB32_index_wait(dev, index_reg, 0x105); + if (val != dword) + goto Trdrd_1; + + /* DCT Read DQS Timing Control - DIMM0 - High */ + dword = Get_NB32_index_wait(dev, index_reg, 0x06); + /* DCT Read DQS Timing Control - DIMM1 - High */ + val = Get_NB32_index_wait(dev, index_reg, 0x106); + if (val != dword) + goto Trdrd_1; + } + } + + /* DqsRcvEn difference between any two DIMMs is + less than half of a MEMCLK */ + /* DqsRcvEn byte 1,0*/ + if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x10)) + goto Trdrd_1; + /* DqsRcvEn byte 3,2*/ + if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x11)) + goto Trdrd_1; + /* DqsRcvEn byte 5,4*/ + if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x20)) + goto Trdrd_1; + /* DqsRcvEn byte 7,6*/ + if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x21)) + goto Trdrd_1; + /* DqsRcvEn ECC*/ + if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x12)) + goto Trdrd_1; + Trdrd = 0; + Trdrd_1: + ; + } + pDCTstat->Trdrd = Trdrd; + +} + + +static void Get_Twrwr(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) +{ + u8 Twrwr = 0; + u32 index_reg = 0x98 + 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + u32 val; + u32 dword; + + /* WrDatGrossDlyByte only use one set register when DDR400~DDR667 + DDR800 have two set register for DIMM0 and DIMM1 */ + if (pDCTstat->Speed > 3) { + val = bsf(pDCTstat->DIMMValid); + dword = bsr(pDCTstat->DIMMValid); + if (dword != val && dword != 0) { + /*the largest WrDatGrossDlyByte of any DIMM minus the + WrDatGrossDlyByte of any other DIMM is equal to CGDD */ + val = Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg); + } + if (val == 0) + Twrwr = 2; + else + Twrwr = 3; + } + pDCTstat->Twrwr = Twrwr; +} + + +static void Get_Twrrd(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) +{ + u8 byte, bytex; + u32 index_reg = 0x98 + 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + + /* On any given byte lane, the largest WrDatGrossDlyByte delay of + any DIMM minus the DqsRcvEnGrossDelay delay of any other DIMM is + equal to the Critical Gross Delay Difference (CGDD) for Twrrd.*/ + pDCTstat->Twrrd = 0; + Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg); + Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg); + bytex = pDCTstat->DqsRcvEnGrossL; + byte = pDCTstat->WrDatGrossH; + if (byte > bytex) { + byte -= bytex; + if (byte == 1) + bytex = 1; + else + bytex = 2; + } else { + bytex = 0; + } + pDCTstat->Twrrd = bytex; +} + + +static void Get_TrwtTO(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 dct) +{ + u8 byte, bytex; + u32 index_reg = 0x98 + 0x100 * dct; + u32 dev = pDCTstat->dev_dct; + + /* On any given byte lane, the largest WrDatGrossDlyByte delay of + any DIMM minus the DqsRcvEnGrossDelay delay of any other DIMM is + equal to the Critical Gross Delay Difference (CGDD) for TrwtTO. */ + Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg); + Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg); + bytex = pDCTstat->DqsRcvEnGrossL; + byte = pDCTstat->WrDatGrossH; + if (bytex > byte) { + bytex -= byte; + if ((bytex == 1) || (bytex == 2)) + bytex = 3; + else + bytex = 4; + } else { + byte -= bytex; + if ((byte == 0) || (byte == 1)) + bytex = 2; + else + bytex = 1; + } + + pDCTstat->TrwtTO = bytex; +} + + +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 + 1; +} + + +static u8 Check_DqsRcvEn_Diff(struct DCTStatStruc *pDCTstat, + u8 dct, u32 dev, u32 index_reg, + u32 index) +{ + u8 Smallest_0, Largest_0, Smallest_1, Largest_1; + u8 i; + u32 val; + u8 byte; + + Smallest_0 = 0xFF; + Smallest_1 = 0xFF; + Largest_0 = 0; + Largest_1 = 0; + + for (i=0; i < 8; i+=2) { + if ( pDCTstat->DIMMValid & (1 << i)) { + val = Get_NB32_index_wait(dev, index_reg, index); + byte = val & 0xFF; + if (byte < Smallest_0) + Smallest_0 = byte; + if (byte > Largest_0) + Largest_0 = byte; + byte = (val >> 16) & 0xFF; + if (byte < Smallest_1) + Smallest_1 = byte; + if (byte > Largest_1) + Largest_1 = byte; + } + index += 3; + } /* while ++i */ + + /* check if total DqsRcvEn delay difference between any + two DIMMs is less than half of a MEMCLK */ + if ((Largest_0 - Smallest_0) > 31) + return 1; + if ((Largest_1 - Smallest_1) > 31) + return 1; + return 0; +} + + +static u8 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->DqsRcvEnGrossL = Largest; + return Largest - Smallest; +} + + +static u8 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 */ + 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; + 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; + + // FIXME: Add Cx support. + + pDCTstat->WrDatGrossH = Largest; + return Largest - 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; + + Smallest = 7; + Largest = 0; + + 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; + 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) +{ + print_t("\tmct_FinalMCT_D: Clr Cl, Wb\n"); + + + mct_ClrClToNB_D(pMCTstat, pDCTstat); + mct_ClrWbEnhWsbDis_D(pMCTstat, pDCTstat); +} + + +static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat) +{ + print_t("\tmct_InitialMCT_D: Set Cl, Wb\n"); + 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); +} + + +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; + u8 cs; + 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)) { + for (cs = 0; cs < 8; cs++) { + if (word & (1 << cs)) { + if (!(cs & 1)) + word |= 1 << (cs + 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; + u8 cs; + u32 index; + u16 word; + + /* Tri-state unused CKEs when motherboard termination is available */ + + // FIXME: skip for Ax + + dev = pDCTstat->dev_dct; + word = 0x101; + for (cs = 0; cs < 8; cs++) { + if (pDCTstat->CSPresent & (1 << cs)) { + if (!(cs & 1)) + word &= 0xFF00; + else + word &= 0x00FF; + } + } + + index = 0x0c; + val = Get_NB32_index_wait(dev, index_reg, index); + if ((word & 0x00FF) == 1) + val |= 1 << 12; + else + val &= ~(1 << 12); + + if ((word >> 8) == 1) + val |= 1 << 13; + else + 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; + u16 word; + + /* Tri-state unused ODTs when motherboard termination is available */ + + // FIXME: skip for Ax + + dev = pDCTstat->dev_dct; + word = 0; + for (cs = 0; cs < 8; cs += 2) { + if (!(pDCTstat->CSPresent & (1 << cs))) { + if (!(pDCTstat->CSPresent & (1 << (cs + 1)))) + word |= (1 << (cs >> 1)); + } + } + + index = 0x0C; + val = Get_NB32_index_wait(dev, index_reg, index); + val |= (word << 8); + 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->Speed == 2) && (pDCTstat->MAdimms[dct] == 4)) + dword &= 0xF18FFF18; + + Set_NB32_index_wait(dev, index_reg, 0x0a, dword); +} + + +static void WaitRoutine_D(u32 time) +{ + while(time) { + _EXECFENCE; + time--; + } +} + + +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); + + //FIXME: check for Cx + 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; + u16 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 = ((u16) &((struct DCTStatStruc *)0)->CH_MaxRdLat[2]); + for (i = start; i < stop ; i++) { + p[i] = 0; + } + + start = ((u16) &((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; + u32 dev = pDCTstat->dev_dct; + + // FIXME: skip for Ax + if ((pDCTstat->Speed == 3) || ( pDCTstat->Speed == 2)) { // MemClkFreq = 667MHz or 533Mhz + for (i=0; i < 2; i++) { + reg_off = 0x100 * i; + Set_NB32(dev, 0x98 + reg_off, 0x0D000030); + Set_NB32(dev, 0x9C + reg_off, 0x00000806); + Set_NB32(dev, 0x98 + reg_off, 0x4D040F30); + } + } +} + + +void mct_AdjustDelayRange_D(struct MCTStatStruc *pMCTstat, + struct DCTStatStruc *pDCTstat, u8 *dqs_pos) +{ + // FIXME: Skip for Ax + if ((pDCTstat->Speed == 3) || ( pDCTstat->Speed == 2)) { // MemClkFreq = 667MHz or 533Mhz + *dqs_pos = 32; + } +} + + +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); +} + + +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); + Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp)); + + //FIXME: check for Bx Cx CPU + // if Ax mct_SetDramConfigHi_Samp_D + + /* errata#177 */ + index = 0x4D014F00; /* F2x[1, 0]9C_x[D0FFFFF:D000000] DRAM Phy Debug Registers */ + index |= 1 << DctAccessWrite; + val = 0; + Set_NB32_index_wait(dev, index_reg, index, val); + + Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi); + + index = 0x08; + val = Get_NB32_index_wait(dev, index_reg, index); + Set_NB32_index_wait(dev, index_reg, index, val & (~(1 << DisAutoComp))); +} + +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 DDR2 training. + */ + + for (Node = 0; Node < 8; Node++) { + pDCTstat = pDCTstatA + Node; + + if (pDCTstat->NodePresent) + mct_BeforeDQSTrain_Samp_D(pMCTstat, pDCTstat); + 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 val; + u32 dev = pDCTstat->dev_dct; + u32 reg_off = 0x100 * dct; + u32 addr; + u8 valid = 0; + + 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 */ + Set_NB32(dev, 0x98 + reg_off, 0x0D00000C); + val = Get_NB32(dev, 0x9C + reg_off); + val |= 1 << 15; + Set_NB32(dev, 0x9C + reg_off, val); + Set_NB32(dev, 0x98 + reg_off, 0x4D0F0F0C); + mct_Wait_10ns(60); /* wait >= 300ns */ + + Set_NB32(dev, 0x98 + reg_off, 0x0D00000C); + val = Get_NB32(dev, 0x9C + reg_off); + val &= ~(1 << 15); + Set_NB32(dev, 0x9C + reg_off, val); + Set_NB32(dev, 0x98 + reg_off, 0x4D0F0F0C); + mct_Wait_10ns(400); /* wait >= 2us */ + break; + } + } + } +} + + +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 |= 36-32; // DisDatMask + Set_NB32(dev, 0x8C, val); + } +} + + +static void mct_SetupSync_D(struct MCTStatStruc *pMCTstat, + 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 |= 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) { + mct_Wait_10ns(5000); /* 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 + */ |