Michael Kinney 9b6bbcdbfd QuarkSocPkg: Add new package for Quark SoC X1000
Changes for V4
==============
1) Remove Unicode character from C source file
2) Move delete of QuarkSocPkg\QuarkNorthCluster\Binary\QuarkMicrocode
   from QuarkPlatformPkg commit to QuarkSocPkg commit

Changes for V2
==============
1) Sync with new APIs in SmmCpuFeaturesLib class
2) Use new generic PCI serial driver PciSioSerialDxe in MdeModulePkg
3) Remove PCI serial driver from QuarkSocPkg
4) Apply optimizations to MtrrLib from MtrrLib in UefiCpuPkg
5) Convert all UNI files to utf-8
6) Replace tabs with spaces and remove trailing spaces
7) Add License.txt

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Michael Kinney <michael.d.kinney@intel.com>
Acked-by: Jordan Justen <jordan.l.justen@intel.com>

git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19286 6f19259b-4bc3-4df7-8a09-765794883524
2015-12-15 19:22:23 +00:00

1581 lines
43 KiB
C

/************************************************************************
*
* Copyright (c) 2013-2015 Intel Corporation.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*
***************************************************************************/
#include "mrc.h"
#include "memory_options.h"
#include "meminit_utils.h"
#include "hte.h"
#include "io.h"
void select_hte(
MRCParams_t *mrc_params);
static uint8_t first_run = 0;
const uint8_t vref_codes[64] =
{ // lowest to highest
0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, // 00 - 15
0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, // 16 - 31
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 32 - 47
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 48 - 63
};
#ifdef EMU
// Track current post code for debugging purpose
uint32_t PostCode;
#endif
// set_rcvn:
//
// This function will program the RCVEN delays.
// (currently doesn't comprehend rank)
void set_rcvn(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count);
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
// BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = (byte_lane & BIT0) ? (BIT23 | BIT22 | BIT21 | BIT20) : (BIT11 | BIT10 | BIT9 | BIT8);
tempD = (byte_lane & BIT0) ? ((pi_count / HALF_CLK) << 20) : ((pi_count / HALF_CLK) << 8);
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24);
tempD = pi_count << 24;
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// BL0/1 -> B01DBCTL1[08/11] (+1 select)
// BL0/1 -> B01DBCTL1[02/05] (enable)
reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= (byte_lane & BIT0) ? (BIT5) : (BIT2);
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= (byte_lane & BIT0) ? (BIT11) : (BIT8);
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
training_message(channel, rank, byte_lane);
post_code(0xEE, 0xE0);
}
LEAVEFN();
return;
}
// get_rcvn:
//
// This function will return the current RCVEN delay on the given channel, rank, byte_lane as an absolute PI count.
// (currently doesn't comprehend rank)
uint32_t get_rcvn(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[11:08] (0x0-0xF)
// BL1 -> B01PTRCTL0[23:20] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= (byte_lane & BIT0) ? (20) : (8);
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = tempD * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
tempD = isbR32m(DDRPHY, reg);
tempD >>= 24;
tempD &= 0x3F;
// Adjust PI_COUNT
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_rdqs:
//
// This function will program the RDQS delays based on an absolute amount of PIs.
// (currently doesn't comprehend rank)
void set_rdqs(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count);
// PI (1/128 MCLK)
// BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
// BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
reg = (byte_lane & BIT0) ? (B1RXDQSPICODE) : (B0RXDQSPICODE);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
msk = (BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0);
tempD = pi_count << 0;
isbM32m(DDRPHY, reg, tempD, msk);
// error check (shouldn't go above 0x3F)
if (pi_count > 0x47)
{
training_message(channel, rank, byte_lane);
post_code(0xEE, 0xE1);
}
LEAVEFN();
return;
}
// get_rdqs:
//
// This function will return the current RDQS delay on the given channel, rank, byte_lane as an absolute PI count.
// (currently doesn't comprehend rank)
uint32_t get_rdqs(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// PI (1/128 MCLK)
// BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47)
// BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47)
reg = (byte_lane & BIT0) ? (B1RXDQSPICODE) : (B0RXDQSPICODE);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
tempD = isbR32m(DDRPHY, reg);
// Adjust PI_COUNT
pi_count = tempD & 0x7F;
LEAVEFN();
return pi_count;
}
// set_wdqs:
//
// This function will program the WDQS delays based on an absolute amount of PIs.
// (currently doesn't comprehend rank)
void set_wdqs(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count);
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
// BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = (byte_lane & BIT0) ? (BIT19 | BIT18 | BIT17 | BIT16) : (BIT7 | BIT6 | BIT5 | BIT4);
tempD = pi_count / HALF_CLK;
tempD <<= (byte_lane & BIT0) ? (16) : (4);
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
msk = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16);
tempD = pi_count << 16;
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// BL0/1 -> B01DBCTL1[07/10] (+1 select)
// BL0/1 -> B01DBCTL1[01/04] (enable)
reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= (byte_lane & BIT0) ? (BIT4) : (BIT1);
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= (byte_lane & BIT0) ? (BIT10) : (BIT7);
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
training_message(channel, rank, byte_lane);
post_code(0xEE, 0xE2);
}
LEAVEFN();
return;
}
// get_wdqs:
//
// This function will return the amount of WDQS delay on the given channel, rank, byte_lane as an absolute PI count.
// (currently doesn't comprehend rank)
uint32_t get_wdqs(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[07:04] (0x0-0xF)
// BL1 -> B01PTRCTL0[19:16] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= (byte_lane & BIT0) ? (16) : (4);
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = (tempD * HALF_CLK);
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
tempD = isbR32m(DDRPHY, reg);
tempD >>= 16;
tempD &= 0x3F;
// Adjust PI_COUNT
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_wdq:
//
// This function will program the WDQ delays based on an absolute number of PIs.
// (currently doesn't comprehend rank)
void set_wdq(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count);
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
// BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = (byte_lane & BIT0) ? (BIT15 | BIT14 | BIT13 | BIT12) : (BIT3 | BIT2 | BIT1 | BIT0);
tempD = pi_count / HALF_CLK;
tempD <<= (byte_lane & BIT0) ? (12) : (0);
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
msk = (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8);
tempD = pi_count << 8;
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// BL0/1 -> B01DBCTL1[06/09] (+1 select)
// BL0/1 -> B01DBCTL1[00/03] (enable)
reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= (byte_lane & BIT0) ? (BIT3) : (BIT0);
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= (byte_lane & BIT0) ? (BIT9) : (BIT6);
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
training_message(channel, rank, byte_lane);
post_code(0xEE, 0xE3);
}
LEAVEFN();
return;
}
// get_wdq:
//
// This function will return the amount of WDQ delay on the given channel, rank, byte_lane as an absolute PI count.
// (currently doesn't comprehend rank)
uint32_t get_wdq(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// BL0 -> B01PTRCTL0[03:00] (0x0-0xF)
// BL1 -> B01PTRCTL0[15:12] (0x0-0xF)
reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= (byte_lane & BIT0) ? (12) : (0);
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = (tempD * HALF_CLK);
// PI (1/64 MCLK, 1 PIs)
// BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F)
// BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F)
reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0);
reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET));
tempD = isbR32m(DDRPHY, reg);
tempD >>= 8;
tempD &= 0x3F;
// Adjust PI_COUNT
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_wcmd:
//
// This function will program the WCMD delays based on an absolute number of PIs.
void set_wcmd(
uint8_t channel,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CMDPTRREG[11:08] (0x0-0xF)
reg = CMDPTRREG + (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT11 | BIT10 | BIT9 | BIT8);
tempD = pi_count / HALF_CLK;
tempD <<= 8;
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
// CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
// CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
// CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
// CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
// CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
// CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
// CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
reg = CMDDLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24) | (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16)
| (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8) | (BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0);
tempD = (pi_count << 24) | (pi_count << 16) | (pi_count << 8) | (pi_count << 0);
isbM32m(DDRPHY, reg, tempD, msk);
reg = CMDDLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET); // PO
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// CMDCFGREG0[17] (+1 select)
// CMDCFGREG0[16] (enable)
reg = CMDCFGREG0 + (channel * DDRIOCCC_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= BIT16;
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= BIT17;
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
post_code(0xEE, 0xE4);
}
LEAVEFN();
return;
}
// get_wcmd:
//
// This function will return the amount of WCMD delay on the given channel as an absolute PI count.
uint32_t get_wcmd(
uint8_t channel)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CMDPTRREG[11:08] (0x0-0xF)
reg = CMDPTRREG + (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= 8;
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = tempD * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused)
// CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused)
// CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused)
// CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused)
// CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused)
// CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F)
// CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused)
// CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused)
reg = CMDDLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= 16;
tempD &= 0x3F;
// Adjust PI_COUNT
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_wclk:
//
// This function will program the WCLK delays based on an absolute number of PIs.
void set_wclk(
uint8_t channel,
uint8_t rank,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CCPTRREG[15:12] -> CLK1 (0x0-0xF)
// CCPTRREG[11:08] -> CLK0 (0x0-0xF)
reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT15 | BIT14 | BIT13 | BIT12) | (BIT11 | BIT10 | BIT9 | BIT8);
tempD = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8);
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
// ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
reg = (rank) ? (ECCB1DLLPICODER0) : (ECCB1DLLPICODER0);
reg += (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16) | (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8);
tempD = (pi_count << 16) | (pi_count << 8);
isbM32m(DDRPHY, reg, tempD, msk);
reg = (rank) ? (ECCB1DLLPICODER1) : (ECCB1DLLPICODER1);
reg += (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
reg = (rank) ? (ECCB1DLLPICODER2) : (ECCB1DLLPICODER2);
reg += (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
reg = (rank) ? (ECCB1DLLPICODER3) : (ECCB1DLLPICODER3);
reg += (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// CCCFGREG1[11:08] (+1 select)
// CCCFGREG1[03:00] (enable)
reg = CCCFGREG1 + (channel * DDRIOCCC_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= (BIT3 | BIT2 | BIT1 | BIT0); // only ??? matters
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= (BIT11 | BIT10 | BIT9 | BIT8); // only ??? matters
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
post_code(0xEE, 0xE5);
}
LEAVEFN();
return;
}
// get_wclk:
//
// This function will return the amout of WCLK delay on the given channel, rank as an absolute PI count.
uint32_t get_wclk(
uint8_t channel,
uint8_t rank)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CCPTRREG[15:12] -> CLK1 (0x0-0xF)
// CCPTRREG[11:08] -> CLK0 (0x0-0xF)
reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= (rank) ? (12) : (8);
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = tempD * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F)
// ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F)
reg = (rank) ? (ECCB1DLLPICODER0) : (ECCB1DLLPICODER0);
reg += (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= (rank) ? (16) : (8);
tempD &= 0x3F;
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_wctl:
//
// This function will program the WCTL delays based on an absolute number of PIs.
// (currently doesn't comprehend rank)
void set_wctl(
uint8_t channel,
uint8_t rank,
uint32_t pi_count)
{
uint32_t reg;
uint32_t msk;
uint32_t tempD;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CCPTRREG[31:28] (0x0-0xF)
// CCPTRREG[27:24] (0x0-0xF)
reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT31 | BIT30 | BIT29 | BIT28) | (BIT27 | BIT26 | BIT25 | BIT24);
tempD = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24);
isbM32m(DDRPHY, reg, tempD, msk);
// Adjust PI_COUNT
pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// ECCB1DLLPICODER?[29:24] (0x00-0x3F)
// ECCB1DLLPICODER?[29:24] (0x00-0x3F)
reg = ECCB1DLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET);
msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24);
tempD = (pi_count << 24);
isbM32m(DDRPHY, reg, tempD, msk);
reg = ECCB1DLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
reg = ECCB1DLLPICODER2 + (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
reg = ECCB1DLLPICODER3 + (channel * DDRIOCCC_CH_OFFSET);
isbM32m(DDRPHY, reg, tempD, msk);
// DEADBAND
// CCCFGREG1[13:12] (+1 select)
// CCCFGREG1[05:04] (enable)
reg = CCCFGREG1 + (channel * DDRIOCCC_CH_OFFSET);
msk = 0x00;
tempD = 0x00;
// enable
msk |= (BIT5 | BIT4); // only ??? matters
if ((pi_count < EARLY_DB) || (pi_count > LATE_DB))
{
tempD |= msk;
}
// select
msk |= (BIT13 | BIT12); // only ??? matters
if (pi_count < EARLY_DB)
{
tempD |= msk;
}
isbM32m(DDRPHY, reg, tempD, msk);
// error check
if (pi_count > 0x3F)
{
post_code(0xEE, 0xE6);
}
LEAVEFN();
return;
}
// get_wctl:
//
// This function will return the amount of WCTL delay on the given channel, rank as an absolute PI count.
// (currently doesn't comprehend rank)
uint32_t get_wctl(
uint8_t channel,
uint8_t rank)
{
uint32_t reg;
uint32_t tempD;
uint32_t pi_count;
ENTERFN();
// RDPTR (1/2 MCLK, 64 PIs)
// CCPTRREG[31:28] (0x0-0xF)
// CCPTRREG[27:24] (0x0-0xF)
reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= 24;
tempD &= 0xF;
// Adjust PI_COUNT
pi_count = tempD * HALF_CLK;
// PI (1/64 MCLK, 1 PIs)
// ECCB1DLLPICODER?[29:24] (0x00-0x3F)
// ECCB1DLLPICODER?[29:24] (0x00-0x3F)
reg = ECCB1DLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET);
tempD = isbR32m(DDRPHY, reg);
tempD >>= 24;
tempD &= 0x3F;
// Adjust PI_COUNT
pi_count += tempD;
LEAVEFN();
return pi_count;
}
// set_vref:
//
// This function will program the internal Vref setting in a given byte lane in a given channel.
void set_vref(
uint8_t channel,
uint8_t byte_lane,
uint32_t setting)
{
uint32_t reg = (byte_lane & 0x1) ? (B1VREFCTL) : (B0VREFCTL);
ENTERFN();
DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n", channel, byte_lane, setting);
isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)),
(vref_codes[setting] << 2), (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2));
//isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)), (setting<<2), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2));
// need to wait ~300ns for Vref to settle (check that this is necessary)
delay_n(300);
// ??? may need to clear pointers ???
LEAVEFN();
return;
}
// get_vref:
//
// This function will return the internal Vref setting for the given channel, byte_lane;
uint32_t get_vref(
uint8_t channel,
uint8_t byte_lane)
{
uint8_t j;
uint32_t ret_val = sizeof(vref_codes) / 2;
uint32_t reg = (byte_lane & 0x1) ? (B1VREFCTL) : (B0VREFCTL);
uint32_t tempD;
ENTERFN();
tempD = isbR32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)));
tempD >>= 2;
tempD &= 0x3F;
for (j = 0; j < sizeof(vref_codes); j++)
{
if (vref_codes[j] == tempD)
{
ret_val = j;
break;
}
}
LEAVEFN();
return ret_val;
}
// clear_pointers:
//
// This function will be used to clear the pointers in a given byte lane in a given channel.
void clear_pointers(
void)
{
uint8_t channel_i;
uint8_t bl_i;
ENTERFN();
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
{
for (bl_i = 0; bl_i < NUM_BYTE_LANES; bl_i++)
{
isbM32m(DDRPHY, (B01PTRCTL1 + (channel_i * DDRIODQ_CH_OFFSET) + ((bl_i >> 1) * DDRIODQ_BL_OFFSET)), ~(BIT8),
(BIT8));
//delay_m(1); // DEBUG
isbM32m(DDRPHY, (B01PTRCTL1 + (channel_i * DDRIODQ_CH_OFFSET) + ((bl_i >> 1) * DDRIODQ_BL_OFFSET)), (BIT8),
(BIT8));
}
}
LEAVEFN();
return;
}
// void enable_cache:
void enable_cache(
void)
{
// Cache control not used in Quark MRC
return;
}
// void disable_cache:
void disable_cache(
void)
{
// Cache control not used in Quark MRC
return;
}
// Send DRAM command, data should be formated
// using DCMD_Xxxx macro or emrsXCommand structure.
static void dram_init_command(
uint32_t data)
{
Wr32(DCMD, 0, data);
}
// find_rising_edge:
//
// This function will find the rising edge transition on RCVN or WDQS.
void find_rising_edge(
MRCParams_t *mrc_params,
uint32_t delay[],
uint8_t channel,
uint8_t rank,
bool rcvn)
{
#define SAMPLE_CNT 3 // number of sample points
#define SAMPLE_DLY 26 // number of PIs to increment per sample
#define FORWARD true // indicates to increase delays when looking for edge
#define BACKWARD false // indicates to decrease delays when looking for edge
bool all_edges_found; // determines stop condition
bool direction[NUM_BYTE_LANES]; // direction indicator
uint8_t sample_i; // sample counter
uint8_t bl_i; // byte lane counter
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
uint32_t sample_result[SAMPLE_CNT]; // results of "sample_dqs()"
uint32_t tempD; // temporary DWORD
uint32_t transition_pattern;
ENTERFN();
// select hte and request initial configuration
select_hte(mrc_params);
first_run = 1;
// Take 3 sample points (T1,T2,T3) to obtain a transition pattern.
for (sample_i = 0; sample_i < SAMPLE_CNT; sample_i++)
{
// program the desired delays for sample
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
{
// increase sample delay by 26 PI (0.2 CLK)
if (rcvn)
{
set_rcvn(channel, rank, bl_i, delay[bl_i] + (sample_i * SAMPLE_DLY));
}
else
{
set_wdqs(channel, rank, bl_i, delay[bl_i] + (sample_i * SAMPLE_DLY));
}
} // bl_i loop
// take samples (Tsample_i)
sample_result[sample_i] = sample_dqs(mrc_params, channel, rank, rcvn);
DPF(D_TRN, "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n",
(rcvn ? "RCVN" : "WDQS"), channel, rank,
sample_i, sample_i * SAMPLE_DLY, sample_result[sample_i]);
} // sample_i loop
// This pattern will help determine where we landed and ultimately how to place RCVEN/WDQS.
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
{
// build "transition_pattern" (MSB is 1st sample)
transition_pattern = 0x00;
for (sample_i = 0; sample_i < SAMPLE_CNT; sample_i++)
{
transition_pattern |= ((sample_result[sample_i] & (1 << bl_i)) >> bl_i) << (SAMPLE_CNT - 1 - sample_i);
} // sample_i loop
DPF(D_TRN, "=== transition pattern %d\n", transition_pattern);
// set up to look for rising edge based on "transition_pattern"
switch (transition_pattern)
{
case 0x00: // sampled 0->0->0
// move forward from T3 looking for 0->1
delay[bl_i] += 2 * SAMPLE_DLY;
direction[bl_i] = FORWARD;
break;
case 0x01: // sampled 0->0->1
case 0x05: // sampled 1->0->1 (bad duty cycle) *HSD#237503*
// move forward from T2 looking for 0->1
delay[bl_i] += 1 * SAMPLE_DLY;
direction[bl_i] = FORWARD;
break;
// HSD#237503
// case 0x02: // sampled 0->1->0 (bad duty cycle)
// training_message(channel, rank, bl_i);
// post_code(0xEE, 0xE8);
// break;
case 0x02: // sampled 0->1->0 (bad duty cycle) *HSD#237503*
case 0x03: // sampled 0->1->1
// move forward from T1 looking for 0->1
delay[bl_i] += 0 * SAMPLE_DLY;
direction[bl_i] = FORWARD;
break;
case 0x04: // sampled 1->0->0 (assumes BL8, HSD#234975)
// move forward from T3 looking for 0->1
delay[bl_i] += 2 * SAMPLE_DLY;
direction[bl_i] = FORWARD;
break;
// HSD#237503
// case 0x05: // sampled 1->0->1 (bad duty cycle)
// training_message(channel, rank, bl_i);
// post_code(0xEE, 0xE9);
// break;
case 0x06: // sampled 1->1->0
case 0x07: // sampled 1->1->1
// move backward from T1 looking for 1->0
delay[bl_i] += 0 * SAMPLE_DLY;
direction[bl_i] = BACKWARD;
break;
default:
post_code(0xEE, 0xEE);
break;
} // transition_pattern switch
// program delays
if (rcvn)
{
set_rcvn(channel, rank, bl_i, delay[bl_i]);
}
else
{
set_wdqs(channel, rank, bl_i, delay[bl_i]);
}
} // bl_i loop
// Based on the observed transition pattern on the byte lane,
// begin looking for a rising edge with single PI granularity.
do
{
all_edges_found = true; // assume all byte lanes passed
tempD = sample_dqs(mrc_params, channel, rank, rcvn); // take a sample
// check all each byte lane for proper edge
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
{
if (tempD & (1 << bl_i))
{
// sampled "1"
if (direction[bl_i] == BACKWARD)
{
// keep looking for edge on this byte lane
all_edges_found = false;
delay[bl_i] -= 1;
if (rcvn)
{
set_rcvn(channel, rank, bl_i, delay[bl_i]);
}
else
{
set_wdqs(channel, rank, bl_i, delay[bl_i]);
}
}
}
else
{
// sampled "0"
if (direction[bl_i] == FORWARD)
{
// keep looking for edge on this byte lane
all_edges_found = false;
delay[bl_i] += 1;
if (rcvn)
{
set_rcvn(channel, rank, bl_i, delay[bl_i]);
}
else
{
set_wdqs(channel, rank, bl_i, delay[bl_i]);
}
}
}
} // bl_i loop
} while (!all_edges_found);
// restore DDR idle state
dram_init_command(DCMD_PREA(rank));
DPF(D_TRN, "Delay %03X %03X %03X %03X\n",
delay[0], delay[1], delay[2], delay[3]);
LEAVEFN();
return;
}
// sample_dqs:
//
// This function will sample the DQTRAINSTS registers in the given channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'.
// It will return an encoded DWORD in which each bit corresponds to the sampled value on the byte lane.
uint32_t sample_dqs(
MRCParams_t *mrc_params,
uint8_t channel,
uint8_t rank,
bool rcvn)
{
uint8_t j; // just a counter
uint8_t bl_i; // which BL in the module (always 2 per module)
uint8_t bl_grp; // which BL module
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
uint32_t msk[2]; // BLx in module
uint32_t sampled_val[SAMPLE_SIZE]; // DQTRAINSTS register contents for each sample
uint32_t num_0s; // tracks the number of '0' samples
uint32_t num_1s; // tracks the number of '1' samples
uint32_t ret_val = 0x00; // assume all '0' samples
uint32_t address = get_addr(mrc_params, channel, rank);
// initialise "msk[]"
msk[0] = (rcvn) ? (BIT1) : (BIT9); // BL0
msk[1] = (rcvn) ? (BIT0) : (BIT8); // BL1
// cycle through each byte lane group
for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++)
{
// take SAMPLE_SIZE samples
for (j = 0; j < SAMPLE_SIZE; j++)
{
HteMemOp(address, first_run, rcvn?0:1);
first_run = 0;
// record the contents of the proper DQTRAINSTS register
sampled_val[j] = isbR32m(DDRPHY, (DQTRAINSTS + (bl_grp * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)));
}
// look for a majority value ( (SAMPLE_SIZE/2)+1 ) on the byte lane
// and set that value in the corresponding "ret_val" bit
for (bl_i = 0; bl_i < 2; bl_i++)
{
num_0s = 0x00; // reset '0' tracker for byte lane
num_1s = 0x00; // reset '1' tracker for byte lane
for (j = 0; j < SAMPLE_SIZE; j++)
{
if (sampled_val[j] & msk[bl_i])
{
num_1s++;
}
else
{
num_0s++;
}
}
if (num_1s > num_0s)
{
ret_val |= (1 << (bl_i + (bl_grp * 2)));
}
}
}
// "ret_val.0" contains the status of BL0
// "ret_val.1" contains the status of BL1
// "ret_val.2" contains the status of BL2
// etc.
return ret_val;
}
// get_addr:
//
// This function will return a 32 bit address in the desired channel and rank.
uint32_t get_addr(
MRCParams_t *mrc_params,
uint8_t channel,
uint8_t rank)
{
uint32_t offset = 0x02000000; // 32MB
// Begin product specific code
if (channel > 0)
{
DPF(D_ERROR, "ILLEGAL CHANNEL\n");
DEAD_LOOP();
}
if (rank > 1)
{
DPF(D_ERROR, "ILLEGAL RANK\n");
DEAD_LOOP();
}
// use 256MB lowest density as per DRP == 0x0003
offset += rank * (256 * 1024 * 1024);
return offset;
}
// byte_lane_mask:
//
// This function will return a 32 bit mask that will be used to check for byte lane failures.
uint32_t byte_lane_mask(
MRCParams_t *mrc_params)
{
uint32_t j;
uint32_t ret_val = 0x00;
// set "ret_val" based on NUM_BYTE_LANES such that you will check only BL0 in "result"
// (each bit in "result" represents a byte lane)
for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES)
{
ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES));
}
// HSD#235037
// need to adjust the mask for 16-bit mode
if (mrc_params->channel_width == x16)
{
ret_val |= (ret_val << 2);
}
return ret_val;
}
// read_tsc:
//
// This function will do some assembly to return TSC register contents as a uint64_t.
uint64_t read_tsc(
void)
{
volatile uint64_t tsc; // EDX:EAX
#if defined (SIM) || defined (GCC)
volatile uint32_t tscH; // EDX
volatile uint32_t tscL;// EAX
asm("rdtsc":"=a"(tscL),"=d"(tscH));
tsc = tscH;
tsc = (tsc<<32)|tscL;
#else
tsc = __rdtsc();
#endif
return tsc;
}
// get_tsc_freq:
//
// This function returns the TSC frequency in MHz
uint32_t get_tsc_freq(
void)
{
static uint32_t freq[] =
{ 533, 400, 200, 100 };
uint32_t fuse;
#if 0
fuse = (isbR32m(FUSE, 0) >> 12) & (BIT1|BIT0);
#else
// todo!!! Fixed 533MHz for emulation or debugging
fuse = 0;
#endif
return freq[fuse];
}
#ifndef SIM
// delay_n:
//
// This is a simple delay function.
// It takes "nanoseconds" as a parameter.
void delay_n(
uint32_t nanoseconds)
{
// 1000 MHz clock has 1ns period --> no conversion required
uint64_t final_tsc = read_tsc();
final_tsc += ((get_tsc_freq() * (nanoseconds)) / 1000);
while (read_tsc() < final_tsc)
;
return;
}
#endif
// delay_u:
//
// This is a simple delay function.
// It takes "microseconds as a parameter.
void delay_u(
uint32_t microseconds)
{
// 64 bit math is not an option, just use loops
while (microseconds--)
{
delay_n(1000);
}
return;
}
// delay_m:
//
// This is a simple delay function.
// It takes "milliseconds" as a parameter.
void delay_m(
uint32_t milliseconds)
{
// 64 bit math is not an option, just use loops
while (milliseconds--)
{
delay_u(1000);
}
return;
}
// delay_s:
//
// This is a simple delay function.
// It takes "seconds" as a parameter.
void delay_s(
uint32_t seconds)
{
// 64 bit math is not an option, just use loops
while (seconds--)
{
delay_m(1000);
}
return;
}
// post_code:
//
// This function will output the POST CODE to the four 7-Segment LED displays.
void post_code(
uint8_t major,
uint8_t minor)
{
#ifdef EMU
// Update global variable for execution tracking in debug env
PostCode = ((major << 8) | minor);
#endif
// send message to UART
DPF(D_INFO, "POST: 0x%01X%02X\n", major, minor);
// error check:
if (major == 0xEE)
{
// todo!!! Consider updating error status and exit MRC
#ifdef SIM
// enable Ctrl-C handling
for(;;) delay_n(100);
#else
DEAD_LOOP();
#endif
}
}
void training_message(
uint8_t channel,
uint8_t rank,
uint8_t byte_lane)
{
// send message to UART
DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane);
return;
}
void print_timings(
MRCParams_t *mrc_params)
{
uint8_t algo_i;
uint8_t channel_i;
uint8_t rank_i;
uint8_t bl_i;
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1;
DPF(D_INFO, "\n---------------------------");
DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3");
DPF(D_INFO, "\n===========================");
for (algo_i = 0; algo_i < eMAX_ALGOS; algo_i++)
{
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
{
if (mrc_params->channel_enables & (1 << channel_i))
{
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
{
if (mrc_params->rank_enables & (1 << rank_i))
{
switch (algo_i)
{
case eRCVN:
DPF(D_INFO, "\nRCVN[%02d:%02d]", channel_i, rank_i);
break;
case eWDQS:
DPF(D_INFO, "\nWDQS[%02d:%02d]", channel_i, rank_i);
break;
case eWDQx:
DPF(D_INFO, "\nWDQx[%02d:%02d]", channel_i, rank_i);
break;
case eRDQS:
DPF(D_INFO, "\nRDQS[%02d:%02d]", channel_i, rank_i);
break;
case eVREF:
DPF(D_INFO, "\nVREF[%02d:%02d]", channel_i, rank_i);
break;
case eWCMD:
DPF(D_INFO, "\nWCMD[%02d:%02d]", channel_i, rank_i);
break;
case eWCTL:
DPF(D_INFO, "\nWCTL[%02d:%02d]", channel_i, rank_i);
break;
case eWCLK:
DPF(D_INFO, "\nWCLK[%02d:%02d]", channel_i, rank_i);
break;
default:
break;
} // algo_i switch
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
{
switch (algo_i)
{
case eRCVN:
DPF(D_INFO, " %03d", get_rcvn(channel_i, rank_i, bl_i));
break;
case eWDQS:
DPF(D_INFO, " %03d", get_wdqs(channel_i, rank_i, bl_i));
break;
case eWDQx:
DPF(D_INFO, " %03d", get_wdq(channel_i, rank_i, bl_i));
break;
case eRDQS:
DPF(D_INFO, " %03d", get_rdqs(channel_i, rank_i, bl_i));
break;
case eVREF:
DPF(D_INFO, " %03d", get_vref(channel_i, bl_i));
break;
case eWCMD:
DPF(D_INFO, " %03d", get_wcmd(channel_i));
break;
case eWCTL:
DPF(D_INFO, " %03d", get_wctl(channel_i, rank_i));
break;
case eWCLK:
DPF(D_INFO, " %03d", get_wclk(channel_i, rank_i));
break;
default:
break;
} // algo_i switch
} // bl_i loop
} // if rank_i enabled
} // rank_i loop
} // if channel_i enabled
} // channel_i loop
} // algo_i loop
DPF(D_INFO, "\n---------------------------");
DPF(D_INFO, "\n");
return;
}
// 32 bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1
// The function takes pointer to previous 32 bit value and modifies it to next value.
void lfsr32(
uint32_t *lfsr_ptr)
{
uint32_t bit;
uint32_t lfsr;
uint32_t i;
lfsr = *lfsr_ptr;
for (i = 0; i < 32; i++)
{
bit = 1 ^ (lfsr & BIT0);
bit = bit ^ ((lfsr & BIT1) >> 1);
bit = bit ^ ((lfsr & BIT2) >> 2);
bit = bit ^ ((lfsr & BIT22) >> 22);
lfsr = ((lfsr >> 1) | (bit << 31));
}
*lfsr_ptr = lfsr;
return;
}
// The purpose of this function is to ensure the SEC comes out of reset
// and IA initiates the SEC enabling Memory Scrambling.
void enable_scrambling(
MRCParams_t *mrc_params)
{
uint32_t lfsr = 0;
uint8_t i;
if (mrc_params->scrambling_enables == 0)
return;
ENTERFN();
// 32 bit seed is always stored in BIOS NVM.
lfsr = mrc_params->timings.scrambler_seed;
if (mrc_params->boot_mode == bmCold)
{
// factory value is 0 and in first boot, a clock based seed is loaded.
if (lfsr == 0)
{
lfsr = read_tsc() & 0x0FFFFFFF; // get seed from system clock and make sure it is not all 1's
}
// need to replace scrambler
// get next 32bit LFSR 16 times which is the last part of the previous scrambler vector.
else
{
for (i = 0; i < 16; i++)
{
lfsr32(&lfsr);
}
}
mrc_params->timings.scrambler_seed = lfsr; // save new seed.
} // if (cold_boot)
// In warm boot or S3 exit, we have the previous seed.
// In cold boot, we have the last 32bit LFSR which is the new seed.
lfsr32(&lfsr); // shift to next value
isbW32m(MCU, SCRMSEED, (lfsr & 0x0003FFFF));
for (i = 0; i < 2; i++)
{
isbW32m(MCU, SCRMLO + i, (lfsr & 0xAAAAAAAA));
}
LEAVEFN();
return;
}
// This function will store relevant timing data
// This data will be used on subsequent boots to speed up boot times
// and is required for Suspend To RAM capabilities.
void store_timings(
MRCParams_t *mrc_params)
{
uint8_t ch, rk, bl;
MrcTimings_t *mt = &mrc_params->timings;
for (ch = 0; ch < NUM_CHANNELS; ch++)
{
for (rk = 0; rk < NUM_RANKS; rk++)
{
for (bl = 0; bl < NUM_BYTE_LANES; bl++)
{
mt->rcvn[ch][rk][bl] = get_rcvn(ch, rk, bl); // RCVN
mt->rdqs[ch][rk][bl] = get_rdqs(ch, rk, bl); // RDQS
mt->wdqs[ch][rk][bl] = get_wdqs(ch, rk, bl); // WDQS
mt->wdq[ch][rk][bl] = get_wdq(ch, rk, bl); // WDQ
if (rk == 0)
{
mt->vref[ch][bl] = get_vref(ch, bl); // VREF (RANK0 only)
}
}
mt->wctl[ch][rk] = get_wctl(ch, rk); // WCTL
}
mt->wcmd[ch] = get_wcmd(ch); // WCMD
}
// need to save for a case of changing frequency after warm reset
mt->ddr_speed = mrc_params->ddr_speed;
return;
}
// This function will retrieve relevant timing data
// This data will be used on subsequent boots to speed up boot times
// and is required for Suspend To RAM capabilities.
void restore_timings(
MRCParams_t *mrc_params)
{
uint8_t ch, rk, bl;
const MrcTimings_t *mt = &mrc_params->timings;
for (ch = 0; ch < NUM_CHANNELS; ch++)
{
for (rk = 0; rk < NUM_RANKS; rk++)
{
for (bl = 0; bl < NUM_BYTE_LANES; bl++)
{
set_rcvn(ch, rk, bl, mt->rcvn[ch][rk][bl]); // RCVN
set_rdqs(ch, rk, bl, mt->rdqs[ch][rk][bl]); // RDQS
set_wdqs(ch, rk, bl, mt->wdqs[ch][rk][bl]); // WDQS
set_wdq(ch, rk, bl, mt->wdq[ch][rk][bl]); // WDQ
if (rk == 0)
{
set_vref(ch, bl, mt->vref[ch][bl]); // VREF (RANK0 only)
}
}
set_wctl(ch, rk, mt->wctl[ch][rk]); // WCTL
}
set_wcmd(ch, mt->wcmd[ch]); // WCMD
}
return;
}
// Configure default settings normally set as part of read training
// Some defaults have to be set earlier as they may affect earlier
// training steps.
void default_timings(
MRCParams_t *mrc_params)
{
uint8_t ch, rk, bl;
for (ch = 0; ch < NUM_CHANNELS; ch++)
{
for (rk = 0; rk < NUM_RANKS; rk++)
{
for (bl = 0; bl < NUM_BYTE_LANES; bl++)
{
set_rdqs(ch, rk, bl, 24); // RDQS
if (rk == 0)
{
set_vref(ch, bl, 32); // VREF (RANK0 only)
}
}
}
}
return;
}