| /* |
| * This file is part of the coreboot project. |
| * |
| * Copyright (C) 2010 Advanced Micro Devices, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; version 2 of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* Description: Main memory controller system configuration for DDR 3 */ |
| |
| /* KNOWN ISSUES - ERRATA |
| * |
| * Trtp is not calculated correctly when the controller is in 64-bit mode, it |
| * is 1 busclock off. No fix planned. The controller is not ordinarily in |
| * 64-bit mode. |
| * |
| * 32 Byte burst not supported. No fix planned. The controller is not |
| * ordinarily in 64-bit mode. |
| * |
| * Trc precision does not use extra Jedec defined fractional component. |
| * InsteadTrc (course) is rounded up to nearest 1 ns. |
| * |
| * Mini and Micro DIMM not supported. Only RDIMM, UDIMM, SO-DIMM defined types |
| * supported. |
| */ |
| |
| static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void DQSTiming_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void MCTMemClr_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static u8 NodePresent_D(u8 Node); |
| static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void StartupDCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void ClearDCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void StitchMemory_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u16 Get_Fk_D(u8 k); |
| static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i); |
| static void mct_initDCT(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void mct_DramInit(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat); |
| static void Get_Trdrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,\ |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void Set_OtherTiming(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void Get_Twrwr(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void Get_Twrrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void Get_TrwtTO(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void Get_TrwtWB(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, |
| u32 dev, u32 index_reg); |
| static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, u8 dct, |
| u32 dev, u32 index_reg); |
| static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat, |
| u32 dev, u32 index_reg, u32 index); |
| static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct, |
| u32 dev, u32 index_reg, u32 index); |
| static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void mct_init(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void SetCSTriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void SetCKETriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void SetODTTriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void InitPhyCompensation(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u32 mct_NodePresent_D(void); |
| static void mct_OtherTiming(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| void mct_ClrWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat); |
| static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA); |
| static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void mct_DramInit_Sw_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct); |
| static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| |
| static u32 mct_MR1Odt_RDimm(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct, u32 MrsChipSel); |
| static u32 mct_DramTermDyn_RDimm(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dimm); |
| static u32 mct_SetDramConfigMisc2(struct DCTStatStruc *pDCTstat, u8 dct, u32 misc2); |
| static void mct_BeforeDQSTrainSamp(struct DCTStatStruc *pDCTstat); |
| static void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA); |
| static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct); |
| static void SyncSetting(struct DCTStatStruc *pDCTstat); |
| static u8 crcCheck(u8 smbaddr); |
| static void mct_ExtMCTConfig_Bx(struct DCTStatStruc *pDCTstat); |
| static void mct_ExtMCTConfig_Cx(struct DCTStatStruc *pDCTstat); |
| |
| /*See mctAutoInitMCT header for index relationships to CL and T*/ |
| static const u16 Table_F_k[] = {00,200,266,333,400,533 }; |
| static const u8 Tab_BankAddr[] = {0x3F,0x01,0x09,0x3F,0x3F,0x11,0x0A,0x19,0x12,0x1A,0x21,0x22,0x23}; |
| static const u8 Table_DQSRcvEn_Offset[] = {0x00,0x01,0x10,0x11,0x2}; |
| |
| /**************************************************************************** |
| Describe how platform maps MemClk pins to logical DIMMs. The MemClk pins |
| are identified based on BKDG definition of Fn2x88[MemClkDis] bitmap. |
| AGESA will base on this value to disable unused MemClk to save power. |
| |
| If MEMCLK_MAPPING or MEMCLK_MAPPING contains all zeroes, AGESA will use |
| default MemClkDis setting based on package type. |
| |
| Example: |
| BKDG definition of Fn2x88[MemClkDis] bitmap for AM3 package is like below: |
| Bit AM3/S1g3 pin name |
| 0 M[B,A]_CLK_H/L[0] |
| 1 M[B,A]_CLK_H/L[1] |
| 2 M[B,A]_CLK_H/L[2] |
| 3 M[B,A]_CLK_H/L[3] |
| 4 M[B,A]_CLK_H/L[4] |
| 5 M[B,A]_CLK_H/L[5] |
| 6 M[B,A]_CLK_H/L[6] |
| 7 M[B,A]_CLK_H/L[7] |
| |
| And platform has the following routing: |
| CS0 M[B,A]_CLK_H/L[4] |
| CS1 M[B,A]_CLK_H/L[2] |
| CS2 M[B,A]_CLK_H/L[3] |
| CS3 M[B,A]_CLK_H/L[5] |
| |
| Then: |
| ; CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7 |
| MEMCLK_MAPPING EQU 00010000b, 00000100b, 00001000b, 00100000b, 00000000b, 00000000b, 00000000b, 00000000b |
| */ |
| |
| /* Note: If you are not sure about the pin mappings at initial stage, we dont have to disable MemClk. |
| * Set entries in the tables all 0xFF. */ |
| static const u8 Tab_L1CLKDis[] = {0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04}; |
| static const u8 Tab_AM3CLKDis[] = {0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; |
| static const u8 Tab_S1CLKDis[] = {0xA2, 0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| static const u8 Tab_ManualCLKDis[]= {0x10, 0x04, 0x08, 0x20, 0x00, 0x00, 0x00, 0x00}; |
| |
| static const u8 Table_Comp_Rise_Slew_20x[] = {7, 3, 2, 2, 0xFF}; |
| static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF}; |
| static const u8 Table_Comp_Fall_Slew_20x[] = {7, 5, 3, 2, 0xFF}; |
| static const u8 Table_Comp_Fall_Slew_15x[] = {7, 7, 5, 3, 0xFF}; |
| |
| static void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| /* |
| * Memory may be mapped contiguously all the way up to 4GB (depending on setup |
| * options). It is the responsibility of PCI subsystem to create an uncacheable |
| * IO region below 4GB and to adjust TOP_MEM downward prior to any IO mapping or |
| * accesses. It is the same responsibility of the CPU sub-system prior to |
| * accessing LAPIC. |
| * |
| * Slot Number is an external convention, and is determined by OEM with accompanying |
| * silk screening. OEM may choose to use Slot number convention which is consistent |
| * with DIMM number conventions. All AMD engineering platforms do. |
| * |
| * Build Requirements: |
| * 1. MCT_SEG0_START and MCT_SEG0_END macros to begin and end the code segment, |
| * defined in mcti.inc. |
| * |
| * Run-Time Requirements: |
| * 1. Complete Hypertransport Bus Configuration |
| * 2. SMBus Controller Initialized |
| * 1. BSP in Big Real Mode |
| * 2. Stack at SS:SP, located somewhere between A000:0000 and F000:FFFF |
| * 3. Checksummed or Valid NVRAM bits |
| * 4. MCG_CTL=-1, MC4_CTL_EN=0 for all CPUs |
| * 5. MCi_STS from shutdown/warm reset recorded (if desired) prior to entry |
| * 6. All var MTRRs reset to zero |
| * 7. State of NB_CFG.DisDatMsk set properly on all CPUs |
| * 8. All CPUs at 2Ghz Speed (unless DQS training is not installed). |
| * 9. All cHT links at max Speed/Width (unless DQS training is not installed). |
| * |
| * |
| * Global relationship between index values and item values: |
| * |
| * pDCTstat.CASL pDCTstat.Speed |
| * j CL(j) k F(k) |
| * -------------------------- |
| * 0 2.0 - - |
| * 1 3.0 1 200 Mhz |
| * 2 4.0 2 266 Mhz |
| * 3 5.0 3 333 Mhz |
| * 4 6.0 4 400 Mhz |
| * 5 7.0 5 533 Mhz |
| * 6 8.0 6 667 Mhz |
| * 7 9.0 7 800 Mhz |
| */ |
| u8 Node, NodesWmem; |
| u32 node_sys_base; |
| |
| restartinit: |
| mctInitMemGPIOs_A_D(); /* Set any required GPIOs*/ |
| NodesWmem = 0; |
| node_sys_base = 0; |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| struct DCTStatStruc *pDCTstat; |
| pDCTstat = pDCTstatA + Node; |
| pDCTstat->Node_ID = Node; |
| pDCTstat->dev_host = PA_HOST(Node); |
| pDCTstat->dev_map = PA_MAP(Node); |
| pDCTstat->dev_dct = PA_DCT(Node); |
| pDCTstat->dev_nbmisc = PA_NBMISC(Node); |
| pDCTstat->NodeSysBase = node_sys_base; |
| |
| mct_init(pMCTstat, pDCTstat); |
| mctNodeIDDebugPort_D(); |
| pDCTstat->NodePresent = NodePresent_D(Node); |
| if (pDCTstat->NodePresent) { /* See if Node is there*/ |
| clear_legacy_Mode(pMCTstat, pDCTstat); |
| pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node); |
| |
| mct_InitialMCT_D(pMCTstat, pDCTstat); |
| |
| mctSMBhub_Init(Node); /* Switch SMBUS crossbar to proper node*/ |
| |
| mct_initDCT(pMCTstat, pDCTstat); |
| if (pDCTstat->ErrCode == SC_FatalErr) { |
| goto fatalexit; /* any fatal errors?*/ |
| } else if (pDCTstat->ErrCode < SC_StopError) { |
| NodesWmem++; |
| } |
| } /* if Node present */ |
| node_sys_base = pDCTstat->NodeSysBase; |
| node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F; |
| } |
| if (NodesWmem == 0) { |
| printk(BIOS_DEBUG, "No Nodes?!\n"); |
| goto fatalexit; |
| } |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: SyncDCTsReady_D\n"); |
| SyncDCTsReady_D(pMCTstat, pDCTstatA); /* Make sure DCTs are ready for accesses.*/ |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: HTMemMapInit_D\n"); |
| HTMemMapInit_D(pMCTstat, pDCTstatA); /* Map local memory into system address space.*/ |
| mctHookAfterHTMap(); |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: CPUMemTyping_D\n"); |
| CPUMemTyping_D(pMCTstat, pDCTstatA); /* Map dram into WB/UC CPU cacheability */ |
| mctHookAfterCPU(); /* Setup external northbridge(s) */ |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: DQSTiming_D\n"); |
| DQSTiming_D(pMCTstat, pDCTstatA); /* Get Receiver Enable and DQS signal timing*/ |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: UMAMemTyping_D\n"); |
| UMAMemTyping_D(pMCTstat, pDCTstatA); /* Fix up for UMA sizing */ |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: :OtherTiming\n"); |
| mct_OtherTiming(pMCTstat, pDCTstatA); |
| |
| if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/ |
| goto restartinit; |
| } |
| |
| InterleaveNodes_D(pMCTstat, pDCTstatA); |
| InterleaveChannels_D(pMCTstat, pDCTstatA); |
| |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: ECCInit_D\n"); |
| if (ECCInit_D(pMCTstat, pDCTstatA)) { /* Setup ECC control and ECC check-bits*/ |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D: MCTMemClr_D\n"); |
| MCTMemClr_D(pMCTstat,pDCTstatA); |
| } |
| |
| mct_FinalMCT_D(pMCTstat, pDCTstatA); |
| printk(BIOS_DEBUG, "mctAutoInitMCT_D Done: Global Status: %x\n", pMCTstat->GStatus); |
| return; |
| |
| fatalexit: |
| die("mct_d: fatalexit"); |
| } |
| |
| static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 ret; |
| |
| if (mctGet_NVbits(NV_CS_SpareCTL)) { |
| if (MCT_DIMM_SPARE_NO_WARM) { |
| /* Do no warm-reset DIMM spare */ |
| if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { |
| LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA); |
| ret = 0; |
| } else { |
| mct_ResetDataStruct_D(pMCTstat, pDCTstatA); |
| pMCTstat->GStatus |= 1 << GSB_EnDIMMSpareNW; |
| ret = 1; |
| } |
| } else { |
| /* Do warm-reset DIMM spare */ |
| if (mctGet_NVbits(NV_DQSTrainCTL)) |
| mctWarmReset_D(); |
| ret = 0; |
| } |
| } else { |
| ret = 0; |
| } |
| |
| return ret; |
| } |
| |
| static void DQSTiming_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 nv_DQSTrainCTL; |
| |
| if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { |
| return; |
| } |
| |
| nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL); |
| /* FIXME: BOZO- DQS training every time*/ |
| nv_DQSTrainCTL = 1; |
| |
| mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA); |
| phyAssistedMemFnceTraining(pMCTstat, pDCTstatA); |
| |
| if (nv_DQSTrainCTL) { |
| mctHookBeforeAnyTraining(pMCTstat, pDCTstatA); |
| /* TODO: should be in mctHookBeforeAnyTraining */ |
| _WRMSR(0x26C, 0x04040404, 0x04040404); |
| _WRMSR(0x26D, 0x04040404, 0x04040404); |
| _WRMSR(0x26E, 0x04040404, 0x04040404); |
| _WRMSR(0x26F, 0x04040404, 0x04040404); |
| mct_WriteLevelization_HW(pMCTstat, pDCTstatA); |
| |
| TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass); |
| |
| mct_TrainDQSPos_D(pMCTstat, pDCTstatA); |
| |
| /* Second Pass never used for Barcelona! */ |
| /* TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass); */ |
| |
| mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA); |
| |
| /* FIXME - currently uses calculated value TrainMaxReadLatency_D(pMCTstat, pDCTstatA); */ |
| mctHookAfterAnyTraining(); |
| mctSaveDQSSigTmg_D(); |
| |
| MCTMemClr_D(pMCTstat, pDCTstatA); |
| } else { |
| mctGetDQSSigTmg_D(); /* get values into data structure */ |
| LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA); /* load values into registers.*/ |
| /* mctDoWarmResetMemClr_D(); */ |
| MCTMemClr_D(pMCTstat, pDCTstatA); |
| } |
| } |
| |
| static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node, Receiver, Channel, Dir, DIMM; |
| u32 dev; |
| u32 index_reg; |
| u32 reg; |
| u32 index; |
| u32 val; |
| u8 ByteLane; |
| u8 txdqs; |
| |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| struct DCTStatStruc *pDCTstat; |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->DCTSysLimit) { |
| dev = pDCTstat->dev_dct; |
| for (Channel = 0;Channel < 2; Channel++) { |
| /* there are four receiver pairs, |
| loosely associated with chipselects.*/ |
| index_reg = 0x98 + Channel * 0x100; |
| for (Receiver = 0; Receiver < 8; Receiver += 2) { |
| /* Set Receiver Enable Values */ |
| mct_SetRcvrEnDly_D(pDCTstat, |
| 0, /* RcvrEnDly */ |
| 1, /* FinalValue, From stack */ |
| Channel, |
| Receiver, |
| dev, index_reg, |
| (Receiver >> 1) * 3 + 0x10, /* Addl_Index */ |
| 2); /* Pass Second Pass ? */ |
| /* Restore Write levelization training data */ |
| for (ByteLane = 0; ByteLane < 9; ByteLane ++) { |
| txdqs = pDCTstat->CH_D_B_TxDqs[Channel][Receiver >> 1][ByteLane]; |
| index = Table_DQSRcvEn_Offset[ByteLane >> 1]; |
| index += (Receiver >> 1) * 3 + 0x10 + 0x20; /* Addl_Index */ |
| val = Get_NB32_index_wait(dev, 0x98 + 0x100*Channel, index); |
| if (ByteLane & 1) { /* odd byte lane */ |
| val &= ~(0xFF << 16); |
| val |= txdqs << 16; |
| } else { |
| val &= ~0xFF; |
| val |= txdqs; |
| } |
| Set_NB32_index_wait(dev, 0x98 + 0x100*Channel, index, val); |
| } |
| } |
| } |
| for (Channel = 0; Channel<2; Channel++) { |
| SetEccDQSRcvrEn_D(pDCTstat, Channel); |
| } |
| |
| for (Channel = 0; Channel < 2; Channel++) { |
| u8 *p; |
| index_reg = 0x98 + Channel * 0x100; |
| |
| /* NOTE: |
| * when 400, 533, 667, it will support dimm0/1/2/3, |
| * and set conf for dimm0, hw will copy to dimm1/2/3 |
| * set for dimm1, hw will copy to dimm3 |
| * Rev A/B only support DIMM0/1 when 800Mhz and above |
| * + 0x100 to next dimm |
| * Rev C support DIMM0/1/2/3 when 800Mhz and above |
| * + 0x100 to next dimm |
| */ |
| for (DIMM = 0; DIMM < 4; DIMM++) { |
| if (DIMM == 0) { |
| index = 0; /* CHA Write Data Timing Low */ |
| } else { |
| if (pDCTstat->Speed >= 4) { |
| index = 0x100 * DIMM; |
| } else { |
| break; |
| } |
| } |
| for (Dir = 0; Dir < 2; Dir++) {/* RD/WR */ |
| p = pDCTstat->CH_D_DIR_B_DQS[Channel][DIMM][Dir]; |
| val = stream_to_int(p); /* CHA Read Data Timing High */ |
| Set_NB32_index_wait(dev, index_reg, index+1, val); |
| val = stream_to_int(p+4); /* CHA Write Data Timing High */ |
| Set_NB32_index_wait(dev, index_reg, index+2, val); |
| val = *(p+8); /* CHA Write ECC Timing */ |
| Set_NB32_index_wait(dev, index_reg, index+3, val); |
| index += 4; |
| } |
| } |
| } |
| |
| for (Channel = 0; Channel<2; Channel++) { |
| reg = 0x78 + Channel * 0x100; |
| val = Get_NB32(dev, reg); |
| val &= ~(0x3ff<<22); |
| val |= ((u32) pDCTstat->CH_MaxRdLat[Channel] << 22); |
| val &= ~(1<<DqsRcvEnTrain); |
| Set_NB32(dev, reg, val); /* program MaxRdLatency to correspond with current delay*/ |
| } |
| } |
| } |
| } |
| |
| static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| u32 NextBase, BottomIO; |
| u8 _MemHoleRemap, DramHoleBase, DramHoleOffset; |
| u32 HoleSize, DramSelBaseAddr; |
| |
| u32 val; |
| u32 base; |
| u32 limit; |
| u32 dev, devx; |
| struct DCTStatStruc *pDCTstat; |
| |
| _MemHoleRemap = mctGet_NVbits(NV_MemHole); |
| |
| if (pMCTstat->HoleBase == 0) { |
| DramHoleBase = mctGet_NVbits(NV_BottomIO); |
| } else { |
| DramHoleBase = pMCTstat->HoleBase >> (24-8); |
| } |
| |
| BottomIO = DramHoleBase << (24-8); |
| |
| NextBase = 0; |
| pDCTstat = pDCTstatA + 0; |
| dev = pDCTstat->dev_map; |
| |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| devx = pDCTstat->dev_map; |
| DramSelBaseAddr = 0; |
| pDCTstat = pDCTstatA + Node; /* ??? */ |
| if (!pDCTstat->GangedMode) { |
| DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit; |
| /*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */ |
| val = pDCTstat->NodeSysLimit; |
| if ((val & 0xFF) == 0xFE) { |
| DramSelBaseAddr++; |
| val++; |
| } |
| pDCTstat->DCTSysLimit = val; |
| } |
| |
| base = pDCTstat->DCTSysBase; |
| limit = pDCTstat->DCTSysLimit; |
| if (limit > base) { |
| base += NextBase; |
| limit += NextBase; |
| DramSelBaseAddr += NextBase; |
| printk(BIOS_DEBUG, " Node: %02x base: %02x limit: %02x BottomIO: %02x\n", Node, base, limit, BottomIO); |
| |
| if (_MemHoleRemap) { |
| if ((base < BottomIO) && (limit >= BottomIO)) { |
| /* HW Dram Remap */ |
| pDCTstat->Status |= 1 << SB_HWHole; |
| pMCTstat->GStatus |= 1 << GSB_HWHole; |
| pDCTstat->DCTSysBase = base; |
| pDCTstat->DCTSysLimit = limit; |
| pDCTstat->DCTHoleBase = BottomIO; |
| pMCTstat->HoleBase = BottomIO; |
| HoleSize = _4GB_RJ8 - BottomIO; /* HoleSize[39:8] */ |
| if ((DramSelBaseAddr > 0) && (DramSelBaseAddr < BottomIO)) |
| base = DramSelBaseAddr; |
| val = ((base + HoleSize) >> (24-8)) & 0xFF; |
| DramHoleOffset = val; |
| val <<= 8; /* shl 16, rol 24 */ |
| val |= DramHoleBase << 24; |
| val |= 1 << DramHoleValid; |
| Set_NB32(devx, 0xF0, val); /* Dram Hole Address Reg */ |
| pDCTstat->DCTSysLimit += HoleSize; |
| base = pDCTstat->DCTSysBase; |
| limit = pDCTstat->DCTSysLimit; |
| } else if (base == BottomIO) { |
| /* SW Node Hoist */ |
| pMCTstat->GStatus |= 1<<GSB_SpIntRemapHole; |
| pDCTstat->Status |= 1<<SB_SWNodeHole; |
| pMCTstat->GStatus |= 1<<GSB_SoftHole; |
| pMCTstat->HoleBase = base; |
| limit -= base; |
| base = _4GB_RJ8; |
| limit += base; |
| pDCTstat->DCTSysBase = base; |
| pDCTstat->DCTSysLimit = limit; |
| } else { |
| /* No Remapping. Normal Contiguous mapping */ |
| pDCTstat->DCTSysBase = base; |
| pDCTstat->DCTSysLimit = limit; |
| } |
| } else { |
| /*No Remapping. Normal Contiguous mapping*/ |
| pDCTstat->DCTSysBase = base; |
| pDCTstat->DCTSysLimit = limit; |
| } |
| base |= 3; /* set WE,RE fields*/ |
| pMCTstat->SysLimit = limit; |
| } |
| Set_NB32(dev, 0x40 + (Node << 3), base); /* [Node] + Dram Base 0 */ |
| |
| val = limit & 0xFFFF0000; |
| val |= Node; |
| Set_NB32(dev, 0x44 + (Node << 3), val); /* set DstNode */ |
| |
| printk(BIOS_DEBUG, " Node: %02x base: %02x limit: %02x \n", Node, base, limit); |
| limit = pDCTstat->DCTSysLimit; |
| if (limit) { |
| NextBase = (limit & 0xFFFF0000) + 0x10000; |
| } |
| } |
| |
| /* Copy dram map from Node 0 to Node 1-7 */ |
| for (Node = 1; Node < MAX_NODES_SUPPORTED; Node++) { |
| u32 reg; |
| pDCTstat = pDCTstatA + Node; |
| devx = pDCTstat->dev_map; |
| |
| if (pDCTstat->NodePresent) { |
| reg = 0x40; /*Dram Base 0*/ |
| do { |
| val = Get_NB32(dev, reg); |
| Set_NB32(devx, reg, val); |
| reg += 4; |
| } while ( reg < 0x80); |
| } else { |
| break; /* stop at first absent Node */ |
| } |
| } |
| |
| /*Copy dram map to F1x120/124*/ |
| mct_HTMemMapExt(pMCTstat, pDCTstatA); |
| } |
| |
| static void MCTMemClr_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| |
| /* Initiates a memory clear operation for all node. The mem clr |
| * is done in parallel. After the memclr is complete, all processors |
| * status are checked to ensure that memclr has completed. |
| */ |
| u8 Node; |
| struct DCTStatStruc *pDCTstat; |
| |
| if (!mctGet_NVbits(NV_DQSTrainCTL)){ |
| /* FIXME: callback to wrapper: mctDoWarmResetMemClr_D */ |
| } else { /* NV_DQSTrainCTL == 1 */ |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->NodePresent) { |
| DCTMemClr_Init_D(pMCTstat, pDCTstat); |
| } |
| } |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->NodePresent) { |
| DCTMemClr_Sync_D(pMCTstat, pDCTstat); |
| } |
| } |
| } |
| } |
| |
| static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 val; |
| u32 dev; |
| u32 reg; |
| |
| /* Initiates a memory clear operation on one node */ |
| if (pDCTstat->DCTSysLimit) { |
| dev = pDCTstat->dev_dct; |
| reg = 0x110; |
| |
| do { |
| val = Get_NB32(dev, reg); |
| } while (val & (1 << MemClrBusy)); |
| |
| val |= (1 << MemClrInit); |
| Set_NB32(dev, reg, val); |
| } |
| } |
| |
| static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| /* Ensures that memory clear has completed on all node.*/ |
| u8 Node; |
| struct DCTStatStruc *pDCTstat; |
| |
| if (!mctGet_NVbits(NV_DQSTrainCTL)){ |
| /* callback to wrapper: mctDoWarmResetMemClr_D */ |
| } else { /* NV_DQSTrainCTL == 1 */ |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->NodePresent) { |
| DCTMemClr_Sync_D(pMCTstat, pDCTstat); |
| } |
| } |
| } |
| } |
| |
| static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 val; |
| u32 dev = pDCTstat->dev_dct; |
| u32 reg; |
| |
| /* Ensure that a memory clear operation has completed on one node */ |
| if (pDCTstat->DCTSysLimit){ |
| reg = 0x110; |
| |
| do { |
| val = Get_NB32(dev, reg); |
| } while (val & (1 << MemClrBusy)); |
| |
| do { |
| val = Get_NB32(dev, reg); |
| } while (!(val & (1 << Dr_MemClrStatus))); |
| } |
| |
| val = 0x0FE40FC0; /* BKDG recommended */ |
| val |= MCCH_FlushWrOnStpGnt; /* Set for S3 */ |
| Set_NB32(dev, 0x11C, val); |
| } |
| |
| static u8 NodePresent_D(u8 Node) |
| { |
| /* |
| * Determine if a single Hammer Node exists within the network. |
| */ |
| u32 dev; |
| u32 val; |
| u32 dword; |
| u8 ret = 0; |
| |
| dev = PA_HOST(Node); /*test device/vendor id at host bridge */ |
| val = Get_NB32(dev, 0); |
| dword = mct_NodePresent_D(); /* FIXME: BOZO -11001022h rev for F */ |
| if (val == dword) { /* AMD Hammer Family CPU HT Configuration */ |
| if (oemNodePresent_D(Node, &ret)) |
| goto finish; |
| /* Node ID register */ |
| val = Get_NB32(dev, 0x60); |
| val &= 0x07; |
| dword = Node; |
| if (val == dword) /* current nodeID = requested nodeID ? */ |
| ret = 1; |
| } |
| finish: |
| return ret; |
| } |
| |
| static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* |
| * Initialize DRAM on single Athlon 64/Opteron Node. |
| */ |
| u8 stopDCTflag; |
| u32 val; |
| |
| ClearDCT_D(pMCTstat, pDCTstat, dct); |
| stopDCTflag = 1; /*preload flag with 'disable' */ |
| /* enable DDR3 support */ |
| val = Get_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100); |
| val |= 1 << Ddr3Mode; |
| Set_NB32(pDCTstat->dev_dct, 0x94 + dct * 0x100, val); |
| if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_DIMMPresence Done\n"); |
| if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: mct_SPDCalcWidth Done\n"); |
| if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoCycTiming_D Done\n"); |
| if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: AutoConfig_D Done\n"); |
| if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: PlatformSpec_D Done\n"); |
| stopDCTflag = 0; |
| if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) { |
| printk(BIOS_DEBUG, "\t\tDCTInit_D: StartupDCT_D\n"); |
| StartupDCT_D(pMCTstat, pDCTstat, dct); /*yeaahhh! */ |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| if (stopDCTflag) { |
| u32 reg_off = dct * 0x100; |
| val = 1<<DisDramInterface; |
| Set_NB32(pDCTstat->dev_dct, reg_off+0x94, val); |
| /*To maximize power savings when DisDramInterface=1b, |
| all of the MemClkDis bits should also be set.*/ |
| val = 0xFF000000; |
| Set_NB32(pDCTstat->dev_dct, reg_off+0x88, val); |
| } else { |
| mct_EnDllShutdownSR(pMCTstat, pDCTstat, dct); |
| } |
| } |
| |
| static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| /* Wait (and block further access to dram) for all DCTs to be ready, |
| * by polling all InitDram bits and waiting for possible memory clear |
| * operations to be complete. Read MemClkFreqVal bit to see if |
| * the DIMMs are present in this node. |
| */ |
| u8 Node; |
| u32 val; |
| |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| struct DCTStatStruc *pDCTstat; |
| pDCTstat = pDCTstatA + Node; |
| mct_SyncDCTsReady(pDCTstat); |
| } |
| /* v6.1.3 */ |
| /* re-enable phy compensation engine when dram init is completed on all nodes. */ |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| struct DCTStatStruc *pDCTstat; |
| pDCTstat = pDCTstatA + Node; |
| if (pDCTstat->NodePresent) { |
| if (pDCTstat->DIMMValidDCT[0] > 0 || pDCTstat->DIMMValidDCT[1] > 0) { |
| /* re-enable phy compensation engine when dram init on both DCTs is completed. */ |
| val = Get_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8); |
| val &= ~(1 << DisAutoComp); |
| Set_NB32_index_wait(pDCTstat->dev_dct, 0x98, 0x8, val); |
| } |
| } |
| } |
| /* wait 750us before any memory access can be made. */ |
| mct_Wait(15000); |
| } |
| |
| static void StartupDCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Read MemClkFreqVal bit to see if the DIMMs are present in this node. |
| * If the DIMMs are present then set the DRAM Enable bit for this node. |
| * |
| * Setting dram init starts up the DCT state machine, initializes the |
| * dram devices with MRS commands, and kicks off any |
| * HW memory clear process that the chip is capable of. The sooner |
| * that dram init is set for all nodes, the faster the memory system |
| * initialization can complete. Thus, the init loop is unrolled into |
| * two loops so as to start the processes for non BSP nodes sooner. |
| * This procedure will not wait for the process to finish. |
| * Synchronization is handled elsewhere. |
| */ |
| u32 val; |
| u32 dev; |
| u32 reg_off = dct * 0x100; |
| |
| dev = pDCTstat->dev_dct; |
| val = Get_NB32(dev, 0x94 + reg_off); |
| if (val & (1<<MemClkFreqVal)) { |
| mctHookBeforeDramInit(); /* generalized Hook */ |
| if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) |
| mct_DramInit(pMCTstat, pDCTstat, dct); |
| AfterDramInit_D(pDCTstat, dct); |
| mctHookAfterDramInit(); /* generalized Hook*/ |
| } |
| } |
| |
| static void ClearDCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 reg_end; |
| u32 dev = pDCTstat->dev_dct; |
| u32 reg = 0x40 + 0x100 * dct; |
| u32 val = 0; |
| |
| if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { |
| reg_end = 0x78 + 0x100 * dct; |
| } else { |
| reg_end = 0xA4 + 0x100 * dct; |
| } |
| |
| while(reg < reg_end) { |
| if ((reg & 0xFF) == 0x90) { |
| if (pDCTstat->LogicalCPUID & AMD_DR_Dx) { |
| val = Get_NB32(dev, reg); /* get DRAMConfigLow */ |
| val |= 0x08000000; /* preserve value of DisDllShutdownSR for only Rev.D */ |
| } |
| } |
| Set_NB32(dev, reg, val); |
| val = 0; |
| reg += 4; |
| } |
| |
| val = 0; |
| dev = pDCTstat->dev_map; |
| reg = 0xF0; |
| Set_NB32(dev, reg, val); |
| } |
| |
| static void SPD2ndTiming(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 i; |
| u16 Twr, Trtp; |
| u16 Trp, Trrd, Trcd, Tras, Trc; |
| u8 Trfc[4]; |
| u16 Tfaw; |
| u32 DramTimingLo, DramTimingHi; |
| u8 tCK16x; |
| u16 Twtr; |
| u8 LDIMM; |
| u8 MTB16x; |
| u8 byte; |
| u32 dword; |
| u32 dev; |
| u32 reg_off; |
| u32 val; |
| u16 smbaddr; |
| |
| /* Gather all DIMM mini-max values for cycle timing data */ |
| Trp = 0; |
| Trrd = 0; |
| Trcd = 0; |
| Trtp = 0; |
| Tras = 0; |
| Trc = 0; |
| Twr = 0; |
| Twtr = 0; |
| for (i=0; i < 4; i++) |
| Trfc[i] = 0; |
| Tfaw = 0; |
| |
| for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) { |
| LDIMM = i >> 1; |
| if (pDCTstat->DIMMValid & (1 << i)) { |
| smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i)); |
| |
| val = mctRead_SPD(smbaddr, SPD_MTBDivisor); /* MTB=Dividend/Divisor */ |
| MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4); |
| MTB16x /= val; /* transfer to MTB*16 */ |
| |
| byte = mctRead_SPD(smbaddr, SPD_tRPmin); |
| val = byte * MTB16x; |
| if (Trp < val) |
| Trp = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_tRRDmin); |
| val = byte * MTB16x; |
| if (Trrd < val) |
| Trrd = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_tRCDmin); |
| val = byte * MTB16x; |
| if (Trcd < val) |
| Trcd = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_tRTPmin); |
| val = byte * MTB16x; |
| if (Trtp < val) |
| Trtp = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_tWRmin); |
| val = byte * MTB16x; |
| if (Twr < val) |
| Twr = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_tWTRmin); |
| val = byte * MTB16x; |
| if (Twtr < val) |
| Twtr = val; |
| |
| val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xFF; |
| val >>= 4; |
| val <<= 8; |
| val |= mctRead_SPD(smbaddr, SPD_tRCmin) & 0xFF; |
| val *= MTB16x; |
| if (Trc < val) |
| Trc = val; |
| |
| byte = mctRead_SPD(smbaddr, SPD_Density) & 0xF; |
| if (Trfc[LDIMM] < byte) |
| Trfc[LDIMM] = byte; |
| |
| val = mctRead_SPD(smbaddr, SPD_Upper_tRAS_tRC) & 0xF; |
| val <<= 8; |
| val |= (mctRead_SPD(smbaddr, SPD_tRASmin) & 0xFF); |
| val *= MTB16x; |
| if (Tras < val) |
| Tras = val; |
| |
| val = mctRead_SPD(smbaddr, SPD_Upper_tFAW) & 0xF; |
| val <<= 8; |
| val |= mctRead_SPD(smbaddr, SPD_tFAWmin) & 0xFF; |
| val *= MTB16x; |
| if (Tfaw < val) |
| Tfaw = val; |
| } /* Dimm Present */ |
| } |
| |
| /* Convert DRAM CycleTiming values and store into DCT structure */ |
| byte = pDCTstat->DIMMAutoSpeed; |
| if (byte == 7) |
| tCK16x = 20; |
| else if (byte == 6) |
| tCK16x = 24; |
| else if (byte == 5) |
| tCK16x = 30; |
| else |
| tCK16x = 40; |
| |
| /* Notes: |
| 1. All secondary time values given in SPDs are in binary with units of ns. |
| 2. Some time values are scaled by 16, in order to have least count of 0.25 ns |
| (more accuracy). JEDEC SPD spec. shows which ones are x1 and x4. |
| 3. Internally to this SW, cycle time, tCK16x, is scaled by 16 to match time values |
| */ |
| |
| /* Tras */ |
| pDCTstat->DIMMTras = (u16)Tras; |
| val = Tras / tCK16x; |
| if (Tras % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TrasT) |
| val = Min_TrasT; |
| else if (val > Max_TrasT) |
| val = Max_TrasT; |
| pDCTstat->Tras = val; |
| |
| /* Trp */ |
| pDCTstat->DIMMTrp = Trp; |
| val = Trp / tCK16x; |
| if (Trp % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TrpT) |
| val = Min_TrpT; |
| else if (val > Max_TrpT) |
| val = Max_TrpT; |
| pDCTstat->Trp = val; |
| |
| /*Trrd*/ |
| pDCTstat->DIMMTrrd = Trrd; |
| val = Trrd / tCK16x; |
| if (Trrd % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TrrdT) |
| val = Min_TrrdT; |
| else if (val > Max_TrrdT) |
| val = Max_TrrdT; |
| pDCTstat->Trrd = val; |
| |
| /* Trcd */ |
| pDCTstat->DIMMTrcd = Trcd; |
| val = Trcd / tCK16x; |
| if (Trcd % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TrcdT) |
| val = Min_TrcdT; |
| else if (val > Max_TrcdT) |
| val = Max_TrcdT; |
| pDCTstat->Trcd = val; |
| |
| /* Trc */ |
| pDCTstat->DIMMTrc = Trc; |
| val = Trc / tCK16x; |
| if (Trc % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TrcT) |
| val = Min_TrcT; |
| else if (val > Max_TrcT) |
| val = Max_TrcT; |
| pDCTstat->Trc = val; |
| |
| /* Trtp */ |
| pDCTstat->DIMMTrtp = Trtp; |
| val = Trtp / tCK16x; |
| if (Trtp % tCK16x) { |
| val ++; |
| } |
| if (val < Min_TrtpT) |
| val = Min_TrtpT; |
| else if (val > Max_TrtpT) |
| val = Max_TrtpT; |
| pDCTstat->Trtp = val; |
| |
| /* Twr */ |
| pDCTstat->DIMMTwr = Twr; |
| val = Twr / tCK16x; |
| if (Twr % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TwrT) |
| val = Min_TwrT; |
| else if (val > Max_TwrT) |
| val = Max_TwrT; |
| pDCTstat->Twr = val; |
| |
| /* Twtr */ |
| pDCTstat->DIMMTwtr = Twtr; |
| val = Twtr / tCK16x; |
| if (Twtr % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TwtrT) |
| val = Min_TwtrT; |
| else if (val > Max_TwtrT) |
| val = Max_TwtrT; |
| pDCTstat->Twtr = val; |
| |
| /* Trfc0-Trfc3 */ |
| for (i=0; i<4; i++) |
| pDCTstat->Trfc[i] = Trfc[i]; |
| |
| /* Tfaw */ |
| pDCTstat->DIMMTfaw = Tfaw; |
| val = Tfaw / tCK16x; |
| if (Tfaw % tCK16x) { /* round up number of busclocks */ |
| val++; |
| } |
| if (val < Min_TfawT) |
| val = Min_TfawT; |
| else if (val > Max_TfawT) |
| val = Max_TfawT; |
| pDCTstat->Tfaw = val; |
| |
| mctAdjustAutoCycTmg_D(); |
| |
| /* Program DRAM Timing values */ |
| DramTimingLo = 0; /* Dram Timing Low init */ |
| val = pDCTstat->CASL - 2; /* pDCTstat.CASL to reg. definition */ |
| DramTimingLo |= val; |
| |
| val = pDCTstat->Trcd - Bias_TrcdT; |
| DramTimingLo |= val<<4; |
| |
| val = pDCTstat->Trp - Bias_TrpT; |
| val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); |
| DramTimingLo |= val<<7; |
| |
| val = pDCTstat->Trtp - Bias_TrtpT; |
| DramTimingLo |= val<<10; |
| |
| val = pDCTstat->Tras - Bias_TrasT; |
| DramTimingLo |= val<<12; |
| |
| val = pDCTstat->Trc - Bias_TrcT; |
| DramTimingLo |= val<<16; |
| |
| val = pDCTstat->Trrd - Bias_TrrdT; |
| DramTimingLo |= val<<22; |
| |
| DramTimingHi = 0; /* Dram Timing High init */ |
| val = pDCTstat->Twtr - Bias_TwtrT; |
| DramTimingHi |= val<<8; |
| |
| val = 2; |
| DramTimingHi |= val<<16; |
| |
| val = 0; |
| for (i=4;i>0;i--) { |
| val <<= 3; |
| val |= Trfc[i-1]; |
| } |
| DramTimingHi |= val << 20; |
| |
| dev = pDCTstat->dev_dct; |
| reg_off = 0x100 * dct; |
| /* Twr */ |
| val = pDCTstat->Twr; |
| if (val == 10) |
| val = 9; |
| else if (val == 12) |
| val = 10; |
| val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); |
| val -= Bias_TwrT; |
| val <<= 4; |
| dword = Get_NB32(dev, 0x84 + reg_off); |
| dword &= ~0x70; |
| dword |= val; |
| Set_NB32(dev, 0x84 + reg_off, dword); |
| |
| /* Tfaw */ |
| val = pDCTstat->Tfaw; |
| val = mct_AdjustSPDTimings(pMCTstat, pDCTstat, val); |
| val -= Bias_TfawT; |
| val >>= 1; |
| val <<= 28; |
| dword = Get_NB32(dev, 0x94 + reg_off); |
| dword &= ~0xf0000000; |
| dword |= val; |
| Set_NB32(dev, 0x94 + reg_off, dword); |
| |
| /* dev = pDCTstat->dev_dct; */ |
| /* reg_off = 0x100 * dct; */ |
| |
| if (pDCTstat->Speed > 4) { |
| val = Get_NB32(dev, 0x88 + reg_off); |
| val &= 0xFF000000; |
| DramTimingLo |= val; |
| } |
| Set_NB32(dev, 0x88 + reg_off, DramTimingLo); /*DCT Timing Low*/ |
| |
| if (pDCTstat->Speed > 4) { |
| DramTimingHi |= 1 << DisAutoRefresh; |
| } |
| DramTimingHi |= 0x000018FF; |
| Set_NB32(dev, 0x8c + reg_off, DramTimingHi); /*DCT Timing Hi*/ |
| |
| /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ |
| } |
| |
| static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Initialize DCT Timing registers as per DIMM SPD. |
| * For primary timing (T, CL) use best case T value. |
| * For secondary timing params., use most aggressive settings |
| * of slowest DIMM. |
| * |
| * There are three components to determining "maximum frequency": |
| * SPD component, Bus load component, and "Preset" max frequency |
| * component. |
| * |
| * The SPD component is a function of the min cycle time specified |
| * by each DIMM, and the interaction of cycle times from all DIMMs |
| * in conjunction with CAS latency. The SPD component only applies |
| * when user timing mode is 'Auto'. |
| * |
| * The Bus load component is a limiting factor determined by electrical |
| * characteristics on the bus as a result of varying number of device |
| * loads. The Bus load component is specific to each platform but may |
| * also be a function of other factors. The bus load component only |
| * applies when user timing mode is 'Auto'. |
| * |
| * The Preset component is subdivided into three items and is |
| * the minimum of the set: Silicon revision, user limit |
| * setting when user timing mode is 'Auto' and memclock mode |
| * is 'Limit', OEM build specification of the maximum |
| * frequency. The Preset component is only applies when user |
| * timing mode is 'Auto'. |
| */ |
| |
| /* Get primary timing (CAS Latency and Cycle Time) */ |
| if (pDCTstat->Speed == 0) { |
| mctGet_MaxLoadFreq(pDCTstat); |
| |
| /* and Factor in presets (setup options, Si cap, etc.) */ |
| GetPresetmaxF_D(pMCTstat, pDCTstat); |
| |
| /* Go get best T and CL as specified by DIMM mfgs. and OEM */ |
| SPDGetTCL_D(pMCTstat, pDCTstat, dct); |
| /* skip callback mctForce800to1067_D */ |
| pDCTstat->Speed = pDCTstat->DIMMAutoSpeed; |
| pDCTstat->CASL = pDCTstat->DIMMCASL; |
| |
| } |
| mct_AfterGetCLT(pMCTstat, pDCTstat, dct); |
| |
| SPD2ndTiming(pMCTstat, pDCTstat, dct); |
| |
| printk(BIOS_DEBUG, "AutoCycTiming: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "AutoCycTiming: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "AutoCycTiming: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "AutoCycTiming: Done\n\n"); |
| |
| mctHookAfterAutoCycTmg(); |
| |
| return pDCTstat->ErrCode; |
| } |
| |
| static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| /* Get max frequency from OEM platform definition, from any user |
| * override (limiting) of max frequency, and from any Si Revision |
| * Specific information. Return the least of these three in |
| * DCTStatStruc.PresetmaxFreq. |
| */ |
| /* TODO: Set the proper max frequency in wrappers/mcti_d.c. */ |
| u16 proposedFreq; |
| u16 word; |
| |
| /* Get CPU Si Revision defined limit (NPT) */ |
| proposedFreq = 800; /* Rev F0 programmable max memclock is */ |
| |
| /*Get User defined limit if "limit" mode */ |
| if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) { |
| word = Get_Fk_D(mctGet_NVbits(NV_MemCkVal) + 1); |
| if (word < proposedFreq) |
| proposedFreq = word; |
| |
| /* Get Platform defined limit */ |
| word = mctGet_NVbits(NV_MAX_MEMCLK); |
| if (word < proposedFreq) |
| proposedFreq = word; |
| |
| word = pDCTstat->PresetmaxFreq; |
| if (word > proposedFreq) |
| word = proposedFreq; |
| |
| pDCTstat->PresetmaxFreq = word; |
| } |
| /* Check F3xE8[DdrMaxRate] for maximum DRAM data rate support */ |
| } |
| |
| static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Find the best T and CL primary timing parameter pair, per Mfg., |
| * for the given set of DIMMs, and store into DCTStatStruc |
| * (.DIMMAutoSpeed and .DIMMCASL). See "Global relationship between |
| * index values and item values" for definition of CAS latency |
| * index (j) and Frequency index (k). |
| */ |
| u8 i, CASLatLow, CASLatHigh; |
| u16 tAAmin16x; |
| u8 MTB16x; |
| u16 tCKmin16x; |
| u16 tCKproposed16x; |
| u8 CLactual, CLdesired, CLT_Fail; |
| |
| u8 smbaddr, byte = 0, bytex = 0; |
| |
| CASLatLow = 0xFF; |
| CASLatHigh = 0xFF; |
| tAAmin16x = 0; |
| tCKmin16x = 0; |
| CLT_Fail = 0; |
| |
| for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) { |
| if (pDCTstat->DIMMValid & (1 << i)) { |
| smbaddr = Get_DIMMAddress_D(pDCTstat, (dct + i)); |
| /* Step 1: Determine the common set of supported CAS Latency |
| * values for all modules on the memory channel using the CAS |
| * Latencies Supported in SPD bytes 14 and 15. |
| */ |
| byte = mctRead_SPD(smbaddr, SPD_CASLow); |
| CASLatLow &= byte; |
| byte = mctRead_SPD(smbaddr, SPD_CASHigh); |
| CASLatHigh &= byte; |
| /* Step 2: Determine tAAmin(all) which is the largest tAAmin |
| value for all modules on the memory channel (SPD byte 16). */ |
| byte = mctRead_SPD(smbaddr, SPD_MTBDivisor); |
| |
| MTB16x = ((mctRead_SPD(smbaddr, SPD_MTBDividend) & 0xFF)<<4); |
| MTB16x /= byte; /* transfer to MTB*16 */ |
| |
| byte = mctRead_SPD(smbaddr, SPD_tAAmin); |
| if (tAAmin16x < byte * MTB16x) |
| tAAmin16x = byte * MTB16x; |
| /* Step 3: Determine tCKmin(all) which is the largest tCKmin |
| value for all modules on the memory channel (SPD byte 12). */ |
| byte = mctRead_SPD(smbaddr, SPD_tCKmin); |
| |
| if (tCKmin16x < byte * MTB16x) |
| tCKmin16x = byte * MTB16x; |
| } |
| } |
| /* calculate tCKproposed16x */ |
| tCKproposed16x = 16000 / pDCTstat->PresetmaxFreq; |
| if (tCKmin16x > tCKproposed16x) |
| tCKproposed16x = tCKmin16x; |
| |
| /* mctHookTwo1333DimmOverride(); */ |
| /* For UDIMM, if there are two DDR3-1333 on the same channel, |
| downgrade DDR speed to 1066. */ |
| |
| /* TODO: get user manual tCK16x(Freq.) and overwrite current tCKproposed16x if manual. */ |
| if (tCKproposed16x == 20) |
| pDCTstat->TargetFreq = 7; |
| else if (tCKproposed16x <= 24) { |
| pDCTstat->TargetFreq = 6; |
| tCKproposed16x = 24; |
| } |
| else if (tCKproposed16x <= 30) { |
| pDCTstat->TargetFreq = 5; |
| tCKproposed16x = 30; |
| } |
| else { |
| pDCTstat->TargetFreq = 4; |
| tCKproposed16x = 40; |
| } |
| /* Running through this loop twice: |
| - First time find tCL at target frequency |
| - Second tim find tCL at 400MHz */ |
| |
| for (;;) { |
| CLT_Fail = 0; |
| /* Step 4: For a proposed tCK value (tCKproposed) between tCKmin(all) and tCKmax, |
| determine the desired CAS Latency. If tCKproposed is not a standard JEDEC |
| value (2.5, 1.875, 1.5, or 1.25 ns) then tCKproposed must be adjusted to the |
| next lower standard tCK value for calculating CLdesired. |
| CLdesired = ceiling ( tAAmin(all) / tCKproposed ) |
| where tAAmin is defined in Byte 16. The ceiling function requires that the |
| quotient be rounded up always. */ |
| CLdesired = tAAmin16x / tCKproposed16x; |
| if (tAAmin16x % tCKproposed16x) |
| CLdesired ++; |
| /* Step 5: Chose an actual CAS Latency (CLactual) that is greather than or equal |
| to CLdesired and is supported by all modules on the memory channel as |
| determined in step 1. If no such value exists, choose a higher tCKproposed |
| value and repeat steps 4 and 5 until a solution is found. */ |
| for (i = 0, CLactual = 4; i < 15; i++, CLactual++) { |
| if ((CASLatHigh << 8 | CASLatLow) & (1 << i)) { |
| if (CLdesired <= CLactual) |
| break; |
| } |
| } |
| if (i == 15) |
| CLT_Fail = 1; |
| /* Step 6: Once the calculation of CLactual is completed, the BIOS must also |
| verify that this CAS Latency value does not exceed tAAmax, which is 20 ns |
| for all DDR3 speed grades, by multiplying CLactual times tCKproposed. If |
| not, choose a lower CL value and repeat steps 5 and 6 until a solution is found. */ |
| if (CLactual * tCKproposed16x > 320) |
| CLT_Fail = 1; |
| /* get CL and T */ |
| if (!CLT_Fail) { |
| bytex = CLactual - 2; |
| if (tCKproposed16x == 20) |
| byte = 7; |
| else if (tCKproposed16x == 24) |
| byte = 6; |
| else if (tCKproposed16x == 30) |
| byte = 5; |
| else |
| byte = 4; |
| } else { |
| /* mctHookManualCLOverride */ |
| /* TODO: */ |
| } |
| |
| if (tCKproposed16x != 40) { |
| if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) { |
| pDCTstat->DIMMAutoSpeed = byte; |
| pDCTstat->DIMMCASL = bytex; |
| break; |
| } else { |
| pDCTstat->TargetCASL = bytex; |
| tCKproposed16x = 40; |
| } |
| } else { |
| pDCTstat->DIMMAutoSpeed = byte; |
| pDCTstat->DIMMCASL = bytex; |
| break; |
| } |
| } |
| |
| printk(BIOS_DEBUG, "SPDGetTCL_D: DIMMCASL %x\n", pDCTstat->DIMMCASL); |
| printk(BIOS_DEBUG, "SPDGetTCL_D: DIMMAutoSpeed %x\n", pDCTstat->DIMMAutoSpeed); |
| |
| printk(BIOS_DEBUG, "SPDGetTCL_D: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "SPDGetTCL_D: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "SPDGetTCL_D: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "SPDGetTCL_D: Done\n\n"); |
| } |
| |
| static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 dev; |
| u32 reg; |
| u32 val; |
| |
| mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct); |
| |
| if (pDCTstat->GangedMode == 1) { |
| mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1); |
| mct_BeforePlatformSpec(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_BeforePlatformSpec(pMCTstat, pDCTstat, dct); |
| mct_PlatformSpec(pMCTstat, pDCTstat, dct); |
| if (pDCTstat->DIMMAutoSpeed == 4) |
| InitPhyCompensation(pMCTstat, pDCTstat, dct); |
| mctHookAfterPSCfg(); |
| |
| return pDCTstat->ErrCode; |
| } |
| |
| static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 DramControl, DramTimingLo, Status; |
| u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2; |
| u32 val; |
| u32 reg_off; |
| u32 dev; |
| u16 word; |
| u32 dword; |
| u8 byte; |
| |
| DramConfigLo = 0; |
| DramConfigHi = 0; |
| DramConfigMisc = 0; |
| DramConfigMisc2 = 0; |
| |
| /* set bank addressing and Masks, plus CS pops */ |
| SPDSetBanks_D(pMCTstat, pDCTstat, dct); |
| if (pDCTstat->ErrCode == SC_StopError) |
| goto AutoConfig_exit; |
| |
| /* map chip-selects into local address space */ |
| StitchMemory_D(pMCTstat, pDCTstat, dct); |
| InterleaveBanks_D(pMCTstat, pDCTstat, dct); |
| |
| /* temp image of status (for convenience). RO usage! */ |
| Status = pDCTstat->Status; |
| |
| dev = pDCTstat->dev_dct; |
| reg_off = 0x100 * dct; |
| |
| |
| /* Build Dram Control Register Value */ |
| DramConfigMisc2 = Get_NB32 (dev, 0xA8 + reg_off); /* Dram Control*/ |
| DramControl = Get_NB32 (dev, 0x78 + reg_off); /* Dram Control*/ |
| |
| /* FIXME: Skip mct_checkForDxSupport */ |
| /* REV_CALL mct_DoRdPtrInit if not Dx */ |
| if (pDCTstat->LogicalCPUID & AMD_DR_Bx) |
| val = 5; |
| else |
| val = 6; |
| DramControl &= ~0xFF; |
| DramControl |= val; /* RdPtrInit = 6 for Cx CPU */ |
| |
| if (mctGet_NVbits(NV_CLKHZAltVidC3)) |
| DramControl |= 1<<16; /* check */ |
| |
| DramControl |= 0x00002A00; |
| |
| /* FIXME: Skip for Ax versions */ |
| /* callback not required - if (!mctParityControl_D()) */ |
| if (Status & (1 << SB_128bitmode)) |
| DramConfigLo |= 1 << Width128; /* 128-bit mode (normal) */ |
| |
| word = dct; |
| dword = X4Dimm; |
| while (word < 8) { |
| if (pDCTstat->Dimmx4Present & (1 << word)) |
| DramConfigLo |= 1 << dword; /* X4Dimm[3:0] */ |
| word++; |
| word++; |
| dword++; |
| } |
| |
| if (!(Status & (1 << SB_Registered))) |
| DramConfigLo |= 1 << UnBuffDimm; /* Unbuffered DIMMs */ |
| |
| if (mctGet_NVbits(NV_ECC_CAP)) |
| if (Status & (1 << SB_ECCDIMMs)) |
| if ( mctGet_NVbits(NV_ECC)) |
| DramConfigLo |= 1 << DimmEcEn; |
| |
| DramConfigLo = mct_DisDllShutdownSR(pMCTstat, pDCTstat, DramConfigLo, dct); |
| |
| /* Build Dram Config Hi Register Value */ |
| dword = pDCTstat->Speed; |
| DramConfigHi |= dword - 1; /* get MemClk encoding */ |
| DramConfigHi |= 1 << MemClkFreqVal; |
| |
| if (Status & (1 << SB_Registered)) |
| if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0)) |
| /* set only if x8 Registered DIMMs in System*/ |
| DramConfigHi |= 1 << RDqsEn; |
| |
| if (mctGet_NVbits(NV_CKE_CTL)) |
| /*Chip Select control of CKE*/ |
| DramConfigHi |= 1 << 16; |
| |
| /* Control Bank Swizzle */ |
| if (0) /* call back not needed mctBankSwizzleControl_D()) */ |
| DramConfigHi &= ~(1 << BankSwizzleMode); |
| else |
| DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */ |
| |
| /* Check for Quadrank DIMM presence */ |
| if ( pDCTstat->DimmQRPresent != 0) { |
| byte = mctGet_NVbits(NV_4RANKType); |
| if (byte == 2) |
| DramConfigHi |= 1 << 17; /* S4 (4-Rank SO-DIMMs) */ |
| else if (byte == 1) |
| DramConfigHi |= 1 << 18; /* R4 (4-Rank Registered DIMMs) */ |
| } |
| |
| if (0) /* call back not needed mctOverrideDcqBypMax_D ) */ |
| val = mctGet_NVbits(NV_BYPMAX); |
| else |
| val = 0x0f; /* recommended setting (default) */ |
| DramConfigHi |= val << 24; |
| |
| if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Bx)) |
| DramConfigHi |= 1 << DcqArbBypassEn; |
| |
| /* Build MemClkDis Value from Dram Timing Lo and |
| Dram Config Misc Registers |
| 1. We will assume that MemClkDis field has been preset prior to this |
| point. |
| 2. We will only set MemClkDis bits if a DIMM is NOT present AND if: |
| NV_AllMemClks <>0 AND SB_DiagClks ==0 */ |
| |
| /* Dram Timing Low (owns Clock Enable bits) */ |
| DramTimingLo = Get_NB32(dev, 0x88 + reg_off); |
| if (mctGet_NVbits(NV_AllMemClks) == 0) { |
| /* Special Jedec SPD diagnostic bit - "enable all clocks" */ |
| if (!(pDCTstat->Status & (1<<SB_DiagClks))) { |
| const u8 *p; |
| const u32 *q; |
| p = Tab_ManualCLKDis; |
| q = (u32 *)p; |
| |
| byte = mctGet_NVbits(NV_PACK_TYPE); |
| if (byte == PT_L1) |
| p = Tab_L1CLKDis; |
| else if (byte == PT_M2 || byte == PT_AS) |
| p = Tab_AM3CLKDis; |
| else |
| p = Tab_S1CLKDis; |
| |
| dword = 0; |
| byte = 0xFF; |
| while(dword < MAX_CS_SUPPORTED) { |
| if (pDCTstat->CSPresent & (1<<dword)){ |
| /* re-enable clocks for the enabled CS */ |
| val = p[dword]; |
| byte &= ~val; |
| } |
| dword++ ; |
| } |
| DramTimingLo |= byte << 24; |
| } |
| } |
| |
| printk(BIOS_DEBUG, "AutoConfig_D: DramControl: %x\n", DramControl); |
| printk(BIOS_DEBUG, "AutoConfig_D: DramTimingLo: %x\n", DramTimingLo); |
| printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc: %x\n", DramConfigMisc); |
| printk(BIOS_DEBUG, "AutoConfig_D: DramConfigMisc2: %x\n", DramConfigMisc2); |
| printk(BIOS_DEBUG, "AutoConfig_D: DramConfigLo: %x\n", DramConfigLo); |
| printk(BIOS_DEBUG, "AutoConfig_D: DramConfigHi: %x\n", DramConfigHi); |
| |
| /* Write Values to the registers */ |
| Set_NB32(dev, 0x78 + reg_off, DramControl); |
| Set_NB32(dev, 0x88 + reg_off, DramTimingLo); |
| Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc); |
| DramConfigMisc2 = mct_SetDramConfigMisc2(pDCTstat, dct, DramConfigMisc2); |
| Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2); |
| Set_NB32(dev, 0x90 + reg_off, DramConfigLo); |
| ProgDramMRSReg_D(pMCTstat, pDCTstat, dct); |
| dword = Get_NB32(dev, 0x94 + reg_off); |
| DramConfigHi |= dword; |
| mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi); |
| mct_EarlyArbEn_D(pMCTstat, pDCTstat, dct); |
| mctHookAfterAutoCfg(); |
| |
| /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ |
| |
| printk(BIOS_DEBUG, "AutoConfig: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "AutoConfig: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "AutoConfig: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "AutoConfig: Done\n\n"); |
| AutoConfig_exit: |
| return pDCTstat->ErrCode; |
| } |
| |
| static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Set bank addressing, program Mask values and build a chip-select |
| * population map. This routine programs PCI 0:24N:2x80 config register |
| * and PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3). |
| */ |
| u8 ChipSel, Rows, Cols, Ranks, Banks; |
| u32 BankAddrReg, csMask; |
| |
| u32 val; |
| u32 reg; |
| u32 dev; |
| u32 reg_off; |
| u8 byte; |
| u16 word; |
| u32 dword; |
| u16 smbaddr; |
| |
| dev = pDCTstat->dev_dct; |
| reg_off = 0x100 * dct; |
| |
| BankAddrReg = 0; |
| for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel+=2) { |
| byte = ChipSel; |
| if ((pDCTstat->Status & (1 << SB_64MuxedMode)) && ChipSel >=4) |
| byte -= 3; |
| |
| if (pDCTstat->DIMMValid & (1<<byte)) { |
| smbaddr = Get_DIMMAddress_D(pDCTstat, (ChipSel + dct)); |
| |
| byte = mctRead_SPD(smbaddr, SPD_Addressing); |
| Rows = (byte >> 3) & 0x7; /* Rows:0b=12-bit,... */ |
| Cols = byte & 0x7; /* Cols:0b=9-bit,... */ |
| |
| byte = mctRead_SPD(smbaddr, SPD_Density); |
| Banks = (byte >> 4) & 7; /* Banks:0b=3-bit,... */ |
| |
| byte = mctRead_SPD(smbaddr, SPD_Organization); |
| Ranks = ((byte >> 3) & 7) + 1; |
| |
| /* Configure Bank encoding |
| * Use a 6-bit key into a lookup table. |
| * Key (index) = RRRBCC, where CC is the number of Columns minus 9, |
| * RRR is the number of Rows minus 12, and B is the number of banks |
| * minus 3. |
| */ |
| byte = Cols; |
| if (Banks == 1) |
| byte |= 4; |
| |
| byte |= Rows << 3; /* RRRBCC internal encode */ |
| |
| for (dword=0; dword < 13; dword++) { |
| if (byte == Tab_BankAddr[dword]) |
| break; |
| } |
| |
| if (dword > 12) |
| continue; |
| |
| /* bit no. of CS field in address mapping reg.*/ |
| dword <<= (ChipSel<<1); |
| BankAddrReg |= dword; |
| |
| /* Mask value=(2pow(rows+cols+banks+3)-1)>>8, |
| or 2pow(rows+cols+banks-5)-1*/ |
| csMask = 0; |
| |
| byte = Rows + Cols; /* cl=rows+cols*/ |
| byte += 21; /* row:12+col:9 */ |
| byte -= 2; /* 3 banks - 5 */ |
| |
| if (pDCTstat->Status & (1 << SB_128bitmode)) |
| byte++; /* double mask size if in 128-bit mode*/ |
| |
| csMask |= 1 << byte; |
| csMask--; |
| |
| /*set ChipSelect population indicator even bits*/ |
| pDCTstat->CSPresent |= (1<<ChipSel); |
| if (Ranks >= 2) |
| /*set ChipSelect population indicator odd bits*/ |
| pDCTstat->CSPresent |= 1 << (ChipSel + 1); |
| |
| reg = 0x60+(ChipSel<<1) + reg_off; /*Dram CS Mask Register */ |
| val = csMask; |
| val &= 0x1FF83FE0; /* Mask out reserved bits.*/ |
| Set_NB32(dev, reg, val); |
| } else { |
| if (pDCTstat->DIMMSPDCSE & (1<<ChipSel)) |
| pDCTstat->CSTestFail |= (1<<ChipSel); |
| } /* if DIMMValid*/ |
| } /* while ChipSel*/ |
| |
| SetCSTriState(pMCTstat, pDCTstat, dct); |
| SetCKETriState(pMCTstat, pDCTstat, dct); |
| SetODTTriState(pMCTstat, pDCTstat, dct); |
| |
| if (pDCTstat->Status & (1 << SB_128bitmode)) { |
| SetCSTriState(pMCTstat, pDCTstat, 1); /* force dct1) */ |
| SetCKETriState(pMCTstat, pDCTstat, 1); /* force dct1) */ |
| SetODTTriState(pMCTstat, pDCTstat, 1); /* force dct1) */ |
| } |
| |
| word = pDCTstat->CSPresent; |
| mctGetCS_ExcludeMap(); /* mask out specified chip-selects */ |
| word ^= pDCTstat->CSPresent; |
| pDCTstat->CSTestFail |= word; /* enable ODT to disabled DIMMs */ |
| if (!pDCTstat->CSPresent) |
| pDCTstat->ErrCode = SC_StopError; |
| |
| reg = 0x80 + reg_off; /* Bank Addressing Register */ |
| Set_NB32(dev, reg, BankAddrReg); |
| |
| pDCTstat->CSPresent_DCT[dct] = pDCTstat->CSPresent; |
| /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ |
| |
| printk(BIOS_DEBUG, "SPDSetBanks: CSPresent %x\n", pDCTstat->CSPresent_DCT[dct]); |
| printk(BIOS_DEBUG, "SPDSetBanks: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "SPDSetBanks: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "SPDSetBanks: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "SPDSetBanks: Done\n\n"); |
| } |
| |
| static void SPDCalcWidth_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| /* Per SPDs, check the symmetry of DIMM pairs (DIMM on Channel A |
| * matching with DIMM on Channel B), the overall DIMM population, |
| * and determine the width mode: 64-bit, 64-bit muxed, 128-bit. |
| */ |
| u8 i; |
| u8 smbaddr, smbaddr1; |
| u8 byte, byte1; |
| |
| /* Check Symmetry of Channel A and Channel B DIMMs |
| (must be matched for 128-bit mode).*/ |
| for (i=0; i < MAX_DIMMS_SUPPORTED; i += 2) { |
| if ((pDCTstat->DIMMValid & (1 << i)) && (pDCTstat->DIMMValid & (1<<(i+1)))) { |
| smbaddr = Get_DIMMAddress_D(pDCTstat, i); |
| smbaddr1 = Get_DIMMAddress_D(pDCTstat, i+1); |
| |
| byte = mctRead_SPD(smbaddr, SPD_Addressing) & 0x7; |
| byte1 = mctRead_SPD(smbaddr1, SPD_Addressing) & 0x7; |
| if (byte != byte1) { |
| pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); |
| break; |
| } |
| |
| byte = mctRead_SPD(smbaddr, SPD_Density) & 0x0f; |
| byte1 = mctRead_SPD(smbaddr1, SPD_Density) & 0x0f; |
| if (byte != byte1) { |
| pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); |
| break; |
| } |
| |
| byte = mctRead_SPD(smbaddr, SPD_Organization) & 0x7; |
| byte1 = mctRead_SPD(smbaddr1, SPD_Organization) & 0x7; |
| if (byte != byte1) { |
| pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); |
| break; |
| } |
| |
| byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3) & 0x7; |
| byte1 = (mctRead_SPD(smbaddr1, SPD_Organization) >> 3) & 0x7; |
| if (byte != byte1) { |
| pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); |
| break; |
| } |
| |
| byte = mctRead_SPD(smbaddr, SPD_DMBANKS) & 7; /* #ranks-1 */ |
| byte1 = mctRead_SPD(smbaddr1, SPD_DMBANKS) & 7; /* #ranks-1 */ |
| if (byte != byte1) { |
| pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO); |
| break; |
| } |
| |
| } |
| } |
| |
| } |
| |
| static void StitchMemory_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Requires that Mask values for each bank be programmed first and that |
| * the chip-select population indicator is correctly set. |
| */ |
| u8 b = 0; |
| u32 nxtcsBase, curcsBase; |
| u8 p, q; |
| u32 Sizeq, BiggestBank; |
| u8 _DSpareEn; |
| |
| u16 word; |
| u32 dev; |
| u32 reg; |
| u32 reg_off; |
| u32 val; |
| |
| dev = pDCTstat->dev_dct; |
| reg_off = 0x100 * dct; |
| |
| _DSpareEn = 0; |
| |
| /* CS Sparing 1=enabled, 0=disabled */ |
| if (mctGet_NVbits(NV_CS_SpareCTL) & 1) { |
| if (MCT_DIMM_SPARE_NO_WARM) { |
| /* Do no warm-reset DIMM spare */ |
| if (pMCTstat->GStatus & 1 << GSB_EnDIMMSpareNW) { |
| word = pDCTstat->CSPresent; |
| val = bsf(word); |
| word &= ~(1<<val); |
| if (word) |
| /* Make sure at least two chip-selects are available */ |
| _DSpareEn = 1; |
| else |
| pDCTstat->ErrStatus |= 1 << SB_SpareDis; |
| } |
| } else { |
| if (!mctGet_NVbits(NV_DQSTrainCTL)) { /*DQS Training 1=enabled, 0=disabled */ |
| word = pDCTstat->CSPresent; |
| val = bsf(word); |
| word &= ~(1 << val); |
| if (word) |
| /* Make sure at least two chip-selects are available */ |
| _DSpareEn = 1; |
| else |
| pDCTstat->ErrStatus |= 1 << SB_SpareDis; |
| } |
| } |
| } |
| |
| nxtcsBase = 0; /* Next available cs base ADDR[39:8] */ |
| for (p=0; p < MAX_DIMMS_SUPPORTED; p++) { |
| BiggestBank = 0; |
| for (q = 0; q < MAX_CS_SUPPORTED; q++) { /* from DIMMS to CS */ |
| if (pDCTstat->CSPresent & (1 << q)) { /* bank present? */ |
| reg = 0x40 + (q << 2) + reg_off; /* Base[q] reg.*/ |
| val = Get_NB32(dev, reg); |
| if (!(val & 3)) { /* (CSEnable|Spare==1)bank is enabled already? */ |
| reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/ |
| val = Get_NB32(dev, reg); |
| val >>= 19; |
| val++; |
| val <<= 19; |
| Sizeq = val; /* never used */ |
| if (val > BiggestBank) { |
| /*Bingo! possibly Map this chip-select next! */ |
| BiggestBank = val; |
| b = q; |
| } |
| } |
| } /*if bank present */ |
| } /* while q */ |
| if (BiggestBank !=0) { |
| curcsBase = nxtcsBase; /* curcsBase=nxtcsBase*/ |
| /* DRAM CS Base b Address Register offset */ |
| reg = 0x40 + (b << 2) + reg_off; |
| if (_DSpareEn) { |
| BiggestBank = 0; |
| val = 1 << Spare; /* Spare Enable*/ |
| } else { |
| val = curcsBase; |
| val |= 1 << CSEnable; /* Bank Enable */ |
| } |
| if (((reg - 0x40) >> 2) & 1) { |
| if (!(pDCTstat->Status & (1 << SB_Registered))) { |
| u16 dimValid; |
| dimValid = pDCTstat->DIMMValid; |
| if (dct & 1) |
| dimValid <<= 1; |
| if ((dimValid & pDCTstat->MirrPresU_NumRegR) != 0) { |
| val |= 1 << onDimmMirror; |
| } |
| } |
| } |
| Set_NB32(dev, reg, val); |
| if (_DSpareEn) |
| _DSpareEn = 0; |
| else |
| /* let nxtcsBase+=Size[b] */ |
| nxtcsBase += BiggestBank; |
| } |
| |
| /* bank present but disabled?*/ |
| if ( pDCTstat->CSTestFail & (1 << p)) { |
| /* DRAM CS Base b Address Register offset */ |
| reg = (p << 2) + 0x40 + reg_off; |
| val = 1 << TestFail; |
| Set_NB32(dev, reg, val); |
| } |
| } |
| |
| if (nxtcsBase) { |
| pDCTstat->DCTSysLimit = nxtcsBase - 1; |
| mct_AfterStitchMemory(pMCTstat, pDCTstat, dct); |
| } |
| |
| /* dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2)); */ |
| |
| printk(BIOS_DEBUG, "StitchMemory: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "StitchMemory: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "StitchMemory: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "StitchMemory: Done\n\n"); |
| } |
| |
| static u16 Get_Fk_D(u8 k) |
| { |
| return Table_F_k[k]; /* FIXME: k or k<<1 ? */ |
| } |
| |
| static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| /* Check DIMMs present, verify checksum, flag SDRAM type, |
| * build population indicator bitmaps, and preload bus loading |
| * of DIMMs into DCTStatStruc. |
| * MAAload=number of devices on the "A" bus. |
| * MABload=number of devices on the "B" bus. |
| * MAAdimms=number of DIMMs on the "A" bus slots. |
| * MABdimms=number of DIMMs on the "B" bus slots. |
| * DATAAload=number of ranks on the "A" bus slots. |
| * DATABload=number of ranks on the "B" bus slots. |
| */ |
| u16 i, j; |
| u8 smbaddr; |
| u8 SPDCtrl; |
| u16 RegDIMMPresent, MaxDimms; |
| u8 devwidth; |
| u16 DimmSlots; |
| u8 byte = 0, bytex; |
| |
| /* preload data structure with addrs */ |
| mctGet_DIMMAddr(pDCTstat, pDCTstat->Node_ID); |
| |
| DimmSlots = MaxDimms = mctGet_NVbits(NV_MAX_DIMMS); |
| |
| SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT); |
| |
| RegDIMMPresent = 0; |
| pDCTstat->DimmQRPresent = 0; |
| |
| for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) { |
| if (i >= MaxDimms) |
| break; |
| |
| if ((pDCTstat->DimmQRPresent & (1 << i)) || (i < DimmSlots)) { |
| int status; |
| smbaddr = Get_DIMMAddress_D(pDCTstat, i); |
| status = mctRead_SPD(smbaddr, SPD_ByteUse); |
| if (status >= 0) { /* SPD access is ok */ |
| pDCTstat->DIMMPresent |= 1 << i; |
| if (crcCheck(smbaddr)) { /* CRC is OK */ |
| byte = mctRead_SPD(smbaddr, SPD_TYPE); |
| if (byte == JED_DDR3SDRAM) { |
| /*Dimm is 'Present'*/ |
| pDCTstat->DIMMValid |= 1 << i; |
| } |
| } else { |
| pDCTstat->DIMMSPDCSE = 1 << i; |
| if (SPDCtrl == 0) { |
| pDCTstat->ErrStatus |= 1 << SB_DIMMChkSum; |
| pDCTstat->ErrCode = SC_StopError; |
| } else { |
| /*if NV_SPDCHK_RESTRT is set to 1, ignore faulty SPD checksum*/ |
| pDCTstat->ErrStatus |= 1<<SB_DIMMChkSum; |
| byte = mctRead_SPD(smbaddr, SPD_TYPE); |
| if (byte == JED_DDR3SDRAM) |
| pDCTstat->DIMMValid |= 1 << i; |
| } |
| } |
| /* Check module type */ |
| byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE) & 0x7; |
| if (byte == JED_RDIMM || byte == JED_MiniRDIMM) |
| RegDIMMPresent |= 1 << i; |
| /* Check ECC capable */ |
| byte = mctRead_SPD(smbaddr, SPD_BusWidth); |
| if (byte & JED_ECC) { |
| /* DIMM is ECC capable */ |
| pDCTstat->DimmECCPresent |= 1 << i; |
| } |
| /* Check if x4 device */ |
| devwidth = mctRead_SPD(smbaddr, SPD_Organization) & 0x7; /* 0:x4,1:x8,2:x16 */ |
| if (devwidth == 0) { |
| /* DIMM is made with x4 or x16 drams */ |
| pDCTstat->Dimmx4Present |= 1 << i; |
| } else if (devwidth == 1) { |
| pDCTstat->Dimmx8Present |= 1 << i; |
| } else if (devwidth == 2) { |
| pDCTstat->Dimmx16Present |= 1 << i; |
| } |
| |
| byte = (mctRead_SPD(smbaddr, SPD_Organization) >> 3); |
| byte &= 7; |
| if (byte == 3) { /* 4ranks */ |
| /* if any DIMMs are QR, we have to make two passes through DIMMs*/ |
| if ( pDCTstat->DimmQRPresent == 0) { |
| MaxDimms <<= 1; |
| } |
| if (i < DimmSlots) { |
| pDCTstat->DimmQRPresent |= (1 << i) | (1 << (i+4)); |
| } else { |
| pDCTstat->MAdimms[i & 1] --; |
| } |
| byte = 1; /* upper two ranks of QR DIMM will be counted on another DIMM number iteration*/ |
| } else if (byte == 1) { /* 2ranks */ |
| pDCTstat->DimmDRPresent |= 1 << i; |
| } |
| bytex = devwidth; |
| if (devwidth == 0) |
| bytex = 16; |
| else if (devwidth == 1) |
| bytex = 8; |
| else if (devwidth == 2) |
| bytex = 4; |
| |
| byte++; /* al+1=rank# */ |
| if (byte == 2) |
| bytex <<= 1; /*double Addr bus load value for dual rank DIMMs*/ |
| |
| j = i & (1<<0); |
| pDCTstat->DATAload[j] += byte; /*number of ranks on DATA bus*/ |
| pDCTstat->MAload[j] += bytex; /*number of devices on CMD/ADDR bus*/ |
| pDCTstat->MAdimms[j]++; /*number of DIMMs on A bus */ |
| |
| /* check address mirror support for unbuffered dimm */ |
| /* check number of registers on a dimm for registered dimm */ |
| byte = mctRead_SPD(smbaddr, SPD_AddressMirror); |
| if (RegDIMMPresent & (1 << i)) { |
| if ((byte & 3) > 1) |
| pDCTstat->MirrPresU_NumRegR |= 1 << i; |
| } else { |
| if ((byte & 1) == 1) |
| pDCTstat->MirrPresU_NumRegR |= 1 << i; |
| } |
| /* Get byte62: Reference Raw Card information. We dont need it now. */ |
| /* byte = mctRead_SPD(smbaddr, SPD_RefRawCard); */ |
| /* Get Byte65/66 for register manufacture ID code */ |
| if ((0x97 == mctRead_SPD(smbaddr, SPD_RegManufactureID_H)) && |
| (0x80 == mctRead_SPD(smbaddr, SPD_RegManufactureID_L))) { |
| if (0x16 == mctRead_SPD(smbaddr, SPD_RegManRevID)) |
| pDCTstat->RegMan2Present |= 1 << i; |
| else |
| pDCTstat->RegMan1Present |= 1 << i; |
| } |
| /* Get Control word values for RC3. We dont need it. */ |
| byte = mctRead_SPD(smbaddr, 70); |
| pDCTstat->CtrlWrd3 |= (byte >> 4) << (i << 2); /* C3 = SPD byte 70 [7:4] */ |
| /* Get Control word values for RC4, and RC5 */ |
| byte = mctRead_SPD(smbaddr, 71); |
| pDCTstat->CtrlWrd4 |= (byte & 0xFF) << (i << 2); /* RC4 = SPD byte 71 [3:0] */ |
| pDCTstat->CtrlWrd5 |= (byte >> 4) << (i << 2); /* RC5 = SPD byte 71 [7:4] */ |
| } |
| } |
| } |
| printk(BIOS_DEBUG, "\t DIMMPresence: DIMMValid=%x\n", pDCTstat->DIMMValid); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DIMMPresent=%x\n", pDCTstat->DIMMPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: RegDIMMPresent=%x\n", RegDIMMPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DimmECCPresent=%x\n", pDCTstat->DimmECCPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DimmPARPresent=%x\n", pDCTstat->DimmPARPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx4Present=%x\n", pDCTstat->Dimmx4Present); |
| printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx8Present=%x\n", pDCTstat->Dimmx8Present); |
| printk(BIOS_DEBUG, "\t DIMMPresence: Dimmx16Present=%x\n", pDCTstat->Dimmx16Present); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DimmPlPresent=%x\n", pDCTstat->DimmPlPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DimmDRPresent=%x\n", pDCTstat->DimmDRPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DimmQRPresent=%x\n", pDCTstat->DimmQRPresent); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DATAload[0]=%x\n", pDCTstat->DATAload[0]); |
| printk(BIOS_DEBUG, "\t DIMMPresence: MAload[0]=%x\n", pDCTstat->MAload[0]); |
| printk(BIOS_DEBUG, "\t DIMMPresence: MAdimms[0]=%x\n", pDCTstat->MAdimms[0]); |
| printk(BIOS_DEBUG, "\t DIMMPresence: DATAload[1]=%x\n", pDCTstat->DATAload[1]); |
| printk(BIOS_DEBUG, "\t DIMMPresence: MAload[1]=%x\n", pDCTstat->MAload[1]); |
| printk(BIOS_DEBUG, "\t DIMMPresence: MAdimms[1]=%x\n", pDCTstat->MAdimms[1]); |
| |
| if (pDCTstat->DIMMValid != 0) { /* If any DIMMs are present...*/ |
| if (RegDIMMPresent != 0) { |
| if ((RegDIMMPresent ^ pDCTstat->DIMMValid) !=0) { |
| /* module type DIMM mismatch (reg'ed, unbuffered) */ |
| pDCTstat->ErrStatus |= 1<<SB_DimmMismatchM; |
| pDCTstat->ErrCode = SC_StopError; |
| } else{ |
| /* all DIMMs are registered */ |
| pDCTstat->Status |= 1<<SB_Registered; |
| } |
| } |
| if (pDCTstat->DimmECCPresent != 0) { |
| if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) { |
| /* all DIMMs are ECC capable */ |
| pDCTstat->Status |= 1<<SB_ECCDIMMs; |
| } |
| } |
| if (pDCTstat->DimmPARPresent != 0) { |
| if ((pDCTstat->DimmPARPresent ^ pDCTstat->DIMMValid) == 0) { |
| /*all DIMMs are Parity capable */ |
| pDCTstat->Status |= 1<<SB_PARDIMMs; |
| } |
| } |
| } else { |
| /* no DIMMs present or no DIMMs that qualified. */ |
| pDCTstat->ErrStatus |= 1<<SB_NoDimms; |
| pDCTstat->ErrCode = SC_StopError; |
| } |
| |
| printk(BIOS_DEBUG, "\t DIMMPresence: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "\t DIMMPresence: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "\t DIMMPresence: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "\t DIMMPresence: Done\n\n"); |
| |
| mctHookAfterDIMMpre(); |
| |
| return pDCTstat->ErrCode; |
| } |
| |
| static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i) |
| { |
| u8 *p; |
| |
| p = pDCTstat->DIMMAddr; |
| /* mct_BeforeGetDIMMAddress(); */ |
| return p[i]; |
| } |
| |
| static void mct_initDCT(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 val; |
| u8 err_code; |
| |
| /* Config. DCT0 for Ganged or unganged mode */ |
| DCTInit_D(pMCTstat, pDCTstat, 0); |
| if (pDCTstat->ErrCode == SC_FatalErr) { |
| /* Do nothing goto exitDCTInit; any fatal errors? */ |
| } else { |
| /* Configure DCT1 if unganged and enabled*/ |
| if (!pDCTstat->GangedMode) { |
| if (pDCTstat->DIMMValidDCT[1] > 0) { |
| err_code = pDCTstat->ErrCode; /* save DCT0 errors */ |
| pDCTstat->ErrCode = 0; |
| DCTInit_D(pMCTstat, pDCTstat, 1); |
| if (pDCTstat->ErrCode == 2) /* DCT1 is not Running */ |
| pDCTstat->ErrCode = err_code; /* Using DCT0 Error code to update pDCTstat.ErrCode */ |
| } else { |
| val = 1 << DisDramInterface; |
| Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val); |
| } |
| } |
| } |
| /* exitDCTInit: */ |
| } |
| |
| static void mct_DramInit(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat); |
| mct_DramInit_Sw_D(pMCTstat, pDCTstat, dct); |
| /* mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct); */ |
| } |
| |
| static u8 mct_setMode(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u8 byte; |
| u8 bytex; |
| u32 val; |
| u32 reg; |
| |
| byte = bytex = pDCTstat->DIMMValid; |
| bytex &= 0x55; /* CHA DIMM pop */ |
| pDCTstat->DIMMValidDCT[0] = bytex; |
| |
| byte &= 0xAA; /* CHB DIMM popa */ |
| byte >>= 1; |
| pDCTstat->DIMMValidDCT[1] = byte; |
| |
| if (byte != bytex) { |
| pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); |
| } else { |
| byte = mctGet_NVbits(NV_Unganged); |
| if (byte) |
| pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO); /* Set temp. to avoid setting of ganged mode */ |
| |
| if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) { |
| pDCTstat->GangedMode = 1; |
| /* valid 128-bit mode population. */ |
| pDCTstat->Status |= 1 << SB_128bitmode; |
| reg = 0x110; |
| val = Get_NB32(pDCTstat->dev_dct, reg); |
| val |= 1 << DctGangEn; |
| Set_NB32(pDCTstat->dev_dct, reg, val); |
| } |
| if (byte) /* NV_Unganged */ |
| pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO); /* Clear so that there is no DIMM missmatch error */ |
| } |
| return pDCTstat->ErrCode; |
| } |
| |
| u32 Get_NB32(u32 dev, u32 reg) |
| { |
| return pci_read_config32(dev, reg); |
| } |
| |
| void Set_NB32(u32 dev, u32 reg, u32 val) |
| { |
| pci_write_config32(dev, reg, val); |
| } |
| |
| |
| u32 Get_NB32_index(u32 dev, u32 index_reg, u32 index) |
| { |
| u32 dword; |
| |
| Set_NB32(dev, index_reg, index); |
| dword = Get_NB32(dev, index_reg+0x4); |
| |
| return dword; |
| } |
| |
| void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data) |
| { |
| Set_NB32(dev, index_reg, index); |
| Set_NB32(dev, index_reg + 0x4, data); |
| } |
| |
| u32 Get_NB32_index_wait(u32 dev, u32 index_reg, u32 index) |
| { |
| |
| u32 dword; |
| |
| |
| index &= ~(1 << DctAccessWrite); |
| Set_NB32(dev, index_reg, index); |
| do { |
| dword = Get_NB32(dev, index_reg); |
| } while (!(dword & (1 << DctAccessDone))); |
| dword = Get_NB32(dev, index_reg + 0x4); |
| |
| return dword; |
| } |
| |
| void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data) |
| { |
| u32 dword; |
| |
| |
| Set_NB32(dev, index_reg + 0x4, data); |
| index |= (1 << DctAccessWrite); |
| Set_NB32(dev, index_reg, index); |
| do { |
| dword = Get_NB32(dev, index_reg); |
| } while (!(dword & (1 << DctAccessDone))); |
| |
| } |
| |
| static u8 mct_BeforePlatformSpec(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* mct_checkForCxDxSupport_D */ |
| if (pDCTstat->LogicalCPUID & AMD_DR_GT_Bx) { |
| /* 1. Write 00000000h to F2x[1,0]9C_xD08E000 */ |
| Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0x0D08E000, 0); |
| /* 2. If DRAM Configuration Register[MemClkFreq] (F2x[1,0]94[2:0]) is |
| greater than or equal to 011b (DDR-800 and higher), |
| then write 00000080h to F2x[1,0]9C_xD02E001, |
| else write 00000090h to F2x[1,0]9C_xD02E001. */ |
| if (pDCTstat->Speed >= 4) |
| Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x80); |
| else |
| Set_NB32_index_wait(pDCTstat->dev_dct, 0x98 + dct * 0x100, 0xD02E001, 0x90); |
| } |
| return pDCTstat->ErrCode; |
| } |
| |
| static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| /* Get platform specific config/timing values from the interface layer |
| * and program them into DCT. |
| */ |
| |
| u32 dev = pDCTstat->dev_dct; |
| u32 index_reg; |
| u8 i, i_start, i_end; |
| |
| if (pDCTstat->GangedMode) { |
| SyncSetting(pDCTstat); |
| /* mct_SetupSync_D */ |
| i_start = 0; |
| i_end = 2; |
| } else { |
| i_start = dct; |
| i_end = dct + 1; |
| } |
| for (i=i_start; i<i_end; i++) { |
| index_reg = 0x98 + (i * 0x100); |
| Set_NB32_index_wait(dev, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */ |
| Set_NB32_index_wait(dev, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */ |
| } |
| |
| return pDCTstat->ErrCode; |
| } |
| |
| static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat) |
| { |
| u32 dev; |
| u32 val; |
| |
| if (pDCTstat->NodePresent) { |
| dev = pDCTstat->dev_dct; |
| |
| if ((pDCTstat->DIMMValidDCT[0] ) || (pDCTstat->DIMMValidDCT[1])) { /* This Node has dram */ |
| do { |
| val = Get_NB32(dev, 0x110); |
| } while (!(val & (1 << DramEnabled))); |
| } |
| } /* Node is present */ |
| } |
| |
| static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| if (!pDCTstat->GangedMode) { |
| if (dct == 0 ) { |
| pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct]; |
| if (pDCTstat->DIMMValidDCT[dct] == 0) |
| pDCTstat->ErrCode = SC_StopError; |
| } else { |
| pDCTstat->CSPresent = 0; |
| pDCTstat->CSTestFail = 0; |
| pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct]; |
| if (pDCTstat->DIMMValidDCT[dct] == 0) |
| pDCTstat->ErrCode = SC_StopError; |
| } |
| } |
| } |
| |
| static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 ret; |
| u32 val; |
| |
| if ( dct == 0) { |
| SPDCalcWidth_D(pMCTstat, pDCTstat); |
| ret = mct_setMode(pMCTstat, pDCTstat); |
| } else { |
| ret = pDCTstat->ErrCode; |
| } |
| |
| if (pDCTstat->DIMMValidDCT[0] == 0) { |
| val = Get_NB32(pDCTstat->dev_dct, 0x94); |
| val |= 1 << DisDramInterface; |
| Set_NB32(pDCTstat->dev_dct, 0x94, val); |
| } |
| if (pDCTstat->DIMMValidDCT[1] == 0) { |
| val = Get_NB32(pDCTstat->dev_dct, 0x94 + 0x100); |
| val |= 1 << DisDramInterface; |
| Set_NB32(pDCTstat->dev_dct, 0x94 + 0x100, val); |
| } |
| |
| printk(BIOS_DEBUG, "SPDCalcWidth: Status %x\n", pDCTstat->Status); |
| printk(BIOS_DEBUG, "SPDCalcWidth: ErrStatus %x\n", pDCTstat->ErrStatus); |
| printk(BIOS_DEBUG, "SPDCalcWidth: ErrCode %x\n", pDCTstat->ErrCode); |
| printk(BIOS_DEBUG, "SPDCalcWidth: Done\n"); |
| /* Disable dram interface before DRAM init */ |
| |
| return ret; |
| } |
| |
| static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 val; |
| u32 dword; |
| u32 dev; |
| u32 reg; |
| u8 _MemHoleRemap; |
| u32 DramHoleBase; |
| |
| _MemHoleRemap = mctGet_NVbits(NV_MemHole); |
| DramHoleBase = mctGet_NVbits(NV_BottomIO); |
| DramHoleBase <<= 8; |
| /* Increase hole size so;[31:24]to[31:16] |
| * it has granularity of 128MB shl eax,8 |
| * Set 'effective' bottom IOmov DramHoleBase,eax |
| */ |
| pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8; |
| |
| /* In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */ |
| if (!pDCTstat->GangedMode) { |
| dev = pDCTstat->dev_dct; |
| pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit; |
| /* if DCT0 and DCT1 both exist, set DctSelBaseAddr[47:27] to the top of DCT0 */ |
| if (dct == 0) { |
| if (pDCTstat->DIMMValidDCT[1] > 0) { |
| dword = pDCTstat->DCTSysLimit + 1; |
| dword += pDCTstat->NodeSysBase; |
| dword >>= 8; /* scale [39:8] to [47:27],and to F2x110[31:11] */ |
| if ((dword >= DramHoleBase) && _MemHoleRemap) { |
| pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8; |
| val = pMCTstat->HoleBase; |
| val >>= 16; |
| val = (((~val) & 0xFF) + 1); |
| val <<= 8; |
| dword += val; |
| } |
| reg = 0x110; |
| val = Get_NB32(dev, reg); |
| val &= 0x7F; |
| val |= dword; |
| val |= 3; /* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */ |
| Set_NB32(dev, reg, val); |
| |
| reg = 0x114; |
| val = dword; |
| Set_NB32(dev, reg, val); |
| } |
| } else { |
| /* Program the DctSelBaseAddr value to 0 |
| if DCT 0 is disabled */ |
| if (pDCTstat->DIMMValidDCT[0] == 0) { |
| dword = pDCTstat->NodeSysBase; |
| dword >>= 8; |
| if ((dword >= DramHoleBase) && _MemHoleRemap) { |
| pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8; |
| val = pMCTstat->HoleBase; |
| val >>= 8; |
| val &= ~(0xFFFF); |
| val |= (((~val) & 0xFFFF) + 1); |
| dword += val; |
| } |
| reg = 0x114; |
| val = dword; |
| Set_NB32(dev, reg, val); |
| |
| reg = 0x110; |
| val |= 3; /* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */ |
| Set_NB32(dev, reg, val); |
| } |
| } |
| } else { |
| pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit; |
| } |
| printk(BIOS_DEBUG, "AfterStitch pDCTstat->NodeSysBase = %x\n", pDCTstat->NodeSysBase); |
| printk(BIOS_DEBUG, "mct_AfterStitchMemory: pDCTstat->NodeSysLimit = %x\n", pDCTstat->NodeSysLimit); |
| } |
| |
| static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 ret; |
| |
| if (dct == 0) |
| ret = DIMMPresence_D(pMCTstat, pDCTstat); |
| else |
| ret = pDCTstat->ErrCode; |
| |
| return ret; |
| } |
| |
| /* mct_BeforeGetDIMMAddress inline in C */ |
| |
| static void mct_OtherTiming(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| struct DCTStatStruc *pDCTstat; |
| pDCTstat = pDCTstatA + Node; |
| if (pDCTstat->NodePresent) { |
| if (pDCTstat->DIMMValidDCT[0]) { |
| pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[0]; |
| Set_OtherTiming(pMCTstat, pDCTstat, 0); |
| } |
| if (pDCTstat->DIMMValidDCT[1] && !pDCTstat->GangedMode ) { |
| pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[1]; |
| Set_OtherTiming(pMCTstat, pDCTstat, 1); |
| } |
| } /* Node is present*/ |
| } /* while Node */ |
| } |
| |
| static void Set_OtherTiming(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 reg; |
| u32 reg_off = 0x100 * dct; |
| u32 val; |
| u32 dword; |
| u32 dev = pDCTstat->dev_dct; |
| |
| Get_DqsRcvEnGross_Diff(pDCTstat, dev, 0x98 + reg_off); |
| Get_WrDatGross_Diff(pDCTstat, dct, dev, 0x98 + reg_off); |
| Get_Trdrd(pMCTstat, pDCTstat, dct); |
| Get_Twrwr(pMCTstat, pDCTstat, dct); |
| Get_Twrrd(pMCTstat, pDCTstat, dct); |
| Get_TrwtTO(pMCTstat, pDCTstat, dct); |
| Get_TrwtWB(pMCTstat, pDCTstat); |
| |
| reg = 0x8C + reg_off; /* Dram Timing Hi */ |
| val = Get_NB32(dev, reg); |
| val &= 0xffff0300; |
| dword = pDCTstat->TrwtTO; |
| val |= dword << 4; |
| dword = pDCTstat->Twrrd & 3; |
| val |= dword << 10; |
| dword = pDCTstat->Twrwr & 3; |
| val |= dword << 12; |
| dword = pDCTstat->Trdrd & 3; |
| val |= dword << 14; |
| dword = pDCTstat->TrwtWB; |
| val |= dword; |
| Set_NB32(dev, reg, val); |
| |
| reg = 0x78 + reg_off; |
| val = Get_NB32(dev, reg); |
| val &= 0xFFFFC0FF; |
| dword = pDCTstat->Twrrd >> 2; |
| val |= dword << 8; |
| dword = pDCTstat->Twrwr >> 2; |
| val |= dword << 10; |
| dword = pDCTstat->Trdrd >> 2; |
| val |= dword << 12; |
| Set_NB32(dev, reg, val); |
| } |
| |
| static void Get_Trdrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| int8_t Trdrd; |
| |
| Trdrd = ((int8_t)(pDCTstat->DqsRcvEnGrossMax - pDCTstat->DqsRcvEnGrossMin) >> 1) + 1; |
| if (Trdrd > 8) |
| Trdrd = 8; |
| pDCTstat->Trdrd = Trdrd; |
| } |
| |
| static void Get_Twrwr(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| int8_t Twrwr = 0; |
| |
| Twrwr = ((int8_t)(pDCTstat->WrDatGrossMax - pDCTstat->WrDatGrossMin) >> 1) + 2; |
| |
| if (Twrwr < 2) |
| Twrwr = 2; |
| else if (Twrwr > 9) |
| Twrwr = 9; |
| |
| pDCTstat->Twrwr = Twrwr; |
| } |
| |
| static void Get_Twrrd(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 LDplus1; |
| int8_t Twrrd; |
| |
| LDplus1 = Get_Latency_Diff(pMCTstat, pDCTstat, dct); |
| |
| Twrrd = ((int8_t)(pDCTstat->WrDatGrossMax - pDCTstat->DqsRcvEnGrossMin) >> 1) + 4 - LDplus1; |
| |
| if (Twrrd < 2) |
| Twrrd = 2; |
| else if (Twrrd > 10) |
| Twrrd = 10; |
| pDCTstat->Twrrd = Twrrd; |
| } |
| |
| static void Get_TrwtTO(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 LDplus1; |
| int8_t TrwtTO; |
| |
| LDplus1 = Get_Latency_Diff(pMCTstat, pDCTstat, dct); |
| |
| TrwtTO = ((int8_t)(pDCTstat->DqsRcvEnGrossMax - pDCTstat->WrDatGrossMin) >> 1) + LDplus1; |
| |
| pDCTstat->TrwtTO = TrwtTO; |
| } |
| |
| static void Get_TrwtWB(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| /* TrwtWB ensures read-to-write data-bus turnaround. |
| This value should be one more than the programmed TrwtTO.*/ |
| pDCTstat->TrwtWB = pDCTstat->TrwtTO; |
| } |
| |
| static u8 Get_Latency_Diff(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 reg_off = 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val1, val2; |
| |
| val1 = Get_NB32(dev, reg_off + 0x88) & 0xF; |
| val2 = (Get_NB32(dev, reg_off + 0x84) >> 20) & 7; |
| |
| return val1 - val2; |
| } |
| |
| static void Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat, |
| u32 dev, u32 index_reg) |
| { |
| u8 Smallest, Largest; |
| u32 val; |
| u8 byte, bytex; |
| |
| /* The largest DqsRcvEnGrossDelay of any DIMM minus the |
| DqsRcvEnGrossDelay of any other DIMM is equal to the Critical |
| Gross Delay Difference (CGDD) */ |
| /* DqsRcvEn byte 1,0 */ |
| val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x10); |
| Largest = val & 0xFF; |
| Smallest = (val >> 8) & 0xFF; |
| |
| /* DqsRcvEn byte 3,2 */ |
| val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11); |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| |
| /* DqsRcvEn byte 5,4 */ |
| val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20); |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| |
| /* DqsRcvEn byte 7,6 */ |
| val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21); |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| |
| if (pDCTstat->DimmECCPresent> 0) { |
| /*DqsRcvEn Ecc */ |
| val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12); |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| |
| pDCTstat->DqsRcvEnGrossMax = Largest; |
| pDCTstat->DqsRcvEnGrossMin = Smallest; |
| } |
| |
| static void Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, |
| u8 dct, u32 dev, u32 index_reg) |
| { |
| u8 Smallest = 0, Largest = 0; |
| u32 val; |
| u8 byte, bytex; |
| |
| /* The largest WrDatGrossDlyByte of any DIMM minus the |
| WrDatGrossDlyByte of any other DIMM is equal to CGDD */ |
| if (pDCTstat->DIMMValid & (1 << 0)) { |
| val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x01); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM0 */ |
| Largest = val & 0xFF; |
| Smallest = (val >> 8) & 0xFF; |
| } |
| if (pDCTstat->DIMMValid & (1 << 2)) { |
| val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x101); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM1 */ |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| |
| /* If Cx, 2 more dimm need to be checked to find out the largest and smallest */ |
| if (pDCTstat->LogicalCPUID & AMD_DR_Cx) { |
| if (pDCTstat->DIMMValid & (1 << 4)) { |
| val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x201); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM2 */ |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| if (pDCTstat->DIMMValid & (1 << 6)) { |
| val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x301); /* WrDatGrossDlyByte byte 0,1,2,3 for DIMM2 */ |
| byte = val & 0xFF; |
| bytex = (val >> 8) & 0xFF; |
| if (bytex < Smallest) |
| Smallest = bytex; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| } |
| |
| pDCTstat->WrDatGrossMax = Largest; |
| pDCTstat->WrDatGrossMin = Smallest; |
| } |
| |
| static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat, |
| u32 dev, u32 index_reg, |
| u32 index) |
| { |
| u8 Smallest, Largest; |
| u8 i; |
| u8 byte; |
| u32 val; |
| u16 word; |
| u8 ecc_reg = 0; |
| |
| Smallest = 7; |
| Largest = 0; |
| |
| if (index == 0x12) |
| ecc_reg = 1; |
| |
| for (i=0; i < 8; i+=2) { |
| if ( pDCTstat->DIMMValid & (1 << i)) { |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| val &= 0x00E000E0; |
| byte = (val >> 5) & 0xFF; |
| if (byte < Smallest) |
| Smallest = byte; |
| if (byte > Largest) |
| Largest = byte; |
| if (!(ecc_reg)) { |
| byte = (val >> (16 + 5)) & 0xFF; |
| if (byte < Smallest) |
| Smallest = byte; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| } |
| index += 3; |
| } /* while ++i */ |
| |
| word = Smallest; |
| word <<= 8; |
| word |= Largest; |
| |
| return word; |
| } |
| |
| static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, |
| u8 dct, u32 dev, u32 index_reg, |
| u32 index) |
| { |
| u8 Smallest, Largest; |
| u8 i, j; |
| u32 val; |
| u8 byte; |
| u16 word; |
| |
| Smallest = 3; |
| Largest = 0; |
| for (i=0; i < 2; i++) { |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| val &= 0x60606060; |
| val >>= 5; |
| for (j=0; j < 4; j++) { |
| byte = val & 0xFF; |
| if (byte < Smallest) |
| Smallest = byte; |
| if (byte > Largest) |
| Largest = byte; |
| val >>= 8; |
| } /* while ++j */ |
| index++; |
| } /*while ++i*/ |
| |
| if (pDCTstat->DimmECCPresent > 0) { |
| index++; |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| val &= 0x00000060; |
| val >>= 5; |
| byte = val & 0xFF; |
| if (byte < Smallest) |
| Smallest = byte; |
| if (byte > Largest) |
| Largest = byte; |
| } |
| |
| word = Smallest; |
| word <<= 8; |
| word |= Largest; |
| |
| return word; |
| } |
| |
| static void mct_PhyController_Config(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| |
| if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3 | AMD_RB_C3)) { |
| if (pDCTstat->Dimmx4Present == 0) { |
| /* Set bit7 RxDqsUDllPowerDown to register F2x[1, 0]98_x0D0F0F13 for power saving */ |
| val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0F13); /* Agesa v3 v6 might be wrong here. */ |
| val |= 1 << 7; /* BIOS should set this bit when x4 DIMMs are not present */ |
| Set_NB32_index_wait(dev, index_reg, 0x0D0F0F13, val); |
| } |
| } |
| |
| if (pDCTstat->LogicalCPUID & AMD_DR_DAC2_OR_C3) { |
| if (pDCTstat->DimmECCPresent == 0) { |
| /* Set bit4 PwrDn to register F2x[1, 0]98_x0D0F0830 for power saving */ |
| val = Get_NB32_index_wait(dev, index_reg, 0x0D0F0830); |
| val |= 1 << 4; /* BIOS should set this bit if ECC DIMMs are not present */ |
| Set_NB32_index_wait(dev, index_reg, 0x0D0F0830, val); |
| } |
| } |
| |
| } |
| |
| static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| struct DCTStatStruc *pDCTstat; |
| u32 val; |
| |
| for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->NodePresent) { |
| mct_PhyController_Config(pMCTstat, pDCTstat, 0); |
| mct_PhyController_Config(pMCTstat, pDCTstat, 1); |
| } |
| if (!(pDCTstat->LogicalCPUID & AMD_DR_Dx)) { /* mct_checkForDxSupport */ |
| mct_ExtMCTConfig_Cx(pDCTstat); |
| mct_ExtMCTConfig_Bx(pDCTstat); |
| } else { /* For Dx CPU */ |
| val = 0x0CE00F00 | 1 << 29/* FlushWrOnStpGnt */; |
| if (!(pDCTstat->GangedMode)) |
| val |= 0x20; /* MctWrLimit = 8 for Unganed mode */ |
| else |
| val |= 0x40; /* MctWrLimit = 16 for ganed mode */ |
| Set_NB32(pDCTstat->dev_dct, 0x11C, val); |
| |
| val = Get_NB32(pDCTstat->dev_dct, 0x1B0); |
| val &= 0xFFFFF8C0; |
| val |= 0x101; /* BKDG recommended settings */ |
| val |= 0x0FC00000; /* Agesa V5 */ |
| if (!(pDCTstat->GangedMode)) |
| val |= 1 << 12; |
| else |
| val &= ~(1 << 12); |
| |
| val &= 0x0FFFFFFF; |
| switch (pDCTstat->Speed) { |
| case 4: |
| val |= 0x50000000; /* 5 for DDR800 */ |
| break; |
| case 5: |
| val |= 0x60000000; /* 6 for DDR1066 */ |
| break; |
| case 6: |
| val |= 0x80000000; /* 8 for DDR800 */ |
| break; |
| default: |
| val |= 0x90000000; /* 9 for DDR1600 */ |
| break; |
| } |
| Set_NB32(pDCTstat->dev_dct, 0x1B0, val); |
| } |
| } |
| |
| /* ClrClToNB_D postponed until we're done executing from ROM */ |
| mct_ClrWbEnhWsbDis_D(pMCTstat, pDCTstat); |
| |
| /* set F3x8C[DisFastTprWr] on all DR, if L3Size=0 */ |
| if (pDCTstat->LogicalCPUID & AMD_DR_ALL) { |
| if (!(cpuid_edx(0x80000006) & 0xFFFC0000)) { |
| val = Get_NB32(pDCTstat->dev_nbmisc, 0x8C); |
| val |= 1 << 24; |
| Set_NB32(pDCTstat->dev_nbmisc, 0x8C, val); |
| } |
| } |
| } |
| |
| static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat) |
| { |
| mct_SetClToNB_D(pMCTstat, pDCTstat); |
| mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat); |
| } |
| |
| static u32 mct_NodePresent_D(void) |
| { |
| u32 val; |
| val = 0x12001022; |
| return val; |
| } |
| |
| static void mct_init(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 lo, hi; |
| u32 addr; |
| |
| pDCTstat->GangedMode = 0; |
| pDCTstat->DRPresent = 1; |
| |
| /* enable extend PCI configuration access */ |
| addr = 0xC001001F; |
| _RDMSR(addr, &lo, &hi); |
| if (hi & (1 << (46-32))) { |
| pDCTstat->Status |= 1 << SB_ExtConfig; |
| } else { |
| hi |= 1 << (46-32); |
| _WRMSR(addr, lo, hi); |
| } |
| } |
| |
| static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 reg; |
| u32 val; |
| u32 dev = pDCTstat->dev_dct; |
| |
| /* Clear Legacy BIOS Mode bit */ |
| reg = 0x94; |
| val = Get_NB32(dev, reg); |
| val &= ~(1<<LegacyBiosMode); |
| Set_NB32(dev, reg, val); |
| |
| reg = 0x94 + 0x100; |
| val = Get_NB32(dev, reg); |
| val &= ~(1<<LegacyBiosMode); |
| Set_NB32(dev, reg, val); |
| } |
| |
| static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| u32 Drambase, Dramlimit; |
| u32 val; |
| u32 reg; |
| u32 dev; |
| u32 devx; |
| u32 dword; |
| struct DCTStatStruc *pDCTstat; |
| |
| pDCTstat = pDCTstatA + 0; |
| dev = pDCTstat->dev_map; |
| |
| /* Copy dram map from F1x40/44,F1x48/4c, |
| to F1x120/124(Node0),F1x120/124(Node1),...*/ |
| for (Node=0; Node < MAX_NODES_SUPPORTED; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| devx = pDCTstat->dev_map; |
| |
| /* get base/limit from Node0 */ |
| reg = 0x40 + (Node << 3); /* Node0/Dram Base 0 */ |
| val = Get_NB32(dev, reg); |
| Drambase = val >> ( 16 + 3); |
| |
| reg = 0x44 + (Node << 3); /* Node0/Dram Base 0 */ |
| val = Get_NB32(dev, reg); |
| Dramlimit = val >> (16 + 3); |
| |
| /* set base/limit to F1x120/124 per Node */ |
| if (pDCTstat->NodePresent) { |
| reg = 0x120; /* F1x120,DramBase[47:27] */ |
| val = Get_NB32(devx, reg); |
| val &= 0xFFE00000; |
| val |= Drambase; |
| Set_NB32(devx, reg, val); |
| |
| reg = 0x124; |
| val = Get_NB32(devx, reg); |
| val &= 0xFFE00000; |
| val |= Dramlimit; |
| Set_NB32(devx, reg, val); |
| |
| if ( pMCTstat->GStatus & ( 1 << GSB_HWHole)) { |
| reg = 0xF0; |
| val = Get_NB32(devx, reg); |
| val |= (1 << DramMemHoistValid); |
| val &= ~(0xFF << 24); |
| dword = (pMCTstat->HoleBase >> (24 - 8)) & 0xFF; |
| dword <<= 24; |
| val |= dword; |
| Set_NB32(devx, reg, val); |
| } |
| |
| } |
| } |
| } |
| |
| static void SetCSTriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 val; |
| u32 dev = pDCTstat->dev_dct; |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u32 index; |
| u16 word; |
| |
| /* Tri-state unused chipselects when motherboard |
| termination is available */ |
| |
| /* FIXME: skip for Ax */ |
| |
| word = pDCTstat->CSPresent; |
| if (pDCTstat->Status & (1 << SB_Registered)) { |
| word |= (word & 0x55) << 1; |
| } |
| word = (~word) & 0xFF; |
| index = 0x0c; |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| val |= word; |
| Set_NB32_index_wait(dev, index_reg, index, val); |
| } |
| |
| static void SetCKETriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 val; |
| u32 dev; |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u32 index; |
| u16 word; |
| |
| /* Tri-state unused CKEs when motherboard termination is available */ |
| |
| /* FIXME: skip for Ax */ |
| |
| dev = pDCTstat->dev_dct; |
| word = pDCTstat->CSPresent; |
| |
| index = 0x0c; |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| if ((word & 0x55) == 0) |
| val |= 1 << 12; |
| |
| if ((word & 0xAA) == 0) |
| val |= 1 << 13; |
| |
| Set_NB32_index_wait(dev, index_reg, index, val); |
| } |
| |
| static void SetODTTriState(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 val; |
| u32 dev; |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u8 cs; |
| u32 index; |
| u8 odt; |
| u8 max_dimms; |
| |
| /* FIXME: skip for Ax */ |
| |
| dev = pDCTstat->dev_dct; |
| |
| /* Tri-state unused ODTs when motherboard termination is available */ |
| max_dimms = (u8) mctGet_NVbits(NV_MAX_DIMMS); |
| odt = 0x0F; /* ODT tri-state setting */ |
| |
| if (pDCTstat->Status & (1 <<SB_Registered)) { |
| for (cs = 0; cs < 8; cs += 2) { |
| if (pDCTstat->CSPresent & (1 << cs)) { |
| odt &= ~(1 << (cs / 2)); |
| if (mctGet_NVbits(NV_4RANKType) != 0) { /* quad-rank capable platform */ |
| if (pDCTstat->CSPresent & (1 << (cs + 1))) |
| odt &= ~(4 << (cs / 2)); |
| } |
| } |
| } |
| } else { /* AM3 package */ |
| val = ~(pDCTstat->CSPresent); |
| odt = val & 9; /* swap bits 1 and 2 */ |
| if (val & (1 << 1)) |
| odt |= 1 << 2; |
| if (val & (1 << 2)) |
| odt |= 1 << 1; |
| } |
| |
| index = 0x0C; |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| val |= ((odt & 0xFF) << 8); /* set bits 11:8 ODTTriState[3:0] */ |
| Set_NB32_index_wait(dev, index_reg, index, val); |
| |
| } |
| |
| static void InitPhyCompensation(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 i; |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| u32 valx = 0; |
| u32 dword; |
| const u8 *p; |
| |
| val = Get_NB32_index_wait(dev, index_reg, 0x00); |
| dword = 0; |
| for (i=0; i < 6; i++) { |
| switch (i) { |
| case 0: |
| case 4: |
| p = Table_Comp_Rise_Slew_15x; |
| valx = p[(val >> 16) & 3]; |
| break; |
| case 1: |
| case 5: |
| p = Table_Comp_Fall_Slew_15x; |
| valx = p[(val >> 16) & 3]; |
| break; |
| case 2: |
| p = Table_Comp_Rise_Slew_20x; |
| valx = p[(val >> 8) & 3]; |
| break; |
| case 3: |
| p = Table_Comp_Fall_Slew_20x; |
| valx = p[(val >> 8) & 3]; |
| break; |
| |
| } |
| dword |= valx << (5 * i); |
| } |
| |
| /* Override/Exception */ |
| if (!pDCTstat->GangedMode) { |
| i = 0; /* use i for the dct setting required */ |
| if (pDCTstat->MAdimms[0] < 4) |
| i = 1; |
| if (((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) && (pDCTstat->MAdimms[i] == 4)) { |
| dword &= 0xF18FFF18; |
| index_reg = 0x98; /* force dct = 0 */ |
| } |
| } |
| |
| Set_NB32_index_wait(dev, index_reg, 0x0a, dword); |
| } |
| |
| static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 reg; |
| u32 val; |
| u32 dev = pDCTstat->dev_dct; |
| |
| /* GhEnhancement #18429 modified by askar: For low NB CLK : |
| * Memclk ratio, the DCT may need to arbitrate early to avoid |
| * unnecessary bubbles. |
| * bit 19 of F2x[1,0]78 Dram Control Register, set this bit only when |
| * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive) |
| */ |
| reg = 0x78 + 0x100 * dct; |
| val = Get_NB32(dev, reg); |
| |
| if (pDCTstat->LogicalCPUID & (AMD_DR_Cx | AMD_DR_Dx)) |
| val |= (1 << EarlyArbEn); |
| else if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat)) |
| val |= (1 << EarlyArbEn); |
| |
| Set_NB32(dev, reg, val); |
| } |
| |
| static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 reg; |
| u32 val; |
| u32 tmp; |
| u32 rem; |
| u32 dev = pDCTstat->dev_dct; |
| u32 hi, lo; |
| u8 NbDid = 0; |
| |
| /* Check if NB COF >= 4*Memclk, if it is not, return a fatal error |
| */ |
| |
| /* 3*(Fn2xD4[NBFid]+4)/(2^NbDid)/(3+Fn2x94[MemClkFreq]) */ |
| _RDMSR(0xC0010071, &lo, &hi); |
| if (lo & (1 << 22)) |
| NbDid |= 1; |
| |
| reg = 0x94; |
| val = Get_NB32(dev, reg); |
| if (!(val & (1 << MemClkFreqVal))) |
| val = Get_NB32(dev, reg + 0x100); /* get the DCT1 value */ |
| |
| val &= 0x07; |
| val += 3; |
| if (NbDid) |
| val <<= 1; |
| tmp = val; |
| |
| dev = pDCTstat->dev_nbmisc; |
| reg = 0xD4; |
| val = Get_NB32(dev, reg); |
| val &= 0x1F; |
| val += 3; |
| val *= 3; |
| val = val / tmp; |
| rem = val % tmp; |
| tmp >>= 1; |
| |
| /* Yes this could be nicer but this was how the asm was.... */ |
| if (val < 3) { /* NClk:MemClk < 3:1 */ |
| return 0; |
| } else if (val > 4) { /* NClk:MemClk >= 5:1 */ |
| return 0; |
| } else if ((val == 4) && (rem > tmp)) { /* NClk:MemClk > 4.5:1 */ |
| return 0; |
| } else { |
| return 1; /* 3:1 <= NClk:MemClk <= 4.5:1*/ |
| } |
| } |
| |
| static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| u32 i; |
| struct DCTStatStruc *pDCTstat; |
| u32 start, stop; |
| u8 *p; |
| u16 host_serv1, host_serv2; |
| |
| /* Initialize Data structures by clearing all entries to 0 */ |
| p = (u8 *) pMCTstat; |
| for (i = 0; i < sizeof(struct MCTStatStruc); i++) { |
| p[i] = 0; |
| } |
| |
| for (Node = 0; Node < 8; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| host_serv1 = pDCTstat->HostBiosSrvc1; |
| host_serv2 = pDCTstat->HostBiosSrvc2; |
| |
| p = (u8 *) pDCTstat; |
| start = 0; |
| stop = ((u32) &((struct DCTStatStruc *)0)->CH_MaxRdLat[2]); |
| for (i = start; i < stop ; i++) { |
| p[i] = 0; |
| } |
| |
| start = ((u32) &((struct DCTStatStruc *)0)->CH_D_BC_RCVRDLY[2][4]); |
| stop = sizeof(struct DCTStatStruc); |
| for (i = start; i < stop; i++) { |
| p[i] = 0; |
| } |
| pDCTstat->HostBiosSrvc1 = host_serv1; |
| pDCTstat->HostBiosSrvc2 = host_serv2; |
| } |
| } |
| |
| static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u8 i; |
| u32 reg_off, dword; |
| u32 dev = pDCTstat->dev_dct; |
| |
| if (pDCTstat->LogicalCPUID & AMD_DR_Dx) { |
| if ((pDCTstat->Speed == 3)) |
| dword = 0x00000800; |
| else |
| dword = 0x00000000; |
| for (i=0; i < 2; i++) { |
| reg_off = 0x100 * i; |
| Set_NB32(dev, 0x98 + reg_off, 0x0D000030); |
| Set_NB32(dev, 0x9C + reg_off, dword); |
| Set_NB32(dev, 0x98 + reg_off, 0x4D040F30); |
| } |
| } |
| } |
| |
| static void mct_EnDllShutdownSR(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 reg_off = 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct, val; |
| |
| /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */ |
| if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) { |
| Set_NB32(dev, 0x9C + reg_off, 0x1C); |
| Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006); |
| Set_NB32(dev, 0x9C + reg_off, 0x13D); |
| Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007); |
| |
| val = Get_NB32(dev, 0x90 + reg_off); |
| val &= ~(1 << 27/* DisDllShutdownSR */); |
| Set_NB32(dev, 0x90 + reg_off, val); |
| } |
| } |
| |
| static u32 mct_DisDllShutdownSR(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u32 DramConfigLo, u8 dct) |
| { |
| u32 reg_off = 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct; |
| |
| /* Write 0000_07D0h to register F2x[1, 0]98_x4D0FE006 */ |
| if (pDCTstat->LogicalCPUID & (AMD_DR_DAC2_OR_C3)) { |
| Set_NB32(dev, 0x9C + reg_off, 0x7D0); |
| Set_NB32(dev, 0x98 + reg_off, 0x4D0FE006); |
| Set_NB32(dev, 0x9C + reg_off, 0x190); |
| Set_NB32(dev, 0x98 + reg_off, 0x4D0FE007); |
| |
| DramConfigLo |= /* DisDllShutdownSR */ 1 << 27; |
| } |
| |
| return DramConfigLo; |
| } |
| |
| 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 ProgDramMRSReg_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 DramMRS, dword; |
| u8 byte; |
| |
| DramMRS = 0; |
| |
| /* Set chip select CKE control mode */ |
| if (mctGet_NVbits(NV_CKE_CTL)) { |
| if (pDCTstat->CSPresent == 3) { |
| u16 word; |
| word = pDCTstat->DIMMSPDCSE; |
| if (dct == 0) |
| word &= 0b01010100; |
| else |
| word &= 0b10101000; |
| if (word == 0) |
| DramMRS |= 1 << 23; |
| } |
| } |
| /* |
| DRAM MRS Register |
| DrvImpCtrl: drive impedance control.01b(34 ohm driver; Ron34 = Rzq/7) |
| */ |
| DramMRS |= 1 << 2; |
| /* Dram nominal termination: */ |
| byte = pDCTstat->MAdimms[dct]; |
| if (!(pDCTstat->Status & (1 << SB_Registered))) { |
| DramMRS |= 1 << 7; /* 60 ohms */ |
| if (byte & 2) { |
| if (pDCTstat->Speed < 6) |
| DramMRS |= 1 << 8; /* 40 ohms */ |
| else |
| DramMRS |= 1 << 9; /* 30 ohms */ |
| } |
| } |
| /* Dram dynamic termination: Disable(1DIMM), 120ohm(>=2DIMM) */ |
| if (!(pDCTstat->Status & (1 << SB_Registered))) { |
| if (byte >= 2) { |
| if (pDCTstat->Speed == 7) |
| DramMRS |= 1 << 10; |
| else |
| DramMRS |= 1 << 11; |
| } |
| } else { |
| DramMRS |= mct_DramTermDyn_RDimm(pMCTstat, pDCTstat, byte); |
| } |
| |
| /* burst length control */ |
| if (pDCTstat->Status & (1 << SB_128bitmode)) |
| DramMRS |= 1 << 1; |
| /* Qoff=0, output buffers enabled */ |
| /* Tcwl */ |
| DramMRS |= (pDCTstat->Speed - 4) << 20; |
| /* ASR=1, auto self refresh */ |
| /* SRT=0 */ |
| DramMRS |= 1 << 18; |
| |
| dword = Get_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84); |
| dword &= ~0x00FC2F8F; |
| dword |= DramMRS; |
| Set_NB32(pDCTstat->dev_dct, 0x100 * dct + 0x84, dword); |
| } |
| |
| void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct, |
| u32 DramConfigHi) |
| { |
| /* Bug#15114: Comp. update interrupted by Freq. change can cause |
| * subsequent update to be invalid during any MemClk frequency change: |
| * Solution: From the bug report: |
| * 1. A software-initiated frequency change should be wrapped into the |
| * following sequence : |
| * - a) Disable Compensation (F2[1, 0]9C_x08[30] ) |
| * b) Reset the Begin Compensation bit (D3CMP->COMP_CONFIG[0]) in all the compensation engines |
| * c) Do frequency change |
| * d) Enable Compensation (F2[1, 0]9C_x08[30] ) |
| * 2. A software-initiated Disable Compensation should always be |
| * followed by step b) of the above steps. |
| * Silicon Status: Fixed In Rev B0 |
| * |
| * Errata#177: DRAM Phy Automatic Compensation Updates May Be Invalid |
| * Solution: BIOS should disable the phy automatic compensation prior |
| * to initiating a memory clock frequency change as follows: |
| * 1. Disable PhyAutoComp by writing 1'b1 to F2x[1, 0]9C_x08[30] |
| * 2. Reset the Begin Compensation bits by writing 32'h0 to |
| * F2x[1, 0]9C_x4D004F00 |
| * 3. Perform frequency change |
| * 4. Enable PhyAutoComp by writing 1'b0 to F2x[1, 0]9C_08[30] |
| * In addition, any time software disables the automatic phy |
| * compensation it should reset the begin compensation bit per step 2. |
| * Silicon Status: Fixed in DR-B0 |
| */ |
| |
| u32 dev = pDCTstat->dev_dct; |
| u32 index_reg = 0x98 + 0x100 * dct; |
| u32 index; |
| |
| u32 val; |
| |
| index = 0x08; |
| val = Get_NB32_index_wait(dev, index_reg, index); |
| if (!(val & (1 << DisAutoComp))) |
| Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp)); |
| |
| mct_Wait(100); |
| |
| Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi); |
| } |
| |
| static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstatA) |
| { |
| u8 Node; |
| struct DCTStatStruc *pDCTstat; |
| |
| /* Errata 178 |
| * |
| * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations |
| * In TX FIFO |
| * Solution: BIOS should program DRAM Control Register[RdPtrInit] = |
| * 5h, (F2x[1, 0]78[3:0] = 5h). |
| * Silicon Status: Fixed In Rev B0 |
| * |
| * Bug#15880: Determine validity of reset settings for DDR PHY timing. |
| * Solution: At least, set WrDqs fine delay to be 0 for DDR3 training. |
| */ |
| for (Node = 0; Node < 8; Node++) { |
| pDCTstat = pDCTstatA + Node; |
| |
| if (pDCTstat->NodePresent) { |
| mct_BeforeDQSTrainSamp(pDCTstat); /* only Bx */ |
| mct_ResetDLL_D(pMCTstat, pDCTstat, 0); |
| mct_ResetDLL_D(pMCTstat, pDCTstat, 1); |
| } |
| } |
| } |
| |
| static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u8 Receiver; |
| u32 dev = pDCTstat->dev_dct; |
| u32 reg_off = 0x100 * dct; |
| u32 addr; |
| u32 lo, hi; |
| u8 wrap32dis = 0; |
| u8 valid = 0; |
| |
| /* Skip reset DLL for B3 */ |
| if (pDCTstat->LogicalCPUID & AMD_DR_B3) { |
| return; |
| } |
| |
| addr = HWCR; |
| _RDMSR(addr, &lo, &hi); |
| if(lo & (1<<17)) { /* save the old value */ |
| wrap32dis = 1; |
| } |
| lo |= (1<<17); /* HWCR.wrap32dis */ |
| /* Setting wrap32dis allows 64-bit memory references in 32bit mode */ |
| _WRMSR(addr, lo, hi); |
| |
| pDCTstat->Channel = dct; |
| Receiver = mct_InitReceiver_D(pDCTstat, dct); |
| /* there are four receiver pairs, loosely associated with chipselects.*/ |
| for (; Receiver < 8; Receiver += 2) { |
| if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) { |
| addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, dct, Receiver, &valid); |
| if (valid) { |
| mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr); /* cache fills */ |
| |
| /* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */ |
| Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00008000); |
| mct_Wait(80); /* wait >= 300ns */ |
| |
| /* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */ |
| Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00000000); |
| mct_Wait(800); /* wait >= 2us */ |
| break; |
| } |
| } |
| } |
| |
| if(!wrap32dis) { |
| addr = HWCR; |
| _RDMSR(addr, &lo, &hi); |
| lo &= ~(1<<17); /* restore HWCR.wrap32dis */ |
| _WRMSR(addr, lo, hi); |
| } |
| } |
| |
| static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat) |
| { |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| |
| /* Enable F2x110[DctDatIntlv] */ |
| /* Call back not required mctHookBeforeDatIntlv_D() */ |
| /* FIXME Skip for Ax */ |
| if (!pDCTstat->GangedMode) { |
| val = Get_NB32(dev, 0x110); |
| val |= 1 << 5; /* DctDatIntlv */ |
| Set_NB32(dev, 0x110, val); |
| |
| /* FIXME Skip for Cx */ |
| dev = pDCTstat->dev_nbmisc; |
| val = Get_NB32(dev, 0x8C); /* NB Configuration Hi */ |
| val |= 1 << (36-32); /* DisDatMask */ |
| Set_NB32(dev, 0x8C, val); |
| } |
| } |
| |
| static void SetDllSpeedUp_D(struct MCTStatStruc *pMCTstat, |
| struct DCTStatStruc *pDCTstat, u8 dct) |
| { |
| u32 val; |
| u32 dev = pDCTstat->dev_dct; |
| u32 reg_off = 0x100 * dct; |
| |
| if (pDCTstat->Speed >= 7) { /* DDR1600 and above */ |
| /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F10 */ |
| Set_NB32(dev, reg_off + 0x98, 0x0D080F10); |
| val = Get_NB32(dev, reg_off + 0x9C); |
| val |= 1 < 13; |
| Set_NB32(dev, reg_off + 0x9C, val); |
| Set_NB32(dev, reg_off + 0x98, 0x4D080F10); |
| |
| /* Set bit13 PowerDown to register F2x[1, 0]98_x0D080F11 */ |
| Set_NB32(dev, reg_off + 0x98, 0x0D080F11); |
| val = Get_NB32(dev, reg_off + 0x9C); |
| val |= 1 < 13; |
| Set_NB32(dev, reg_off + 0x9C, val); |
| Set_NB32(dev, reg_off + 0x98, 0x4D080F11); |
| |
| /* Set bit13 PowerDown to register F2x[1, 0]98_x0D088F30 */ |
| Set_NB32(dev, reg_off + 0x98, 0x0D088F30); |
| val = Get_NB32(dev, reg_off + 0x9C); |
| val |= 1 < 13; |
| Set_NB32(dev, reg_off + 0x9C, val); |
| Set_NB32(dev, reg_off + 0x98, 0x4D088F30); |
| |
| /* Set bit13 PowerDown to register F2x[1, 0]98_x0D08CF30 */ |
| Set_NB32(dev, reg_off + 0x98, 0x0D08CF30); |
| val = Get_NB32(dev, reg_off + 0x9C); |
| val |= 1 < 13; |
| Set_NB32(dev, reg_off + 0x9C, val); |
| Set_NB32(dev, reg_off + 0x98, 0x4D08CF30); |
| |
| } |
| } |
| |
| static void SyncSetting(struct DCTStatStruc *pDCTstat) |
| { |
| /* set F2x78[ChSetupSync] when F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup, |
| * CkeSetup] setups for one DCT are all 0s and at least one of the setups, |
| * F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup, CkeSetup], of the other |
| * controller is 1 |
| */ |
| u32 cha, chb; |
| u32 dev = pDCTstat->dev_dct; |
| u32 val; |
| |
| cha = pDCTstat->CH_ADDR_TMG[0] & 0x0202020; |
| chb = pDCTstat->CH_ADDR_TMG[1] & 0x0202020; |
| |
| if ((cha != chb) && ((cha == 0) || (chb == 0))) { |
| val = Get_NB32(dev, 0x78); |
| val |= 1 << ChSetupSync; |
| Set_NB32(dev, 0x78, val); |
| } |
| } |
| |
| static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) { |
| |
| u32 val; |
| u32 reg_off = 0x100 * dct; |
| u32 dev = pDCTstat->dev_dct; |
| |
| if (pDCTstat->LogicalCPUID & (AMD_DR_B2 | AMD_DR_B3)) { |
| mct_Wait(10000); /* Wait 50 us*/ |
| val = Get_NB32(dev, 0x110); |
| if (!(val & (1 << DramEnabled))) { |
| /* If 50 us expires while DramEnable =0 then do the following */ |
| val = Get_NB32(dev, 0x90 + reg_off); |
| val &= ~(1 << Width128); /* Program Width128 = 0 */ |
| Set_NB32(dev, 0x90 + reg_off, val); |
| |
| val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05); /* Perform dummy CSR read to F2x09C_x05 */ |
| |
| if (pDCTstat->GangedMode) { |
| val = Get_NB32(dev, 0x90 + reg_off); |
| val |= 1 << Width128; /* Program Width128 = 0 */ |
| Set_NB32(dev, 0x90 + reg_off, val); |
| } |
| } |
| } |
| } |
| |
| /* ========================================================== |
| * 6-bit Bank Addressing Table |
| * RR=rows-13 binary |
| * B=Banks-2 binary |
| * CCC=Columns-9 binary |
| * ========================================================== |
| * DCT CCCBRR Rows Banks Columns 64-bit CS Size |
| * Encoding |
| * 0000 000000 13 2 9 128MB |
| * 0001 001000 13 2 10 256MB |
| * 0010 001001 14 2 10 512MB |
| * 0011 010000 13 2 11 512MB |
| * 0100 001100 13 3 10 512MB |
| * 0101 001101 14 3 10 1GB |
| * 0110 010001 14 2 11 1GB |
| * 0111 001110 15 3 10 2GB |
| * 1000 010101 14 3 11 2GB |
| * 1001 010110 15 3 11 4GB |
| * 1010 001111 16 3 10 4GB |
| * 1011 010111 16 3 11 8GB |
| */ |
| u8 crcCheck(u8 smbaddr) |
| { |
| u8 byte_use; |
| u8 Index; |
| u16 CRC; |
| u8 byte, i; |
| |
| byte_use = mctRead_SPD(smbaddr, SPD_ByteUse); |
| if (byte_use & 0x80) |
| byte_use = 117; |
| else |
| byte_use = 126; |
| |
| CRC = 0; |
| for (Index = 0; Index < byte_use; Index ++) { |
| byte = mctRead_SPD(smbaddr, Index); |
| CRC ^= byte << 8; |
| for (i=0; i<8; i++) { |
| if (CRC & 0x8000) { |
| CRC <<= 1; |
| CRC ^= 0x1021; |
| } else |
| CRC <<= 1; |
| } |
| } |
| return CRC == (mctRead_SPD(smbaddr, SPD_byte_127) << 8 | mctRead_SPD(smbaddr, SPD_byte_126)); |
| } |