blob: 86cea18fe0ebe487c33b10c0f5606d6d80bce71b [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright (C) 2010 Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
static void SetTargetFreq(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat);
static void AgesaHwWlPhase1(sMCTStruct *pMCTData,
sDCTStruct *pDCTData, u8 dimm, u8 pass);
static void EnableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void DisableZQcalibration(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void PrepareC_MCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void PrepareC_DCT(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
static void MultiplyDelay(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct);
static void Restore_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void Clear_OnDimmMirror(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat);
static void SetEccWrDQS_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
{
u8 ByteLane, DimmNum, OddByte, Addl_Index, Channel;
u8 EccRef1, EccRef2, EccDQSScale;
u32 val;
u16 word;
for (Channel = 0; Channel < 2; Channel ++) {
for (DimmNum = 0; DimmNum < C_MAX_DIMMS; DimmNum ++) { /* we use DimmNum instead of DimmNumx3 */
for (ByteLane = 0; ByteLane < 9; ByteLane ++) {
/* Get RxEn initial value from WrDqs */
if (ByteLane & 1)
OddByte = 1;
else
OddByte = 0;
if (ByteLane < 2)
Addl_Index = 0x30;
else if (ByteLane < 4)
Addl_Index = 0x31;
else if (ByteLane < 6)
Addl_Index = 0x40;
else if (ByteLane < 8)
Addl_Index = 0x41;
else
Addl_Index = 0x32;
Addl_Index += DimmNum * 3;
val = Get_NB32_index_wait(pDCTstat->dev_dct, Channel * 0x100 + 0x98, Addl_Index);
if (OddByte)
val >>= 16;
/* Save WrDqs to stack for later usage */
pDCTstat->CH_D_B_TxDqs[Channel][DimmNum][ByteLane] = val & 0xFF;
EccDQSScale = pDCTstat->CH_EccDQSScale[Channel];
word = pDCTstat->CH_EccDQSLike[Channel];
if ((word & 0xFF) == ByteLane) EccRef1 = val & 0xFF;
if (((word >> 8) & 0xFF) == ByteLane) EccRef2 = val & 0xFF;
}
}
}
}
static void EnableAutoRefresh_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
{
u32 val;
val = Get_NB32(pDCTstat->dev_dct, 0x8C);
val &= ~(1 << DisAutoRefresh);
Set_NB32(pDCTstat->dev_dct, 0x8C, val);
val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
val &= ~(1 << DisAutoRefresh);
Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
}
static void DisableAutoRefresh_D(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat)
{
u32 val;
val = Get_NB32(pDCTstat->dev_dct, 0x8C);
val |= 1 << DisAutoRefresh;
Set_NB32(pDCTstat->dev_dct, 0x8C, val);
val = Get_NB32(pDCTstat->dev_dct, 0x8C + 0x100);
val |= 1 << DisAutoRefresh;
Set_NB32(pDCTstat->dev_dct, 0x8C + 0x100, val);
}
static void PhyWLPass1(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
u8 dimm;
u16 DIMMValid;
void *DCTPtr;
dct &= 1;
DCTPtr = (void *)(pDCTstat->C_DCTPtr[dct]);
pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
if (pDCTstat->GangedMode & 1)
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
if (pDCTstat->DIMMValid) {
DIMMValid = pDCTstat->DIMMValid;
PrepareC_DCT(pMCTstat, pDCTstat, dct);
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
if (DIMMValid & (1 << (dimm << 1)))
AgesaHwWlPhase1(pDCTstat->C_MCTPtr, DCTPtr, dimm, FirstPass);
}
}
}
static void PhyWLPass2(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat, u8 dct)
{
u8 dimm;
u16 DIMMValid;
void *DCTPtr;
dct &= 1;
DCTPtr = (void *)&(pDCTstat->C_DCTPtr[dct]); /* todo: */
pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[dct];
if (pDCTstat->GangedMode & 1)
pDCTstat->CSPresent = pDCTstat->CSPresent_DCT[0];
if (pDCTstat->DIMMValid) {
DIMMValid = pDCTstat->DIMMValid;
PrepareC_DCT(pMCTstat, pDCTstat, dct);
pDCTstat->Speed = pDCTstat->DIMMAutoSpeed = pDCTstat->TargetFreq;
pDCTstat->CASL = pDCTstat->DIMMCASL = pDCTstat->TargetCASL;
SPD2ndTiming(pMCTstat, pDCTstat, dct);
ProgDramMRSReg_D(pMCTstat, pDCTstat, dct);
PlatformSpec_D(pMCTstat, pDCTstat, dct);
fenceDynTraining_D(pMCTstat, pDCTstat, dct);
Restore_OnDimmMirror(pMCTstat, pDCTstat);
StartupDCT_D(pMCTstat, pDCTstat, dct);
Clear_OnDimmMirror(pMCTstat, pDCTstat);
SetDllSpeedUp_D(pMCTstat, pDCTstat, dct);
DisableAutoRefresh_D(pMCTstat, pDCTstat);
MultiplyDelay(pMCTstat, pDCTstat, dct);
for (dimm = 0; dimm < MAX_DIMMS_SUPPORTED; dimm ++) {
if (DIMMValid & (1 << (dimm << 1)))
AgesaHwWlPhase1(pDCTstat->C_MCTPtr, pDCTstat->C_DCTPtr[dct], dimm, SecondPass);
}
}
}
static void WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstat)
{
pDCTstat->C_MCTPtr = &(pDCTstat->s_C_MCTPtr);
pDCTstat->C_DCTPtr[0] = &(pDCTstat->s_C_DCTPtr[0]);
pDCTstat->C_DCTPtr[1] = &(pDCTstat->s_C_DCTPtr[1]);
/* Disable auto refresh by configuring F2x[1, 0]8C[DisAutoRefresh] = 1 */
DisableAutoRefresh_D(pMCTstat, pDCTstat);
/* Disable ZQ calibration short command by F2x[1,0]94[ZqcsInterval]=00b */
DisableZQcalibration(pMCTstat, pDCTstat);
PrepareC_MCT(pMCTstat, pDCTstat);
if (pDCTstat->GangedMode & (1 << 0)) {
pDCTstat->DIMMValidDCT[1] = pDCTstat->DIMMValidDCT[0];
}
PhyWLPass1(pMCTstat, pDCTstat, 0);
PhyWLPass1(pMCTstat, pDCTstat, 1);
if (pDCTstat->TargetFreq > 4) {
/* 8.Prepare the memory subsystem for the target MEMCLK frequency.
* Note: BIOS must program both DCTs to the same frequency.
*/
SetTargetFreq(pMCTstat, pDCTstat);
PhyWLPass2(pMCTstat, pDCTstat, 0);
PhyWLPass2(pMCTstat, pDCTstat, 1);
}
SetEccWrDQS_D(pMCTstat, pDCTstat);
EnableAutoRefresh_D(pMCTstat, pDCTstat);
EnableZQcalibration(pMCTstat, pDCTstat);
}
void mct_WriteLevelization_HW(struct MCTStatStruc *pMCTstat,
struct DCTStatStruc *pDCTstatA)
{
u8 Node;
for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
struct DCTStatStruc *pDCTstat;
pDCTstat = pDCTstatA + Node;
if (pDCTstat->NodePresent) {
mctSMBhub_Init(Node);
Clear_OnDimmMirror(pMCTstat, pDCTstat);
WriteLevelization_HW(pMCTstat, pDCTstat);
Restore_OnDimmMirror(pMCTstat, pDCTstat);
}
}
}