diff options
author | Siyuan Wang <wangsiyuanbuaa@gmail.com> | 2013-07-25 15:14:15 +0800 |
---|---|---|
committer | Bruce Griffith <Bruce.Griffith@se-eng.com> | 2013-08-04 05:40:37 +0200 |
commit | affe85fbc8a13d35960aa92ae87cbb6330ad253f (patch) | |
tree | 9c1ace69f12b06b6544faf041994aa4288fb2e45 /src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat | |
parent | ae8d06969bdde9b1250bc3c4ad93f5db408dae98 (diff) |
AMD Kabini: Add AGESA/PI code for new processor family
Change-Id: Icb6f64e2e3cfd678fb4fb4f13f0e4b678d5acc4a
Signed-off-by: Siyuan Wang <SiYuan.Wang@amd.com>
Signed-off-by: Siyuan Wang <wangsiyuanbuaa@gmail.com>
Reviewed-by: Nick Dill <nick.dill@se-eng.com>
Tested-by: Bruce Griffith <bruce.griffith@se-eng.com>
Reviewed-on: http://review.coreboot.org/3836
Tested-by: build bot (Jenkins)
Reviewed-by: Martin Roth <martin.roth@se-eng.com>
Diffstat (limited to 'src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat')
25 files changed, 8410 insertions, 0 deletions
diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CRAT/mfCrat.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CRAT/mfCrat.h new file mode 100644 index 0000000000..e7f9267864 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CRAT/mfCrat.h @@ -0,0 +1,95 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfCrat.h + * + * Feature CRAT table support + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + +#ifndef _MFCRAT_H_ +#define _MFCRAT_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/// DCT Select Interleaving Address Mapping +typedef struct { + UINT8 DctSelIntLvAddr; ///< F2x110 DctSelIntLvAddr + UINT8 DctSelFunctionBit; ///< Lowest function bit to select DCT for interleaving +} DCTSELBIT_ENTRY; + +/// CRAT Memory Affinity Info Header +typedef struct { + UINT8 NumOfMemAffinityInfoEntries; ///< Integer that represents the proximity domain to + UINT32 MemoryWidth; ///< Specifies the number of parallel bits of the memory interface +} CRAT_MEMORY_AFFINITY_INFO_HEADER; + +/// CRAT Memory Affinity Info Entry +typedef struct { + UINT8 Domain; ///< Integer that represents the proximity domain to + ///< which the memory belongs + UINT32 BaseAddressLow; ///< Low 32Bits of the Base Address of the memory range + UINT32 BaseAddressHigh; ///< High 32Bits of the Base Address of the memory range + UINT32 LengthLow; ///< Low 32Bits of the length of the memory range + UINT32 LengthHigh; ///< High 32Bits of the length of the memory range +} CRAT_MEMORY_AFFINITY_INFO_ENTRY; + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +#endif /* _MFCRAT_H_ */ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.c new file mode 100644 index 0000000000..8baba277e6 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.c @@ -0,0 +1,357 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfcsi.c + * + * Feature bank interleaving support (AKA Chip Select Interleaving ) + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/Csintlv) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + +/* This file contains functions for Chip Select interleaving */ + + + +#include "AGESA.h" +#include "amdlib.h" +#include "mm.h" +#include "mn.h" +#include "mt.h" +#include "mfcsi.h" +#include "Ids.h" +#include "GeneralServices.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_CSINTLV_MFCSI_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +STATIC +MemFDctInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +VOID +STATIC +CsIntSwap ( + IN OUT UINT32 *BaseMaskRegPtr, + IN UINT8 EnChipSels, + IN UINT8 LoBit, + IN UINT8 HiBit + ); + +BOOLEAN +MemFUndoInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +/* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function Applies DIMM bank (chip-select) interleaving if enabled + * and if all criteria are met. Interleaves chip-selects on page boundaries. + * This function calls subfunctions that sets up CS interleaving on multiple Sockets + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Dct; + BOOLEAN RetFlag; + + ASSERT (NBPtr != NULL); + + RetFlag = FALSE; + if (NBPtr->RefPtr->EnableBankIntlv) { + if (NBPtr->MCTPtr->NodeMemSize) { + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + RetFlag |= MemFDctInterleaveBanks (NBPtr); + } + } + } + return RetFlag; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function checks if bank interleaving has been enabled or not. If yes, it will + * undo bank interleaving. Otherwise, it does nothing. + * + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - Bank interleaving has been enabled. + * @return FALSE - Bank interleaving has not been enabled. + */ + +BOOLEAN +MemFUndoInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Cs; + UINT8 Dct; + UINT32 CSMask; + BOOLEAN CSIntlvEnabled; + BOOLEAN RetFlag; + + ASSERT (NBPtr != NULL); + + RetFlag = FALSE; + + if (NBPtr->RefPtr->EnableBankIntlv) { + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + if (NBPtr->DCTPtr->Timings.DctMemSize) { + CSIntlvEnabled = FALSE; + for (Cs = 0; Cs < MAX_CS_PER_CHANNEL; Cs++) { + if ((NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + Cs) & 1) != 0) { + CSMask = NBPtr->GetBitField (NBPtr, BFCSMask0Reg + (Cs / 2)); + if (((CSMask >> 5) & 0x1FF) != 0x1FF) { + CSIntlvEnabled = TRUE; + break; + } + } + } + if (CSIntlvEnabled) { + MemFDctInterleaveBanks (NBPtr); + RetFlag = TRUE; + } + } + } + } + return RetFlag; +} + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function Applies DIMM bank (chip-select) interleaving if enabled + * and if all criteria are met. Interleaves chip-selects on page boundaries. + * This function is run once per Socket + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - Register bits have been swapped. + * @return FALSE - Register bits have not been swapped. + * + */ + +BOOLEAN +STATIC +MemFDctInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Cs; + UINT8 EnChipSels; + UINT8 BankEncd; + UINT8 BankEncd0; + UINT8 i; + UINT8 j; + UINT32 BankAddrReg; + UINT32 BaseRegS0; + UINT32 BaseRegS1; + UINT32 MaskReg; + UINT8 Offset; + UINT8 Dct; + + ASSERT (NBPtr != NULL); + + Dct = NBPtr->Dct; + + // Check if CS interleaving can be enabled + EnChipSels = 0; + BankEncd0 = 0xFF; + Offset = 0; + for (Cs = 0; Cs < MAX_CS_PER_CHANNEL; Cs++) { + if ((NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + Cs) & 3) != 0) { + BankAddrReg = NBPtr->GetBitField (NBPtr, BFDramBankAddrReg); + BankEncd = (UINT8) ((BankAddrReg >> ((Cs / 2) * 4)) & 0xF); + if (BankEncd0 == 0xFF) { + BankEncd0 = BankEncd; + } else if (BankEncd0 != BankEncd) { + break; + } + if ((NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + Cs) & 1) != 0) { + EnChipSels++; + } + } + } + + // Swap Dram Base/Mask Addr to enable CS interleaving + if ((Cs == MAX_CS_PER_CHANNEL) && ((EnChipSels == 2) || (EnChipSels == 4) || (EnChipSels == 8))) { + NBPtr->TechPtr->GetCSIntLvAddr (BankEncd0, &i, &j); + NBPtr->DCTPtr->BankAddrMap = BankEncd0; + NBPtr->DCTPtr->EnabledChipSels = EnChipSels; + // Family specific CS interleaving low address adjustment + NBPtr->FamilySpecificHook[AdjustCSIntLvLowAddr] (NBPtr, &i); + + if (NBPtr->MCTPtr->Status[Sb128bitmode]) { + i++; + j++; + } + + for (Cs = 0; Cs < MAX_CS_PER_CHANNEL; Cs += 2) { + // + // LRDIMMS - Add an offset to the bit positions specified based on D18F2x[6C:60]_dct[1:0][RankDef] as follows: + // RankDef=0xb: 0 RankDef=10b: 1 RankDef=11b: 2 + // Using RankMult information: Lo/HiBit <<= (Mult >> 1) + // + if (NBPtr->MCTPtr->Status[SbLrdimms]) { + Offset = ((NBPtr->ChannelPtr->LrDimmRankMult[Cs >> 1]) >> 1); + } + BaseRegS0 = NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + Cs); + BaseRegS1 = NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + Cs + 1); + if (((BaseRegS0 | BaseRegS1) & 1) != 0) { + // Swap Mask register bits + MaskReg = NBPtr->GetBitField (NBPtr, BFCSMask0Reg + (Cs / 2)); + CsIntSwap (&MaskReg, EnChipSels, (i + Offset), (j + Offset)); + NBPtr->SetBitField (NBPtr, BFCSMask0Reg + (Cs / 2), MaskReg); + + // Swap Base register bits + CsIntSwap (&BaseRegS0, EnChipSels, (i + Offset), (j + Offset)); + NBPtr->SetBitField (NBPtr, BFCSBaseAddr0Reg + Cs, BaseRegS0); + CsIntSwap (&BaseRegS1, EnChipSels, (i + Offset), (j + Offset)); + NBPtr->SetBitField (NBPtr, BFCSBaseAddr0Reg + Cs + 1, BaseRegS1); + } + } + // + // Bank Interleaving is requested and has been enabled as well + // + NBPtr->MCTPtr->DctData[Dct].BkIntDis = FALSE; + return TRUE; + } else { + // + // Bank Interleaving is requested but cannot be enabled + // + PutEventLog (AGESA_WARNING, MEM_WARNING_BANK_INTERLEAVING_NOT_ENABLED, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + SetMemError (AGESA_WARNING, NBPtr->MCTPtr); + NBPtr->MCTPtr->DctData[Dct].BkIntDis = TRUE; + } + return FALSE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This supporting function swaps Chip selects + * + * @param[in,out] *BaseMaskRegPtr - Pointer to the Mask Register + * @param[in] *EnChipSels - Chip Selects to Enable + * @param[in] *LoBit - Lowest Bit + * @param[in] *HiBit - Highest Bit + * + * + */ + +VOID +STATIC +CsIntSwap ( + IN OUT UINT32 *BaseMaskRegPtr, + IN UINT8 EnChipSels, + IN UINT8 LoBit, + IN UINT8 HiBit + ) +{ + UINT8 BitDelta; + UINT32 TempHi; + UINT32 TempLo; + UINT32 AddrLoMask; + UINT32 AddrHiMask; + + ASSERT (BaseMaskRegPtr != NULL); + ASSERT (HiBit > LoBit); + + BitDelta = HiBit - LoBit; + AddrLoMask = (((UINT32)EnChipSels) - 1) << LoBit; + AddrHiMask = AddrLoMask << BitDelta; + + TempHi = TempLo = *BaseMaskRegPtr; + TempLo &= AddrLoMask; + TempLo <<= BitDelta; // move lower bits to upper bit position + TempHi &= AddrHiMask; + TempHi >>= BitDelta; // move upper bits to lower bit position + + *BaseMaskRegPtr &= ~AddrLoMask; + *BaseMaskRegPtr &= ~AddrHiMask; + *BaseMaskRegPtr |= TempLo | TempHi; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.h new file mode 100644 index 0000000000..b660f05d55 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/CSINTLV/mfcsi.h @@ -0,0 +1,80 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfcsi.h + * + * Memory Controller + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + +#ifndef _MFCSI_H_ +#define _MFCSI_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +MemFInterleaveBanks ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +#endif /* _MFCSI_H_ */ + + diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/DMI/mfDMI.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/DMI/mfDMI.c new file mode 100644 index 0000000000..af7bdaf357 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/DMI/mfDMI.c @@ -0,0 +1,721 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfDMI.c + * + * Memory DMI table support. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Main) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + +#include "AGESA.h" +#include "Ids.h" +#include "heapManager.h" +#include "cpuServices.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "GeneralServices.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_DMI_MFDMI_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +#define MAX_DCTS_PER_DIE 2 + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +UINT64 +MemFGetNodeMemBase ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 NodeLimit + ); + +UINT64 +MemFGetDctMemBase ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 DctLimit + ); + +UINT64 +MemFGetDctInterleavedMemSize ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT64 +MemFGetDctInterleavedLimit ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Dct, + IN UINT64 DctMemBase, + IN UINT64 DctInterleavedMemSize + ); + +BOOLEAN +MemFDMISupport3 ( + IN OUT MEM_MAIN_DATA_BLOCK *MemMainPtr + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function gets DDR3 DMI information from SPD buffer and stores the info into heap + * + * @param[in,out] *MemMainPtr - Pointer to the MEM_MAIN_DATA_BLOCK + * + */ +BOOLEAN +MemFDMISupport3 ( + IN OUT MEM_MAIN_DATA_BLOCK *MemMainPtr + ) +{ + UINT8 i; + UINT8 Dimm; + UINT8 Socket; + UINT8 NodeId; + UINT8 Dct; + UINT8 Channel; + UINT8 temp; + UINT16 LocalHandle; + UINT8 MaxPhysicalDimms; + UINT8 MaxLogicalDimms; + UINT8 NumLogicalDimms; + UINT32 *TotalMemSizePtr; + UINT8 *DmiTable; + UINT8 MaxChannelsPerSocket; + UINT8 MaxDimmsPerChannel; + UINT8 FormFactor; + UINT16 TotalWidth; + UINT16 Capacity; + UINT16 Width; + UINT16 Rank; + UINT16 BusWidth; + UINT64 ManufacturerIdCode; + UINT32 MaxSockets; + UINT32 Address; + UINT8 ChipSelectPairScale; + UINT32 TotalSize; + UINT32 DimmSize; + UINT32 CsBaseAddrReg0; + UINT32 CsBaseAddrReg1; + UINT64 AddrValue; + UINT64 DctMemBase; + UINT64 NodeMemBase; + UINT64 SysMemSize; + INT32 MTB_ps; + INT32 FTB_ps; + INT32 Value32; + BOOLEAN DctInterleaveEnabled; + UINT64 DctInterleavedMemSize; + BOOLEAN NodeInterleaveEnabled; + UINT16 T17HandleMatrix[MAX_SOCKETS_SUPPORTED][MAX_CHANNELS_PER_SOCKET][MAX_DIMMS_PER_CHANNEL]; + + MEM_NB_BLOCK *NBPtr; + MEM_PARAMETER_STRUCT *RefPtr; + MEM_DATA_STRUCT *MemPtr; + DIE_STRUCT *MCTPtr; + CH_DEF_STRUCT *ChannelPtr; + SPD_DEF_STRUCT *SpdDataStructure; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + MEM_DMI_PHYSICAL_DIMM_INFO *DmiPhysicalDimmInfoTable; + MEM_DMI_LOGICAL_DIMM_INFO *DmiLogicalDimmInfoTable; + DMI_T17_MEMORY_TYPE *DmiType17MemTypePtr; + + NBPtr = MemMainPtr->NBPtr; + MemPtr = MemMainPtr->MemPtr; + SpdDataStructure = MemPtr->SpdDataStructure; + MCTPtr = NBPtr->MCTPtr; + RefPtr = MemPtr->ParameterListPtr; + + // Initialize local variables + LocalHandle = 1; + MaxPhysicalDimms = 0; + MaxLogicalDimms = 0; + NumLogicalDimms = 0; + TotalSize = 0; + NodeMemBase = 0; + SysMemSize = 0; + + AGESA_TESTPOINT (TpProcMemDmi, &MemPtr->StdHeader); + + ASSERT (NBPtr != NULL); + + MaxSockets = (UINT8) (0x000000FF & GetPlatformNumberOfSockets ()); + for (Socket = 0; Socket < MaxSockets; Socket++) { + for (Channel = 0; Channel < GetMaxChannelsPerSocket (RefPtr->PlatformMemoryConfiguration, Socket, &MemPtr->StdHeader); Channel++) { + temp = (UINT8) GetMaxDimmsPerChannel (RefPtr->PlatformMemoryConfiguration, Socket, Channel); + MaxPhysicalDimms = MaxPhysicalDimms + temp; + MaxLogicalDimms += MAX_DIMMS_PER_CHANNEL; + } + } + + // Allocate heap for memory DMI table 16, 17, 19, 20 + AllocHeapParams.RequestedBufferSize = sizeof (UINT8) + // Storage for MaxPhysicalDimms + sizeof (UINT8) + // Storage for Type Detail + sizeof (UINT32) + // Storage for Total Memory Size + sizeof (DMI_T17_MEMORY_TYPE) + + MaxPhysicalDimms * sizeof (MEM_DMI_PHYSICAL_DIMM_INFO) + + sizeof (UINT8) + // Storage for MaxLogicalDimms + MaxLogicalDimms * sizeof (MEM_DMI_LOGICAL_DIMM_INFO); + + AllocHeapParams.BufferHandle = AMD_DMI_MEM_DEV_INFO_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (AGESA_SUCCESS != HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader)) { + PutEventLog (AGESA_CRITICAL, MEM_ERROR_HEAP_ALLOCATE_FOR_DMI_TABLE_DDR3, NBPtr->Node, 0, 0, 0, &MemPtr->StdHeader); + SetMemError (AGESA_CRITICAL, MCTPtr); + // Could not allocate heap for memory DMI table 16,17,19 and 20 for DDR3 + ASSERT (FALSE); + return FALSE; + } + + DmiTable = (UINT8 *) AllocHeapParams.BufferPtr; + // Max number of physical DIMMs + *DmiTable = MaxPhysicalDimms; + DmiTable ++; + // Type Detail; + *DmiTable = 0; + DmiTable ++; + // Pointer to total memory size + TotalMemSizePtr = (UINT32 *) DmiTable; + DmiTable += sizeof (UINT32); + // Memory Type + DmiType17MemTypePtr = (DMI_T17_MEMORY_TYPE *) DmiTable; + *DmiType17MemTypePtr = Ddr3MemType; + DmiType17MemTypePtr ++; + DmiTable = (UINT8 *) DmiType17MemTypePtr; + // Pointer to DMI info of Physical DIMMs + DmiPhysicalDimmInfoTable = (MEM_DMI_PHYSICAL_DIMM_INFO *) DmiTable; + + // + // Gather physical DIMM DMI info + // + for (Socket = 0; Socket < MaxSockets; Socket ++) { + MaxChannelsPerSocket = GetMaxChannelsPerSocket (RefPtr->PlatformMemoryConfiguration, Socket, &MemPtr->StdHeader); + DctMemBase = 0; + for (Channel = 0; Channel < MaxChannelsPerSocket; Channel ++) { + // + // Get Node number and Dct number for this channel + // + ChannelPtr = MemPtr->SocketList[Socket].ChannelPtr[Channel]; + NodeId = ChannelPtr->MCTPtr->NodeId; + NBPtr = MemMainPtr->NBPtr; + Dct = ChannelPtr->Dct; + NBPtr = &NBPtr[NodeId]; + NBPtr->SwitchDCT (NBPtr, Dct); + + MaxDimmsPerChannel = GetMaxDimmsPerChannel (RefPtr->PlatformMemoryConfiguration, Socket, Channel); + for (Dimm = 0; Dimm < MaxDimmsPerChannel; Dimm ++, SpdDataStructure ++, LocalHandle++) { + // + // Initialize default value for DMI fields + // + DmiPhysicalDimmInfoTable->DimmPresent = 0; + DmiPhysicalDimmInfoTable->Socket = Socket; + DmiPhysicalDimmInfoTable->Channel = Channel; + DmiPhysicalDimmInfoTable->Dimm = Dimm; + DmiPhysicalDimmInfoTable->TotalWidth = 0xFFFF; + DmiPhysicalDimmInfoTable->DataWidth = 0xFFFF; + DmiPhysicalDimmInfoTable->MemorySize = 0; + DmiPhysicalDimmInfoTable->Speed = 0; + DmiPhysicalDimmInfoTable->ManufacturerIdCode = 0; + DmiPhysicalDimmInfoTable->Attributes = 0; + DmiPhysicalDimmInfoTable->ConfigSpeed = 0; + DmiPhysicalDimmInfoTable->ExtSize = 0; + DmiPhysicalDimmInfoTable->FormFactor = UnknowFormFactor; + DmiPhysicalDimmInfoTable->Handle = LocalHandle; + T17HandleMatrix[Socket][Channel][Dimm] = LocalHandle; + + for (i = 0; i < 4; i++) { + DmiPhysicalDimmInfoTable->SerialNumber[i] = 0xFF; + } + + for (i = 0; i < 18; i++) { + DmiPhysicalDimmInfoTable->PartNumber[i] = 0x0; + } + + if (SpdDataStructure->DimmPresent) { + // Total Width (offset 08h) & Data Width (offset 0Ah) + TotalWidth = (UINT16) SpdDataStructure->Data[8]; + if ((TotalWidth & 0x18) == 0) { + // non ECC + if ((TotalWidth & 0x07) == 0) { + DmiPhysicalDimmInfoTable->TotalWidth = 8; // 8 bits + } else if ((TotalWidth & 0x07) == 1) { + DmiPhysicalDimmInfoTable->TotalWidth = 16; // 16 bits + } else if ((TotalWidth & 0x07) == 2) { + DmiPhysicalDimmInfoTable->TotalWidth = 32; // 32 bits + } else if ((TotalWidth & 0x07) == 3) { + DmiPhysicalDimmInfoTable->TotalWidth = 64; // 64 bits + } + DmiPhysicalDimmInfoTable->DataWidth = DmiPhysicalDimmInfoTable->TotalWidth ; + } else { + // ECC + if ((TotalWidth & 0x07) == 0) { + DmiPhysicalDimmInfoTable->TotalWidth = 8 + 8; // 8 bits + } else if ((TotalWidth & 0x07) == 1) { + DmiPhysicalDimmInfoTable->TotalWidth = 16 + 8; // 16 bits + } else if ((TotalWidth & 0x07) == 2) { + DmiPhysicalDimmInfoTable->TotalWidth = 32 + 8; // 32 bits + } else if ((TotalWidth & 0x07) == 3) { + DmiPhysicalDimmInfoTable->TotalWidth = 64 + 8; // 64 bits + } + DmiPhysicalDimmInfoTable->DataWidth = DmiPhysicalDimmInfoTable->TotalWidth - 8; + } + + // Memory Size (offset 0Ch) + Capacity = 0; + BusWidth = 0; + Width = 0; + Rank = 0; + temp = (UINT8) SpdDataStructure->Data[4]; + if ((temp & 0x0F) == 0) { + Capacity = 0x0100; // 256M + } else if ((temp & 0x0F) == 1) { + Capacity = 0x0200; // 512M + } else if ((temp & 0x0F) == 2) { + Capacity = 0x0400; // 1G + } else if ((temp & 0x0F) == 3) { + Capacity = 0x0800; // 2G + } else if ((temp & 0x0F) == 4) { + Capacity = 0x1000; // 4G + } else if ((temp & 0x0F) == 5) { + Capacity = 0x2000; // 8G + } else if ((temp & 0x0F) == 6) { + Capacity = 0x4000; // 16G + } + + temp = (UINT8) SpdDataStructure->Data[8]; + if ((temp & 0x07) == 0) { + BusWidth = 8; // 8 bits + } else if ((temp & 0x07) == 1) { + BusWidth = 16; // 16 bits + } else if ((temp & 0x07) == 2) { + BusWidth = 32; // 32 bits + } else if ((temp & 0x07) == 3) { + BusWidth = 64; // 64 bits + } + + temp = (UINT8) SpdDataStructure->Data[7]; + if ((temp & 0x07) == 0) { + Width = 4; // 4 bits + } else if ((temp & 0x07) == 1) { + Width = 8; // 8 bits + } else if ((temp & 0x07) == 2) { + Width = 16; // 16 bits + } else if ((temp & 0x07) == 3) { + Width = 32; // 32 bits + } + + temp = (UINT8) SpdDataStructure->Data[7]; + if (((temp >> 3) & 0x07) == 0) { + Rank = 1; // 4 bits + DmiPhysicalDimmInfoTable->Attributes = 1; // Single Rank Dimm + } else if (((temp >> 3) & 0x07) == 1) { + Rank = 2; // 8 bits + DmiPhysicalDimmInfoTable->Attributes = 2; // Dual Rank Dimm + } else if (((temp >> 3) & 0x07) == 2) { + Rank = 3; // 16 bits + } else if (((temp >> 3) & 0x07) == 3) { + Rank = 4; // 32 bits + DmiPhysicalDimmInfoTable->Attributes = 4; // Quad Rank Dimm + } + + DimmSize = (UINT32) (Capacity / 8 * BusWidth / Width * Rank); + if (DimmSize < 0x7FFF) { + DmiPhysicalDimmInfoTable->MemorySize = (UINT16) DimmSize; + } else { + DmiPhysicalDimmInfoTable->MemorySize = 0x7FFF; + DmiPhysicalDimmInfoTable->ExtSize = DimmSize; + } + + // Form Factor (offset 0Eh) + FormFactor = (UINT8) SpdDataStructure->Data[3]; + if (((FormFactor & 0x0F) == 0x01) || ((FormFactor & 0x0F) == 0x02) || ((FormFactor & 0x0F) == 0x0B)) { + DmiPhysicalDimmInfoTable->FormFactor = 0x09; // RDIMM or UDIMM or LRDIMM + } else if ((FormFactor & 0x0F) == 0x03) { + DmiPhysicalDimmInfoTable->FormFactor = 0x0D; // SO-DIMM + } else { + DmiPhysicalDimmInfoTable->FormFactor = 0x02; // UNKNOWN + } + + // DIMM Present + DmiPhysicalDimmInfoTable->DimmPresent = 1; + + // Type Detail (offset 13h) + if (((FormFactor & 0x0F) == 0x01) || ((FormFactor & 0x0F) == 0x0B)) { + *((UINT8 *) (AllocHeapParams.BufferPtr) + 1) = 1; // Registered (Buffered) + } else { + *((UINT8 *) (AllocHeapParams.BufferPtr) + 1) = 2; // Unbuffered (Unregistered) + } + + // Speed (offset 15h) + MTB_ps = ((INT32) SpdDataStructure->Data[10] * 1000) / SpdDataStructure->Data[11]; + FTB_ps = (SpdDataStructure->Data[9] >> 4) / (SpdDataStructure->Data[9] & 0xF); + Value32 = (MTB_ps * SpdDataStructure->Data[12]) + (FTB_ps * (INT8) SpdDataStructure->Data[34]) ; + if (Value32 <= 938) { + DmiPhysicalDimmInfoTable->Speed = 1067; // DDR3-2133 + } else if (Value32 <= 1071) { + DmiPhysicalDimmInfoTable->Speed = 933; // DDR3-1866 + } else if (Value32 <= 1250) { + DmiPhysicalDimmInfoTable->Speed = 800; // DDR3-1600 + } else if (Value32 <= 1500) { + DmiPhysicalDimmInfoTable->Speed = 667; // DDR3-1333 + } else if (Value32 <= 1875) { + DmiPhysicalDimmInfoTable->Speed = 533; // DDR3-1066 + } else if (Value32 <= 2500) { + DmiPhysicalDimmInfoTable->Speed = 400; // DDR3-800 + } + + // Manufacturer (offset 17h) + ManufacturerIdCode = (UINT64) SpdDataStructure->Data[118]; + DmiPhysicalDimmInfoTable->ManufacturerIdCode = (ManufacturerIdCode << 8) | ((UINT64) SpdDataStructure->Data[117]); + + // Serial Number (offset 18h) + for (i = 0; i < 4; i++) { + DmiPhysicalDimmInfoTable->SerialNumber[i] = (UINT8) SpdDataStructure->Data[i + 122]; + } + // Part Number (offset 1Ah) + for (i = 0; i < 18; i++) { + DmiPhysicalDimmInfoTable->PartNumber[i] = (UINT8) SpdDataStructure->Data[i + 128]; + } + + // Configured Memory Clock Speed (offset 20h) + DmiPhysicalDimmInfoTable->ConfigSpeed = NBPtr->DCTPtr->Timings.Speed; + } // Dimm present + TotalSize += (UINT32) DmiPhysicalDimmInfoTable->MemorySize; + DmiPhysicalDimmInfoTable ++; + } // Dimm loop + } // Channel loop + } // Socket loop + + // Max number of logical DIMMs + DmiTable = (UINT8 *) DmiPhysicalDimmInfoTable; + *DmiTable = MaxLogicalDimms; + DmiTable ++; + + // Pointer to DMI info of Logical DIMMs + DmiLogicalDimmInfoTable = (MEM_DMI_LOGICAL_DIMM_INFO *) DmiTable; + + // Calculate the total memory size in the system + SysMemSize = MemFGetNodeMemBase (MemMainPtr->NBPtr, MemPtr->DieCount); + // + // Gather logical DIMM DMI info + // + for (Socket = 0; Socket < MaxSockets; Socket ++) { + MaxChannelsPerSocket = GetMaxChannelsPerSocket (RefPtr->PlatformMemoryConfiguration, Socket, &MemPtr->StdHeader); + DctMemBase = 0; + for (Channel = 0; Channel < MaxChannelsPerSocket; Channel ++) { + MaxDimmsPerChannel = GetMaxDimmsPerChannel (RefPtr->PlatformMemoryConfiguration, Socket, Channel); + // + // Get Node number and Dct number for this channel + // + ChannelPtr = MemPtr->SocketList[Socket].ChannelPtr[Channel]; + NodeId = ChannelPtr->MCTPtr->NodeId; + NBPtr = MemMainPtr->NBPtr; + Dct = ChannelPtr->Dct; + NodeMemBase = MemFGetNodeMemBase (NBPtr, NodeId); + NBPtr = &NBPtr[NodeId]; + + DctMemBase = MemFGetDctMemBase (NBPtr, Dct); + DctInterleavedMemSize = MemFGetDctInterleavedMemSize (NBPtr); + NBPtr->SwitchDCT (NBPtr, Dct); + NodeInterleaveEnabled = (NBPtr->GetBitField (NBPtr, BFDramIntlvEn) == 0) ? FALSE : TRUE; + DctInterleaveEnabled = (NBPtr->GetBitField (NBPtr, BFDctSelIntLvEn) == 0) ? FALSE : TRUE; + + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm ++, DmiLogicalDimmInfoTable ++) { + // + // Initialize default value for DMI fields + // + DmiLogicalDimmInfoTable->DimmPresent = 0; + DmiLogicalDimmInfoTable->Socket = Socket; + DmiLogicalDimmInfoTable->Channel = Channel; + DmiLogicalDimmInfoTable->Dimm = Dimm; + DmiLogicalDimmInfoTable->StartingAddr = 0; + DmiLogicalDimmInfoTable->EndingAddr = 0; + DmiLogicalDimmInfoTable->ExtStartingAddr = 0; + DmiLogicalDimmInfoTable->ExtEndingAddr = 0; + // + // Starting/Ending Address for each DIMM + // + // If Ending Address >= 0xFFFFFFFF, update Starting Address & Ending Address to 0xFFFFFFFF, + // and use the Extended Starting Address & Extended Ending Address instead. + // + CsBaseAddrReg0 = NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + 2 * Dimm); + CsBaseAddrReg1 = NBPtr->GetBitField (NBPtr, BFCSBaseAddr0Reg + 2 * Dimm + 1); + if ((CsBaseAddrReg0 & 1) != 0 || (CsBaseAddrReg1 & 1) != 0) { + ASSERT (NumLogicalDimms < MaxLogicalDimms); + NumLogicalDimms ++; + DmiLogicalDimmInfoTable->DimmPresent = 1; + if (Dimm < MaxDimmsPerChannel) { + // The logical DIMM is mapped from a SR/QR physical DIMM + DmiLogicalDimmInfoTable->MemoryDeviceHandle = T17HandleMatrix[Socket][Channel][Dimm]; + } else { + // The logical DIMM is mapped from a QR physical DIMM + DmiLogicalDimmInfoTable->MemoryDeviceHandle = T17HandleMatrix[Socket][Channel][Dimm - 2]; + } + // + // For DR and QR DIMMs, DIMM size should be scaled based on the CS pair presence + // + ChipSelectPairScale = ((CsBaseAddrReg0 & 1) != 0 && (CsBaseAddrReg1 & 1) != 0) ? 1 : 0; + Address = ((CsBaseAddrReg0 & 1) != 0 ? CsBaseAddrReg0 : CsBaseAddrReg1) & NBPtr->CsRegMsk; + Address = (Address & 0xFFFF0000) >> 2; + if (DctInterleaveEnabled) { + // + // When channel interleaving is enabled, all the DIMMs on the node share the same starting address + // + Address = (UINT32)NodeMemBase; + } else { + if (NBPtr->DCTPtr->BkIntDis == FALSE && NBPtr->RefPtr->EnableBankIntlv == TRUE) { + // + // When bank interleaving is enabled, all the chip selects share the same starting address + // + Address = (UINT32) (NodeMemBase + DctMemBase); + } else { + Address += (UINT32) (NodeMemBase + DctMemBase); + } + } + if (NodeInterleaveEnabled) { + // + // When node interleaving is enabled, all the DIMMs in the system share the same starting address + // + Address = 0; + } + DmiLogicalDimmInfoTable->StartingAddr = Address; + AddrValue = (UINT64) Address + + ((UINT64) ((NBPtr->GetBitField (NBPtr, BFCSMask0Reg + Dimm) & 0xFFFF0000) + 0x00080000) >> + (2 - ChipSelectPairScale) ) - 1; + if (NBPtr->DCTPtr->BkIntDis == FALSE && NBPtr->RefPtr->EnableBankIntlv == TRUE) { + // + // When bank interleaving is enabled, all the chip selects share the same ending address + // + AddrValue = NodeMemBase + DctMemBase + (NBPtr->DCTPtr->Timings.DctMemSize << 6) - 1; + } + if (DctInterleaveEnabled) { + // + // When channel interleaving is enabled, the interleaved range is accounted for in the ending address of each DCT + // + AddrValue = NodeMemBase + MemFGetDctInterleavedLimit (NBPtr, Dct, DctMemBase, DctInterleavedMemSize); + } + if (NodeInterleaveEnabled) { + // + // When node interleaving is enabled, all the DIMMs in the system share the same ending address + // + AddrValue = SysMemSize - 1; + } + if (AddrValue >= ((UINT64) 0xFFFFFFFF)) { + DmiLogicalDimmInfoTable->StartingAddr = 0xFFFFFFFFUL; + DmiLogicalDimmInfoTable->EndingAddr = 0xFFFFFFFFUL; + DmiLogicalDimmInfoTable->ExtStartingAddr = (UINT64) Address; + DmiLogicalDimmInfoTable->ExtEndingAddr = AddrValue; + } else { + DmiLogicalDimmInfoTable->EndingAddr = (UINT32) AddrValue; + } + } + } // Dimm loop + } // Channel loop + } // Socket loop + + // Max Capacity + *TotalMemSizePtr = TotalSize; + + return TRUE; +} + +/*--------------------------------------------------------------------------------------- + * L O C A L F U N C T I O N S + *--------------------------------------------------------------------------------------- + */ + +/** + * + * + * This function obtains the memory size RJ6 contributed by the previous nodes + * + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] NodeLimit - Node number + * + */ +UINT64 +MemFGetNodeMemBase ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 NodeLimit + ) +{ + UINT8 Node; + UINT64 NodeMemBase; + + NodeMemBase = 0; + // Calculate the total memory size in the system + for (Node = 0; Node < NodeLimit; Node++) { + NodeMemBase += (NBPtr[Node].MCTPtr->NodeMemSize << 6); + } + + return NodeMemBase; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function obtains the memory size RJ6 contributed by the previous dcts on + * the current node + * + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] DctLimit - Dct number + * + */ +UINT64 +MemFGetDctMemBase ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 DctLimit + ) +{ + UINT8 Dct; + UINT64 DctMemBase; + + DctMemBase = 0; + // Calculate the total memory size in the system + for (Dct = 0; Dct < DctLimit; Dct++) { + MemNSwitchDCTNb (NBPtr, Dct); + DctMemBase += (NBPtr->DCTPtr->Timings.DctMemSize << 6); + } + + return DctMemBase; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function obtains the interleaved memory size RJ6 contributed on the + * current dct + * + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + */ +UINT64 +MemFGetDctInterleavedMemSize ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Dct; + UINT64 DctInterleavedMemSize; + + DctInterleavedMemSize = NBPtr->MCTPtr->NodeMemSize << 6; + // Find minimum memory size among the interleaved DCTs + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + MemNSwitchDCTNb (NBPtr, Dct); + if (DctInterleavedMemSize > (NBPtr->DCTPtr->Timings.DctMemSize << 6)) { + DctInterleavedMemSize = (NBPtr->DCTPtr->Timings.DctMemSize << 6); + } + } + + return DctInterleavedMemSize; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function obtains the memory ending address RJ6 contributed by a dct + * under channel interleaving + * + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] Dct - Dct number + * @param[in] DctMemBase - Dct memory base + * @param[in] DctInterleavedMemSize - Dct interleaved memory size + * + */ +UINT64 +MemFGetDctInterleavedLimit ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Dct, + IN UINT64 DctMemBase, + IN UINT64 DctInterleavedMemSize + ) +{ + UINT64 DctMemLimit; + UINT8 i; + + DctMemLimit = 0; + if (DctInterleavedMemSize == NBPtr->DCTPtr->Timings.DctMemSize << 6) { + // The whole memory range is interleaved for the DCTs with the minimum memory size + for (i = 0; i < NBPtr->DctCount; i++) { + DctMemLimit += DctInterleavedMemSize; + } + DctMemLimit -= 1; + } else { + // Part of the memory range is interleaved for the DCTs with memory size larger than + // the minimum. The remaining is treated as non-interleaved. + for (i = 0; i < NBPtr->DctCount - 1 - Dct; i++) { + DctMemLimit += DctInterleavedMemSize; + } + DctMemLimit += DctMemBase + (NBPtr->DCTPtr->Timings.DctMemSize << 6) - 1; + } + + return DctMemLimit; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.c new file mode 100644 index 0000000000..7ff3960971 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.c @@ -0,0 +1,290 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfecc.c + * + * Feature ECC initialization functions + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/ECC) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + +#include "AGESA.h" +#include "amdlib.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mfecc.h" +#include "Filecode.h" +#include "mfmemclr.h" +#include "GeneralServices.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_ECC_MFECC_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +VOID +STATIC +InitECCOverriedeStruct ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT ECC_OVERRIDE_STRUCT *pecc_override_struct + ); + +BOOLEAN +MemFCheckECC ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +/* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ + +extern BUILD_OPT_CFG UserOptions; + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function checks to see if ECC can be enabled on all nodes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFCheckECC ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + DIE_STRUCT *MCTPtr; + MEM_SHARED_DATA *SharedPtr; + BOOLEAN ErrorRecovery; + + ASSERT (NBPtr != NULL); + + MCTPtr = NBPtr->MCTPtr; + SharedPtr = NBPtr->SharedPtr; + + ErrorRecovery = TRUE; + IDS_OPTION_HOOK (IDS_MEM_ERROR_RECOVERY, &ErrorRecovery, &NBPtr->MemPtr->StdHeader); + + if (MCTPtr->NodeMemSize != 0) { + if (SharedPtr->AllECC && MCTPtr->Status[SbEccDimms] && (ErrorRecovery || (MCTPtr->ErrCode < AGESA_ERROR))) { + // Clear all MCA reports before using scrubber + // to initialize ECC check bits + // + NBPtr->McaNbCtlReg = NBPtr->GetBitField (NBPtr, BFMcaNbCtlReg); + NBPtr->SetBitField (NBPtr, BFMcaNbCtlReg, 0); + NBPtr->SetBitField (NBPtr, BFSyncOnUcEccEn, 0); + // In unganged mode, set DctDctIntlv + if (!NBPtr->Ganged) { + NBPtr->SetBitField (NBPtr, BFDctDatIntLv, 1); + } + // + // Set Ecc Symbol Size + // + NBPtr->SetEccSymbolSize (NBPtr); + // If ECC can be enabled on this node, + // set the master ECCen bit (according to setup) + // + NBPtr->SetBitField (NBPtr, BFDramEccEn, 1); + // Do mem clear on current node + MemFMctMemClr_Init (NBPtr); + return TRUE; + } else { + if (SharedPtr->AllECC) { + SharedPtr->AllECC = FALSE; + } + // ECC requested but cannot be enabled + MCTPtr->Status[SbEccDimms] = FALSE; + MCTPtr->ErrStatus[EsbDramECCDis] = TRUE; + PutEventLog (AGESA_WARNING, MEM_WARNING_ECC_DIS, NBPtr->Node, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + SetMemError (AGESA_WARNING, MCTPtr); + } + } + return FALSE; +} + + /* -----------------------------------------------------------------------------*/ +/** + * + * + * This function initializes the ECC on all nodes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFInitECC ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Node; + UINT32 ScrubAddrRJ16; + DIE_STRUCT *MCTPtr; + MEM_SHARED_DATA *SharedPtr; + ECC_OVERRIDE_STRUCT ecc_override_struct; + BOOLEAN Flag; + + InitECCOverriedeStruct (NBPtr, &ecc_override_struct); + IDS_OPTION_HOOK (IDS_ECC, &ecc_override_struct, &(NBPtr->MemPtr->StdHeader)); + + ASSERT (NBPtr != NULL); + + MCTPtr = NBPtr->MCTPtr; + Node = MCTPtr->NodeId; + SharedPtr = NBPtr->SharedPtr; + Flag = TRUE; + + NBPtr->FamilySpecificHook[ScrubberErratum] (NBPtr, (VOID *) &Flag); + + if ((MCTPtr->Status[SbEccDimms]) && (SharedPtr->AllECC)) { + // Check if the input dram scrub rate is supported or not + ASSERT (ecc_override_struct.CfgScrubDramRate <= 0x16); + if (ecc_override_struct.CfgScrubDramRate != 0) { + // Program scrub address, + // let the scrub Addr be the Base of this Node + // Only enable Dram scrubber when there is memory on current node + // + NBPtr->SetBitField (NBPtr, BFScrubReDirEn, 0); + ScrubAddrRJ16 = (NBPtr->GetBitField (NBPtr, BFDramBaseReg0 + Node) & 0xFFFF0000) >> 8; + ScrubAddrRJ16 |= NBPtr->GetBitField (NBPtr, BFDramBaseHiReg0 + Node) << 24; + NBPtr->SetBitField (NBPtr, BFScrubAddrLoReg, ScrubAddrRJ16 << 16); + NBPtr->SetBitField (NBPtr, BFScrubAddrHiReg, ScrubAddrRJ16 >> 16); + NBPtr->SetBitField (NBPtr, BFDramScrub, ecc_override_struct.CfgScrubDramRate); + NBPtr->IsSupported[ScrubberEn] = TRUE; + } + } + // Scrub CTL for Dcache, L2, L3 + // Check if the input L2 scrub rate is supported or not + ASSERT (ecc_override_struct.CfgScrubL2Rate <= 0x16); + NBPtr->SetBitField (NBPtr, BFL2Scrub, ecc_override_struct.CfgScrubL2Rate); + // Check if the input Dcache scrub rate is supported or not + ASSERT (ecc_override_struct.CfgScrubDcRate <= 0x16); + NBPtr->SetBitField (NBPtr, BFDcacheScrub, ecc_override_struct.CfgScrubDcRate); + // Do not enable L3 Scrub if F3xE8[L3Capable] is 0 or F3x188[DisableL3] is 1 + if ((NBPtr->GetBitField (NBPtr, BFL3Capable) == 1) && (NBPtr->GetBitField (NBPtr, BFDisableL3) == 0)) { + // Check if input L3 scrub rate is supported or not + ASSERT (ecc_override_struct.CfgScrubL3Rate <= 0x16); + NBPtr->SetBitField (NBPtr, BFL3Scrub, ecc_override_struct.CfgScrubL3Rate); + } + + // Check if Dcache scrubber or L2 scrubber is enabled + if ((ecc_override_struct.CfgScrubL2Rate != 0) || (ecc_override_struct.CfgScrubDcRate!= 0)) { + // If ClkDivisor is deeper than divide-by-16 + if (NBPtr->GetBitField (NBPtr, BFC1ClkDivisor) > 4) { + // Set it to divide-by-16 + NBPtr->SetBitField (NBPtr, BFC1ClkDivisor, 4); + } + } + + NBPtr->SetBitField (NBPtr, BFScrubReDirEn, ecc_override_struct.CfgEccRedirection); + NBPtr->SetBitField (NBPtr, BFSyncOnUcEccEn, ecc_override_struct.CfgEccSyncFlood); + // Restore MCA reports after scrubber is done + // with initializing ECC check bits + NBPtr->SetBitField (NBPtr, BFMcaNbCtlReg, NBPtr->McaNbCtlReg); + + Flag = FALSE; + NBPtr->FamilySpecificHook[ScrubberErratum] (NBPtr, (VOID *) &Flag); + + return TRUE; +} + +VOID +STATIC +InitECCOverriedeStruct ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT ECC_OVERRIDE_STRUCT *pecc_override_struct + ) +{ + // + // If (D18F3x44[DramEccEn]==1) THEN 1 ELSE 0 ENDIF + // + if (NBPtr->GetBitField (NBPtr, BFDramEccEn) == 1) { + pecc_override_struct->CfgEccRedirection = 1; + } else { + pecc_override_struct->CfgEccRedirection = 0; + } + + pecc_override_struct->CfgEccSyncFlood = UserOptions.CfgEccSyncFlood; + pecc_override_struct->CfgScrubDcRate = UserOptions.CfgScrubDcRate; + + if (UserOptions.CfgScrubDramRate != 0xFF) { + pecc_override_struct->CfgScrubDramRate = UserOptions.CfgScrubDramRate; + } else { + if (NBPtr->MCTPtr->NodeMemSize <= 0x4000) { + pecc_override_struct->CfgScrubDramRate = 0x12; // 1 ~ 1 GB + } else if (NBPtr->MCTPtr->NodeMemSize <= 0x8000) { + pecc_override_struct->CfgScrubDramRate = 0x11; // 1 GB + 1 ~ 2 GB + } else if (NBPtr->MCTPtr->NodeMemSize <= 0x10000) { + pecc_override_struct->CfgScrubDramRate = 0x10; // 2 GB + 1 ~ 4 GB + } else if (NBPtr->MCTPtr->NodeMemSize <= 0x20000) { + pecc_override_struct->CfgScrubDramRate = 0x0F; // 4 GB + 1 ~ 8 GB + } else if (NBPtr->MCTPtr->NodeMemSize <= 0x40000) { + pecc_override_struct->CfgScrubDramRate = 0x0E; // 8 GB + 1 ~ 16 GB + } else { + pecc_override_struct->CfgScrubDramRate = 0x0D; //16 GB + 1 above + } + } + + pecc_override_struct->CfgScrubL2Rate = UserOptions.CfgScrubL2Rate; + pecc_override_struct->CfgScrubL3Rate = UserOptions.CfgScrubL3Rate; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.h new file mode 100644 index 0000000000..a25b12e6ab --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfecc.h @@ -0,0 +1,80 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfecc.h + * + * Feature ECC initialization functions + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + +#ifndef _MFECC_H_ +#define _MFECC_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +MemFInitECC ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +#endif /* _MFECC_H_ */ + + diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfemp.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfemp.c new file mode 100644 index 0000000000..54487d2a1b --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ECC/mfemp.c @@ -0,0 +1,177 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfemp.c + * + * Feature EMP initialization functions + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/ECC) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + + + +#include "AGESA.h" +#include "mm.h" +#include "mn.h" +#include "Ids.h" +#include "GeneralServices.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_ECC_MFEMP_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +STATIC +IsPowerOfTwo ( + IN UINT32 TestNumber + ); + +BOOLEAN +MemFInitEMP ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +/* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ + +extern BUILD_OPT_CFG UserOptions; + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function initializes EMP (Enhanced Memory Protection) + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFInitEMP ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + MEM_PARAMETER_STRUCT *RefPtr; + DIE_STRUCT *MCTPtr; + + ASSERT (NBPtr != NULL); + + RefPtr = NBPtr->RefPtr; + MCTPtr = NBPtr->MCTPtr; + if (RefPtr->EnableEccFeature) { + if (NBPtr->GetBitField (NBPtr, BFEnhMemProtCap) == 0) { + PutEventLog (AGESA_WARNING, MEM_WARNING_EMP_NOT_SUPPORTED, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + MCTPtr->ErrStatus[EsbEMPNotSupported] = TRUE; + } else if (RefPtr->EnableChannelIntlv || RefPtr->EnableBankIntlv || RefPtr->EnableBankSwizzle) { + PutEventLog (AGESA_WARNING, MEM_WARNING_EMP_CONFLICT, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + MCTPtr->ErrStatus[EsbEMPConflict] = TRUE; + } else if ((!MCTPtr->GangedMode) && + (!IsPowerOfTwo (MCTPtr->DctData[0].Timings.DctMemSize) && + !IsPowerOfTwo (MCTPtr->DctData[1].Timings.DctMemSize))) { + PutEventLog (AGESA_WARNING, MEM_WARNING_EMP_NOT_ENABLED, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + MCTPtr->ErrStatus[EsbEMPDis] = TRUE; + } else { + // Reduce memory size to 7/8 of the original memory size + ASSERT ((MCTPtr->NodeMemSize % 8) == 0); + NBPtr->SetBitField (NBPtr, BFDramHoleValid, 0); + MCTPtr->NodeMemSize = (MCTPtr->NodeMemSize / 8) * 7; + NBPtr->HtMemMapInit (NBPtr); + NBPtr->CpuMemTyping (NBPtr); + + // Enable EMP + NBPtr->SetBitField (NBPtr, BFDramEccEn, 1); + + // Scrub CTL settings for Dcache, L2 + NBPtr->SetBitField (NBPtr, BFL2Scrub, UserOptions.CfgScrubL2Rate); + NBPtr->SetBitField (NBPtr, BFDcacheScrub, UserOptions.CfgScrubDcRate); + + NBPtr->SetBitField (NBPtr, BFSyncOnUcEccEn, UserOptions.CfgEccSyncFlood); + return TRUE; + } + } + return FALSE; +} + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function checks to see if the input is power of two. + * + * @param[in] TestNumber - Value to check for power of two + * + * @return TRUE - is power of two + * FALSE - is not power of two + */ +BOOLEAN +STATIC +IsPowerOfTwo ( + IN UINT32 TestNumber + ) +{ + return (BOOLEAN) ((TestNumber & (TestNumber - 1)) == 0); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/EXCLUDIMM/mfdimmexclud.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/EXCLUDIMM/mfdimmexclud.c new file mode 100644 index 0000000000..475b04ffab --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/EXCLUDIMM/mfdimmexclud.c @@ -0,0 +1,206 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfdimmexclud.c + * + * Feature DIMM exclude. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/EXCLUDIMM) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + +#include "AGESA.h" +#include "OptionMemory.h" +#include "mm.h" +#include "mn.h" +#include "mt.h" +#include "Ids.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_EXCLUDIMM_MFDIMMEXCLUD_FILECODE + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +MemFRASExcludeDIMM ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * Check and disable Chip selects that fail training for each node. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ +BOOLEAN +MemFRASExcludeDIMM ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 Dct; + UINT8 ReserveDCT; + UINT8 q; + BOOLEAN Flag; + BOOLEAN IsCSIntlvEnabled; + UINT16 CsTestFail; + DIE_STRUCT *MCTPtr; + BOOLEAN RetVal; + + ASSERT (NBPtr != NULL); + ReserveDCT = NBPtr->Dct; + CsTestFail = 0; + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + if (NBPtr->DCTPtr->Timings.CsTestFail != 0) { + // When there is no new failed dimm that needs to be excluded, then no need to go through the process. + switch (NBPtr->SharedPtr->DimmExcludeFlag) { + case NORMAL: + // See there is new dimm that needs to be excluded + if ((NBPtr->DCTPtr->Timings.CsTestFail & NBPtr->DCTPtr->Timings.CsEnabled) != 0) { + CsTestFail |= NBPtr->DCTPtr->Timings.CsTestFail; + } + break; + case TRAINING: + // Do not do any dimm excluding during training + // Dimm exclude will be done at the end of training + break; + case END_TRAINING: + // Exclude all dimms that have failures during training + if ((NBPtr->DCTPtr->Timings.CsTrainFail != 0) || + ((NBPtr->DCTPtr->Timings.CsTestFail & NBPtr->DCTPtr->Timings.CsEnabled) != 0)) { + CsTestFail |= NBPtr->DCTPtr->Timings.CsTestFail; + } + break; + default: + IDS_ERROR_TRAP; + } + } + } + + if (CsTestFail != 0) { + IsCSIntlvEnabled = FALSE; + MCTPtr = NBPtr->MCTPtr; + MCTPtr->NodeMemSize = 0; + NBPtr->SharedPtr->NodeMap[NBPtr->Node].IsValid = FALSE; + NBPtr->SharedPtr->NodeMap[NBPtr->Node].SysBase = 0; + NBPtr->SharedPtr->NodeMap[NBPtr->Node].SysLimit = 0; + NBPtr->SetBitField (NBPtr, BFDramBaseAddr, 0); + NBPtr->SetBitField (NBPtr, BFDramLimitAddr, 0); + + if (MCTPtr->GangedMode) { + // if ganged mode, disable all pairs of CS that fail. + NBPtr->DCTPtr->Timings.CsTestFail |= CsTestFail; + } + + // if chip select interleaving has been enabled, need to undo it before remapping memory + if (NBPtr->FeatPtr->UndoInterleaveBanks (NBPtr)) { + IsCSIntlvEnabled = TRUE; + } + + Flag = TRUE; + NBPtr->FamilySpecificHook[BfAfExcludeDimm] (NBPtr, &Flag); + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + if (!MCTPtr->GangedMode || (MCTPtr->Dct == 0)) { + if (NBPtr->DCTPtr->Timings.DctMemSize != 0) { + NBPtr->DCTPtr->Timings.DctMemSize = 0; + + NBPtr->DCTPtr->Timings.CsEnabled = 0; + for (q = 0; q < MAX_CS_PER_CHANNEL; q++) { + NBPtr->SetBitField (NBPtr, BFCSBaseAddr0Reg + q, 0); + } + + // Set F2x94[DisDramInterface] = 1 if all chip selects fail training on the DCT + if ((NBPtr->DCTPtr->Timings.CsPresent & ~NBPtr->DCTPtr->Timings.CsTestFail) == 0) { + NBPtr->DisableDCT (NBPtr); + } + + Flag = NBPtr->StitchMemory (NBPtr); + ASSERT (Flag == TRUE); + } + } + } + Flag = FALSE; + NBPtr->FamilySpecificHook[BfAfExcludeDimm] (NBPtr, &Flag); + + // Re-enable chip select interleaving when remapping is done. + if (IsCSIntlvEnabled) { + NBPtr->FeatPtr->InterleaveBanks (NBPtr); + } + + RetVal = TRUE; + } else { + RetVal = FALSE; + } + NBPtr->SwitchDCT (NBPtr, ReserveDCT); + return RetVal; +} + diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.c new file mode 100644 index 0000000000..6bd85214af --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.c @@ -0,0 +1,548 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfidendimm.c + * + * Translate physical system address to dimm identification. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat) + * @e \$Revision: 84482 $ @e \$Date: 2012-12-16 22:48:10 -0600 (Sun, 16 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + +#include "AGESA.h" +#include "amdlib.h" +#include "mm.h" +#include "mn.h" +#include "Ids.h" +#include "OptionMemory.h" +#include "heapManager.h" +#include "mfidendimm.h" +#include "GeneralServices.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_IDENDIMM_MFIDENDIMM_FILECODE +extern MEM_NB_SUPPORT memNBInstalled[]; + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ +#define MAX_DCTS_PER_DIE 2 ///< Max DCTs per die +#define MAX_CHLS_PER_DCT 1 ///< Max Channels per DCT + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +AGESA_STATUS +STATIC +MemFTransSysAddrToCS ( + IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify, + IN MEM_MAIN_DATA_BLOCK *mmPtr + ); + +UINT32 +STATIC +MemFGetPCI ( + IN MEM_NB_BLOCK *NBPtr, + IN UINT8 NodeID, + IN UINT8 DctNum, + IN BIT_FIELD_NAME BitFieldName + ); + +UINT8 +STATIC +MemFUnaryXOR ( + IN UINT32 address + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +/*-----------------------------------------------------------------------------*/ +/** +* +* This function identifies the dimm on which the given memory address locates. +* +* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM +* +* @retval AGESA_SUCCESS - Successfully translate physical system address +* to dimm identification. +* AGESA_BOUNDS_CHK - Targeted address is out of bound. +* +*/ + +AGESA_STATUS +AmdIdentifyDimm ( + IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify + ) +{ + UINT8 i; + AGESA_STATUS RetVal; + MEM_MAIN_DATA_BLOCK mmData; // Main Data block + MEM_NB_BLOCK *NBPtr; + MEM_DATA_STRUCT MemData; + LOCATE_HEAP_PTR LocHeap; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + UINT8 Node; + UINT8 Dct; + UINT8 Die; + UINT8 DieCount; + + LibAmdMemCopy (&(MemData.StdHeader), &(AmdDimmIdentify->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(AmdDimmIdentify->StdHeader)); + mmData.MemPtr = &MemData; + RetVal = MemSocketScan (&mmData); + if (RetVal == AGESA_FATAL) { + return RetVal; + } + DieCount = mmData.DieCount; + + // Search for AMD_MEM_AUTO_HANDLE on the heap first. + // Only apply for space on the heap if cannot find AMD_MEM_AUTO_HANDLE on the heap. + LocHeap.BufferHandle = AMD_MEM_AUTO_HANDLE; + if (HeapLocateBuffer (&LocHeap, &AmdDimmIdentify->StdHeader) == AGESA_SUCCESS) { + // NB block has already been constructed by main block. + // No need to construct it here. + NBPtr = (MEM_NB_BLOCK *)LocHeap.BufferPtr; + mmData.NBPtr = NBPtr; + } else { + AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK))); + AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (HeapAllocateBuffer (&AllocHeapParams, &AmdDimmIdentify->StdHeader) != AGESA_SUCCESS) { + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_IDENTIFY_DIMM_MEM_NB_BLOCK, 0, 0, 0, 0, &AmdDimmIdentify->StdHeader); + ASSERT(FALSE); // Could not allocate heap space for NB block for Identify DIMM + return AGESA_FATAL; + } + NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr; + mmData.NBPtr = NBPtr; + // Construct each die. + for (Die = 0; Die < DieCount; Die ++) { + i = 0; + while (memNBInstalled[i].MemIdentifyDimmConstruct != 0) { + if (memNBInstalled[i].MemIdentifyDimmConstruct (&NBPtr[Die], &MemData, Die)) { + break; + } + i++; + }; + if (memNBInstalled[i].MemIdentifyDimmConstruct == 0) { + PutEventLog (AGESA_FATAL, MEM_ERROR_NO_CONSTRUCTOR_FOR_IDENTIFY_DIMM, Die, 0, 0, 0, &AmdDimmIdentify->StdHeader); + ASSERT(FALSE); // No Identify DIMM constructor found + return AGESA_FATAL; + } + } + } + + if ((RetVal = MemFTransSysAddrToCS (AmdDimmIdentify, &mmData)) == AGESA_SUCCESS) { + // Translate Node, DCT and Chip select number to Socket, Channel and Dimm number. + Node = AmdDimmIdentify->SocketId; + Dct = AmdDimmIdentify->MemChannelId; + AmdDimmIdentify->SocketId = MemData.DiesPerSystem[Node].SocketId; + AmdDimmIdentify->MemChannelId = NBPtr[Node].GetSocketRelativeChannel (&NBPtr[Node], Dct, 0); + AmdDimmIdentify->DimmId /= 2; + } + + return RetVal; +} + + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*-----------------------------------------------------------------------------*/ +/** +* +* This function translates the given physical system address to +* a node, channel select, chip select, bank, row, and column address. +* +* @param[in, out] *AmdDimmIdentify - Pointer to the parameter structure AMD_IDENTIFY_DIMM +* @param[in, out] *mmPtr - Pointer to the MEM_MAIN_DATA_BLOCK +* +* @retval AGESA_SUCCESS - The chip select address is found +* @retval AGESA_BOUNDS_CHK - Targeted address is out of bound. +* +*/ +AGESA_STATUS +STATIC +MemFTransSysAddrToCS ( + IN OUT AMD_IDENTIFY_DIMM *AmdDimmIdentify, + IN MEM_MAIN_DATA_BLOCK *mmPtr + ) +{ + BOOLEAN CSFound; + BOOLEAN DctSelHiRngEn; + BOOLEAN DctSelIntLvEn; + BOOLEAN DctGangEn; + BOOLEAN HiRangeSelected; + BOOLEAN DramHoleValid; + BOOLEAN CSEn; + BOOLEAN SwapDone; + BOOLEAN IntLvRgnSwapEn; + UINT8 DctSelHi; + UINT8 DramEn; + UINT8 range; + UINT8 IntlvEn; + UINT8 IntlvSel; + UINT8 ILog; + UINT8 DctSelIntLvAddr; + UINT8 DctNum; + UINT8 cs; + UINT8 BadDramCs; + UINT8 spare; + UINT8 IntLvRgnBaseAddr; + UINT8 IntLvRgnLmtAddr; + UINT8 IntLvRgnSize; + UINT32 temp; + UINT32 DramHoleOffset; + UINT32 DramHoleBase; + UINT64 DramBase; + UINT64 DramLimit; + UINT64 DramLimitSysAddr; + UINT64 DctSelBaseAddr; + UINT64 DctSelBaseOffset; + UINT64 ChannelAddr; + UINT64 CSBase; + UINT64 CSMask; + UINT64 InputAddr; + UINT64 ChannelOffset; + MEM_NB_BLOCK *NBPtr; + UINT8 Die; + + UINT64 SysAddr; + UINT8 *NodeID; + UINT8 *ChannelSelect; + UINT8 *ChipSelect; + + SysAddr = AmdDimmIdentify->MemoryAddress; + NodeID = &(AmdDimmIdentify->SocketId); + ChannelSelect = &(AmdDimmIdentify->MemChannelId); + ChipSelect = &(AmdDimmIdentify->DimmId); + CSFound = FALSE; + ILog = 0; + NBPtr = mmPtr->NBPtr; + + NBPtr->FamilySpecificHook[FixupSysAddr] (NBPtr, &SysAddr); + + // Loop to determine the dram range + for (Die = 0; Die < mmPtr->DieCount; Die ++) { + range = NBPtr[Die].Node; + + // DRAM Base + temp = MemFGetPCI (NBPtr, 0, 0, BFDramBaseReg0 + range); + DramEn = (UINT8) (temp & 0x3); + IntlvEn = (UINT8) ((temp >> 8) & 0x7); + + DramBase = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramBaseHiReg0 + range) & 0xFF) << 40) | + (((UINT64) temp & 0xFFFF0000) << 8); + + // DRAM Limit + temp = MemFGetPCI (NBPtr, 0, 0, BFDramLimitReg0 + range); + *NodeID = (UINT8) (temp & 0x7); + IntlvSel = (UINT8) ((temp >> 8) & 0x7); + DramLimit = ((UINT64) (MemFGetPCI (NBPtr, 0, 0, BFDramLimitHiReg0 + range) & 0xFF) << 40) | + (((UINT64) temp << 8) | 0xFFFFFF); + DramLimitSysAddr = (((UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDramLimitAddr)) << 27) | 0x7FFFFFF; + ASSERT (DramLimit <= DramLimitSysAddr); + + if ((DramEn != 0) && (DramBase <= SysAddr) && (SysAddr <= DramLimitSysAddr) && + ((IntlvEn == 0) || (IntlvSel == ((SysAddr >> 12) & IntlvEn)))) { + // Determine the number of bit positions consumed by Node Interleaving + switch (IntlvEn) { + + case 0x0: + ILog = 0; + break; + + case 0x1: + ILog = 1; + break; + + case 0x3: + ILog = 2; + break; + + case 0x7: + ILog = 3; + break; + + default: + IDS_ERROR_TRAP; + } + + DramHoleOffset = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleOffset) << 23; + DramHoleValid = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, (mmPtr->DieCount == 1) ? BFDramMemHoistValid : BFDramHoleValid); + DramHoleBase = MemFGetPCI (NBPtr, *NodeID, 0, BFDramHoleBase) << 24; + // Address belongs to this node based on DramBase/Limit, + // but is in the memory hole so it doesn't map to DRAM + if (DramHoleValid && (DramHoleBase <= SysAddr) && (SysAddr < 0x100000000)) { + return AGESA_BOUNDS_CHK; + } + + // F2x10C Swapped Interleaved Region + IntLvRgnSwapEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSwapEn); + if (IntLvRgnSwapEn) { + IntLvRgnBaseAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnBaseAddr); + IntLvRgnLmtAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnLmtAddr); + IntLvRgnSize = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFIntLvRgnSize); + ASSERT (IntLvRgnSize == (IntLvRgnLmtAddr - IntLvRgnBaseAddr + 1)); + if (((SysAddr >> 34) == 0) && + ((((SysAddr >> 27) >= IntLvRgnBaseAddr) && ((SysAddr >> 27) <= IntLvRgnLmtAddr)) + || ((SysAddr >> 27) < IntLvRgnSize))) { + SysAddr ^= (UINT64) IntLvRgnBaseAddr << 27; + } + } + + // Extract variables from F2x110 DRAM Controller Select Low Register + DctSelHiRngEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHiRngEn); + DctSelHi = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelHi); + DctSelIntLvEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvEn); + DctGangEn = (BOOLEAN) MemFGetPCI (NBPtr, *NodeID, 0, BFDctGangEn); + DctSelIntLvAddr = (UINT8) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelIntLvAddr); + DctSelBaseAddr = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseAddr) << 27; + DctSelBaseOffset = (UINT64) MemFGetPCI (NBPtr, *NodeID, 0, BFDctSelBaseOffset) << 26; + + + // Determine if high DCT address range is being selected + if (DctSelHiRngEn && !DctGangEn && (SysAddr >= DctSelBaseAddr)) { + HiRangeSelected = TRUE; + } else { + HiRangeSelected = FALSE; + } + + // Determine Channel + if (DctGangEn) { + *ChannelSelect = (UINT8) ((SysAddr >> 3) & 0x1); + } else if (HiRangeSelected) { + *ChannelSelect = DctSelHi; + } else if (DctSelIntLvEn && (DctSelIntLvAddr == 0)) { + *ChannelSelect = (UINT8) ((SysAddr >> 6) & 0x1); + } else if (DctSelIntLvEn && (((DctSelIntLvAddr >> 1) & 0x1) != 0)) { + temp = MemFUnaryXOR ((UINT32) ((SysAddr >> 16) & 0x1F)); + if ((DctSelIntLvAddr & 0x1) != 0) { + *ChannelSelect = (UINT8) (((SysAddr >> 9) & 0x1) ^ temp); + } else { + *ChannelSelect = (UINT8) (((SysAddr >> 6) & 0x1) ^ temp); + } + } else if (DctSelIntLvEn) { + *ChannelSelect = (UINT8) ((SysAddr >> (12 + ILog)) & 0x1); + } else if (DctSelHiRngEn) { + *ChannelSelect = ~DctSelHi & 0x1; + } else { + *ChannelSelect = 0; + } + ASSERT (*ChannelSelect < NBPtr[*NodeID].DctCount); + + // Determine base address offset + if (HiRangeSelected) { + if ((DctSelBaseAddr < DramHoleBase) && DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) { + ChannelOffset = (UINT64) DramHoleOffset; + } else { + ChannelOffset = DctSelBaseOffset; + } + } else { + if (DramHoleValid && (SysAddr >= (UINT64) 0x100000000)) { + ChannelOffset = (UINT64) DramHoleOffset; + } else { + ChannelOffset = DramBase; + } + } + + // Remove hoisting offset and normalize to DRAM bus addresses + ChannelAddr = SysAddr - ChannelOffset; + + // Remove node interleaving + if (IntlvEn != 0) { + ChannelAddr = ((ChannelAddr >> (12 + ILog)) << 12) | (ChannelAddr & 0xFFF); + } + + // Remove channel interleave + if (DctSelIntLvEn && !HiRangeSelected && !DctGangEn) { + if ((DctSelIntLvAddr & 1) != 1) { + // A[6] Select or Hash 6 + ChannelAddr = ((ChannelAddr >> 7) << 6) | (ChannelAddr & 0x3F); + } else if (DctSelIntLvAddr == 1) { + // A[12] + ChannelAddr = ((ChannelAddr >> 13) << 12) | (ChannelAddr & 0xFFF); + } else { + // Hash 9 + ChannelAddr = ((ChannelAddr >> 10) << 9) | (ChannelAddr & 0x1FF); + } + } + + // Determine the Chip Select + for (cs = 0; cs < MAX_CS_PER_CHANNEL; ++ cs) { + DctNum = DctGangEn ? 0 : *ChannelSelect; + + // Obtain the CS Base + temp = MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + cs); + CSEn = (BOOLEAN) (temp & 0x1); + CSBase = ((UINT64) temp & NBPtr->CsRegMsk) << 8; + + // Obtain the CS Mask + CSMask = ((UINT64) MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSMask0Reg + (cs >> 1)) & NBPtr->CsRegMsk) << 8; + + // Adjust the Channel Addr for easy comparison + InputAddr = ((ChannelAddr >> 8) & NBPtr->CsRegMsk) << 8; + + if (CSEn && ((InputAddr & ~CSMask) == (CSBase & ~CSMask))) { + CSFound = TRUE; + + *ChipSelect = cs; + + temp = MemFGetPCI (NBPtr, *NodeID, 0, BFOnLineSpareControl); + SwapDone = (BOOLEAN) ((temp >> (1 + 2 * (*ChannelSelect))) & 0x1); + BadDramCs = (UINT8) ((temp >> (4 + 4 * (*ChannelSelect))) & 0x7); + if (SwapDone && (cs == BadDramCs)) { + // Find the spare rank for the channel + for (spare = 0; spare < MAX_CS_PER_CHANNEL; ++spare) { + if ((MemFGetPCI (NBPtr, *NodeID, DctNum, BFCSBaseAddr0Reg + spare) & 0x2) != 0) { + *ChipSelect = spare; + break; + } + } + } + ASSERT (*ChipSelect < MAX_CS_PER_CHANNEL); + + break; + } + } + } + if (CSFound) { + break; + } + } + + // last ditch sanity check + ASSERT (!CSFound || ((*NodeID < mmPtr->DieCount) && (*ChannelSelect < NBPtr[*NodeID].DctCount) && (*ChipSelect < MAX_CS_PER_CHANNEL))); + if (CSFound) { + return AGESA_SUCCESS; + } else { + return AGESA_BOUNDS_CHK; + } + +} + + +/*-----------------------------------------------------------------------------*/ +/** +* +* This function is the interface to call the PCI register access function +* defined in NB block. +* +* @param[in] *NBPtr - Pointer to the parameter structure MEM_NB_BLOCK +* @param[in] NodeID - Node ID number of the target Northbridge +* @param[in] DctNum - DCT number if applicable, otherwise, put 0 +* @param[in] BitFieldName - targeted bitfield +* +* @retval UINT32 - 32 bits PCI register value +* +*/ +UINT32 +STATIC +MemFGetPCI ( + IN MEM_NB_BLOCK *NBPtr, + IN UINT8 NodeID, + IN UINT8 DctNum, + IN BIT_FIELD_NAME BitFieldName + ) +{ + MEM_NB_BLOCK *LocalNBPtr; + UINT8 Die; + + // Find NBBlock that associates with node NodeID + for (Die = 0; (Die < MAX_NODES_SUPPORTED) && (NBPtr[Die].Node != NodeID); Die ++); + ASSERT (Die < MAX_NODES_SUPPORTED); + + // Get the northbridge pointer for the targeted node. + LocalNBPtr = &NBPtr[Die]; + LocalNBPtr->FamilySpecificHook[DCTSelectSwitch] (LocalNBPtr, &DctNum); + LocalNBPtr->Dct = DctNum; + // The caller of this function will take care of the ganged/unganged situation. + // So Ganged is set to be false here, and do PCI read on the DCT specified by DctNum. + return LocalNBPtr->GetBitField (LocalNBPtr, BitFieldName); +} + +/*-----------------------------------------------------------------------------*/ +/** +* +* This function returns an even parity bit (making the total # of 1's even) +* {0, 1} = number of set bits in argument is {even, odd}. +* +* @param[in] address - the address on which the parity bit will be calculated +* +* @retval UINT8 - parity bit +* +*/ + +UINT8 +STATIC +MemFUnaryXOR ( + IN UINT32 address + ) +{ + UINT8 parity; + UINT8 index; + parity = 0; + for (index = 0; index < 32; ++ index) { + parity = (UINT8) (parity ^ (address & 0x1)); + address = address >> 1; + } + return parity; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.h new file mode 100644 index 0000000000..bf95ce0593 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/IDENDIMM/mfidendimm.h @@ -0,0 +1,72 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfidendimm.h + * + * Header file for address to dimm identification translator. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +#ifndef _MFIDENDIMM_H_ +#define _MFIDENDIMM_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +#endif //_MFIDENDIMM_H_ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.c new file mode 100644 index 0000000000..9407f45b04 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.c @@ -0,0 +1,172 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * lvddr3.c + * + * Voltage change for DDR3 DIMMs. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/LVDDR3) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + +#include "AGESA.h" +#include "amdlib.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mt.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_LVDDR3_MFLVDDR3_FILECODE +/* features */ +#include "mflvddr3.h" + + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*-----------------------------------------------------------------------------*/ +/** + * + * This function calculate the common lowest voltage supported by all DDR3 + * DIMMs in the system. This function only needs to be called on BSP. + * + * @param[in, out] *NBPtr - Pointer to NB block + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFLvDdr3 ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + CH_DEF_STRUCT *ChannelPtr; + MEM_TECH_BLOCK *TechPtr; + MEM_SHARED_DATA *mmSharedPtr; + UINT8 Dct; + UINT8 Channel; + UINT8 Dimm; + UINT8 *SpdBufferPtr; + UINT8 VDDByte; + UINT8 VoltageMap; + + mmSharedPtr = NBPtr->SharedPtr; + TechPtr = NBPtr->TechPtr; + VoltageMap = 0xFF; + + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + for (Channel = 0; Channel < NBPtr->ChannelCount; Channel++) { + NBPtr->SwitchChannel (NBPtr, Channel); + ChannelPtr = NBPtr->ChannelPtr; + for (Dimm = 0; Dimm < MAX_DIMMS_PER_CHANNEL; Dimm++) { + if (TechPtr->GetDimmSpdBuffer (TechPtr, &SpdBufferPtr, Dimm)) { + // SPD byte 6: Module Nominal Voltage, VDD + // 1.5v - bit 0 + // 1.35v - bit 1 + // 1.2v - bit 2 + VDDByte = SpdBufferPtr[MNVVDD]; + IDS_HDT_CONSOLE (MEM_FLOW, "Node%d DCT%d Channel%d Dimm%d VDD Byte: 0x%02x\n", NBPtr->Node, Dct, Channel, Dimm, VDDByte); + + // Reverse the 1.5V operable bit. So its encoding can be consistent + // with that of 1.35V and 1.25V operable bit. + VDDByte ^= 1; + ASSERT (VDDByte != 0); + + if (mmSharedPtr->VoltageMap != 0) { + // Get the common supported voltage map + VoltageMap &= VDDByte; + } else { + // This is the second execution of all the loop as no common voltage is found + if (VDDByte == (1 << VOLT1_5_ENCODED_VAL)) { + // Always exclude 1.5V dimm if no common voltage is found + ChannelPtr->DimmExclude |= (UINT16) 1 << Dimm; + } + } + } + } + if (mmSharedPtr->VoltageMap == 0) { + NBPtr->DCTPtr->Timings.DimmExclude |= ChannelPtr->DimmExclude; + } + } + } + + if (mmSharedPtr->VoltageMap != 0) { + mmSharedPtr->VoltageMap &= VoltageMap; + } + + return TRUE; +} + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.h new file mode 100644 index 0000000000..93bc4f4aa4 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/LVDDR3/mflvddr3.h @@ -0,0 +1,78 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mflvddr3.h + * + * Header file for DDR3 DIMMs voltage configuration. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +#ifndef _MFLVDDR3_H_ +#define _MFLVDDR3_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ +#define MNVVDD 6 +#define LOWEST_VOLT_BIT 2 + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +MemFLvDdr3 ( + IN OUT MEM_NB_BLOCK *NBPtr +); + +#endif //_MFLVDDR3_H_ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/MEMCLR/mfmemclr.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/MEMCLR/mfmemclr.c new file mode 100644 index 0000000000..5a0c67584a --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/MEMCLR/mfmemclr.c @@ -0,0 +1,153 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfmemclr.c + * + * Feature function for memory clear operation + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/Memclr) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + + + +#include "AGESA.h" +#include "mm.h" +#include "mn.h" +#include "mfmemclr.h" +#include "Ids.h" +#include "merrhdl.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_MEMCLR_MFMEMCLR_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* + *----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * Initiates memory clear operation on one node with Dram on it. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + */ + +BOOLEAN +MemFMctMemClr_Init ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + AGESA_TESTPOINT (TpProcMemMemClr, &NBPtr->MemPtr->StdHeader); + IDS_PERF_TIMESTAMP (TP_BEGINMEMFMCTMEMCLR_INIT, &(NBPtr->MemPtr->StdHeader)); + if (NBPtr->RefPtr->EnableMemClr == TRUE) { + if (NBPtr->MCTPtr->NodeMemSize != 0) { + if (!NBPtr->MemCleared) { + NBPtr->PollBitField (NBPtr, BFMemClrBusy, 0, SPECIAL_PCI_ACCESS_TIMEOUT, FALSE); + if (NBPtr->GetBitField (NBPtr, BFDramEnabled) == 1) { + NBPtr->FamilySpecificHook[BeforeMemClr] (NBPtr, NBPtr); + NBPtr->SetBitField (NBPtr, BFDramBaseAddr, 0); + NBPtr->SetBitField (NBPtr, BFMemClrInit, 1); + } + } + } + } + IDS_PERF_TIMESTAMP (TP_ENDNMEMFMCTMEMCLR_INIT, &(NBPtr->MemPtr->StdHeader)); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * Ensures memory clear operation has completed on one node with Dram on it. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + */ + +BOOLEAN +MemFMctMemClr_Sync ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT32 MicroSecondToWait; + + MicroSecondToWait = 0; + if (NBPtr->RefPtr->EnableMemClr == TRUE) { + if (NBPtr->MCTPtr->NodeMemSize != 0) { + // Calculate Timeout value: + // Timeout (in microsecond) = Memory Size * 1.5 ns / 8 Byte * 4 (Margin) * 1000 (change millisecond to us) + // NodeMemSize is system address right shifted by 16, so shift it 4 bits to right to convert it to MB. + // 1.5 / 8 * 4 * 1000 = 750 + MicroSecondToWait = (NBPtr->MCTPtr->NodeMemSize >> 4) * 750; + + if (!NBPtr->MemCleared) { + NBPtr->PollBitField (NBPtr, BFMemClrBusy, 0, MicroSecondToWait, FALSE); + NBPtr->PollBitField (NBPtr, BFMemCleared, 1, MicroSecondToWait, FALSE); + NBPtr->SetBitField (NBPtr, BFDramBaseAddr, NBPtr->MCTPtr->NodeSysBase >> (27 - 16)); + NBPtr->MemCleared = TRUE; + } + } + } + return TRUE; +} + diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.c new file mode 100644 index 0000000000..41a14ae3f3 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.c @@ -0,0 +1,179 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfodthermal.c + * + * On Dimm thermal management. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + +#include "AGESA.h" +#include "mm.h" +#include "mn.h" +#include "mt.h" +#include "Ids.h" +#include "mfodthermal.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_ODTHERMAL_MFODTHERMAL_FILECODE + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +/*-----------------------------------------------------------------------------*/ +/** + * + * This function does On-Dimm thermal management. + * + * @param[in, out] *NBPtr - Pointer to the MEM_NB_BLOCK. + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ + +BOOLEAN +MemFOnDimmThermal ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 i; + UINT8 Dct; + CH_DEF_STRUCT *ChannelPtr; + MEM_DATA_STRUCT *MemPtr; + UINT8 *SpdBufferPtr; + UINT8 ThermalOp; + BOOLEAN ODTSEn; + BOOLEAN ExtendTmp; + BOOLEAN FirstLoop; + + ODTSEn = FALSE; + ExtendTmp = FALSE; + FirstLoop = TRUE; + + ASSERT (NBPtr != NULL); + MemPtr = NBPtr->MemPtr; + AGESA_TESTPOINT (TpProcMemOnDimmThermal, &MemPtr->StdHeader); + if (NBPtr->MCTPtr->NodeMemSize != 0) { + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + // Only go through the DCT if it is not disabled. + if (NBPtr->GetBitField (NBPtr, BFDisDramInterface) == 0) { + ChannelPtr = NBPtr->ChannelPtr; + // If Ganged mode is enabled, need to go through all dram devices on both DCTs. + if (!NBPtr->Ganged || (NBPtr->Dct != 1)) { + if (!(NBPtr->IsSupported[CheckSetSameDctODTsEn]) || (NBPtr->IsSupported[CheckSetSameDctODTsEn] && (NBPtr->Dct != 1)) || + ((NBPtr->Dct != 0) && (FirstLoop == TRUE))) { + ODTSEn = TRUE; + ExtendTmp = TRUE; + } + } + for (i = 0; i < MAX_DIMMS_PER_CHANNEL; i ++) { + if (NBPtr->TechPtr->GetDimmSpdBuffer (NBPtr->TechPtr, &SpdBufferPtr, i)) { + // Check byte 31: thermal and refresh option. + ThermalOp = SpdBufferPtr[THERMAL_OPT]; + // Bit 3: ODTS readout + if (!((ThermalOp >> 3) & 1)) { + ODTSEn = FALSE; + } + // Bit 0: Extended Temperature Range. + if (!(ThermalOp & 1)) { + ExtendTmp = FALSE; + } + } + } + + if (!NBPtr->Ganged || (NBPtr->Dct == 1)) { + // If in ganged mode, need to switch back to DCT0 to set the registers. + if (NBPtr->Ganged || NBPtr->IsSupported[CheckSetSameDctODTsEn]) { + NBPtr->SwitchDCT (NBPtr, 0); + ChannelPtr = NBPtr->ChannelPtr; + } + // If all dram devices on support ODTS + NBPtr->SetBitField (NBPtr, BFODTSEn, (ODTSEn == TRUE) ? 1 : 0); + ChannelPtr->ExtendTmp = ExtendTmp; + } + FirstLoop = FALSE; + } else { + ODTSEn = FALSE; + ExtendTmp = FALSE; + } + IDS_HDT_CONSOLE (MEM_FLOW, "\tDct %d\n", Dct); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tODTSEn = %d\n", ODTSEn); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tExtendTmp = %d\n", ExtendTmp); + } + } + return TRUE; +} + + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.h new file mode 100644 index 0000000000..50945f3124 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/ODTHERMAL/mfodthermal.h @@ -0,0 +1,77 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfodthermal.h + * + * Header file for On-Dimm thermal management. + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +#ifndef _MFODTHERMAL_H_ +#define _MFODTHERMAL_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +MemFOnDimmThermal ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +#endif //_MFODTHERMAL_H_ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfParallelTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfParallelTraining.c new file mode 100644 index 0000000000..2fd10c5e07 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfParallelTraining.c @@ -0,0 +1,288 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfParallelTraining.c + * + * This is the parallel training feature + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/PARTRN) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + + + +#include "AGESA.h" +#include "amdlib.h" +#include "OptionMemory.h" +#include "mm.h" +#include "mn.h" +#include "Ids.h" +#include "cpuRegisters.h" +#include "cpuApicUtilities.h" +#include "mfParallelTraining.h" +#include "heapManager.h" +#include "GeneralServices.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_PARTRN_MFPARALLELTRAINING_FILECODE + +/*----------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *----------------------------------------------------------------------------- + */ +extern MEM_TECH_CONSTRUCTOR* memTechInstalled[]; + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This is the main function to perform parallel training on all nodes. + * This is the routine which will run on the remote AP. + * + * @param[in,out] *EnvPtr - Pointer to the Training Environment Data + * @param[in,out] *StdHeader - Pointer to the Standard Header of the AP + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ +BOOLEAN +MemFParallelTraining ( + IN OUT REMOTE_TRAINING_ENV *EnvPtr, + IN OUT AMD_CONFIG_PARAMS *StdHeader + ) +{ + MEM_PARAMETER_STRUCT ParameterList; + MEM_NB_BLOCK NB; + MEM_TECH_BLOCK TB; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + MEM_DATA_STRUCT *MemPtr; + DIE_STRUCT *MCTPtr; + UINT8 p; + UINT8 i; + UINT8 Dct; + UINT8 Channel; + UINT8 *BufferPtr; + UINT8 DctCount; + UINT8 ChannelCount; + UINT8 RowCount; + UINT8 ColumnCount; + UINT16 SizeOfNewBuffer; + AP_DATA_TRANSFER ReturnData; + + // + // Initialize Parameters + // + ReturnData.DataPtr = NULL; + ReturnData.DataSizeInDwords = 0; + ReturnData.DataTransferFlags = 0; + + ASSERT (EnvPtr != NULL); + // + // Replace Standard header of a AP + // + LibAmdMemCopy (StdHeader, &(EnvPtr->StdHeader), sizeof (AMD_CONFIG_PARAMS), &(EnvPtr->StdHeader)); + + + // + // Allocate buffer for training data + // + BufferPtr = (UINT8 *) (&EnvPtr->DieStruct); + DctCount = EnvPtr->DieStruct.DctCount; + BufferPtr += sizeof (DIE_STRUCT); + ChannelCount = ((DCT_STRUCT *) BufferPtr)->ChannelCount; + BufferPtr += DctCount * sizeof (DCT_STRUCT); + RowCount = ((CH_DEF_STRUCT *) BufferPtr)->RowCount; + ColumnCount = ((CH_DEF_STRUCT *) BufferPtr)->ColumnCount; + + SizeOfNewBuffer = sizeof (DIE_STRUCT) + + DctCount * ( + sizeof (DCT_STRUCT) + ( + ChannelCount * ( + sizeof (CH_DEF_STRUCT) + sizeof (MEM_PS_BLOCK) + ( + RowCount * ColumnCount * NUMBER_OF_DELAY_TABLES + + (MAX_BYTELANES_PER_CHANNEL * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) + + (MAX_DIMMS_PER_CHANNEL * MAX_NUMBER_LANES) + ) + ) + ) + ); + AllocHeapParams.RequestedBufferSize = SizeOfNewBuffer; + AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_PAR_TRN_HANDLE, 0, 0, 0); + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) == AGESA_SUCCESS) { + BufferPtr = AllocHeapParams.BufferPtr; + LibAmdMemCopy ( BufferPtr, + &(EnvPtr->DieStruct), + sizeof (DIE_STRUCT) + DctCount * (sizeof (DCT_STRUCT) + ChannelCount * (sizeof (CH_DEF_STRUCT) + sizeof (MEM_PS_BLOCK))), + StdHeader + ); + + // + // Fix up pointers + // + MCTPtr = (DIE_STRUCT *) BufferPtr; + BufferPtr += sizeof (DIE_STRUCT); + MCTPtr->DctData = (DCT_STRUCT *) BufferPtr; + BufferPtr += MCTPtr->DctCount * sizeof (DCT_STRUCT); + for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { + MCTPtr->DctData[Dct].ChData = (CH_DEF_STRUCT *) BufferPtr; + BufferPtr += MCTPtr->DctData[Dct].ChannelCount * sizeof (CH_DEF_STRUCT); + for (Channel = 0; Channel < MCTPtr->DctData[Dct].ChannelCount; Channel++) { + MCTPtr->DctData[Dct].ChData[Channel].MCTPtr = MCTPtr; + MCTPtr->DctData[Dct].ChData[Channel].DCTPtr = &MCTPtr->DctData[Dct]; + } + } + NB.PSBlock = (MEM_PS_BLOCK *) BufferPtr; + BufferPtr += DctCount * ChannelCount * sizeof (MEM_PS_BLOCK); + + ReturnData.DataPtr = AllocHeapParams.BufferPtr; + ReturnData.DataSizeInDwords = (SizeOfNewBuffer + 3) / 4; + ReturnData.DataTransferFlags = 0; + + // + // Allocate Memory for the MEM_DATA_STRUCT we will use + // + AllocHeapParams.RequestedBufferSize = sizeof (MEM_DATA_STRUCT); + AllocHeapParams.BufferHandle = AMD_MEM_DATA_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) == AGESA_SUCCESS) { + MemPtr = (MEM_DATA_STRUCT *)AllocHeapParams.BufferPtr; + + LibAmdMemCopy (&(MemPtr->StdHeader), &(EnvPtr->StdHeader), sizeof (AMD_CONFIG_PARAMS), StdHeader); + + // + // Copy Parameters from environment + // + ParameterList.HoleBase = EnvPtr->HoleBase; + ParameterList.BottomIo = EnvPtr->BottomIo; + ParameterList.UmaSize = EnvPtr->UmaSize; + ParameterList.SysLimit = EnvPtr->SysLimit; + ParameterList.TableBasedAlterations = EnvPtr->TableBasedAlterations; + ParameterList.PlatformMemoryConfiguration = EnvPtr->PlatformMemoryConfiguration; + MemPtr->ParameterListPtr = &ParameterList; + + for (p = 0; p < MAX_PLATFORM_TYPES; p++) { + MemPtr->GetPlatformCfg[p] = EnvPtr->GetPlatformCfg[p]; + } + + MemPtr->ErrorHandling = EnvPtr->ErrorHandling; + // + // Create Local NBBlock and Tech Block + // + EnvPtr->NBBlockCtor (&NB, MCTPtr, EnvPtr->FeatPtr); + NB.RefPtr = &ParameterList; + NB.MemPtr = MemPtr; + i = 0; + while (memTechInstalled[i] != NULL) { + if (memTechInstalled[i] (&TB, &NB)) { + break; + } + i++; + } + NB.TechPtr = &TB; + NB.TechBlockSwitch (&NB); + + // + // Setup CPU Mem Type MSRs on the AP + // + NB.CpuMemTyping (&NB); + + IDS_HDT_CONSOLE (MEM_STATUS, "Node %d\n", NB.Node); + // + // Call Technology Specific Training routine + // + NB.TrainingFlow (&NB); + // + // Copy training data to ReturnData buffer + // + LibAmdMemCopy ( BufferPtr, + MCTPtr->DctData[0].ChData[0].RcvEnDlys, + ((DctCount * ChannelCount) * ( + (RowCount * ColumnCount * NUMBER_OF_DELAY_TABLES) + + (MAX_BYTELANES_PER_CHANNEL * MAX_CS_PER_CHANNEL * NUMBER_OF_FAILURE_MASK_TABLES) + + (MAX_DIMMS_PER_CHANNEL * MAX_NUMBER_LANES) + ) + ), + StdHeader); + + HeapDeallocateBuffer (AMD_MEM_DATA_HANDLE, StdHeader); + // + // Restore pointers + // + for (Dct = 0; Dct < MCTPtr->DctCount; Dct++) { + for (Channel = 0; Channel < MCTPtr->DctData[Dct].ChannelCount; Channel++) { + MCTPtr->DctData[Dct].ChData[Channel].MCTPtr = &EnvPtr->DieStruct; + MCTPtr->DctData[Dct].ChData[Channel].DCTPtr = &EnvPtr->DieStruct.DctData[Dct]; + + MCTPtr->DctData[Dct].ChData[Channel].RcvEnDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RcvEnDlys; + MCTPtr->DctData[Dct].ChData[Channel].WrDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDqsDlys; + MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsDlys; + MCTPtr->DctData[Dct].ChData[Channel].RdDqsDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsDlys; + MCTPtr->DctData[Dct].ChData[Channel].WrDatDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatDlys; + MCTPtr->DctData[Dct].ChData[Channel].RdDqs2dDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqs2dDlys; + MCTPtr->DctData[Dct].ChData[Channel].RdDqsMinDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsMinDlys; + MCTPtr->DctData[Dct].ChData[Channel].RdDqsMaxDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].RdDqsMaxDlys; + MCTPtr->DctData[Dct].ChData[Channel].WrDatMinDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatMinDlys; + MCTPtr->DctData[Dct].ChData[Channel].WrDatMaxDlys = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].WrDatMaxDlys; + MCTPtr->DctData[Dct].ChData[Channel].FailingBitMask = EnvPtr->DieStruct.DctData[Dct].ChData[Channel].FailingBitMask; + } + MCTPtr->DctData[Dct].ChData = EnvPtr->DieStruct.DctData[Dct].ChData; + } + MCTPtr->DctData = EnvPtr->DieStruct.DctData; + } + + // + // Signal to BSP that training is complete and Send Results + // + ASSERT (ReturnData.DataPtr != NULL); + ApUtilTransmitBuffer (EnvPtr->BspSocket, EnvPtr->BspCore, &ReturnData, StdHeader); + + // + // Clean up and exit. + // + HeapDeallocateBuffer (GENERATE_MEM_HANDLE (ALLOC_PAR_TRN_HANDLE, 0, 0, 0), StdHeader); + } else { + MCTPtr = &EnvPtr->DieStruct; + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_TRAINING_DATA, MCTPtr->NodeId, 0, 0, 0, StdHeader); + SetMemError (AGESA_FATAL, MCTPtr); + ASSERT(FALSE); // Could not allocate heap for buffer for parallel training data + } + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfStandardTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfStandardTraining.c new file mode 100644 index 0000000000..654af0e235 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/PARTRN/mfStandardTraining.c @@ -0,0 +1,85 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfStandardTraining.c + * + * This is the standard training routine which performs all training from the BSP + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/PARTRN) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + + + +#include "AGESA.h" +#include "mm.h" +#include "mn.h" +#include "Ids.h" +#include "mfStandardTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_PARTRN_MFSTANDARDTRAINING_FILECODE +/*----------------------------------------------------------------------------- +* EXPORTED FUNCTIONS +* +*----------------------------------------------------------------------------- +*/ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This is the main function to perform memory training on all nodes from + * the BSP only. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return TRUE - This feature is enabled. + * @return FALSE - This feature is not enabled. + */ +BOOLEAN +MemFStandardTraining ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + ASSERT (NBPtr != NULL); + + NBPtr->TrainingFlow (NBPtr); + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c new file mode 100644 index 0000000000..b355d6b093 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/KB/mfRdWr2DKb.c @@ -0,0 +1,348 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DKb.c + * + * KB - Specific funtion for 2D Read and write training feature + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining/Kb) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ +#include "AGESA.h" +#include "amdlib.h" +#include "AdvancedApi.h" +#include "GeneralServices.h" +#include "Ids.h" +#include "heapManager.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_KB_MFRDWR2DKB_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +MemFRdWr2DTrainingInitKB ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ); + +BOOLEAN +STATIC +MemFRdWr2DScaleVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramIntExtVrefSelectKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +STATIC +MemFRdWr2DProgramDataPatternKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ); + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function initializes the 2D Read/Write Training Feature Hooks for KB + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE - Function was implemented + * + */ + +BOOLEAN +MemFRdWr2DTrainingInitKB ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + ASSERT (NBPtr != NULL); + NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining; + NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig; + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelectKB; + NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVrefKB; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVrefKB; + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays; + NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch; + NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel; + NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim; + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase; + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPatternKB; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref for 2D Read/Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *VrefPtr - Pointer to Vref value + * + * @return BOOLEAN + * TRUE - Success + * FAIL (External Callout only) + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ) +{ + AGESA_STATUS Status; + MEM_DATA_STRUCT *MemPtr; + ID_INFO CallOutIdInfo; + VOLTAGE_ADJUST Va; + UINT8 Vref; + + ASSERT (NBPtr != NULL); + ASSERT (VrefPtr != NULL); + MemPtr = NBPtr->MemPtr; + Vref = *(UINT8*)VrefPtr; + CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId; + CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId; + LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader); + Va.MemData = MemPtr; + Status = AGESA_SUCCESS; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) { + // + // Internal vref control + // + ASSERT (Vref < 61); + if (Vref < 30) { + Vref = (62 - Vref); + } else { + Vref = (Vref - 30); + } + NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 3); + } else { + // + // External vref control + // + AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref; + IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n"); + Va.VoltageType = VTYPE_CPU_VREF; + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + } + } else { + // + // DIMM Vref Control + // + Va.VoltageType = VTYPE_DIMM_VREF; + // + // Offset by 15 and multiply by 2. + // + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tDimm Vref = %c%d% ", (Va.AdjustValue < 0) ? '-':'+', (Va.AdjustValue < 0) ? (0 - Va.AdjustValue) : Va.AdjustValue ); + if (Status != AGESA_SUCCESS) { + IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *"); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + return (Status == AGESA_SUCCESS) ? TRUE : FALSE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function scales Vref from the range used in Data Collection to + * the range that is programmed into the register. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale. + * + * @return BOOLEAN + * TRUE Function was implemented + * + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVrefKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ) +{ + *(UINT8*)Vref = *(UINT8*)Vref * 2; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref to internal or external control for 2D Read + * or Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - External Vref was selected + * FALSE - Internal Vref was selected + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramIntExtVrefSelectKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0002 : 0x0001)); + } + return NBPtr->RefPtr->ExternalVrefCtl; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Data Pattern that will be sent and compared + * against. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *PatternIndexPtr - Pointer to a generic index used to + * determine which pattern to program. + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +STATIC +MemFRdWr2DProgramDataPatternKB ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ) +{ + UINT8 SeedCount; + UINT32 PrbsSeed; + CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251}; + + ASSERT (NBPtr != 0); + ASSERT (PatternIndexPtr != NULL); + SeedCount = *(UINT8*)PatternIndexPtr; + ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1)); + MemNSetBitFieldNb (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]); + PrbsSeed = 0x7EA05; + switch (SeedCount) { + case 0: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x01); + PrbsSeed = 0x7FFFF; + break; + case 1: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x04); + MemNSetBitFieldNb (NBPtr, BFXorPatOvr, 0xFF); + PrbsSeed = 0x7EA05; + break; + case 2: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0x55555549); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0x55555555); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0x55555555); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0x55555555); + break; + case 3: + MemNSetBitFieldNb (NBPtr, BFDataPatGenSel, 0x03); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern0, 0xA5A5A55A); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern1, 0xA5A5A5A5); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern2, 0xA5A5A5A5); + MemNSetBitFieldNb (NBPtr, BFDramUserDataPattern3, 0xA5A5A5A5); + break; + default: + ASSERT (FALSE); + } + ASSERT (PrbsSeed != 0); + // + // Program the PRBS Seed + // + NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed); + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c new file mode 100644 index 0000000000..62bfb82366 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdDqs2DTraining.c @@ -0,0 +1,115 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdDqs2DTraining.c + * + * RD DQS 2 dimensional training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + +#include "AGESA.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDDQS2DTRAINING_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + /*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function executes 2D training for Read DQS + * + * @param[in,out] *TechPtr - Pointer to the MEM_TECH_BLOCK + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + * + */ + +BOOLEAN +MemFAmdRdDqs2DTraining ( + IN OUT MEM_TECH_BLOCK *TechPtr + ); + +BOOLEAN +MemFAmdRdDqs2DTraining ( + IN OUT MEM_TECH_BLOCK *TechPtr + ) +{ + IDS_HDT_CONSOLE (MEM_STATUS, "\n\nStart Read DQS 2D training.\n\n"); + TechPtr->Direction = DQS_READ_DIR; + return TechPtr->NBPtr->FamilySpecificHook[RdWr2DTraining] (TechPtr->NBPtr, NULL); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c new file mode 100644 index 0000000000..090a9379a9 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DEyeRimSearch.c @@ -0,0 +1,1112 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DEyeRimSearch.c + * + * Eye Rim Sampling Algorithm for use in 2D Read DQS or 2D Write Data Training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ +#include "AGESA.h" +#include "amdlib.h" +#include "AdvancedApi.h" +#include "GeneralServices.h" +#include "Ids.h" +#include "heapManager.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DEYERIMSEARCH_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +BOOLEAN +STATIC +MemFInitializeEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +VOID +STATIC +MemTEyeFill ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT8 +STATIC +DetermineSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + +UINT8 +STATIC +GetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + +VOID +STATIC +SetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ); + +VOID +STATIC +SetSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ); + +INT8 +STATIC +Get1DTrainedEyeCenter ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane + ); + +BOOLEAN +STATIC +CheckForFail ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ); + + +INT8 +STATIC +xlateY ( + IN INT8 y + ); + +BOOLEAN +STATIC +ClearSampledPassResults ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT32 +STATIC +CompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +UINT32 +STATIC +Compare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +VOID +STATIC +ProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Vref + ); + +VOID +STATIC +StartAggressors ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN BOOLEAN TurnOn + ); + +/* -----------------------------------------------------------------------------*/ +/** + * + * Initialize Eye Rim Search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +STATIC +MemFInitializeEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 lane; + UINT8 vref; + MEM_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY *RimData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + Data = TechPtr->RdWr2DData; + RimData = Data->SavedData; + // + // Set Boundaries + // + RimData->xMax = Data->MaxRdWrSweep - 1; + RimData->yMax = (NBPtr->TotalMaxVrefRange / 2) - 1; + RimData->xMin = RimData->xMax * -1; + RimData->yMin = RimData->yMax * -1; + + RimData->ParallelSampling = EYERIM_PARALLEL_SAMPLING; + RimData->BroadcastDelays = EYERIM_BROADCAST_DELAYS; + RimData->Dirs[0] = 1; + RimData->Dirs[1] = -1; + + RimData->SampleCount = 0; + RimData->VrefUpdates = 0; + RimData->RdWrDlyUpdates = 0; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) { + for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) { + // 00b = state not saved + // 01b = state saved + RimData->LaneSaved[lane].Vref[vref].PosRdWrDly = 0; + RimData->LaneSaved[lane].Vref[vref].NegRdWrDly = 0; + // 00b = Fail + // 01b = Pass + Data->Lane[lane].Vref[vref].PosRdWrDly = 0; + Data->Lane[lane].Vref[vref].NegRdWrDly = 0; + } + } + if (TechPtr->Direction == DQS_READ_DIR) { + NBPtr->MaxSeedCount = MAX_2D_RD_SEED_COUNT; + } else { + // + // Set the seed count in terms of the Total desired bit times + // + NBPtr->MaxSeedCount = (UINT8) (NBPtr->TotalBitTimes2DWrTraining / (256 * 8 * MIN ( 1, NBPtr->MaxAggressorDimms[NBPtr->Dct]))); + }; + TechPtr->DqsRdWrPosSaved = 0; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\tTotal Bit Times: %d Max Seed Count: %d\n",(TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalBitTimes2DRdTraining:NBPtr->TotalBitTimes2DWrTraining,NBPtr->MaxSeedCount); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * This function collects data for Eye Rim Search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Not Used + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + * + */ +BOOLEAN +MemFRdWr2DEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID * OptParam + ) +{ + UINT8 lane; + UINT8 j; + UINT8 k; + INT8 ydir; + INT8 xdir; + INT8 xi; + INT8 yi; + INT8 x; + INT8 y; + INT8 xmax; + INT8 ymax; + INT8 xmin; + INT8 ymin; + INT8 xo; + INT8 xt; + INT8 yo; + INT8 yt; + UINT8 slane; + UINT8 result; + INT8 states[2]; + UINT8 InitialCS; + UINT8 ChipSel; + UINT8 Aggr; + UINT8 SeedCount; + UINT32 InPhaseResult; + UINT32 PhaseResult180; + UINT32 Result; + MEM_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY RimData; + MEM_TECH_BLOCK *TechPtr; + RD_WR_2D *VrefPtr; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + Data = TechPtr->RdWr2DData; + // + // Allocate Storage for Rim Search + // + AllocHeapParams.RequestedBufferSize = MemFRdWr2DGetMaxLanes (NBPtr) * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D); + AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_RIM_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &NBPtr->MemPtr->StdHeader) == AGESA_SUCCESS) { + VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr; + } else { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + return FALSE; + } + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane++) { + RimData.LaneSaved[lane].Vref = &VrefPtr[lane * NBPtr->TotalMaxVrefRange]; + } + Data->SavedData = &RimData; + + MemFInitializeEyeRimSearch (NBPtr); + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] (NBPtr, NULL); + InitialCS = TechPtr->ChipSel; + NBPtr->FamilySpecificHook[Adjust2DPhaseMaskBasedOnEcc] (NBPtr, &NBPtr); + NBPtr->FamilySpecificHook[RdWr2DInitVictim] (NBPtr, NULL); + // + // EnableDisable continuous writes on the agressor channels + // + + for (Aggr = 0; Aggr < MAX (1 , NBPtr->MaxAggressorDimms[NBPtr->Dct]) ; Aggr += (NBPtr->IsSupported[PerDimmAggressors2D] ? 2 : NBPtr->CsPerDelay) ) { + ClearSampledPassResults (NBPtr); + // + // Enable continuous writes on the aggressors + // + StartAggressors (NBPtr, TRUE); + for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { + xmax = RimData.xMax; + xmin = RimData.xMin; + ymax = RimData.yMax; + ymin = RimData.yMin; + if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) { + TechPtr->ChipSel = ChipSel; + Data->RdWrDly = Data->MaxRdWrSweep; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) { + // + // Two loops to handle each quadrant from the trained point + // + for ( j = 0; j < 2; j++) { + ydir = RimData.Dirs[j]; + for ( k = 0; k < 2; k++) { + xdir = RimData.Dirs[k]; + // + // Sample Loops - stay w/n the defined quadrant + // assume xmax = -1 * xmin, ymax = -1 * ymin + // initial point must always pass + // end point - boundary or two consecutive fails along the y-axis + // + // Initial dx, dy step state + // Starting dy at 8 to reduce samples necessary searching for the eye height + states[0] = 0; + states[1] = 8; + // xi and yi are inital trained points, assumed to be inside the eye + // These set the coordinate syst em for the quadrants + xi = Get1DTrainedEyeCenter (NBPtr, lane); + yi = 0; // Initial center is always at Vref nominal + x = xi; + y = yi; + while ( + ((xdir > 0 && x >= xi && x <= xmax) || + (xdir < 0 && x <= xi && x >= xmin)) && + ((ydir > 0 && y >= yi && y <= ymax) || + (ydir < 0 && y <= yi && y >= ymin)) && + !(y == yi && (xdir * (xi - x) >= 2)) + ) { + // + // Decide if result is already sampled, or need to take the sample + // + if (0 == DetermineSavedState (NBPtr, lane, y, x)) { + RimData.SampleCount++; + // + // Result for this point is not in the cache, sample it + // + for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) { + if (!RimData.ParallelSampling && ( slane != lane)) { + continue; + } + // + // Calculate the relative offset from the initial trained position for this lane + // + xo = Get1DTrainedEyeCenter (NBPtr, slane); + yo = 0; + xt = xo + (x - xi); + yt = yo + (y - yi); + if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) { + continue; + } + // + // Update the vref and lane delays (yt, xt) + // + if (slane == lane) { + // + // For processors w/ a single Vref per channel, only set Vref once + // + if ( NBPtr->Vref != xlateY (yt)) { + // + // If not already set + // + RimData.VrefUpdates++; + NBPtr->Vref = xlateY (yt); + ProgramVref (NBPtr, NBPtr->Vref); + } + } + if (RimData.BroadcastDelays) { + // + // When BroadcastDelays, only set RdDqs once + // + if (slane == lane) { + // + // If current lane + // + if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) { + // + // If the not already set + // Account for rollover. + // + RimData.RdWrDlyUpdates++; + Data->RdWrDly = (UINT8) (xt & RimData.xMax); + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] (NBPtr, &Data->RdWrDly); + } + } + } else { + // + // For Reads, Program all RdDqs Delay Values the same + // For Writes, Program all WrDat Values with same value offset by WrDqs for that lane. + // + if ( Data->RdWrDly != (UINT8) (xt & RimData.xMax)) { + // + // If the not already set + // Account for rollover. + // + RimData.RdWrDlyUpdates++; + Data->RdWrDly = (UINT8) (xt & RimData.xMax); + MemTSetDQSDelayAllCSR (TechPtr, (UINT8) (xt & RimData.xMax)); + } + } + } + // + // Perform Memory RW test + // + InPhaseResult = 0; + PhaseResult180 = 0; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL); + for (SeedCount = 0; SeedCount < NBPtr->MaxSeedCount; SeedCount++) { + // + // Begin continuous reads and writes on the victim channels + // + NBPtr->FamilySpecificHook[RdWr2DStartVictim] (NBPtr, &SeedCount); + // + // Occasionally check if all trained lanes have already failed + // + if ((NBPtr->MaxSeedCount < 4) || ((SeedCount % (NBPtr->MaxSeedCount / 4)) == 0)) { + InPhaseResult |= CompareInPhase (NBPtr); + PhaseResult180 |= Compare180Phase (NBPtr); + if (((InPhaseResult & NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask) && + ((PhaseResult180& NBPtr->PhaseLaneMask) == NBPtr->PhaseLaneMask)) { + break; + } + } + } + // + // Obtain the results + // + for (slane = 0; slane < MemFRdWr2DGetMaxLanes (NBPtr); slane ++ ) { + if (!RimData.ParallelSampling && (slane != lane)) { + continue; + } + // + // Calculate the relative offset from legacy trained + // + xo = Get1DTrainedEyeCenter (NBPtr, RimData.BroadcastDelays?lane:slane); + yo = 0; + xt = xo + (x - xi); + yt = yo + (y - yi); + if (xt > xmax || xt < xmin || yt > ymax || yt < ymin) { + continue; + } + // + // In this example, data{}{}{} = 1 is a Fail, 0 is a Pass + // In-Phase Results + // + if (CheckForFail (NBPtr, slane, yt, xt)) { + // + // Don't overwrite a fail + // + if (xt >= 0) { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // x8, so combine "Nibble X" and "Nibble X+1" results + Result = (InPhaseResult >> (slane * 2)) & 0x03; + } else { + // x4, so use "Nibble" results + Result = (InPhaseResult >> slane) & 0x01; + } + SetPassFailValue (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + } else { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // x8, so combine "Nibble X" and "Nibble X+1" results + Result = (PhaseResult180 >> (slane * 2)) & 0x03; + } else { + // x4, so use "Nibble" results + Result = (PhaseResult180 >> slane) & 0x01; + } + SetPassFailValue (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + SetSavedState (NBPtr, slane, yt, xt, (UINT8) (Result & 0x0F)); + } + } + } + } + // Decide the next sample point based on the result of the current lane + result = GetPassFailValue (NBPtr, lane, y, x); + InPhaseResult = 0; + PhaseResult180 = 0; + // States && comments are relative to the ++ Quadrant + if (result == 3) { + // Current Pass + if (states[0] > 0 || states[1] > 0) { + if (states[1] > 1 && y * ydir <= ymax - states[1]) { + // Current Pass, Continue searching up by leaps + states[0] = 0; + } else if (states[1] > 1 && y * ydir <= ymax - 4) { + // Current Pass, Continue searching up by leaps + states[0] = 0; + states[1] = 4; + } else if (states[1] > 1 && y * ydir <= ymax - 2) { + // Current Pass, Continue searching up by leaps + states[0] = 0; + states[1] = 2; + } else if (y * ydir <= ymax - 1) { + // Current Pass, Continue searching up by one + states[0] = 0; + states[1] = 1; + } else if (y * ydir == ymax) { + // Current Pass and at the top edge, Move right + states[0] = 1; + states[1] = 0; + } else { + ASSERT (FALSE); + } + } else { + // Move right one + states[0] = 1; + states[1] = 0; + } + } else if (result == 2) { + // Current Fail + if (states[0] > 0) { + // Search down and left + states[0] = -1; + states[1] = -1; + } else if (states[1] > 1 || states[1] < 0) { + // Search down + states[0] = 0; + states[1] = -1; + } else if (states[1] == 1) { + // Move down and right + states[0] = 1; + states[1] = -1; + } else { + ASSERT (FALSE); + } + } else { + ASSERT (FALSE); + } + // Update the coordinates based on the new state and quadrant direction + x += xdir * states[0]; + y += ydir * states[1]; + } + } + } + } + } + } + // + // Disable continuous writes on the agressor channels + // + StartAggressors (NBPtr, FALSE); + } + IDS_HDT_CONSOLE (MEM_FLOW,"\n\tSampleCount:%d",RimData.SampleCount); + IDS_HDT_CONSOLE (MEM_FLOW,"\t\tVref Updates:%d",RimData.VrefUpdates); + IDS_HDT_CONSOLE (MEM_FLOW,"\t\tRdDqs Dly Updates:%d\n",RimData.RdWrDlyUpdates); + // + // Finilazing Victim Continuous writes + // + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] (NBPtr, NULL); + TechPtr->ChipSel = InitialCS; + // + // Fill eye based on Rim Search results + // + MemTEyeFill (NBPtr); + // + // Display the results the completed eye + // + MemFRdWr2DDisplaySearch (NBPtr, Data); + // + // Restore environment settings after training + // + if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_RIM_HANDLE, &NBPtr->MemPtr->StdHeader) != AGESA_SUCCESS) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &NBPtr->MemPtr->StdHeader); + } + return TRUE; +} + + +/* -----------------------------------------------------------------------------*/ +/** + * Fill the data eye + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + */ +VOID +STATIC +MemTEyeFill ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + INT8 x; + INT8 y; + UINT8 lane; + UINT8 result; + INT8 yLastPass; + UINT8 xMax; + UINT8 yMax; + UINT8 xMin; + UINT8 yMin; + BOOLEAN FirstPassFound; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + xMax = SavedData->xMax; + yMax = SavedData->yMax; + xMin = SavedData->xMin; + yMin = SavedData->yMin; + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane++) { + for (x = xMin ; x <= xMax ; x++) { + FirstPassFound = FALSE; + yLastPass = yMin; + // + // Scan for the last passing value + // + for (y = yMax ; y >= yLastPass ; y--) { + result = GetPassFailValue (NBPtr, lane, y, x); + if (result == 0) { + // + // Not Saved, Mark it as FAIL. (Should already be cleared) + // + } else if (result == 2) { + // + // FAIL, so Mark as FAIL (Do nothing) + // + } else if (result == 3) { + // + // PASS, Mark it and save y value (This will end the loop) + // + SetPassFailValue (NBPtr, lane, y, x, result); + yLastPass = y; + } else { + ASSERT (FALSE); + } + } + // + // Scan for the first pass, the fill until the last pass + // + for (y = yMin ; y < yLastPass ; y++) { + result = GetPassFailValue (NBPtr, lane, y, x); + if (result == 0) { + // + // Not Saved, if we've already found a first Passing value, mark it as a PASS + // otherwise, mark it as FAIL. (Should already be cleared) + // + if (FirstPassFound == TRUE) { + SetPassFailValue (NBPtr, lane, y, x, result); + } + } else if (result == 2) { + // + // FAIL, so Mark as FAIL (Do nothing) + // + } else if (result == 3) { + // + // PASS, Mark it and set FirstPassFound + // + SetPassFailValue (NBPtr, lane, y, x, result); + FirstPassFound = TRUE; + } else { + ASSERT (FALSE); + } + } // y Loop + } //x Loop + } // Lane Loop +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Get the 1D trained center + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] lane - UINT8 of current lane + * + * @return INT8 - Eye Center + */ +INT8 +STATIC +Get1DTrainedEyeCenter ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane + ) +{ + MEM_TECH_BLOCK *TechPtr; + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + if (TechPtr->Direction == DQS_READ_DIR) { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // Program Byte based for x8 and x16 + return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane]; + } else { + return (INT8)NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (lane >> 1)]; + } + } else { + return (INT8) (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane] - + NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + lane]); + } +} + + +/* -----------------------------------------------------------------------------*/ +/** + * + * Determine if a Byte Lane result has been saved + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay + * + * @return UINT8 + * 1 - value saved + * 0 - value not saved + */ +UINT8 +STATIC +DetermineSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1); + } else { + return (UINT8) (SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Determine if a failure has occured + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * + * @return BOOLEAN + * FALSE - Fail + * TRUE - Pass + */ +BOOLEAN +STATIC +CheckForFail ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved, so it is not fail + return TRUE; + } else { + // value saved, so examine result + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // result = fail + return FALSE; + } else { + // result = pass + return TRUE; + } + } + } else { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved, so it is not fail + return TRUE; + } else { + // value saved, so examine result + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // result = fail + return FALSE; + } else { + // result = pass + return TRUE; + } + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Get pass fail state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * + * @return UINT8 + * 0 - Value not saved, + * 2 - Fail, + * 3 - Pass + */ +UINT8 +STATIC +GetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_RD_WR_2D_ENTRY* RdWr2DData; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + RdWr2DData = TechPtr->RdWr2DData; + SavedData = RdWr2DData->SavedData; + + if (x >= 0) { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved + return 0; + } else { + // value saved, so return pass/fail + return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3; + } + } else { + if ((SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) { + // value not saved + return 0; + } else { + // value saved, so return pass/fail + return ((RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly >> (SavedData->xMax - (x & SavedData->xMax)) & 0x1) == 0) ? 2 : 3; + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Set the Pass/Fail state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * @param[in] result - result value + * + */ +VOID +STATIC +SetPassFailValue ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_RD_WR_2D_ENTRY* RdWr2DData; + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + RdWr2DData = TechPtr->RdWr2DData; + SavedData = RdWr2DData->SavedData; + + if (x >= 0) { + RdWr2DData->Lane[lane].Vref[xlateY (y)].PosRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0; + } else { + RdWr2DData->Lane[lane].Vref[xlateY (y)].NegRdWrDly |= (result == 0) ? (1 << (SavedData->xMax - (x & SavedData->xMax))) : 0; + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Set the save state of lane + * + * @param[in,out] *NBPtr - Pointer to MEM_NB_BLOCK + * @param[in] lane - Current lane + * @param[in] y - Vref value + * @param[in] x - Delay Value + * @param[in] result - result value + * + */ +VOID +STATIC +SetSavedState ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 lane, + IN INT8 y, + IN INT8 x, + IN UINT8 result + ) +{ + MEM_RD_WR_2D_RIM_ENTRY *SavedData; + + ASSERT (NBPtr != NULL); + SavedData = ((MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData)->SavedData; + + if (x >= 0) { + SavedData->LaneSaved[lane].Vref[xlateY (y)].PosRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax))); + } else { + SavedData->LaneSaved[lane].Vref[xlateY (y)].NegRdWrDly |= (1 << (SavedData->xMax - (x & SavedData->xMax))); + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * Translate Vref into a positive, linear value that can be used as an + * array index. + * + * @param[in] y - INT8 of the (signed) Vref value + * + * @return UINT8 - Translated Value + */ +INT8 +STATIC +xlateY ( + IN INT8 y + ) +{ + ASSERT ( y > -0x10); + ASSERT ( y < 0x10); + return (y + 0xF) & 0x1F; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Re-walk the eye rim for each aggressor combination, which invalidates + * previous Passes in the sample array. Previous Fails in the sample array + * remain valid. Knowledge of previous fails and speeds sampling for the + * subsequent walks, esp. when used in conjunction w/ ParallelSampling. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +STATIC +ClearSampledPassResults ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT8 lane; + UINT8 vref; + MEM_RD_WR_2D_ENTRY *Data; + MEM_RD_WR_2D_RIM_ENTRY *RimData; + + ASSERT (NBPtr != NULL); + + Data = (MEM_RD_WR_2D_ENTRY*)NBPtr->TechPtr->RdWr2DData; + RimData = Data->SavedData; + + for (lane = 0; lane < MemFRdWr2DGetMaxLanes (NBPtr); lane ++ ) { + for ( vref = 0; vref < NBPtr->TotalMaxVrefRange; vref ++ ) { + RimData->LaneSaved[lane].Vref[vref].PosRdWrDly &= ~(Data->Lane[lane].Vref[vref].PosRdWrDly); + Data->Lane[lane].Vref[vref].PosRdWrDly = 0; + RimData->LaneSaved[lane].Vref[vref].NegRdWrDly &= ~(Data->Lane[lane].Vref[vref].NegRdWrDly); + Data->Lane[lane].Vref[vref].NegRdWrDly = 0; + } + } + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Perform in-phase comparison + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT32 - Bitmap of results of comparison + * 1 = FAIL + * 0 = PASS + */ + +UINT32 +STATIC +CompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT32 Result; + Result = 0; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] (NBPtr, &Result); + return Result; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Perform 180 Degree out-of-phase comparison + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT32 - Bitmap of results of comparison + * 1 = FAIL + * 0 = PASS + */ + +UINT32 +STATIC +Compare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + UINT32 Result; + Result = 0; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] (NBPtr, &Result); + return Result; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Program Vref after scaling to accomodate the register definition + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] Vref - UINT8 value of Vref relative to the sample point + * + */ + +VOID +STATIC +ProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 Vref + ) +{ + UINT8 ScaledVref; + ScaledVref = Vref; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &ScaledVref); + NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &ScaledVref); +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Turn Aggressor Channels On or Off + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] TurnOn - BOOLEAN + * TRUE - Turn On, False = Turn Off. + */ + +VOID +STATIC +StartAggressors ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN BOOLEAN TurnOn + ) +{ + NBPtr->FamilySpecificHook[TurnOnAggressorChannels] (NBPtr, &TurnOn); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c new file mode 100644 index 0000000000..b8d8429822 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DPatternGeneration.c @@ -0,0 +1,424 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DPatternGeneration.c + * + * Common Northbridge features + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + +#include "AGESA.h" +#include "AdvancedApi.h" +#include "amdlib.h" +#include "Ids.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mport.h" +#include "PlatformMemoryConfiguration.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DPATTERNGENERATION_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + + /* -----------------------------------------------------------------------------*/ +/** + * + * This function Initializes the Victim for 2D RdDqs Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +MemFRdWr2DInitVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + RRW_SETTINGS *Rrw; + UINT8 InitialCS; + UINT8 ChipSel; + UINT8 SeedCount; + BOOLEAN OptPatWr2D; + + OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]) && (NBPtr->TechPtr->Direction == DQS_READ_DIR); + InitialCS = NBPtr->TechPtr->ChipSel; + Rrw = &NBPtr->RrwSettings; + // Program the Bubble Count and CmdStreamLen + NBPtr->SetBitField (NBPtr, BFBubbleCnt, 32); + NBPtr->SetBitField (NBPtr, BFBubbleCnt2, 0); + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63); + // Set Comparison Masks + NBPtr->SetBitField (NBPtr, BFDramDqMaskLow, Rrw->CompareMaskLow); + NBPtr->SetBitField (NBPtr, BFDramDqMaskHigh, Rrw->CompareMaskHigh); + // If All Dimms are ECC Capable Test ECC. Otherwise, mask it off + NBPtr->SetBitField (NBPtr, BFDramEccMask, (NBPtr->MCTPtr->Status[SbEccDimms] == TRUE) ? Rrw->CompareMaskEcc : 0xFF); + // Program the Starting Address + NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA); + NBPtr->SetBitField (NBPtr, BFTgtAddressA, Rrw->TgtColAddressA); + NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB); + NBPtr->SetBitField (NBPtr, BFTgtAddressB, Rrw->TgtColAddressB); + NBPtr->SetBitField (NBPtr, BFCmdTgt, CMD_TGT_AB); + // Wait for RRW Engine to be ready and turn it on + NBPtr->PollBitField (NBPtr, BFCmdSendInProg, 0, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 1); + for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { + // Ensure that Odd and Even CS are trained + if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) { + continue; + } + NBPtr->TechPtr->ChipSel = ChipSel; + for (SeedCount = 0; SeedCount < (OptPatWr2D ? NBPtr->MaxSeedCount : 1 ); SeedCount++) { + if (OptPatWr2D) { + // + // Set BankAddress according to seed + // + ASSERT (NBPtr->MaxSeedCount <= 4); + Rrw->TgtBankAddressA = (SeedCount * 2) + CPG_BANK_ADDRESS_A; + Rrw->TgtBankAddressB = (SeedCount * 2) + CPG_BANK_ADDRESS_B; + } + // + // Send ACTIVATE to all Banks + // + // Set Chip select + MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel)); + // Set Bank Address + MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressA); + // Set Row Address + MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressA); + // Send the command + MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1); + // Wait for command complete + MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); + // Set Chip select + MemNSetBitFieldNb (NBPtr, BFCmdChipSelect, (1 << NBPtr->TechPtr->ChipSel)); + // Set Bank Address + MemNSetBitFieldNb (NBPtr, BFCmdBank, Rrw->TgtBankAddressB); + // Set Row Address + MemNSetBitFieldNb (NBPtr, BFCmdAddress, Rrw->TgtRowAddressB); + // Send the command + MemNSetBitFieldNb (NBPtr, BFSendActCmd, 1); + // Wait for command complete + MemNPollBitFieldNb (NBPtr, BFSendActCmd, 0, PCI_ACCESS_TIMEOUT, FALSE); + + if (OptPatWr2D) { + // + // Write the Pattern to the bank pairs for each seed. + // + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] (NBPtr, NULL); + NBPtr->SetBitField (NBPtr, BFTgtBankA, Rrw->TgtBankAddressA); + NBPtr->SetBitField (NBPtr, BFTgtBankB, Rrw->TgtBankAddressB); + // Program the PRBS Seed + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount); + // + // Enable continuous writes on the victim channels + // + // Set the Command Count + NBPtr->SetBitField (NBPtr, BFCmdCount, 256); + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0. + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + } + } + } + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + // + // Do not use LFSR Rollover during Wr Training + // + NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 1); + } + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function Initializes the Victim chipSelects for 2D Read or Write + * Training Continuous Writes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE + */ +BOOLEAN +MemFRdWr2DInitVictimChipSel ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + NBPtr->SetBitField (NBPtr, BFTgtChipSelectA, NBPtr->TechPtr->ChipSel); + NBPtr->SetBitField (NBPtr, BFTgtChipSelectB, NBPtr->TechPtr->ChipSel); + NBPtr->SetBitField (NBPtr, BFResetAllErr, 1); + return TRUE; +} + + + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function checks the In Phase Error status bits for comparison + * results for RD/WR 2D training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[out] *Result - Pointer to UINT32 for storing results + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DCompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ) +{ + *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErrSts); + return TRUE; +} + + /*-----------------------------------------------------------------------------*/ +/** + * + * This function checks the 180 Error status bits for RD/WR 2D training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[out] *Result - Pointer to UINT32 for storing results + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DCompare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ) +{ + *(UINT32*)Result = NBPtr->GetBitField (NBPtr, BFNibbleErr180Sts); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function starts the Victim for 2D RdDqs Training Continuous Writes + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] SeedCountPtr - UINT8 Pointer to Seed count + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DStartVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *SeedCountPtr + ) +{ + BOOLEAN OptPatWr2D; + UINT32 CommandCount; + UINT8 SeedCount; + + ASSERT (NBPtr != NULL); + ASSERT (SeedCountPtr != NULL); + + SeedCount = *(UINT8*)SeedCountPtr; + + OptPatWr2D = FALSE; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + OptPatWr2D = (NBPtr->IsSupported[OptimizedPatternWrite2D]); + CommandCount = NBPtr->TotalBitTimes2DRdTraining / (8 * NBPtr->MaxSeedCount * NBPtr->MaxAggressorDimms[NBPtr->Dct]); + } else { + CommandCount = 256; + } + + // Program the PRBS Seed + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] (NBPtr, &SeedCount); + if (OptPatWr2D) { + // + // Set BankAddress according to seed + // + NBPtr->SetBitField (NBPtr, BFTgtBankA, (SeedCount * 2) + CPG_BANK_ADDRESS_A); + NBPtr->SetBitField (NBPtr, BFTgtBankB, (SeedCount * 2) + CPG_BANK_ADDRESS_B); + } else { + // + // Enable continuous writes on the victim channels + // + // Set the Command Count + NBPtr->SetBitField (NBPtr, BFCmdCount, 256); + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_WRITE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0. + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + } + // + // Enable continuous reads on the victim channels + // + // Set the Command Count + ASSERT (NBPtr->MaxAggressorDimms[NBPtr->Dct] != 0); + // + NBPtr->SetBitField (NBPtr, BFCmdCount, CommandCount ); + // Reset All Errors and Disable StopOnErr + NBPtr->SetBitField (NBPtr, BFCmdType, CMD_TYPE_READ); + NBPtr->SetBitField (NBPtr, BFSendCmd, 1); + // Wait for TestStatus = 1 and CmdSendInProg = 0 + NBPtr->PollBitField (NBPtr, BFTestStatus, 1, PCI_ACCESS_TIMEOUT, FALSE); + //} + NBPtr->SetBitField (NBPtr, BFSendCmd, 0); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finalizes the Victim for 2D RdDqs Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - Success + */ +BOOLEAN +MemFRdWr2DFinalizeVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + UINT8 InitialCS; + UINT8 ChipSel; + InitialCS = NBPtr->TechPtr->ChipSel; + NBPtr->SetBitField (NBPtr, BFLfsrRollOver, 0); + for (ChipSel = InitialCS; ChipSel < (InitialCS + NBPtr->CsPerDelay); ChipSel++) { + // Ensure that Odd and Even CS are precharged + if ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) == 0) { + continue; + } + NBPtr->TechPtr->ChipSel = ChipSel; + // Send the Precharge All Command + MemNRrwPrechargeCmd (NBPtr, ChipSel, PRECHARGE_ALL_BANKS); + } + // Turn Off the RRW Engine + NBPtr->SetBitField (NBPtr, BFCmdTestEnable, 0); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Data Pattern that will be sent and compared + * against. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *PatternIndexPtr - Pointer to a generic index used to + * determine which pattern to program. + * + * @return BOOLEAN + * TRUE + * + */ +BOOLEAN +MemFRdWr2DProgramDataPattern ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ) +{ + UINT8 SeedCount; + UINT32 PrbsSeed; + CONST STATIC UINT32 PrbsSeedTbl[4] = {0x7ea05, 0x44443, 0x22b97, 0x3f167}; + CONST STATIC UINT32 CmdStreamLenTbl[4] = {13, 61, 127, 251}; + + ASSERT (NBPtr != 0); + ASSERT (PatternIndexPtr != NULL); + SeedCount = *(UINT8*)PatternIndexPtr; + ASSERT (SeedCount <= (NBPtr->MaxSeedCount - 1)); + // + // Program the Command Stream Length + // + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, CmdStreamLenTbl[SeedCount]); + } else { + NBPtr->SetBitField (NBPtr, BFCmdStreamLen, 63); + } + PrbsSeed = PrbsSeedTbl[SeedCount % GET_SIZE_OF (PrbsSeedTbl)]; + ASSERT (PrbsSeed != 0); + // + // Program the PRBS Seed + // + NBPtr->SetBitField (NBPtr, BFDataPrbsSeed, PrbsSeed); + return TRUE; +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c new file mode 100644 index 0000000000..6a9e873e94 --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.c @@ -0,0 +1,1368 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfRdWr2DTraining.c + * + * Common Read/Write 2D Training Feature Function + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/RdWr2DTraining) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + +#include "AGESA.h" +#include "amdlib.h" +#include "Ids.h" +#include "AdvancedApi.h" +#include "GeneralServices.h" +#include "heapManager.h" +#include "mm.h" +#include "mn.h" +#include "mu.h" +#include "mt.h" +#include "mport.h" +#include "merrhdl.h" +#include "OptionMemory.h" +#include "mfRdWr2DTraining.h" +#include "Filecode.h" + +CODE_GROUP (G1_PEICC) +RDATA_GROUP (G1_PEICC) + +#define FILECODE PROC_MEM_FEAT_RDWR2DTRAINING_MFRDWR2DTRAINING_FILECODE +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ); + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + extern MEM_PSC_FLOW_BLOCK* memPlatSpecFlowArray[]; + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function initializes the 2D Read/Write Training Feature Hooks. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return BOOLEAN + * TRUE - Function was implemented + */ + +BOOLEAN +MemFRdWr2DTrainingInit ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + ASSERT (NBPtr != NULL); + NBPtr->FamilySpecificHook[RdWr2DTraining] = MemFAmdRdWr2DTraining; + NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] = MemFCheckRdWr2DTrainingPerConfig; + NBPtr->FamilySpecificHook[RdWr2DSelectIntExtVref] = MemFRdWr2DProgramIntExtVrefSelect; + NBPtr->FamilySpecificHook[RdWr2DProgramVref] = MemFRdWr2DProgramVref; + NBPtr->FamilySpecificHook[RdWr2DScaleVref] = MemFRdWr2DScaleVref; + NBPtr->FamilySpecificHook[RdWr2DProgramDelays] = MemFRdWr2DProgramDelays; + NBPtr->FamilySpecificHook[RdWr2DDataCollection] = MemFRdWr2DEyeRimSearch; + NBPtr->FamilySpecificHook[RdWr2DInitVictim] = MemFRdWr2DInitVictim; + NBPtr->FamilySpecificHook[RdWr2DInitVictimChipSel] = MemFRdWr2DInitVictimChipSel; + NBPtr->FamilySpecificHook[RdWr2DStartVictim] = MemFRdWr2DStartVictim; + NBPtr->FamilySpecificHook[RdWr2DFinalizeVictim] = MemFRdWr2DFinalizeVictim; + NBPtr->FamilySpecificHook[RdWr2DCompareInPhase] = MemFRdWr2DCompareInPhase; + NBPtr->FamilySpecificHook[RdWr2DCompare180Phase] = MemFRdWr2DCompare180Phase; + NBPtr->FamilySpecificHook[RdWr2DProgramDataPattern] = MemFRdWr2DProgramDataPattern; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function executes 2D training for Read DQS or Write DQ + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors occurred + */ + +BOOLEAN +MemFAmdRdWr2DTraining ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID* OptParam + ) +{ + MEM_TECH_BLOCK *TechPtr; + MEM_DATA_STRUCT *MemPtr; + MEM_RD_WR_2D_ENTRY Data; + RD_WR_2D *VrefPtr; + PSO_TABLE *PsoTable; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + UINT8 Dct; + UINT8 ChipSel; + UINT8 Lane; + UINT8 Vref; + UINT8 MaxLanes; + UINT8 TmpLanes; + UINT8 TotalDlyRange; + BOOLEAN Status; + // + // Initialize Pointers + // + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + MemPtr = NBPtr->MemPtr; + PsoTable = MemPtr->ParameterListPtr->PlatformMemoryConfiguration; + // + // Set environment settings before training + // + AGESA_TESTPOINT (TpProcMem2dRdDqsTraining, &(MemPtr->StdHeader)); + MemTBeginTraining (TechPtr); + // + // Allocate heap for the 2D RdWR/Vref Data structure + // + MaxLanes = 0; + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + NBPtr->SwitchDCT (NBPtr, Dct); + TmpLanes = MemFRdWr2DGetMaxLanes (NBPtr); + MaxLanes = MAX (MaxLanes, TmpLanes); + } + + AllocHeapParams.RequestedBufferSize = MaxLanes * NBPtr->TotalMaxVrefRange * sizeof (RD_WR_2D); + AllocHeapParams.BufferHandle = AMD_MEM_2D_RD_WR_HANDLE; + AllocHeapParams.Persist = HEAP_LOCAL_CACHE; + if (HeapAllocateBuffer (&AllocHeapParams, &MemPtr->StdHeader) == AGESA_SUCCESS) { + VrefPtr = (RD_WR_2D *) AllocHeapParams.BufferPtr; + } else { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + return TRUE; + } + for (Lane = 0; Lane < MaxLanes; Lane++) { + Data.Lane[Lane].Vref = &VrefPtr[Lane * NBPtr->TotalMaxVrefRange]; + } + // + // Setup hardware training engine + // + TechPtr->TrainingType = TRN_DQS_POSITION; + NBPtr->FamilySpecificHook[SetupHwTrainingEngine] (NBPtr, &TechPtr->TrainingType); + + Data.Vnom = NBPtr->TotalMaxVrefRange / 2; // Set Nominal Vref + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + Data.MaxRdWrSweep = TotalDlyRange / 2; // Set Max Sweep Size + ASSERT (TotalDlyRange <= MAX_RD_WR_DLY_ENTRIES); + // + // Execute 2d Rd DQS training for all Dcts/Chipselects + // + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + IDS_HDT_CONSOLE (MEM_STATUS, "\tDct %d\n", Dct); + NBPtr->SwitchDCT (NBPtr, Dct); + NBPtr->Vref = 0xFF; + Status = FALSE; + if (NBPtr->FamilySpecificHook[CheckRdWr2DTrainingPerConfig] (NBPtr, NULL)) { + for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) { + if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + // + //Initialize storage + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (Vref = 0; Vref < NBPtr->TotalMaxVrefRange; Vref++) { + Data.Lane[Lane].Vref[Vref].PosRdWrDly = 0; + Data.Lane[Lane].Vref[Vref].NegRdWrDly = 0; + } + } + TechPtr->ChipSel = ChipSel; + IDS_HDT_CONSOLE (MEM_FLOW,"\tChip Select: %02x \n", TechPtr->ChipSel); + // + // 1. Sample the data eyes for each channel: + // + TechPtr->RdWr2DData = &Data; + if (NBPtr->FamilySpecificHook[RdWr2DDataCollection] (NBPtr, NULL)) { + // + // 2. Process the array of results with a diamond convolution mask, summing the number passing sample points. + // + // Determine Diamond Mask Height + if (MemFRdWr2DHeight (NBPtr, &Data)) { + // + // Apply Mask + // + if (MemFRdWr2DApplyMask (NBPtr, &Data)) { + // + // Convolution + // + if (MemFRdWr2DProcessConvolution (NBPtr, &Data)) { + // + // 3. Program the final DQS delay values. + // + if (MemFRdWr2DProgramMaxDelays (NBPtr, &Data)) { + // + // Find the Smallest Positive or Negative Margin for current CS + // + if (MemFRdWr2DFindCsVrefMargin (NBPtr, &Data)) { + Status = TRUE; + } + } + } + } + } + } + if (Status == FALSE) { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_ERROR : MEM_ERROR_2D_WRDAT_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } + } + // + // Find the Max and Min Vref values for each DCT + // + if (Status == TRUE) { + if (MemFRdWr2DFinalVrefMargin (NBPtr, &Data)) { + // + // Program the Max Vref value + // + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Final Vref for channel\n"); + NBPtr->FamilySpecificHook[RdWr2DProgramVref] (NBPtr, &NBPtr->ChannelPtr->MaxVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tActual Vref programmed = %02x\n", + ( NBPtr->GetBitField (NBPtr, BFVrefDAC) >> TSEFO_END (NBPtr->NBRegTable[BFVrefDAC])) ); + Status = TRUE; + } else { + SetMemError (AGESA_ERROR, NBPtr->MCTPtr); + PutEventLog (AGESA_ERROR, (TechPtr->Direction == DQS_READ_DIR) ? MEM_ERROR_2D_DQS_VREF_MARGIN_ERROR: MEM_ERROR_2D_WRDAT_VREF_MARGIN_ERROR, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &NBPtr->MemPtr->StdHeader); + } + } + } + } + // + // Restore environment settings after training + // + if (HeapDeallocateBuffer (AMD_MEM_2D_RD_WR_HANDLE, &MemPtr->StdHeader) != AGESA_SUCCESS) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_DEALLOCATE_FOR_2D, 0, 0, 0, 0, &MemPtr->StdHeader); + } + IDS_HDT_CONSOLE (MEM_STATUS, "\tEnd\n"); + MemTEndTraining (TechPtr); + + IDS_HDT_CONSOLE (MEM_FLOW, "\n\nEnd %s 2D training\n\n",(TechPtr->Direction == DQS_READ_DIR) ? "Read DQS":"Write DQ"); + return (BOOLEAN) (NBPtr->MCTPtr->ErrCode < AGESA_FATAL); +} +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines whether the current configuration is a valid + * config for applying 2D Training + * @todo: Update to work for 2D WR and 2D RD training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - Configuration valid + * FALSE - Configuration invalid + * + */ +BOOLEAN +MemFCheckRdWr2DTrainingPerConfig ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + UINT8 i; + if (NBPtr->RefPtr->ForceTrainMode == FORCE_TRAIN_AUTO) { + i = 0; + while (memPlatSpecFlowArray[i] != NULL) { + if ((memPlatSpecFlowArray[i])->S2D (NBPtr, (memPlatSpecFlowArray[i])->EntryOfTables)) { + return TRUE; + } + i++; + } + } + return FALSE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the maximum number of lanes for puposes of 2D + * Read or Write training. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * + * @return UINT8 - Max Number of Lanes + * + */ +UINT8 +MemFRdWr2DGetMaxLanes ( + IN OUT MEM_NB_BLOCK *NBPtr + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 MaxLanes; + + TechPtr = NBPtr->TechPtr; + if ((TechPtr->Direction == DQS_READ_DIR) && ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) != 0)) { + // Per Nibble + MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 18 : 16; + } else { + // Per Byte + MaxLanes = (NBPtr->MCTPtr->Status[SbEccDimms] && (NBPtr->IsSupported[EccByteTraining] == TRUE)) ? 9 : 8; + } + return MaxLanes; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref to internal or external control for 2D Read + * or Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *OptParam - Unused + * + * @return BOOLEAN + * TRUE - External Vref was selected + * FALSE - Internal Vref was selected + * + */ +BOOLEAN +MemFRdWr2DProgramIntExtVrefSelect ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + NBPtr->SetBitField (NBPtr, BFVrefSel, (NBPtr->RefPtr->ExternalVrefCtl ? 0x0000 : 0x0001)); + } + return NBPtr->RefPtr->ExternalVrefCtl; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function scales Vref from the range used in Data Collection to + * the range that is programmed into the register. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *Vref - Pointer to UINT8 Vref Value to scale. + * + * @return BOOLEAN + * TRUE Function was implemented + * + */ +BOOLEAN +STATIC +MemFRdWr2DScaleVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *Vref + ) +{ + *(UINT8*)Vref = *(UINT8*)Vref * 2; + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Vref for 2D Read/Write Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *VrefPtr - Pointer to Vref value + * + * @return BOOLEAN + * TRUE - Success + * FAIL (External Callout only) + * + */ +BOOLEAN +MemFRdWr2DProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ) +{ + AGESA_STATUS Status; + MEM_DATA_STRUCT *MemPtr; + ID_INFO CallOutIdInfo; + VOLTAGE_ADJUST Va; + UINT8 Vref; + + ASSERT (NBPtr != NULL); + ASSERT (VrefPtr != NULL); + MemPtr = NBPtr->MemPtr; + Vref = *(UINT8*)VrefPtr; + CallOutIdInfo.IdField.SocketId = NBPtr->MCTPtr->SocketId; + CallOutIdInfo.IdField.ModuleId = NBPtr->MCTPtr->DieId; + LibAmdMemCopy ((VOID *)&Va, (VOID *)MemPtr, (UINTN)sizeof (Va.StdHeader), &MemPtr->StdHeader); + Va.MemData = MemPtr; + Status = AGESA_SUCCESS; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + if (NBPtr->RefPtr->ExternalVrefCtl == FALSE) { + // + // Internal vref control + // + // This is 1/2 VrefDAC value Sign bit is shifted into place. + // + ASSERT (Vref < 32); + if (Vref < 15) { + Vref = (31 - Vref) << 1; + } else { + Vref = (Vref - 15) << 1; + } + NBPtr->SetBitField (NBPtr, BFVrefDAC, Vref << 2); + } else { + // External vref control + AGESA_TESTPOINT (TpProcMemBefore2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + NBPtr->MemPtr->ParameterListPtr->ExternalVrefValue = Vref; + IDS_HDT_CONSOLE (MEM_FLOW, "\n2D Read Training External CPU Vref Callout \n"); + Va.VoltageType = VTYPE_CPU_VREF; + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + AGESA_TESTPOINT (TpProcMemAfter2dTrainExtVrefChange, &(NBPtr->MemPtr->StdHeader)); + } + } else { + // + // DIMM Vref Control + // + Va.VoltageType = VTYPE_DIMM_VREF; + // + // Offset by 15 and multiply by 2. + // + Va.AdjustValue = Vref = (Vref - 15) << 1; + Status = AgesaExternalVoltageAdjust ((UINTN)CallOutIdInfo.IdInformation, &Va); + if (Status != AGESA_SUCCESS) { + IDS_HDT_CONSOLE (MEM_FLOW, "* Dimm Vref Callout Failed *"); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + return (Status == AGESA_SUCCESS) ? TRUE : FALSE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs Read DQS or Write DQ Delay values for Read/Write + * Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Delay - Pointer to UINT8 containing Delay value + * + * @return BOOLEAN + * TRUE + * + */ + +BOOLEAN +MemFRdWr2DProgramDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *Delay + ) +{ + UINT32 RdDqsTime; + UINT8 RdWrDly; + + ASSERT (NBPtr != 0); + ASSERT (Delay != 0); + RdWrDly = *(UINT8*) Delay; + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + // This function should only be used for read training + ASSERT (NBPtr->TechPtr->Direction == DQS_READ_DIR); + // Program BL registers for both nibble (x4) and bytes (x8, x16) + RdDqsTime = 0; + RdDqsTime = (RdWrDly & 0x1F) << 8; + RdDqsTime = RdDqsTime | (RdWrDly & 0x1F); + if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 0) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm0Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 1) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm1Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 2) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm2Broadcast, RdDqsTime); + } else if ((NBPtr->TechPtr->ChipSel / NBPtr->CsPerDelay) == 3) { + NBPtr->SetBitField (NBPtr, BFDataByteRxDqsDLLDimm3Broadcast, RdDqsTime); + } + } else { + MemTSetDQSDelayAllCSR (NBPtr->TechPtr, RdWrDly); + } + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function stores data for 2D Read DQS and Write DQ Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] *InPhaseResult[] - Array of inphase results + * @param[in] *PhaseResult180[] - Array of Phase 180 results + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +VOID +MemFRdWr2DStoreResult ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT32 InPhaseResult[], + IN UINT32 PhaseResult180[] + ) +{ + UINT8 Lane; + UINT8 Vref; + UINT8 RdWrDly; + UINT32 Result; + UINT32 Result180; + UINT8 Index; + Vref = NBPtr->Vref; + RdWrDly = Data->RdWrDly; + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (RdWrDly = 0; RdWrDly < Data->MaxRdWrSweep; RdWrDly++) { + if ((NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (NBPtr->TechPtr->ChipSel >> 1))) == 0) { + // x8, so combine "Nibble X" and "Nibble X+1" results + Index = Lane * 2; + Result = (InPhaseResult[RdWrDly] >> Index) & 0x03; + Result180 = (PhaseResult180[RdWrDly] >> Index) & 0x03; + } else { + // x4, so use "Nibble" results + Result = (InPhaseResult[RdWrDly] >> Lane) & 0x01; + Result180 = (PhaseResult180[RdWrDly] >> Lane) & 0x01; + } + Data->Lane[Lane].Vref[Vref].PosRdWrDly |= (Result == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0; + Data->Lane[Lane].Vref[Vref].NegRdWrDly |= (Result180 == 0) ? (1 << (Data->MaxRdWrSweep - 1 - RdWrDly)) : 0; + } + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function determines the height of data for 2D Read and Write training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + */ +BOOLEAN +MemFRdWr2DHeight ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 Lane; + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + Data->Lane[Lane].HalfDiamondHeight = 0x0F; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Lane: "); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tHeight: "); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (2*(Data->Lane[Lane].HalfDiamondHeight) + 1)); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the width for 2D RdDQS and WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return UINT8 Width + */ +UINT8 +MemFGetRdWr2DWidth ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + return NBPtr->DiamondWidthRd; + } else { + return NBPtr->DiamondWidthWr; + } +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function gets the step height for the diamond mask for 2D RdDQS or + * WrDat Training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * @param[in] Vref - current Vref value + * @param[in] Lane - current Lane + * + * @return BOOLEAN + * TRUE - Step found and value should be updated + * FALSE - Step not found and value should not be updated + * + */ +BOOLEAN +MemFCheckRdWr2DDiamondMaskStep ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT8 Vref, + IN UINT8 Lane + ) +{ + UINT8 M; + UINT8 VrefVal; + UINT8 width; + UINT8 i; + BOOLEAN status; + // m = -1 * height/width + // (y-b)/m = x + status = FALSE; + if (Vref > (Data->Vnom - 1)) { + VrefVal = (Vref + 1) - Data->Vnom; + } else { + VrefVal = Vref; + } + width = (MemFGetRdWr2DWidth (NBPtr, Data) - 1) / 2; + M = Data->Lane[Lane].HalfDiamondHeight / width; + i = 1; + while (i <= Data->Lane[Lane].HalfDiamondHeight) { + i = i + M; + if (VrefVal == i) { + status = TRUE; + } + } + return status; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function applies a mask for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + * + */ +BOOLEAN +MemFRdWr2DApplyMask ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 RdWrDly; + UINT8 Lane; + UINT8 Height; + UINT8 Width; + UINT32 PosNegData; + UINT8 Vref; + UINT8 count; + UINT8 Dly; + UINT8 endWidth; + UINT8 startWidth; + UINT8 origEndWidth; + UINT8 origStartWidth; + UINT8 maxOverLapWidth; + UINT8 startOverLapWidth; + UINT8 TotalDlyRange; + BOOLEAN maxHeightExceeded; + BOOLEAN negVrefComplete; + BOOLEAN PosRdWrToNegRdWr; + BOOLEAN NegRdWrToPosRdWr; + + TechPtr = NBPtr->TechPtr; + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + // + // Initialize Convolution + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + Data->Lane[Lane].Convolution[RdWrDly] = 0; + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly); + } + } + endWidth = 0; + startWidth = 0; + origEndWidth = 0; + origStartWidth = 0; + startOverLapWidth = 0; + maxOverLapWidth = 0; + maxHeightExceeded = FALSE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Width"); + // + // Get the Width of Diamond + // + Width = MemFGetRdWr2DWidth (NBPtr, Data); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\tWidth: %02x\n", Width); + ASSERT (Width != 0); + + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tExecuting convolution function\n"); + // + // Perform the convolution by sweeping the mask function centered at nominal Vref. Results in a one + // dimensional array with FOM values at each delay for each lane. Choose the delay setting at the peak + // FOM value. + // + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + Height = Data->Lane[Lane].HalfDiamondHeight; + ASSERT (Height < Data->Vnom); + // + // RdWrDly is divided around "Data->MaxRdWrSweep" into positive and negative directions + // Positive direction -> RdWrDly = 0 to (Data->MaxRdWrSweep - 1) + // Negative direction -> RdWrDly = Data->MaxRdWrSweep to (TotalDlyRange - 1) + // + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + // Vref loop is divided around "Data->Vnom - 1" into positive and negative directions + // Negative direction -> Vref = 0 ("Data->Vnom - 1") to Height("Data->Vnom - 1" - Height) + // Positive direction -> Vref = "Data->Vnom" to Height("Data->Vnom" + Height) + // + negVrefComplete = FALSE; + PosRdWrToNegRdWr = FALSE; + NegRdWrToPosRdWr = FALSE; + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Initial negative direction where Vref = 0 ("Data->Vnom - 1"), so we need to set + // initial startWidth and endWidth for +/- RdDqs + // + // Create common delay based on +/- RdDqs + if (RdWrDly > (Data->MaxRdWrSweep - 1)) { + Dly = RdWrDly - Data->MaxRdWrSweep; + } else { + Dly = RdWrDly; + } + if (Vref == 0 ) { + // Initialize -Vref + maxHeightExceeded = FALSE; // reset for start of -Vref + // Case 1: if +RdDqs - Check for lower bound (Width/2 > RdDqs > 0) + // : if -RdDqs - Check for lower bound (Width/2 + Data->MaxRdDqsSweep > RdDqs > Data->MaxRdDqsSweep) + if (Dly < Width / 2) { + endWidth = Dly + Width / 2 + 1; + startWidth = 0; + } else if ((Dly + Width / 2) > (Data->MaxRdWrSweep - 1)) { + // Case 2: if +RdWr - Check for upper bound ((Data->MaxRdWrSweep - 1) < RdWr < ((Data->MaxRdWrSweep - 1) - Width/2)) + // : if -RdWr - Check for lower bound ((DatNBPtr->TotalRdWrDlyRange - 1) < RdWr < ((NBPtr->TotalRdWrDlyRange - 1) - Width/2)) + endWidth = Data->MaxRdWrSweep; + startWidth = Dly - Width / 2; + } else { + // Set the initial "startWidth" and "endWidth" for +/- RdDqs + endWidth = Dly + Width / 2 + 1; + startWidth = Dly - Width / 2; + } + origEndWidth = endWidth; + origStartWidth = startWidth; + } else if (Vref == Data->Vnom) { + // Initialize +Vref + endWidth = origEndWidth; + startWidth = origStartWidth; + maxHeightExceeded = FALSE; // reset for start of +Vref + negVrefComplete = TRUE; + } else if ((Vref > (Data->Vnom + Height)) && negVrefComplete == TRUE) { + break; //switch to next RdDqs Dly if height exceeded for +vref and -vref complete + } else { + if (startWidth >= endWidth) { + if (RdWrDly == (TotalDlyRange - 1)) { + // Special condition for end of -RdDqs range + startWidth = Data->MaxRdWrSweep - 1; + endWidth = Data->MaxRdWrSweep; + } else { + // Width = 0, but Height not reached, + startWidth = Dly; + endWidth = Dly + 1; + } + } else { + // Check for Case 1 and Case 2 above + if ((RdWrDly + Width / 2) > (TotalDlyRange - 1)) { + endWidth = origEndWidth; + } + } + maxHeightExceeded = FALSE; + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + if (Lane == 0) { + if (RdWrDly == (Data->MaxRdWrSweep - (Width / 2)) ) { + Data->DiamondLeft[Vref] = startWidth; + Data->DiamondRight[Vref] = endWidth - 1; + } + } + ); + // + // Determine the correct Delay (+/-) and Vref (+/-)direction + // + if (maxHeightExceeded == FALSE) { + if (RdWrDly < Data->MaxRdWrSweep) { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; // +RdWr Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly; // +RdDqs Dly, -Vref + } + } else { + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly; // -RdWr Dly, +Vref + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly; // -RdWr Dly, -Vref + } + } + // + // Case 1: Non-overlap condition: + // Count the number of passes from "startWidth" to "endWidth" + // + for (count = startWidth; count < endWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + // Case 2: Overlay between +RdWr and -RdWr starting from +RdWr + // Count the number of passes from "startWidth" to "endWidth" + // + if ((RdWrDly <= (Data->MaxRdWrSweep - 1) && (RdWrDly > ((Data->MaxRdWrSweep - 1) - Width / 2)))) { + startOverLapWidth = 0; + if (Vref == 0 || Vref == Data->Vnom) { + maxOverLapWidth = (RdWrDly + Width / 2) - (Data->MaxRdWrSweep - 1); // Initial overlap max width size + } else if (maxOverLapWidth == 0) { + maxOverLapWidth = startOverLapWidth; // Stop counting after overlap region complete + } + // Ensure that +/- vref is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].NegRdWrDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].NegRdWrDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + if (maxOverLapWidth > 0) { + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + maxOverLapWidth--; // Reduce overlap width outside of diamond mask + } + PosRdWrToNegRdWr = TRUE; + } + } + if (((RdWrDly - Data->MaxRdWrSweep) < Width / 2) && (RdWrDly > (Data->MaxRdWrSweep - 1))) { + // + // Case 3: Overlay between -RdDqs and +RdDqs starting from -RdDqs + // Count the number of passes from "startWidth" to "endWidth" + // + maxOverLapWidth = Data->MaxRdWrSweep; + if (Vref == 0 || Vref == Data->Vnom) { + startOverLapWidth = RdWrDly - Width / 2; // Initial overlap start point + } else if (startOverLapWidth > maxOverLapWidth) { + maxOverLapWidth = maxOverLapWidth - 1; // Continue to count until MaxHeight excceded + } + // Ensure that vref + or - is set correctly + if (Vref > (Data->Vnom - 1)) { + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + } else { + PosNegData = Data->Lane[Lane].Vref[(Data->Vnom - 1) - Vref].PosRdWrDly; + } + // Need to count the number of passes when range extends from Pos RdDqs to Neg RdDqs + for (count = startOverLapWidth; count < maxOverLapWidth; count++) { + Data->Lane[Lane].Convolution[RdWrDly] = (UINT8) ((PosNegData >> count) & 0x1) + Data->Lane[Lane].Convolution[RdWrDly]; + } + if (startOverLapWidth < maxOverLapWidth) { + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + startOverLapWidth++; // Reduce overlap width outside of diamond mask + } + NegRdWrToPosRdWr = TRUE; + } + } + } + if (MemFCheckRdWr2DDiamondMaskStep (NBPtr, Data, Vref, Lane) || (Vref == 1) || (Vref == Data->Vnom)) { + if (PosRdWrToNegRdWr) { + startWidth++; + endWidth = Data->MaxRdWrSweep; + PosRdWrToNegRdWr = FALSE; + } else if (NegRdWrToPosRdWr) { + startWidth = 0; + endWidth--; + NegRdWrToPosRdWr = FALSE; + } else { + startWidth++; + endWidth--; + } + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + NBPtr->FamilySpecificHook[Adjust2DDelayStepSize] (NBPtr, &RdWrDly); + } + } + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Diamond Shape: \n"); + for (Vref = 0; Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (RdWrDly = (Data->MaxRdWrSweep - Width); RdWrDly < Data->MaxRdWrSweep; RdWrDly++) { + if (Vref < (Data->Vnom - 1)) { + if (RdWrDly == Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdWrDly == Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[(NBPtr->TotalMaxVrefRange - 2) - Vref]) - (Data->DiamondLeft[(NBPtr->TotalMaxVrefRange - 2) - Vref])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } else { + if (RdWrDly == Data->DiamondLeft[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | "); + } else if (RdWrDly == Data->DiamondRight[Vref - (Data->Vnom - 1)]) { + IDS_HDT_CONSOLE (MEM_FLOW, " | -> Width = %02x", (Data->DiamondRight[Vref - (Data->Vnom - 1)]) - (Data->DiamondLeft[Vref - (Data->Vnom - 1)])); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + } + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t Convolution results after processing raw data:\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t Delay: "); + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, " %02x ", RdWrDly <= (Data->MaxRdWrSweep - 1) ? (Data->MaxRdWrSweep - 1) - RdWrDly : (TotalDlyRange - 1) - RdWrDly); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tConv: "); + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%03x ", Data->Lane[Lane].Convolution[RdWrDly]); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + } + ); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function Examines the convolution function and determines the Max Delay + * for 2D RdDQS and WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ + +BOOLEAN +MemFRdWr2DProcessConvolution ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 RdWrDly; + UINT8 Lane; + UINT16 MaxFOM; + UINT8 MaxRange; + UINT8 CurrRange; + UINT8 TotalDlyRange; + BOOLEAN status; + + ASSERT (NBPtr != NULL); + TechPtr = NBPtr->TechPtr; + TotalDlyRange = (TechPtr->Direction == DQS_READ_DIR) ? NBPtr->TotalRdDQSDlyRange : NBPtr->TotalWrDatDlyRange; + status = TRUE; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tDetermining Delay based on Convolution function\n"); + // Determine the Max RdDqs or WrDat Dly for the convolution function + // - Choose the delay setting at the peak FOM value. + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + // Find largest value as MaxFOM + MaxFOM = 0; + for (RdWrDly = 0; RdWrDly < TotalDlyRange; RdWrDly++) { + if (Data->Lane[Lane].Convolution[RdWrDly] > MaxFOM) { + MaxFOM = Data->Lane[Lane].Convolution[RdWrDly]; + } + } + status = MaxFOM > 0 ? TRUE : FALSE; // It is an error if all convolution points are zero + + // Then find the midpoint of the largest consecutive window w/ that MaxFOM + // In cases of an even number of consecutive points w/ that MaxFOM exists, + // choose the midpoint to the right + // All things being equal, favor the right side of a bi-modal eye + // Stressful SSO patterns shift the eye right! + MaxRange = 0; + CurrRange = 0; + for (RdWrDly = 0; (MaxFOM > 0) && RdWrDly < TotalDlyRange; RdWrDly++) { + if (Data->Lane[Lane].Convolution[RdWrDly] == MaxFOM) { + CurrRange++; + if (CurrRange >= MaxRange) { + Data->Lane[Lane].MaxRdWrDly = RdWrDly - ((CurrRange - 1) / 2); + MaxRange = CurrRange; + } + } else { + CurrRange = 0; + } + } + + if (Data->Lane[Lane].MaxRdWrDly > Data->MaxRdWrSweep) { + status = FALSE; // Error + } + // Set Actual register value + if (Data->Lane[Lane].MaxRdWrDly < Data->MaxRdWrSweep) { + Data->Lane[Lane].MaxRdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly; + } else { + status = FALSE; // Error + } + } + + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t Cs %d Lane: ", TechPtr->ChipSel); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Lane); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Max %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Data->Lane[Lane].MaxRdWrDly); + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\t\t1D Trained %s Delay: ", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + if (TechPtr->Direction == DQS_READ_DIR) { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane]); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", (NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] - \ + NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane])); + } + } + IDS_HDT_CONSOLE (MEM_FLOW, "\n"); + ); + + if (status == FALSE) { + SetMemError (AGESA_FATAL, NBPtr->MCTPtr); + PutEventLog (AGESA_FATAL, MEM_ERROR_INVALID_2D_RDDQS_VALUE, 0, 0, 0, 0, &TechPtr->NBPtr->MemPtr->StdHeader); + } + return status; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function programs the Max Rd Dqs or Max Wr DQ for 2D training from + * convolution + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DProgramMaxDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + MEM_TECH_BLOCK *TechPtr; + UINT8 Lane; + UINT8 LaneHighRdDqs2dDlys; + UINT8 LaneLowRdDqs2dDlys; + UINT8 MaxWrDatDly; + TechPtr = NBPtr->TechPtr; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tProgramming Max %s Delay per Lane\n\n", TechPtr->Direction == DQS_READ_DIR ? "Rd Dqs" : "Wr DQ"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + if ( TechPtr->Direction == DQS_WRITE_DIR || (NBPtr->ChannelPtr->DimmNibbleAccess & (1 << (TechPtr->ChipSel >> 1))) == 0) { + // Program Byte based for x8 and x16 + if ( TechPtr->Direction == DQS_READ_DIR) { + // + // Read DQS Training + // + NBPtr->SetTrainDly (NBPtr, AccessRdDqsDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly); + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = Data->Lane[Lane].MaxRdWrDly; + } else { + // + // Write DQ Training + // + MaxWrDatDly = (UINT8) (Data->Lane[Lane].MaxRdWrDly + TechPtr->NBPtr->ChannelPtr->WrDqsDlys[(TechPtr->ChipSel / TechPtr->NBPtr->CsPerDelay) * MAX_DELAYS + Lane]); + NBPtr->SetTrainDly (NBPtr, AccessWrDatDly, DIMM_BYTE_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), MaxWrDatDly); + NBPtr->ChannelPtr->WrDatDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + Lane] = MaxWrDatDly; + } + } else { + ASSERT (TechPtr->Direction == DQS_READ_DIR); + // Program nibble based x4, so use "Nibble" + NBPtr->SetTrainDly (NBPtr, AccessRdDqs2dDly, DIMM_NBBL_ACCESS ((TechPtr->ChipSel / NBPtr->CsPerDelay), Lane), (UINT16)Data->Lane[Lane].MaxRdWrDly); + NBPtr->ChannelPtr->RdDqs2dDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_NUMBER_LANES + Lane] = Data->Lane[Lane].MaxRdWrDly; + // For each pair of nibbles (high (Odd Nibble) and Low (Even nibble)), find the largest and use that as the RdDqsDly value + if ((Lane & 0x1) == 0) { + LaneHighRdDqs2dDlys = Data->Lane[Lane + 1].MaxRdWrDly; + LaneLowRdDqs2dDlys = Data->Lane[Lane].MaxRdWrDly; + if (LaneHighRdDqs2dDlys > LaneLowRdDqs2dDlys) { + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneHighRdDqs2dDlys; + } else { + NBPtr->ChannelPtr->RdDqsDlys[(TechPtr->ChipSel / NBPtr->CsPerDelay) * MAX_DELAYS + (Lane >> 1)] = LaneLowRdDqs2dDlys; + } + } + NBPtr->DctCachePtr->Is2Dx4 = TRUE; + } + } + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the Positive and negative Vref Margin for the current CS + * for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DFindCsVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 SmallestMaxVrefNeg; + UINT8 Lane; + UINT8 RdWrDly; + UINT8 Vref; + UINT8 MaxVrefPositive; + UINT8 MaxVrefNegative; + UINT8 SmallestMaxVrefPos; + UINT32 PosNegData; + SmallestMaxVrefPos = 0xFF; + SmallestMaxVrefNeg = 0; + MaxVrefPositive = 0; + MaxVrefNegative = 0xFF; + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Smallest Max Positive and Negative Vref\n\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + RdWrDly = (Data->MaxRdWrSweep - 1) - Data->Lane[Lane].MaxRdWrDly; + for (Vref = 0; Vref < (Data->Vnom - 1); Vref++) { + // Neg Vref - (searching from top of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 1) { + MaxVrefNegative = Vref; + break; + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + for (Vref = (Data->Vnom - 1); Vref < (NBPtr->TotalMaxVrefRange - 1); Vref++) { + // Pos Vref - (searching from Vnom + 1 of array down) + PosNegData = Data->Lane[Lane].Vref[Vref].PosRdWrDly; + if ((UINT8) ((PosNegData >> RdWrDly) & 0x1) == 0) { + // Convert to register setting + MaxVrefPositive = Vref - 1;// - Data->Vnom; + break; + } else { + // If Vref = 1F passes, then smallest Vref = 0x1F + if (Vref == ((NBPtr->TotalMaxVrefRange - 1) - 1)) { + MaxVrefPositive = 0x1E; + break; + } + } + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + if (MaxVrefPositive < SmallestMaxVrefPos) { + // Find the smallest Max Pos Vref + SmallestMaxVrefPos = MaxVrefPositive; + } + if (MaxVrefNegative > SmallestMaxVrefNeg) { + // Find the largest Max Neg Vref + SmallestMaxVrefNeg = MaxVrefNegative; + } + } + if (SmallestMaxVrefPos != (Data->Vnom - 2)) { + Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = SmallestMaxVrefPos - Data->Vnom + 1; + } else { + Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] = 0; + } + Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel] = (Data->Vnom - 1) - SmallestMaxVrefNeg; + IDS_HDT_CONSOLE_DEBUG_CODE ( + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Positive Vref Offset from V-Nom for ChipSel %02x = + %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel]); + if (Data->SmallestPosMaxVrefperCS[NBPtr->TechPtr->ChipSel] == 0) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = 00\n"); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tSmallest Max Negative Vref Offset from V-Nom for ChipSel %02x = - %02x\n", NBPtr->TechPtr->ChipSel, Data->SmallestNegMaxVrefperCS[NBPtr->TechPtr->ChipSel]); + } + ); + return TRUE; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * This function finds the final Vref Margin for 2D RdDQS or WrDat training + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + * @return BOOLEAN + * TRUE - No Errors occurred + * FALSE - Errors ccurred + */ +BOOLEAN +MemFRdWr2DFinalVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + UINT8 ChipSel; + UINT8 SmallestMaxPosVref; + UINT8 SmallestMaxNegVref; + UINT8 OffsetFromVref; + UINT8 Vnom; + SmallestMaxNegVref = 0x7F; + SmallestMaxPosVref = 0x7F; + Vnom = (Data->Vnom - 1); + IDS_HDT_CONSOLE (MEM_FLOW, "\n\t\tFinding Final Vref for channel\n\n"); + for (ChipSel = 0; ChipSel < NBPtr->CsPerChannel; ChipSel = ChipSel + NBPtr->CsPerDelay ) { + if ( (NBPtr->MCTPtr->Status[SbLrdimms]) ? ((NBPtr->ChannelPtr->LrDimmPresent & ((UINT8) 1 << (ChipSel >> 1))) != 0) : + ((NBPtr->DCTPtr->Timings.CsEnabled & ((UINT16) 1 << ChipSel)) != 0) ) { + if (Data->SmallestPosMaxVrefperCS[ChipSel] < SmallestMaxPosVref) { + SmallestMaxPosVref = Data->SmallestPosMaxVrefperCS[ChipSel]; + } + if (Data->SmallestNegMaxVrefperCS[ChipSel] < SmallestMaxNegVref) { + SmallestMaxNegVref = Data->SmallestNegMaxVrefperCS[ChipSel]; + } + } + } + // + // Synchronize minimum and Maximums with other Vref values + // + if (NBPtr->TechPtr->Direction == DQS_WRITE_DIR) { + if (NBPtr->SharedPtr->CommonSmallestMaxNegVref < SmallestMaxNegVref) { + SmallestMaxNegVref = NBPtr->SharedPtr->CommonSmallestMaxNegVref; + } else { + NBPtr->SharedPtr->CommonSmallestMaxNegVref = SmallestMaxNegVref; + } + if (NBPtr->SharedPtr->CommonSmallestMaxPosVref < SmallestMaxPosVref) { + SmallestMaxPosVref = NBPtr->SharedPtr->CommonSmallestMaxPosVref; + } else { + NBPtr->SharedPtr->CommonSmallestMaxPosVref = SmallestMaxPosVref; + } + } + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxPosVref); + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &SmallestMaxNegVref); + NBPtr->FamilySpecificHook[RdWr2DScaleVref] (NBPtr, &Vnom); + + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Positive = + %02x\n", SmallestMaxPosVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Smallest Max Negative =%s%02x\n", ((SmallestMaxNegVref != 0) ? " - " : " "), SmallestMaxNegVref); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tScaled Vnom = %02x\n", Vnom); + + if (SmallestMaxPosVref > SmallestMaxNegVref) { + OffsetFromVref = (SmallestMaxPosVref - SmallestMaxNegVref) / 2; + NBPtr->ChannelPtr->MaxVref = Vnom + OffsetFromVref; + } else { + OffsetFromVref = (SmallestMaxNegVref - SmallestMaxPosVref) / 2; + NBPtr->ChannelPtr->MaxVref = Vnom - OffsetFromVref; + } + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tFinal Vref Offset From Vnom =%s%02x\n", + ((OffsetFromVref != 0) ? ((SmallestMaxPosVref > SmallestMaxNegVref) ? " + ":" - "):" "), OffsetFromVref); + return TRUE; +} +/* -----------------------------------------------------------------------------*/ +/** + * + * This function displays ther results of the 2D search + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] *Data - Pointer to Result data structure + * + */ +VOID +MemFRdWr2DDisplaySearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ) +{ + IDS_HDT_CONSOLE_DEBUG_CODE ( + UINT8 Lane; + INT8 Vref; + // Display data collected + IDS_HDT_CONSOLE (MEM_FLOW, "\t\tDisplaying Data collected\n\n"); + for (Lane = 0; Lane < MemFRdWr2DGetMaxLanes (NBPtr); Lane++) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\tLane: %02x\n", Lane); + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t Vref %s\n", (NBPtr->TechPtr->Direction == DQS_READ_DIR) ? "NegRdDqs PosRdDqs" : "PosWrDat"); + for (Vref = NBPtr->TotalMaxVrefRange - 2; Vref >= 0; Vref--) { + if (Vref < (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t - "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", ((Data->Vnom -1) - Vref)); + } else if (Vref == (Data->Vnom - 1)) { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t 00 "); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, "\t\t\t + "); + IDS_HDT_CONSOLE (MEM_FLOW, "%02x ", Vref - (Data->Vnom - 1)); + } + if (NBPtr->TechPtr->Direction == DQS_READ_DIR) { + IDS_HDT_CONSOLE (MEM_FLOW, "%08x", Data->Lane[Lane].Vref[Vref].NegRdWrDly); + } else { + IDS_HDT_CONSOLE (MEM_FLOW, " "); + } + IDS_HDT_CONSOLE (MEM_FLOW, "%08x \n", Data->Lane[Lane].Vref[Vref].PosRdWrDly); + NBPtr->FamilySpecificHook[Adjust2DVrefStepSize] (NBPtr, &Vref); + } + } + ) +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h new file mode 100644 index 0000000000..a2a4b02dda --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/RDWR2DTRAINING/mfRdWr2DTraining.h @@ -0,0 +1,266 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mf2DRdWrTraining.h + * + * Common Definitions ot support 2D Read/Write Training + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/2DRdWrTraining) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + +#ifndef _MFRDWR2DTRAINING_H_ +#define _MFRDWR2DTRAINING_H_ + +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ +#define MAX_2D_VREF_ENTRIES 0x20 ///< Maximum number of vref entries +#define MAX_RD_WR_DLY_ENTRIES 0x40 ///< Maximum number of RD/WR Delay Entries +#define VREF_ADDITIONAL_STEP_SIZE 0x0 ///< Vref Additional Step size +#define RDDQS_ADDITIONAL_STEP_SIZE 0x0 ///< RdDqs Additional Step size +#define MAX_2D_RD_SEED_COUNT 4 ///< Max Seed count +#define MAX_2D_RD_WR_CS_PER_CHANNEL 8 ///< Max CS per Rd Wr delay group + +#define EYERIM_PARALLEL_SAMPLING TRUE +#define EYERIM_BROADCAST_DELAYS TRUE + +/// Structure for RD DQS 2D training RDDQS delays. +typedef struct { + UINT32 PosRdWrDly; ///< Positive RdDQS Delay + UINT32 NegRdWrDly; ///< Negative RdDQS Delay +} RD_WR_2D; + +/// Structure for RD DQS 2D training Vref delays +typedef struct { + RD_WR_2D *Vref; ///< Pointer to Vref Entries + UINT16 Convolution[MAX_RD_WR_DLY_ENTRIES]; ///< Total number of passes in each mask + UINT8 PosHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Positive Vref height Height per Delay + UINT8 NegHeight[MAX_RD_WR_DLY_ENTRIES / 2]; ///< Negative Vref height Height per Delay + UINT8 HalfDiamondHeight; ///< Half of the Height per BL (height of pos/neg vref) + UINT8 MaxRdWrDly; ///< Max RdWrDly from convolution function +} VREF_RD_WR_2D; + +/// Structure for RD DQS 2D training Nibbles +typedef struct { + VREF_RD_WR_2D Lane[MAX_NUMBER_LANES]; ///< Bytelane or Nibble + UINT8 Vnom; ///< Nominal Vref value + UINT8 MaxRdWrSweep; ///< Maximum RdDqs or WrDat Sweep size + UINT8 SmallestPosMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Positive Max Vref per CS + UINT8 SmallestNegMaxVrefperCS[MAX_2D_RD_WR_CS_PER_CHANNEL]; ///< Smallest Negative Max Vref per CS + UINT8 DiamondLeft[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display + UINT8 DiamondRight[MAX_2D_VREF_ENTRIES]; ///< Left edge of Diamond for shape display + UINT8 RdWrDly; ///< RdDQS or WrDat Dly setting + VOID* SavedData; ///< Algorithm-specific saved data +} MEM_RD_WR_2D_ENTRY; + +/// Structure used for RD WR 2D Eye Rim Search Algorithm +typedef struct { + VREF_RD_WR_2D LaneSaved[MAX_NUMBER_LANES]; ///< Bytelane or Nibble that was saved + INT8 xMin; ///< Minimum value for RdDqs delays + INT8 xMax; ///< Maximum value for RdDqs delays + INT8 yMin; ///< Minimum value for vref delays + INT8 yMax; ///< Maximum value for vref delays + BOOLEAN ParallelSampling; ///< Flag to indicate parallel sampling feature + BOOLEAN BroadcastDelays; ///< Flag to indicate if delays will be broadcast + UINT32 SampleCount; ///< Count of samples taken + UINT32 VrefUpdates; ///< Count of updates to Vref + UINT32 RdWrDlyUpdates; ///< Count of updates to RdWrDly + INT8 Dirs[2]; ///< List of directions to search for each axis +} MEM_RD_WR_2D_RIM_ENTRY; + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ +BOOLEAN +MemFAmdRdWr2DTraining ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID* OptParam + ); + +BOOLEAN +MemFCheckRdWr2DTrainingPerConfig ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramIntExtVrefSelect ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramVref ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *VrefPtr + ); + +BOOLEAN +MemFRdWr2DProgramDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *Delay + ); + +VOID +MemFRdWr2DStoreResult ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT32 InPhaseResult[], + IN UINT32 PhaseResult180[] + ); + +BOOLEAN +MemFRdWr2DHeight ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +UINT8 +MemFGetRdWr2DWidth ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFCheckRdWr2DDiamondMaskStep ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data, + IN UINT8 Vref, + IN UINT8 Lane + ); +BOOLEAN +MemFRdWr2DApplyMask ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DProcessConvolution ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DProgramMaxDelays ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DFindCsVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +BOOLEAN +MemFRdWr2DFinalVrefMargin ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +UINT8 +MemFRdWr2DGetMaxLanes ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +MemFRdWr2DTrainingInit ( + IN OUT MEM_NB_BLOCK *NBPtr + ); + +BOOLEAN +MemFRdWr2DEyeRimSearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *OptParam + ); + +VOID +MemFRdWr2DDisplaySearch ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_RD_WR_2D_ENTRY *Data + ); + +/******************************* +* Pattern Generation Functions +*******************************/ +BOOLEAN +MemFRdWr2DCompareInPhase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ); + +BOOLEAN +MemFRdWr2DCompare180Phase ( + IN OUT MEM_NB_BLOCK *NBPtr, + OUT VOID *Result + ); + +BOOLEAN +MemFRdWr2DInitVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DInitVictimChipSel ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DStartVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID *SeedCountPtr + ); + +BOOLEAN +MemFRdWr2DFinalizeVictim ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN OUT VOID *OptParam + ); + +BOOLEAN +MemFRdWr2DProgramDataPattern ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN VOID* PatternIndexPtr + ); + +#endif /* _MFRDWR2DTRAINING_H_ */ diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/S3/mfs3.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/S3/mfs3.c new file mode 100644 index 0000000000..e6914a7d5a --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/S3/mfs3.c @@ -0,0 +1,718 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mfs3.c + * + * Main S3 resume memory Entrypoint file + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/FEAT/S3) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** +* + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* *************************************************************************** +* +*/ + +/* + *---------------------------------------------------------------------------- + * MODULES USED + * + *---------------------------------------------------------------------------- + */ + + + +#include "AGESA.h" +#include "Ids.h" +#include "cpuRegisters.h" +#include "OptionMemory.h" +#include "mm.h" +#include "mn.h" +#include "S3.h" +#include "mfs3.h" +#include "heapManager.h" +#include "amdlib.h" +#include "GeneralServices.h" +#include "cpuFamilyTranslation.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_S3_MFS3_FILECODE + +extern MEM_NB_SUPPORT memNBInstalled[]; + +/*---------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * TYPEDEFS AND STRUCTURES + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * PROTOTYPES OF LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * EXPORTED FUNCTIONS + * + *---------------------------------------------------------------------------- + */ +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function is the main memory entry point for the S3 resume sequence + * Requirements: + * + * Run-Time Requirements: + * 1. Complete Hypertransport Bus Configuration + * 4. BSP in Big Real Mode + * 5. Stack available + * + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +AmdMemS3Resume ( + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + MEM_MAIN_DATA_BLOCK mmData; + S3_MEM_NB_BLOCK *S3NBPtr; + MEM_DATA_STRUCT *MemData; + UINT8 Die; + UINT8 DieCount; + + //--------------------------------------------- + // Creation of NB Block for S3 resume + //--------------------------------------------- + RetVal = MemS3InitNB (&S3NBPtr, &MemData, &mmData, StdHeader); + if (RetVal == AGESA_FATAL) { + return RetVal; + } + DieCount = mmData.DieCount; + + //--------------------------------------------- + //1. Errata Before resume sequence + //2. S3 Resume sequence + //3. Errata After resume sequence + //--------------------------------------------- + for (Die = 0; Die < DieCount; Die ++) { + if (!S3NBPtr[Die].MemS3Resume (&S3NBPtr[Die], Die)) { + return AGESA_FATAL; + } + S3NBPtr[Die].MemS3RestoreScrub (S3NBPtr[Die].NBPtr, Die); + } + + HeapDeallocateBuffer (AMD_MEM_S3_DATA_HANDLE, StdHeader); + return AGESA_SUCCESS; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function deallocates heap space allocated in memory S3 resume. + * + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemS3Deallocate ( + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + AGESA_STATUS tempRetVal; + UINT8 Tab; + + RetVal = AGESA_SUCCESS; + for (Tab = 0; Tab < NumberOfNbRegTables; Tab++) { + HeapDeallocateBuffer (GENERATE_MEM_HANDLE (ALLOC_NB_REG_TABLE, Tab, 0, 0), StdHeader); + } + + tempRetVal = HeapDeallocateBuffer (GENERATE_MEM_HANDLE (ALLOC_DIE_STRUCT_HANDLE, 0, 0, 0), StdHeader); + if (tempRetVal > RetVal) { + RetVal = tempRetVal; + } + tempRetVal = HeapDeallocateBuffer (AMD_MEM_AUTO_HANDLE, StdHeader); + if (tempRetVal > RetVal) { + RetVal = tempRetVal; + } + RetVal = HeapDeallocateBuffer (AMD_MEM_S3_NB_HANDLE, StdHeader); + if (tempRetVal > RetVal) { + RetVal = tempRetVal; + } + RetVal = HeapDeallocateBuffer (AMD_MEM_DATA_HANDLE, StdHeader); + if (tempRetVal > RetVal) { + RetVal = tempRetVal; + } + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function is the entrance to get device list for memory registers. + * + * @param[in, out] **DeviceBlockHdrPtr - Pointer to the memory containing the + * device descriptor list + * @param[in] *StdHeader - Config handle for library and services + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemFS3GetDeviceList ( + IN OUT DEVICE_BLOCK_HEADER **DeviceBlockHdrPtr, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + UINT8 i; + UINT16 BufferSize; + UINT64 BufferOffset; + S3_MEM_NB_BLOCK *S3NBPtr; + MEM_DATA_STRUCT *MemData; + MEM_MAIN_DATA_BLOCK mmData; + UINT8 Die; + UINT8 DieCount; + AGESA_STATUS RetVal; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + DESCRIPTOR_GROUP DeviceDescript[MAX_NODES_SUPPORTED]; + BufferSize = 0; + + //--------------------------------------------- + // Creation of NB Block for S3 resume + //--------------------------------------------- + RetVal = MemS3InitNB (&S3NBPtr, &MemData, &mmData, StdHeader); + if (RetVal == AGESA_FATAL) { + return RetVal; + } + DieCount = mmData.DieCount; + + // Get the mask bit and the register list for node that presents + for (Die = 0; Die < DieCount; Die ++) { + S3NBPtr->MemS3GetConPCIMask (S3NBPtr[Die].NBPtr, (VOID *)&DeviceDescript[Die]); + S3NBPtr->MemS3GetConMSRMask (S3NBPtr[Die].NBPtr, (VOID *)&DeviceDescript[Die]); + BufferSize = BufferSize + S3NBPtr->MemS3GetRegLstPtr (S3NBPtr[Die].NBPtr, (VOID *)&DeviceDescript[Die]); + } + + // Base on the size of the device list, apply for a buffer for it. + AllocHeapParams.RequestedBufferSize = BufferSize + sizeof (DEVICE_BLOCK_HEADER); + AllocHeapParams.BufferHandle = AMD_S3_NB_INFO_BUFFER_HANDLE; + AllocHeapParams.Persist = HEAP_S3_RESUME; + AGESA_TESTPOINT (TpIfBeforeAllocateMemoryS3SaveBuffer, StdHeader); + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) != AGESA_SUCCESS) { + return AGESA_FATAL; + } + AGESA_TESTPOINT (TpIfAfterAllocateMemoryS3SaveBuffer, StdHeader); + + *DeviceBlockHdrPtr = (DEVICE_BLOCK_HEADER *) AllocHeapParams.BufferPtr; + (*DeviceBlockHdrPtr)->RelativeOrMaskOffset = (UINT16) AllocHeapParams.RequestedBufferSize; + + // Copy device list on the stack to the heap. + BufferOffset = sizeof (DEVICE_BLOCK_HEADER) + (UINT64) (UINTN) AllocHeapParams.BufferPtr; + for (Die = 0; Die < DieCount; Die ++) { + for (i = PRESELFREF; i <= POSTSELFREF; i ++) { + // Copy PCI device descriptor to the heap if it exists. + if (DeviceDescript[Die].PCIDevice[i].RegisterListID != 0xFFFFFFFF) { + LibAmdMemCopy ((VOID *) (UINTN) BufferOffset, &(DeviceDescript[Die].PCIDevice[i]), sizeof (PCI_DEVICE_DESCRIPTOR), StdHeader); + (*DeviceBlockHdrPtr)->NumDevices ++; + BufferOffset += sizeof (PCI_DEVICE_DESCRIPTOR); + } + // Copy conditional PCI device descriptor to the heap if it exists. + if (DeviceDescript[Die].CPCIDevice[i].RegisterListID != 0xFFFFFFFF) { + LibAmdMemCopy ((VOID *) (UINTN) BufferOffset, &(DeviceDescript[Die].CPCIDevice[i]), sizeof (CONDITIONAL_PCI_DEVICE_DESCRIPTOR), StdHeader); + (*DeviceBlockHdrPtr)->NumDevices ++; + BufferOffset += sizeof (CONDITIONAL_PCI_DEVICE_DESCRIPTOR); + } + // Copy MSR device descriptor to the heap if it exists. + if (DeviceDescript[Die].MSRDevice[i].RegisterListID != 0xFFFFFFFF) { + LibAmdMemCopy ((VOID *) (UINTN) BufferOffset, &(DeviceDescript[Die].MSRDevice[i]), sizeof (MSR_DEVICE_DESCRIPTOR), StdHeader); + (*DeviceBlockHdrPtr)->NumDevices ++; + BufferOffset += sizeof (MSR_DEVICE_DESCRIPTOR); + } + // Copy conditional MSR device descriptor to the heap if it exists. + if (DeviceDescript[Die].CMSRDevice[i].RegisterListID != 0xFFFFFFFF) { + LibAmdMemCopy ((VOID *) (UINTN) BufferOffset, &(DeviceDescript[Die].PCIDevice[i]), sizeof (CONDITIONAL_MSR_DEVICE_DESCRIPTOR), StdHeader); + (*DeviceBlockHdrPtr)->NumDevices ++; + BufferOffset += sizeof (CONDITIONAL_MSR_DEVICE_DESCRIPTOR); + } + } + } + + return RetVal; +} + + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function initialize the northbridge block and apply for heap space + * before any function call is made to memory component during S3 resume. + * + * @param[in] *StdHeader - Config handle for library and services + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemS3ResumeInitNB ( + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + MEM_MAIN_DATA_BLOCK mmData; + S3_MEM_NB_BLOCK *S3NBPtr; + MEM_DATA_STRUCT *MemData; + UINT8 Die; + UINT8 DieCount; + UINT8 SpecialCaseHeapSize; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + S3_SPECIAL_CASE_HEAP_HEADER SpecialHeapHeader[MAX_NODES_SUPPORTED]; + + SpecialCaseHeapSize = 0; + + //--------------------------------------------- + // Creation of NB Block for S3 resume + //--------------------------------------------- + RetVal = MemS3InitNB (&S3NBPtr, &MemData, &mmData, StdHeader); + if (RetVal == AGESA_FATAL) { + return RetVal; + } + DieCount = mmData.DieCount; + + //-------------------------------------------------- + // Apply for heap space for special case registers + //-------------------------------------------------- + for (Die = 0; Die < DieCount; Die ++) { + // Construct the header for the special case heap. + SpecialHeapHeader[Die].Node = S3NBPtr[Die].NBPtr->Node; + SpecialHeapHeader[Die].Offset = SpecialCaseHeapSize + (DieCount * (sizeof (S3_SPECIAL_CASE_HEAP_HEADER))); + SpecialCaseHeapSize = SpecialCaseHeapSize + S3NBPtr->MemS3SpecialCaseHeapSize; + } + AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (S3_SPECIAL_CASE_HEAP_HEADER))) + SpecialCaseHeapSize; + AllocHeapParams.BufferHandle = AMD_MEM_S3_DATA_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) != AGESA_SUCCESS) { + PutEventLog (AGESA_FATAL, MEM_ERROR_HEAP_ALLOCATE_FOR_S3_SPECIAL_CASE_REGISTERS, S3NBPtr[Die].NBPtr->Node, 0, 0, 0, StdHeader); + SetMemError (AGESA_FATAL, S3NBPtr[Die].NBPtr->MCTPtr); + ASSERT(FALSE); // Could not allocate heap space for "S3_SPECIAL_CASE_HEAP_HEADER" + return AGESA_FATAL; + } + LibAmdMemCopy ((VOID *) AllocHeapParams.BufferPtr, (VOID *) SpecialHeapHeader, (sizeof (S3_SPECIAL_CASE_HEAP_HEADER) * DieCount), StdHeader); + return AGESA_SUCCESS; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function returns the PCI device register list according to the register + * list ID. + * + * @param[in] *Device - pointer to the PCI_DEVICE_DESCRIPTOR + * @param[out] **RegisterHdr - pointer to the address of the register list + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemFS3GetPciDeviceRegisterList ( + IN PCI_DEVICE_DESCRIPTOR *Device, + OUT PCI_REGISTER_BLOCK_HEADER **RegisterHdr, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + S3_MEM_NB_BLOCK *S3NBPtr; + VOID *RegisterHeader; + LOCATE_HEAP_PTR LocHeap; + AGESA_BUFFER_PARAMS LocBufferParams; + LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; + + LibAmdMemCopy (&LocBufferParams.StdHeader, StdHeader, sizeof (AMD_CONFIG_PARAMS), StdHeader); + LocBufferParams.BufferHandle = AMD_MEM_S3_NB_HANDLE; + + AGESA_TESTPOINT (TpIfBeforeLocateS3PciBuffer, StdHeader); + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + ASSERT(FALSE) ; // No match for heap status, but could not locate "AMD_MEM_S3_NB_HANDLE" in heap for S3GetMsr + return AGESA_FATAL; + } + AGESA_TESTPOINT (TpIfAfterLocateS3PciBuffer, StdHeader); + + // NB block has already been constructed by main block. + // No need to construct it here. + RetVal = S3NBPtr[Device->Node].MemS3GetDeviceRegLst (Device->RegisterListID, &RegisterHeader); + *RegisterHdr = (PCI_REGISTER_BLOCK_HEADER *)RegisterHeader; + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function returns the conditional PCI device register list according + * to the register list ID. + * + * @param[in] *Device - pointer to the CONDITIONAL_PCI_DEVICE_DESCRIPTOR + * @param[out] **RegisterHdr - pointer to the address of the register list + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemFS3GetCPciDeviceRegisterList ( + IN CONDITIONAL_PCI_DEVICE_DESCRIPTOR *Device, + OUT CPCI_REGISTER_BLOCK_HEADER **RegisterHdr, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + S3_MEM_NB_BLOCK *S3NBPtr; + VOID *RegisterHeader; + LOCATE_HEAP_PTR LocHeap; + AGESA_BUFFER_PARAMS LocBufferParams; + + LibAmdMemCopy (&LocBufferParams.StdHeader, StdHeader, sizeof (AMD_CONFIG_PARAMS), StdHeader); + LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; + LocBufferParams.BufferHandle = AMD_MEM_S3_NB_HANDLE; + + AGESA_TESTPOINT (TpIfBeforeLocateS3CPciBuffer, StdHeader); + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + ASSERT(FALSE) ; // No match for heap status, but could not locate "AMD_MEM_S3_NB_HANDLE" in heap for S3GetMsr + return AGESA_FATAL; + } + AGESA_TESTPOINT (TpIfAfterLocateS3CPciBuffer, StdHeader); + + // NB block has already been constructed by main block. + // No need to construct it here. + RetVal = S3NBPtr[Device->Node].MemS3GetDeviceRegLst (Device->RegisterListID, &RegisterHeader); + *RegisterHdr = (CPCI_REGISTER_BLOCK_HEADER *)RegisterHeader; + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function returns the MSR device register list according to the register + * list ID. + * + * @param[in] *Device - pointer to the MSR_DEVICE_DESCRIPTOR + * @param[out] **RegisterHdr - pointer to the address of the register list + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemFS3GetMsrDeviceRegisterList ( + IN MSR_DEVICE_DESCRIPTOR *Device, + OUT MSR_REGISTER_BLOCK_HEADER **RegisterHdr, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + S3_MEM_NB_BLOCK *S3NBPtr; + VOID *RegisterHeader; + LOCATE_HEAP_PTR LocHeap; + AGESA_BUFFER_PARAMS LocBufferParams; + + LibAmdMemCopy (&LocBufferParams.StdHeader, StdHeader, sizeof (AMD_CONFIG_PARAMS), StdHeader); + LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; + LocBufferParams.BufferHandle = AMD_MEM_S3_NB_HANDLE; + + AGESA_TESTPOINT (TpIfBeforeLocateS3MsrBuffer, StdHeader); + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + ASSERT(FALSE) ; // No match for heap status, but could not locate "AMD_MEM_S3_NB_HANDLE" in heap for S3GetMsr + return AGESA_FATAL; + } + AGESA_TESTPOINT (TpIfAfterLocateS3MsrBuffer, StdHeader); + + // NB block has already been constructed by main block. + // No need to construct it here. + RetVal = S3NBPtr[BSP_DIE].MemS3GetDeviceRegLst (Device->RegisterListID, &RegisterHeader); + *RegisterHdr = (MSR_REGISTER_BLOCK_HEADER *)RegisterHeader; + return RetVal; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function returns the conditional MSR device register list according + * to the register list ID. + * + * @param[in] *Device - pointer to the CONDITIONAL_PCI_DEVICE_DESCRIPTOR + * @param[out] **RegisterHdr - pointer to the address of the register list + * @param[in] *StdHeader - Config handle for library and services + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemFS3GetCMsrDeviceRegisterList ( + IN CONDITIONAL_MSR_DEVICE_DESCRIPTOR *Device, + OUT CMSR_REGISTER_BLOCK_HEADER **RegisterHdr, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + AGESA_STATUS RetVal; + S3_MEM_NB_BLOCK *S3NBPtr; + VOID *RegisterHeader; + LOCATE_HEAP_PTR LocHeap; + AGESA_BUFFER_PARAMS LocBufferParams; + + LibAmdMemCopy (&LocBufferParams.StdHeader, StdHeader, sizeof (AMD_CONFIG_PARAMS), StdHeader); + LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; + LocBufferParams.BufferHandle = AMD_MEM_S3_NB_HANDLE; + + + AGESA_TESTPOINT (TpIfBeforeLocateS3CMsrBuffer, StdHeader); + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + ASSERT(FALSE) ; // No match for heap status, but could not locate "AMD_MEM_S3_NB_HANDLE" in heap for S3GetMsr + return AGESA_FATAL; + } + AGESA_TESTPOINT (TpIfAfterLocateS3CMsrBuffer, StdHeader); + + // NB block has already been constructed by main block. + // No need to construct it here. + RetVal = S3NBPtr[BSP_DIE].MemS3GetDeviceRegLst (Device->RegisterListID, &RegisterHeader); + *RegisterHdr = (CMSR_REGISTER_BLOCK_HEADER *)RegisterHeader; + return RetVal; +} + +/*---------------------------------------------------------------------------- + * LOCAL FUNCTIONS + * + *---------------------------------------------------------------------------- + */ + +/* -----------------------------------------------------------------------------*/ +/** + * + * + * This function initialize needed data structures for S3 resume. + * + * @param[in, out] **S3NBPtr - Pointer to the pointer of northbridge block. + * @param[in, out] *MemPtr - Pointer to MEM_DATA_STRUCT. + * @param[in, out] *mmData - Pointer to MEM_MAIN_DATA_BLOCK. + * @param[in] *StdHeader - Config handle for library and services. + * + * @return AGESA_STATUS + * - AGESA_ALERT + * - AGESA_FATAL + * - AGESA_SUCCESS + * - AGESA_WARNING + */ +AGESA_STATUS +MemS3InitNB ( + IN OUT S3_MEM_NB_BLOCK **S3NBPtr, + IN OUT MEM_DATA_STRUCT **MemPtr, + IN OUT MEM_MAIN_DATA_BLOCK *mmData, + IN AMD_CONFIG_PARAMS *StdHeader + ) +{ + UINT8 i; + AGESA_STATUS RetVal; + LOCATE_HEAP_PTR LocHeap; + MEM_NB_BLOCK *NBPtr; + ALLOCATE_HEAP_PARAMS AllocHeapParams; + UINT8 Die; + UINT8 DieCount; + BOOLEAN SkipScan; + CPU_SPECIFIC_SERVICES *FamilySpecificServices; + + SkipScan = FALSE; + LocHeap.BufferHandle = AMD_MEM_DATA_HANDLE; + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + // NB block has already been constructed by main block. + // No need to construct it here. + *MemPtr = (MEM_DATA_STRUCT *)LocHeap.BufferPtr; + SkipScan = TRUE; + } else { + AllocHeapParams.RequestedBufferSize = sizeof (MEM_DATA_STRUCT); + AllocHeapParams.BufferHandle = AMD_MEM_DATA_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) != AGESA_SUCCESS) { + ASSERT(FALSE); // Allocate failed for MEM_DATA_STRUCT + return AGESA_FATAL; + } + *MemPtr = (MEM_DATA_STRUCT *)AllocHeapParams.BufferPtr; + LibAmdMemCopy (&(*MemPtr)->StdHeader, StdHeader, sizeof (AMD_CONFIG_PARAMS), StdHeader); + + GetCpuServicesOfCurrentCore ((CONST CPU_SPECIFIC_SERVICES **)&FamilySpecificServices, &(*MemPtr)->StdHeader); + FamilySpecificServices->GetTscRate (FamilySpecificServices, &(*MemPtr)->TscRate, &(*MemPtr)->StdHeader); + + } + mmData->MemPtr = *MemPtr; + + if (!SkipScan) { + RetVal = MemSocketScan (mmData); + if (RetVal == AGESA_FATAL) { + return RetVal; + } + } else { + // We already have initialize data block, no need to do it again. + mmData->DieCount = mmData->MemPtr->DieCount; + } + DieCount = mmData->DieCount; + + //--------------------------------------------- + // Creation of NB Block for S3 resume + //--------------------------------------------- + // Search for AMD_MEM_AUTO_HANDLE on the heap first. + // Only apply for space on the heap if cannot find AMD_MEM_AUTO_HANDLE on the heap. + LocHeap.BufferHandle = AMD_MEM_S3_NB_HANDLE; + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + // NB block has already been constructed by main block. + // No need to construct it here. + *S3NBPtr = (S3_MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (S3_MEM_NB_BLOCK))); + AllocHeapParams.BufferHandle = AMD_MEM_S3_NB_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) != AGESA_SUCCESS) { + ASSERT(FALSE); // Could not allocate space for "S3_MEM_NB_BLOCK" + return AGESA_FATAL; + } + *S3NBPtr = (S3_MEM_NB_BLOCK *)AllocHeapParams.BufferPtr; + + LocHeap.BufferHandle = AMD_MEM_AUTO_HANDLE; + if (HeapLocateBuffer (&LocHeap, StdHeader) == AGESA_SUCCESS) { + // NB block has already been constructed by main block. + // No need to construct it here. + NBPtr = (MEM_NB_BLOCK *)LocHeap.BufferPtr; + } else { + AllocHeapParams.RequestedBufferSize = (DieCount * (sizeof (MEM_NB_BLOCK))); + AllocHeapParams.BufferHandle = AMD_MEM_AUTO_HANDLE; + AllocHeapParams.Persist = HEAP_SYSTEM_MEM; + if (HeapAllocateBuffer (&AllocHeapParams, StdHeader) != AGESA_SUCCESS) { + ASSERT(FALSE); // Allocate failed for "MEM_NB_BLOCK" + return AGESA_FATAL; + } + NBPtr = (MEM_NB_BLOCK *)AllocHeapParams.BufferPtr; + } + // Construct each die. + for (Die = 0; Die < DieCount; Die ++) { + i = 0; + ((*S3NBPtr)[Die]).NBPtr = &NBPtr[Die]; + while (memNBInstalled[i].MemS3ResumeConstructNBBlock != 0) { + if (memNBInstalled[i].MemS3ResumeConstructNBBlock ((VOID *)&((*S3NBPtr)[Die]), *MemPtr, Die)) { + break; + } + i++; + }; + if (memNBInstalled[i].MemS3ResumeConstructNBBlock == 0) { + ASSERT(FALSE); // S3 resume NB constructor not found + return AGESA_FATAL; + } + } + } + return AGESA_SUCCESS; +} + +/* -----------------------------------------------------------------------------*/ +/** + * + * Waits specified number of 10ns cycles + * @param[in,out] MemPtr - pointer to MEM_DATA_STRUCTURE + * @param[in] Count - Number of 10ns cycles to wait + * + * ---------------------------------------------------------------------------- + */ + +VOID +MemFS3Wait10ns ( + IN UINT32 Count, + IN OUT MEM_DATA_STRUCT *MemPtr + ) +{ + UINT64 TargetTsc; + UINT64 CurrentTsc; + + ASSERT (Count <= 1000000); + + LibAmdMsrRead (TSC, &CurrentTsc, &MemPtr->StdHeader); + TargetTsc = CurrentTsc + ((Count * MemPtr->TscRate + 99) / 100); + do { + LibAmdMsrRead (TSC, &CurrentTsc, &MemPtr->StdHeader); + } while (CurrentTsc < TargetTsc); +} diff --git a/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/TABLE/mftds.c b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/TABLE/mftds.c new file mode 100644 index 0000000000..accc96b0ec --- /dev/null +++ b/src/vendorcode/amd/agesa/f16kb/Proc/Mem/Feat/TABLE/mftds.c @@ -0,0 +1,401 @@ +/* $NoKeywords:$ */ +/** + * @file + * + * mftds.c + * + * Northbridge table drive support file for DR + * + * @xrefitem bom "File Content Label" "Release Content" + * @e project: AGESA + * @e sub-project: (Mem/Feat/TABLE) + * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ + * + **/ +/***************************************************************************** + * + * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Advanced Micro Devices, Inc. nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * *************************************************************************** + * + */ + + + +#include "AGESA.h" +#include "amdlib.h" +#include "mm.h" +#include "mn.h" +#include "mt.h" +#include "mftds.h" +#include "Ids.h" +#include "OptionMemory.h" +#include "Filecode.h" +CODE_GROUP (G2_PEI) +RDATA_GROUP (G2_PEI) + +#define FILECODE PROC_MEM_FEAT_TABLE_MFTDS_FILECODE +/*---------------------------------------------------------------------------- + * Mixed (DEFINITIONS AND MACROS / TYPEDEFS, STRUCTURES, ENUMS) + * + *---------------------------------------------------------------------------- + */ + +/*----------------------------------------------------------------------------- + * DEFINITIONS AND MACROS + * + *----------------------------------------------------------------------------- + */ +#define MAX_BYTELANES_PER_CHANNEL (8 + 1) ///< Max Bytelanes per channel + +/*---------------------------------------------------------------------------- + * TYPEDEFS, STRUCTURES, ENUMS + * + *---------------------------------------------------------------------------- + */ + +/*---------------------------------------------------------------------------- + * FUNCTIONS PROTOTYPE + * + *---------------------------------------------------------------------------- + */ + +VOID +SetTableValues ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_TABLE_ALIAS MTPtr + ); + +VOID +SetTableValuesLoop ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_TABLE_ALIAS *MTPtr, + IN UINT8 time + ); + +/*----------------------------------------------------------------------------- + * + * This function initializes bit field translation table + * + * @param[in,out] *NBPtr - Pointer to the MEM_TABLE_ALIAS structure + * @param[in] time - Indicate the timing for the register which is written. + * + * @return None + * ---------------------------------------------------------------------------- + */ +VOID +MemFInitTableDrive ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN UINT8 time + ) +{ + MEM_TABLE_ALIAS *MTPtr; + MEM_TABLE_ALIAS *IdsMTPtr; + + ASSERT (NBPtr != NULL); + IdsMTPtr = NULL; + IDS_HDT_CONSOLE (MEM_FLOW, "MemFInitTableDrive [%X] Start\n", time); + MTPtr = (MEM_TABLE_ALIAS *) NBPtr->RefPtr->TableBasedAlterations; + + IDS_SKIP_HOOK (IDS_GET_DRAM_TABLE, &IdsMTPtr, &(NBPtr->MemPtr->StdHeader)) { + IDS_OPTION_HOOK (IDS_INIT_DRAM_TABLE, NBPtr, &(NBPtr->MemPtr->StdHeader)); + IDS_OPTION_HOOK (IDS_GET_DRAM_TABLE, &IdsMTPtr, &(NBPtr->MemPtr->StdHeader)); + } + + SetTableValuesLoop (NBPtr, MTPtr, time); + SetTableValuesLoop (NBPtr, IdsMTPtr, time); + + IDS_OPTION_HOOK (IDS_MT_BASE + time, NBPtr, &(NBPtr->MemPtr->StdHeader)); + IDS_HDT_CONSOLE (MEM_FLOW, "MemFInitTableDrive End\n"); +} + +/*----------------------------------------------------------------------------- + * + * This function initializes bit field translation table + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in,out] *MTPtr - Pointer to the MEM_TABLE_ALIAS structure + * @param[in] time - Indicate the timing for the register which is written. + * + * @return None + * ---------------------------------------------------------------------------- + */ +VOID +SetTableValuesLoop ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_TABLE_ALIAS *MTPtr, + IN UINT8 time + ) +{ + UINT8 i; + UINT8 CurDct; + + if (MTPtr != NULL) { + CurDct = NBPtr->Dct; + for (i = 0; MTPtr[i].time != MTEnd; i++) { + if ((MTPtr[i].attr != MTAuto) && (MTPtr[i].time == time)) { + SetTableValues (NBPtr, MTPtr[i]); + } + } + NBPtr->SwitchDCT (NBPtr, CurDct); + } +} + +/*----------------------------------------------------------------------------- + * + * Engine for setting Table Value. + * + * @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK + * @param[in] MTPtr - Pointer to the MEM_TABLE_ALIAS structure + * + * @return None + * ---------------------------------------------------------------------------- + */ +VOID +SetTableValues ( + IN OUT MEM_NB_BLOCK *NBPtr, + IN MEM_TABLE_ALIAS MTPtr + ) +{ + UINT8 AccessType; + UINT16 ByteLane; + UINT8 Dct; + UINT8 i; + UINT8 j; + UINT32 TempVal[36]; + UINT32 Temp2Val[72]; + UINT8 *DqsSavePtr; + UINT8 DqsOffset; + BOOLEAN SaveDqs; + + AccessType = 0; + DqsSavePtr = NULL; + SaveDqs = TRUE; + + ASSERT (MTPtr.time <= MTValidTimePointLimit); + ASSERT (MTPtr.attr <= MTOr); + ASSERT (MTPtr.node <= MTNodes); + ASSERT (MTPtr.dct <= MTDcts); + ASSERT (MTPtr.dimm <= MTDIMMs); + ASSERT (MTPtr.data.s.bytelane <= MTBLs); + + for (Dct = 0; Dct < NBPtr->DctCount; Dct++) { + if ((MTPtr.dct == MTDcts) || (MTPtr.dct == Dct)) { + NBPtr->SwitchDCT (NBPtr, Dct); + switch (MTPtr.bfindex) { + case BFRcvEnDly: + AccessType = AccessRcvEnDly; + DqsSavePtr = NULL; + break; + case BFWrDatDly: + AccessType = AccessWrDatDly; + DqsSavePtr = NBPtr->ChannelPtr->WrDatDlys; + break; + case BFRdDqsDly: + AccessType = AccessRdDqsDly; + DqsSavePtr = NBPtr->ChannelPtr->RdDqsDlys; + break; + case BFWrDqsDly: + AccessType = AccessWrDqsDly; + DqsSavePtr = NBPtr->ChannelPtr->WrDqsDlys; + break; + case BFRdDqs2dDly: + AccessType = AccessRdDqs2dDly; + DqsSavePtr = NBPtr->ChannelPtr->RdDqs2dDlys; + break; + case BFPhRecDly: + AccessType = AccessPhRecDly; + SaveDqs = FALSE; + break; + default: + AccessType = 0xFF; + break; + } + if (AccessType == 0xFF) { + if (MTPtr.attr == MTOverride) { + NBPtr->SetBitField (NBPtr, MTPtr.bfindex, MTPtr.data.s.value); + } + if (MTPtr.attr == MTSubtract) { + NBPtr->SetBitField (NBPtr, MTPtr.bfindex, NBPtr->GetBitField (NBPtr, MTPtr.bfindex) - MTPtr.data.s.value); + } + if (MTPtr.attr == MTAdd) { + NBPtr->SetBitField (NBPtr, MTPtr.bfindex, NBPtr->GetBitField (NBPtr, MTPtr.bfindex) + MTPtr.data.s.value); + } + if (MTPtr.attr == MTAnd) { + NBPtr->SetBitField (NBPtr, MTPtr.bfindex, (NBPtr->GetBitField (NBPtr, MTPtr.bfindex) & MTPtr.data.s.value)); + } + if (MTPtr.attr == MTOr) { + NBPtr->SetBitField (NBPtr, MTPtr.bfindex, (NBPtr->GetBitField (NBPtr, MTPtr.bfindex) | MTPtr.data.s.value)); + } + } else { + // Store the DQS data first + for (i = 0; i < NBPtr->CsPerChannel; i = i + NBPtr->CsPerDelay) { + if (AccessType == AccessRdDqs2dDly) { + for (j = 0; j < MAX_NUMBER_LANES; j++) { + Temp2Val[i * MAX_NUMBER_LANES + j] = NBPtr->GetTrainDly (NBPtr, AccessType, DIMM_NBBL_ACCESS (i, j)); + } + } else { + for (j = 0; j < MAX_BYTELANES_PER_CHANNEL; j++) { + TempVal[i / NBPtr->CsPerDelay * MAX_BYTELANES_PER_CHANNEL + j] = NBPtr->GetTrainDly (NBPtr, AccessType, DIMM_BYTE_ACCESS (i / NBPtr->CsPerDelay, j)); + } + } + } + // + // Single Value with Bytleane mask option + // Indicated by the vtype flag + // + if (MTPtr.vtype == VT_MSK_VALUE) { + // set the value which defined in Memory table. + for (i = 0; i < NBPtr->CsPerChannel; i = i + NBPtr->CsPerDelay) { + ByteLane = MTPtr.data.s.bytelane; + if ((MTPtr.dimm == MTDIMMs) || ((MTPtr.dimm * NBPtr->CsPerDelay) == i)) { + if (AccessType == AccessRdDqs2dDly) { + for (j = 0; j < MAX_NUMBER_LANES; j++) { + DqsOffset = (i * MAX_NUMBER_LANES + j); + if ((ByteLane & (UINT16)1) != 0) { + if (MTPtr.attr == MTOverride) { + Temp2Val[DqsOffset] = (UINT16)MTPtr.data.s.value; + } + if (MTPtr.attr == MTSubtract) { + Temp2Val[DqsOffset] -= (UINT16)MTPtr.data.s.value; + } + if (MTPtr.attr == MTAdd) { + Temp2Val[DqsOffset] += (UINT16)MTPtr.data.s.value; + } + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_NBBL_ACCESS (i, j), (UINT16)Temp2Val[DqsOffset]); + if (SaveDqs) { + if (DqsSavePtr == NULL) { + NBPtr->ChannelPtr->RcvEnDlys[DqsOffset] = (UINT16)Temp2Val[DqsOffset]; + } else { + DqsSavePtr[DqsOffset] = (UINT8)Temp2Val[DqsOffset]; + } + } + } + ByteLane = ByteLane >> (UINT16)1; + } + } else { + for (j = 0; j < MAX_BYTELANES_PER_CHANNEL; j++) { + DqsOffset = (i / NBPtr->CsPerDelay * MAX_BYTELANES_PER_CHANNEL + j); + if ((ByteLane & (UINT16)1) != 0) { + if (MTPtr.attr == MTOverride) { + TempVal[DqsOffset] = (UINT16)MTPtr.data.s.value; + } + if (MTPtr.attr == MTSubtract) { + TempVal[DqsOffset] -= (UINT16)MTPtr.data.s.value; + } + if (MTPtr.attr == MTAdd) { + TempVal[DqsOffset] += (UINT16)MTPtr.data.s.value; + } + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_BYTE_ACCESS (i / NBPtr->CsPerDelay, j), (UINT16)TempVal[DqsOffset]); + if (SaveDqs) { + if (DqsSavePtr == NULL) { + NBPtr->ChannelPtr->RcvEnDlys[DqsOffset] = (UINT16)TempVal[DqsOffset]; + } else { + DqsSavePtr[DqsOffset] = (UINT8)TempVal[DqsOffset]; + } + } + } + ByteLane = ByteLane >> (UINT16)1; + } + } + } + } + } else { + // Multiple values specified in a byte array + for (i = 0; i < NBPtr->CsPerChannel; i = i + NBPtr->CsPerDelay) { + if ((MTPtr.dimm == MTDIMMs) || ((MTPtr.dimm * NBPtr->CsPerDelay) == i)) { + if (AccessType == AccessRdDqs2dDly) { + for (j = 0; j < MAX_NUMBER_LANES; j++) { + DqsOffset = (i * MAX_NUMBER_LANES + j); + if (MTPtr.attr == MTOverride) { + Temp2Val[DqsOffset] = MTPtr.data.bytelanevalue[j]; + } + if (MTPtr.attr == MTSubtract) { + Temp2Val[DqsOffset] -= MTPtr.data.bytelanevalue[j]; + } + if (MTPtr.attr == MTAdd) { + Temp2Val[DqsOffset] += MTPtr.data.bytelanevalue[j]; + } + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_NBBL_ACCESS (i, j), (UINT16)Temp2Val[DqsOffset]); + if (SaveDqs) { + if (DqsSavePtr == NULL) { + NBPtr->ChannelPtr->RcvEnDlys[DqsOffset] = (UINT16)Temp2Val[DqsOffset]; + } else { + DqsSavePtr[DqsOffset] = (UINT8)Temp2Val[DqsOffset]; + } + } + } + } else { + for (j = 0; j < MAX_BYTELANES_PER_CHANNEL; j++) { + DqsOffset = (i / NBPtr->CsPerDelay * MAX_BYTELANES_PER_CHANNEL + j); + if (MTPtr.attr == MTOverride) { + TempVal[DqsOffset] = MTPtr.data.bytelanevalue[j]; + } + if (MTPtr.attr == MTSubtract) { + TempVal[DqsOffset] -= MTPtr.data.bytelanevalue[j]; + } + if (MTPtr.attr == MTAdd) { + TempVal[DqsOffset] += MTPtr.data.bytelanevalue[j]; + } + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_BYTE_ACCESS (i / NBPtr->CsPerDelay, j), (UINT16)TempVal[DqsOffset]); + if (SaveDqs) { + if (DqsSavePtr == NULL) { + NBPtr->ChannelPtr->RcvEnDlys[DqsOffset] = (UINT16)TempVal[DqsOffset]; + } else { + DqsSavePtr[DqsOffset] = (UINT8)TempVal[DqsOffset]; + } + } + } + } + } + } + } + // set the DQS value to left DIMMs. + i = MTPtr.dimm; + if (i != MTDIMMs) { + i = i * NBPtr->CsPerDelay + NBPtr->CsPerDelay; + while (i < NBPtr->CsPerChannel) { + if (AccessType == AccessRdDqs2dDly) { + for (j = 0; j < MAX_NUMBER_LANES; j++) { + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_NBBL_ACCESS (i, j), (UINT16)Temp2Val[i * MAX_BYTELANES_PER_CHANNEL + j]); + } + } else { + for (j = 0; j < MAX_BYTELANES_PER_CHANNEL; j++) { + NBPtr->SetTrainDly (NBPtr, AccessType, DIMM_BYTE_ACCESS (i / NBPtr->CsPerDelay, j), (UINT16)TempVal[i / NBPtr->CsPerDelay * MAX_BYTELANES_PER_CHANNEL + j]); + } + } + i = i + NBPtr->CsPerDelay; + } + } + } + } + } +} + + + + + + |