blob: cbf2dc78e14dbb36b4053de59d5522911cc2fc9b [file] [log] [blame]
/* $NoKeywords:$ */
/**
* @file
*
* mnreg.c
*
* Common Northbridge register access functions
*
* @xrefitem bom "File Content Label" "Release Content"
* @e project: AGESA
* @e sub-project: (Mem/NB/)
* @e \$Revision: 56279 $ @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
*
**/
/*****************************************************************************
*
* Copyright (C) 2012 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 "merrhdl.h"
#include "heapManager.h"
#include "Filecode.h"
#include "GeneralServices.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G2_PEI)
#define FILECODE PROC_MEM_NB_MNREG_FILECODE
/*----------------------------------------------------------------------------
* DEFINITIONS AND MACROS
*
*----------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
* TYPEDEFS AND STRUCTURES
*
*----------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
* PROTOTYPES OF LOCAL FUNCTIONS
*
*----------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
* EXPORTED FUNCTIONS
*
*----------------------------------------------------------------------------
*/
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function sets the current DCT to work on.
* Should be called before accessing a certain DCT
* All data structures will be updated to point to the current DCT
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] Dct - ID of the target DCT
*
*/
VOID
MemNSwitchDCTNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN UINT8 Dct
)
{
ASSERT (NBPtr->DctCount > Dct);
//
// Set the DctCfgSel to new DCT
//
NBPtr->FamilySpecificHook[DCTSelectSwitch] (NBPtr, &Dct);
NBPtr->Dct = Dct;
NBPtr->MCTPtr->Dct = NBPtr->Dct;
NBPtr->DCTPtr = &(NBPtr->MCTPtr->DctData[NBPtr->Dct]);
NBPtr->PsPtr = &(NBPtr->PSBlock[NBPtr->Dct]);
NBPtr->DctCachePtr = &(NBPtr->DctCache[NBPtr->Dct]);
MemNSwitchChannelNb (NBPtr, NBPtr->Channel);
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function is used by families that use a separate DctCfgSel bit to
* select the current DCT which will be accessed by function 2.
* NOTE: This function must be called BEFORE the NBPtr->Dct variable is
* updated.
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] *Dct - Pointer to ID of the target DCT
*
*/
BOOLEAN
MemNDctCfgSelectUnb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN VOID *Dct
)
{
//
// Sanity check the current DctCfgSel setting
//
ASSERT (NBPtr->Dct == NBPtr->GetBitField (NBPtr, BFDctCfgSel));
//
// Set the DctCfgSel to new DCT
//
NBPtr->SetBitField (NBPtr, BFDctCfgSel, *(UINT8*)Dct);
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function sets the current channel to work on.
* Should be called before accessing a certain channel
* All data structures will be updated to point to the current channel
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] Channel - ID of the target channel
*
*/
VOID
MemNSwitchChannelNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN UINT8 Channel
)
{
NBPtr->Channel = Channel ? 1 : 0;
NBPtr->ChannelPtr = &(NBPtr->DCTPtr->ChData[NBPtr->Channel]);
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function gets a bit field from PCI register
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] FieldName - Field name
*
* @return Bit field value
*/
UINT32
MemNGetBitFieldNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN BIT_FIELD_NAME FieldName
)
{
UINT32 Value;
ASSERT (FieldName < BFEndOfList);
Value = NBPtr->MemNCmnGetSetFieldNb (NBPtr, 0, FieldName, 0);
return Value;
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function sets a bit field from PCI register
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] FieldName - Field name
* @param[in] Field - Value to be stored in PCT register
*
*/
VOID
MemNSetBitFieldNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN BIT_FIELD_NAME FieldName,
IN UINT32 Field
)
{
ASSERT (FieldName < BFEndOfList);
NBPtr->MemNCmnGetSetFieldNb (NBPtr, 1, FieldName, Field);
}
/* -----------------------------------------------------------------------------*/
/**
*
* Check if bitfields of all enabled DCTs on a die have the expected value. Ignore
* DCTs that are disabled.
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] FieldName - Bit Field name
* @param[in] Field - Value to be checked
*
* @return TRUE - All enabled DCTs have the expected value on the bitfield.
* @return FALSE - Not all enabled DCTs have the expected value on the bitfield.
*
* ----------------------------------------------------------------------------
*/
BOOLEAN
MemNBrdcstCheckNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN BIT_FIELD_NAME FieldName,
IN UINT32 Field
)
{
UINT8 Dct;
UINT8 CurrentDCT;
Dct = NBPtr->Dct;
for (CurrentDCT = 0; CurrentDCT < NBPtr->DctCount; CurrentDCT++) {
MemNSwitchDCTNb (NBPtr, CurrentDCT);
if ((NBPtr->DCTPtr->Timings.DctMemSize != 0) && !((CurrentDCT == 1) && NBPtr->Ganged)) {
if (MemNGetBitFieldNb (NBPtr, FieldName) != Field) {
MemNSwitchDCTNb (NBPtr, Dct);
return FALSE;
}
}
}
MemNSwitchDCTNb (NBPtr, Dct);
return TRUE;
}
/* -----------------------------------------------------------------------------*/
/**
*
* Set bitfields of all enabled DCTs on a die to a value. Ignore
* DCTs that are disabled.
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] FieldName - Bit Field name
* @param[in] Field - Value to be set
*
* ----------------------------------------------------------------------------
*/
VOID
MemNBrdcstSetNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN BIT_FIELD_NAME FieldName,
IN UINT32 Field
)
{
UINT8 Dct;
UINT8 CurrentDCT;
Dct = NBPtr->Dct;
for (CurrentDCT = 0; CurrentDCT < NBPtr->DctCount; CurrentDCT++) {
MemNSwitchDCTNb (NBPtr, CurrentDCT);
if ((NBPtr->DCTPtr->Timings.DctMemSize != 0) && !((CurrentDCT == 1) && NBPtr->Ganged)) {
MemNSetBitFieldNb (NBPtr, FieldName, Field);
}
}
MemNSwitchDCTNb (NBPtr, Dct);
}
/*-----------------------------------------------------------------------------*/
/**
* This function calculates the memory channel index relative to the
* socket, taking the Die number, the Dct, and the channel.
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] Dct
* @param[in] Channel
*
*/
UINT8
MemNGetSocketRelativeChannelNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN UINT8 Dct,
IN UINT8 Channel
)
{
return ((NBPtr->MCTPtr->DieId * NBPtr->DctCount) + Dct);
}
/* -----------------------------------------------------------------------------*/
/**
*
* Poll a bitfield. If the bitfield does not get set to the target value within
* specified microseconds, it times out.
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] FieldName - Bit Field name
* @param[in] Field - Value to be set
* @param[in] MicroSecond - Number of microsecond to wait
* @param[in] IfBroadCast - Need to broadcast to both DCT or not
*
* ----------------------------------------------------------------------------
*/
VOID
MemNPollBitFieldNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN BIT_FIELD_NAME FieldName,
IN UINT32 Field,
IN UINT32 MicroSecond,
IN BOOLEAN IfBroadCast
)
{
BOOLEAN ErrorRecovery;
BOOLEAN IgnoreErr;
UINT8 ExcludeDCT;
UINT16 ExcludeChipSelMask;
UINT32 EventInfo;
UINT64 InitTSC;
UINT64 CurrentTSC;
UINT64 TimeOut;
AGESA_STATUS EventClass;
MEM_DATA_STRUCT *MemPtr;
DIE_STRUCT *MCTPtr;
BOOLEAN TimeoutEn;
MemPtr = NBPtr->MemPtr;
MCTPtr = NBPtr->MCTPtr;
ExcludeDCT = EXCLUDE_ALL_DCT;
ExcludeChipSelMask = EXCLUDE_ALL_CHIPSEL;
TimeoutEn = TRUE;
IDS_TIMEOUT_CTL (&TimeoutEn);
CurrentTSC = 0;
LibAmdMsrRead (TSC, &InitTSC, &MemPtr->StdHeader);
TimeOut = InitTSC + ((UINT64) MicroSecond * MemPtr->TscRate);
while ((CurrentTSC < TimeOut) || !TimeoutEn) {
if (IfBroadCast) {
if (NBPtr->BrdcstCheck (NBPtr, FieldName, Field)) {
break;
}
} else {
if (MemNGetBitFieldNb (NBPtr, FieldName) == Field) {
break;
}
}
LibAmdMsrRead (TSC, &CurrentTSC, &MemPtr->StdHeader);
}
if ((CurrentTSC >= TimeOut) && TimeoutEn) {
ErrorRecovery = TRUE;
IgnoreErr = FALSE;
IDS_OPTION_HOOK (IDS_MEM_ERROR_RECOVERY, &ErrorRecovery, &MemPtr->StdHeader);
IDS_OPTION_HOOK (IDS_MEM_IGNORE_ERROR, &IgnoreErr, &MemPtr->StdHeader);
// Default event class
// If different event class is needed in one entry, override it.
EventClass = AGESA_ERROR;
switch (FieldName) {
case BFDramEnabled:
EventInfo = MEM_ERROR_DRAM_ENABLED_TIME_OUT;
IDS_HDT_CONSOLE (MEM_FLOW, "\tDramEnabled bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFDctAccessDone:
EventInfo = MEM_ERROR_DCT_ACCESS_DONE_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tDctAccessDone bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFSendCtrlWord:
EventInfo = MEM_ERROR_SEND_CTRL_WORD_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tSendCtrlWord bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFPrefDramTrainMode:
EventInfo = MEM_ERROR_PREF_DRAM_TRAIN_MODE_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tPrefDramTrainMode bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFEnterSelfRef:
EventInfo = MEM_ERROR_ENTER_SELF_REF_TIME_OUT;
IDS_HDT_CONSOLE (MEM_FLOW, "\tEnterSelfRef bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFFreqChgInProg:
EventInfo = MEM_ERROR_FREQ_CHG_IN_PROG_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tFreqChgInProg bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFExitSelfRef:
EventInfo = MEM_ERROR_EXIT_SELF_REF_TIME_OUT;
IDS_HDT_CONSOLE (MEM_FLOW, "\tExitSelfRef bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFSendMrsCmd:
EventInfo = MEM_ERROR_SEND_MRS_CMD_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tSendMrsCmd bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFSendZQCmd:
EventInfo = MEM_ERROR_SEND_ZQ_CMD_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tSendZQCmd bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFDctExtraAccessDone:
EventInfo = MEM_ERROR_DCT_EXTRA_ACCESS_DONE_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tDctExtraAccessDone bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFMemClrBusy:
EventInfo = MEM_ERROR_MEM_CLR_BUSY_TIME_OUT;
IDS_HDT_CONSOLE (MEM_FLOW, "\tMemClrBusy bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFMemCleared:
EventInfo = MEM_ERROR_MEM_CLEARED_TIME_OUT;
IDS_HDT_CONSOLE (MEM_FLOW, "\tMemCleared bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
case BFFlushWr:
EventInfo = MEM_ERROR_FLUSH_WR_TIME_OUT;
ExcludeDCT = NBPtr->Dct;
IDS_HDT_CONSOLE (MEM_FLOW, "\tFlushWr bitfield times out.\n");
ASSERT (ErrorRecovery || IgnoreErr);
break;
default:
EventClass = 0;
EventInfo = 0;
IDS_ERROR_TRAP;
}
PutEventLog (EventClass, EventInfo, NBPtr->Node, NBPtr->Dct, NBPtr->Channel, 0, &MemPtr->StdHeader);
SetMemError (EventClass, MCTPtr);
if (!MemPtr->ErrorHandling (MCTPtr, ExcludeDCT, ExcludeChipSelMask, &MemPtr->StdHeader)) {
ASSERT (FALSE);
}
}
}
/* -----------------------------------------------------------------------------*/
/**
*
*
* This function changes memory Pstate context
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] MemPstate - Target Memory Pstate
*
* @return TRUE
* ----------------------------------------------------------------------------
*/
VOID
MemNChangeMemPStateContextNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN MEM_PSTATE MemPstate
)
{
UINT8 PSMasterChannel;
UINT8 Dct;
ASSERT ((MemPstate == 0) || (MemPstate == 1));
ASSERT (NBPtr->MemPstate == ((MemNGetBitFieldNb (NBPtr, BFMemPsSel) == 0) ? MEMORY_PSTATE0 : MEMORY_PSTATE1));
IDS_HDT_CONSOLE (MEM_FLOW, "\nGo to Memory Pstate Conext %d\n", MemPstate);
Dct = NBPtr->Dct;
MemNSwitchDCTNb (NBPtr, 0);
// Figure out what is the master channel
PSMasterChannel = (UINT8) (MemNGetBitFieldNb (NBPtr, BFPhyPSMasterChannel) >> 8);
// Switch to the master channel to change PStateToAccess
// PStateToAccess is only effective on the master channel
MemNSwitchDCTNb (NBPtr, PSMasterChannel);
MemNSetBitFieldNb (NBPtr, BFMemPsSel, MemPstate);
MemNSetBitFieldNb (NBPtr, BFPStateToAccess, MemPstate << 8);
NBPtr->MemPstate = (MemPstate == 0) ? MEMORY_PSTATE0 : MEMORY_PSTATE1;
MemNSwitchDCTNb (NBPtr, Dct);
}
/* -----------------------------------------------------------------------------*/
/**
*
* This function allocates buffer for NB register table
*
* @param[in,out] *NBPtr - Pointer to the MEM_NB_BLOCK
* @param[in] Handle - Handle for heap allocation for NBRegTable
*
* @return TRUE - Successfully allocates buffer the first time
* @return FALSE - Buffer already allocated or fails to allocate
*/
BOOLEAN
MemNAllocateNBRegTableNb (
IN OUT MEM_NB_BLOCK *NBPtr,
IN NB_REG_TAB_HANDLE Handle
)
{
ALLOCATE_HEAP_PARAMS AllocHeapParams;
LOCATE_HEAP_PTR LocHeap;
// If NBRegTable for this family exists, use it
LocHeap.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_NB_REG_TABLE, Handle, 0, 0);
if (HeapLocateBuffer (&LocHeap, &(NBPtr->MemPtr->StdHeader)) == AGESA_SUCCESS) {
NBPtr->NBRegTable = (TSEFO *) LocHeap.BufferPtr;
return FALSE;
}
// Allocate new buffer for NBRegTable if it has not been allocated
AllocHeapParams.RequestedBufferSize = sizeof (TSEFO) * BFEndOfList;
AllocHeapParams.BufferHandle = GENERATE_MEM_HANDLE (ALLOC_NB_REG_TABLE, Handle, 0, 0);
AllocHeapParams.Persist = HEAP_SYSTEM_MEM;
if (AGESA_SUCCESS != HeapAllocateBuffer (&AllocHeapParams, &(NBPtr->MemPtr->StdHeader))) {
ASSERT(FALSE); // NB and Tech Block Heap allocate error
return FALSE;
}
NBPtr->NBRegTable = (TSEFO *)AllocHeapParams.BufferPtr;
return TRUE;
}