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
2646 lines
99 KiB
C
2646 lines
99 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.
|
|
*
|
|
* This file contains all of the Cat Mountain Memory Reference Code (MRC).
|
|
*
|
|
* These functions are generic and should work for any Cat Mountain config.
|
|
*
|
|
* MRC requires two data structures to be passed in which are initialised by "PreMemInit()".
|
|
*
|
|
* The basic flow is as follows:
|
|
* 01) Check for supported DDR speed configuration
|
|
* 02) Set up MEMORY_MANAGER buffer as pass-through (POR)
|
|
* 03) Set Channel Interleaving Mode and Channel Stride to the most aggressive setting possible
|
|
* 04) Set up the MCU logic
|
|
* 05) Set up the DDR_PHY logic
|
|
* 06) Initialise the DRAMs (JEDEC)
|
|
* 07) Perform the Receive Enable Calibration algorithm
|
|
* 08) Perform the Write Leveling algorithm
|
|
* 09) Perform the Read Training algorithm (includes internal Vref)
|
|
* 10) Perform the Write Training algorithm
|
|
* 11) Set Channel Interleaving Mode and Channel Stride to the desired settings
|
|
*
|
|
* Dunit configuration based on Valleyview MRC.
|
|
*
|
|
***************************************************************************/
|
|
|
|
#include "mrc.h"
|
|
#include "memory_options.h"
|
|
|
|
#include "meminit.h"
|
|
#include "meminit_utils.h"
|
|
#include "hte.h"
|
|
#include "io.h"
|
|
|
|
// Override ODT to off state if requested
|
|
#define DRMC_DEFAULT (mrc_params->rd_odt_value==0?BIT12:0)
|
|
|
|
|
|
// tRFC values (in picoseconds) per density
|
|
const uint32_t tRFC[5] =
|
|
{
|
|
90000, // 512Mb
|
|
110000, // 1Gb
|
|
160000, // 2Gb
|
|
300000, // 4Gb
|
|
350000, // 8Gb
|
|
};
|
|
|
|
// tCK clock period in picoseconds per speed index 800, 1066, 1333
|
|
const uint32_t tCK[3] =
|
|
{
|
|
2500,
|
|
1875,
|
|
1500
|
|
};
|
|
|
|
#ifdef SIM
|
|
// Select static timings specific to simulation environment
|
|
#define PLATFORM_ID 0
|
|
#else
|
|
// Select static timings specific to ClantonPeek platform
|
|
#define PLATFORM_ID 1
|
|
#endif
|
|
|
|
|
|
// Global variables
|
|
const uint16_t ddr_wclk[] =
|
|
{193, 158};
|
|
|
|
const uint16_t ddr_wctl[] =
|
|
{ 1, 217};
|
|
|
|
const uint16_t ddr_wcmd[] =
|
|
{ 1, 220};
|
|
|
|
|
|
#ifdef BACKUP_RCVN
|
|
const uint16_t ddr_rcvn[] =
|
|
{129, 498};
|
|
#endif // BACKUP_RCVN
|
|
|
|
#ifdef BACKUP_WDQS
|
|
const uint16_t ddr_wdqs[] =
|
|
{ 65, 289};
|
|
#endif // BACKUP_WDQS
|
|
|
|
#ifdef BACKUP_RDQS
|
|
const uint8_t ddr_rdqs[] =
|
|
{ 32, 24};
|
|
#endif // BACKUP_RDQS
|
|
|
|
#ifdef BACKUP_WDQ
|
|
const uint16_t ddr_wdq[] =
|
|
{ 32, 257};
|
|
#endif // BACKUP_WDQ
|
|
|
|
|
|
|
|
// Select MEMORY_MANAGER as the source for PRI interface
|
|
static void select_memory_manager(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDCO Dco;
|
|
|
|
ENTERFN();
|
|
|
|
Dco.raw = isbR32m(MCU, DCO);
|
|
Dco.field.PMICTL = 0; //0 - PRI owned by MEMORY_MANAGER
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Select HTE as the source for PRI interface
|
|
void select_hte(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDCO Dco;
|
|
|
|
ENTERFN();
|
|
|
|
Dco.raw = isbR32m(MCU, DCO);
|
|
Dco.field.PMICTL = 1; //1 - PRI owned by HTE
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// Send DRAM wake command using special MCU side-band WAKE opcode
|
|
static void dram_wake_command(
|
|
void)
|
|
{
|
|
ENTERFN();
|
|
|
|
Wr32(MMIO, PCIADDR(0,0,0,SB_PACKET_REG),
|
|
(uint32_t) SB_COMMAND(SB_WAKE_CMND_OPCODE, MCU, 0));
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Stop self refresh driven by MCU
|
|
static void clear_self_refresh(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
ENTERFN();
|
|
|
|
// clear the PMSTS Channel Self Refresh bits
|
|
isbM32m(MCU, PMSTS, BIT0, BIT0);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Configure MCU before jedec init sequence
|
|
static void prog_decode_before_jedec(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDRP Drp;
|
|
RegDRCF Drfc;
|
|
RegDCAL Dcal;
|
|
RegDSCH Dsch;
|
|
RegDPMC0 Dpmc0;
|
|
|
|
ENTERFN();
|
|
|
|
// Disable power saving features
|
|
Dpmc0.raw = isbR32m(MCU, DPMC0);
|
|
Dpmc0.field.CLKGTDIS = 1;
|
|
Dpmc0.field.DISPWRDN = 1;
|
|
Dpmc0.field.DYNSREN = 0;
|
|
Dpmc0.field.PCLSTO = 0;
|
|
isbW32m(MCU, DPMC0, Dpmc0.raw);
|
|
|
|
// Disable out of order transactions
|
|
Dsch.raw = isbR32m(MCU, DSCH);
|
|
Dsch.field.OOODIS = 1;
|
|
Dsch.field.NEWBYPDIS = 1;
|
|
isbW32m(MCU, DSCH, Dsch.raw);
|
|
|
|
// Disable issuing the REF command
|
|
Drfc.raw = isbR32m(MCU, DRFC);
|
|
Drfc.field.tREFI = 0;
|
|
isbW32m(MCU, DRFC, Drfc.raw);
|
|
|
|
// Disable ZQ calibration short
|
|
Dcal.raw = isbR32m(MCU, DCAL);
|
|
Dcal.field.ZQCINT = 0;
|
|
Dcal.field.SRXZQCL = 0;
|
|
isbW32m(MCU, DCAL, Dcal.raw);
|
|
|
|
// Training performed in address mode 0, rank population has limited impact, however
|
|
// simulator complains if enabled non-existing rank.
|
|
Drp.raw = 0;
|
|
if (mrc_params->rank_enables & 1)
|
|
Drp.field.rank0Enabled = 1;
|
|
if (mrc_params->rank_enables & 2)
|
|
Drp.field.rank1Enabled = 1;
|
|
isbW32m(MCU, DRP, Drp.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// After Cold Reset, BIOS should set COLDWAKE bit to 1 before
|
|
// sending the WAKE message to the Dunit.
|
|
// For Standby Exit, or any other mode in which the DRAM is in
|
|
// SR, this bit must be set to 0.
|
|
static void perform_ddr_reset(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
ENTERFN();
|
|
|
|
// Set COLDWAKE bit before sending the WAKE message
|
|
isbM32m(MCU, DRMC, BIT16, BIT16);
|
|
|
|
// Send wake command to DUNIT (MUST be done before JEDEC)
|
|
dram_wake_command();
|
|
|
|
// Set default value
|
|
isbW32m(MCU, DRMC, DRMC_DEFAULT);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Dunit Initialisation Complete.
|
|
// Indicates that initialisation of the Dunit has completed.
|
|
// Memory accesses are permitted and maintenance operation
|
|
// begins. Until this bit is set to a 1, the memory controller will
|
|
// not accept DRAM requests from the MEMORY_MANAGER or HTE.
|
|
static void set_ddr_init_complete(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDCO Dco;
|
|
|
|
ENTERFN();
|
|
|
|
Dco.raw = isbR32m(MCU, DCO);
|
|
Dco.field.PMICTL = 0; //0 - PRI owned by MEMORY_MANAGER
|
|
Dco.field.IC = 1; //1 - initialisation complete
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
static void prog_page_ctrl(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDPMC0 Dpmc0;
|
|
|
|
ENTERFN();
|
|
|
|
Dpmc0.raw = isbR32m(MCU, DPMC0);
|
|
|
|
Dpmc0.field.PCLSTO = 0x4;
|
|
Dpmc0.field.PREAPWDEN = 1;
|
|
|
|
isbW32m(MCU, DPMC0, Dpmc0.raw);
|
|
}
|
|
|
|
// Configure MCU Power Management Control Register
|
|
// and Scheduler Control Register.
|
|
static void prog_ddr_control(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDSCH Dsch;
|
|
RegDPMC0 Dpmc0;
|
|
|
|
ENTERFN();
|
|
|
|
Dpmc0.raw = isbR32m(MCU, DPMC0);
|
|
Dsch.raw = isbR32m(MCU, DSCH);
|
|
|
|
Dpmc0.field.DISPWRDN = mrc_params->power_down_disable;
|
|
Dpmc0.field.CLKGTDIS = 0;
|
|
Dpmc0.field.PCLSTO = 4;
|
|
Dpmc0.field.PREAPWDEN = 1;
|
|
|
|
Dsch.field.OOODIS = 0;
|
|
Dsch.field.OOOST3DIS = 0;
|
|
Dsch.field.NEWBYPDIS = 0;
|
|
|
|
isbW32m(MCU, DSCH, Dsch.raw);
|
|
isbW32m(MCU, DPMC0, Dpmc0.raw);
|
|
|
|
// CMDTRIST = 2h - CMD/ADDR are tristated when no valid command
|
|
isbM32m(MCU, DPMC1, 2 << 4, BIT5|BIT4);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// After training complete configure MCU Rank Population Register
|
|
// specifying: ranks enabled, device width, density, address mode.
|
|
static void prog_dra_drb(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDRP Drp;
|
|
RegDCO Dco;
|
|
|
|
ENTERFN();
|
|
|
|
Dco.raw = isbR32m(MCU, DCO);
|
|
Dco.field.IC = 0;
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
Drp.raw = 0;
|
|
if (mrc_params->rank_enables & 1)
|
|
Drp.field.rank0Enabled = 1;
|
|
if (mrc_params->rank_enables & 2)
|
|
Drp.field.rank1Enabled = 1;
|
|
if (mrc_params->dram_width == x16)
|
|
{
|
|
Drp.field.dimm0DevWidth = 1;
|
|
Drp.field.dimm1DevWidth = 1;
|
|
}
|
|
// Density encoding in DRAMParams_t 0=512Mb, 1=Gb, 2=2Gb, 3=4Gb
|
|
// has to be mapped RANKDENSx encoding (0=1Gb)
|
|
Drp.field.dimm0DevDensity = mrc_params->params.DENSITY - 1;
|
|
Drp.field.dimm1DevDensity = mrc_params->params.DENSITY - 1;
|
|
|
|
// Address mode can be overwritten if ECC enabled
|
|
Drp.field.addressMap = mrc_params->address_mode;
|
|
|
|
isbW32m(MCU, DRP, Drp.raw);
|
|
|
|
Dco.field.PMICTL = 0; //0 - PRI owned by MEMORY_MANAGER
|
|
Dco.field.IC = 1; //1 - initialisation complete
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Configure refresh rate and short ZQ calibration interval.
|
|
// Activate dynamic self refresh.
|
|
static void change_refresh_period(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDRCF Drfc;
|
|
RegDCAL Dcal;
|
|
RegDPMC0 Dpmc0;
|
|
|
|
ENTERFN();
|
|
|
|
Drfc.raw = isbR32m(MCU, DRFC);
|
|
Drfc.field.tREFI = mrc_params->refresh_rate;
|
|
Drfc.field.REFDBTCLR = 1;
|
|
isbW32m(MCU, DRFC, Drfc.raw);
|
|
|
|
Dcal.raw = isbR32m(MCU, DCAL);
|
|
Dcal.field.ZQCINT = 3; // 63ms
|
|
isbW32m(MCU, DCAL, Dcal.raw);
|
|
|
|
Dpmc0.raw = isbR32m(MCU, DPMC0);
|
|
Dpmc0.field.ENPHYCLKGATE = 1;
|
|
Dpmc0.field.DYNSREN = 1;
|
|
isbW32m(MCU, DPMC0, Dpmc0.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// Send DRAM wake command
|
|
static void perform_wake(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
ENTERFN();
|
|
|
|
dram_wake_command();
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// prog_ddr_timing_control (aka mcu_init):
|
|
// POST_CODE[major] == 0x02
|
|
//
|
|
// It will initialise timing registers in the MCU (DTR0..DTR4).
|
|
static void prog_ddr_timing_control(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint8_t TCL, WL;
|
|
uint8_t TRP, TRCD, TRAS, TRFC, TWR, TWTR, TRRD, TRTP, TFAW;
|
|
uint32_t TCK;
|
|
|
|
RegDTR0 Dtr0;
|
|
RegDTR1 Dtr1;
|
|
RegDTR2 Dtr2;
|
|
RegDTR3 Dtr3;
|
|
RegDTR4 Dtr4;
|
|
|
|
ENTERFN();
|
|
|
|
// mcu_init starts
|
|
post_code(0x02, 0x00);
|
|
|
|
Dtr0.raw = isbR32m(MCU, DTR0);
|
|
Dtr1.raw = isbR32m(MCU, DTR1);
|
|
Dtr2.raw = isbR32m(MCU, DTR2);
|
|
Dtr3.raw = isbR32m(MCU, DTR3);
|
|
Dtr4.raw = isbR32m(MCU, DTR4);
|
|
|
|
TCK = tCK[mrc_params->ddr_speed]; // Clock in picoseconds
|
|
TCL = mrc_params->params.tCL; // CAS latency in clocks
|
|
TRP = TCL; // Per CAT MRC
|
|
TRCD = TCL; // Per CAT MRC
|
|
TRAS = MCEIL(mrc_params->params.tRAS, TCK);
|
|
TRFC = MCEIL(tRFC[mrc_params->params.DENSITY], TCK);
|
|
TWR = MCEIL(15000, TCK); // Per JEDEC: tWR=15000ps DDR2/3 from 800-1600
|
|
|
|
TWTR = MCEIL(mrc_params->params.tWTR, TCK);
|
|
TRRD = MCEIL(mrc_params->params.tRRD, TCK);
|
|
TRTP = 4; // Valid for 800 and 1066, use 5 for 1333
|
|
TFAW = MCEIL(mrc_params->params.tFAW, TCK);
|
|
|
|
WL = 5 + mrc_params->ddr_speed;
|
|
|
|
Dtr0.field.dramFrequency = mrc_params->ddr_speed;
|
|
|
|
Dtr0.field.tCL = TCL - 5; //Convert from TCL (DRAM clocks) to VLV indx
|
|
Dtr0.field.tRP = TRP - 5; //5 bit DRAM Clock
|
|
Dtr0.field.tRCD = TRCD - 5; //5 bit DRAM Clock
|
|
|
|
Dtr1.field.tWCL = WL - 3; //Convert from WL (DRAM clocks) to VLV indx
|
|
Dtr1.field.tWTP = WL + 4 + TWR - 14; //Change to tWTP
|
|
Dtr1.field.tRTP = MMAX(TRTP, 4) - 3; //4 bit DRAM Clock
|
|
Dtr1.field.tRRD = TRRD - 4; //4 bit DRAM Clock
|
|
Dtr1.field.tCMD = 1; //2N
|
|
Dtr1.field.tRAS = TRAS - 14; //6 bit DRAM Clock
|
|
|
|
Dtr1.field.tFAW = ((TFAW + 1) >> 1) - 5; //4 bit DRAM Clock
|
|
Dtr1.field.tCCD = 0; //Set 4 Clock CAS to CAS delay (multi-burst)
|
|
Dtr2.field.tRRDR = 1;
|
|
Dtr2.field.tWWDR = 2;
|
|
Dtr2.field.tRWDR = 2;
|
|
Dtr3.field.tWRDR = 2;
|
|
Dtr3.field.tWRDD = 2;
|
|
|
|
if (mrc_params->ddr_speed == DDRFREQ_800)
|
|
{
|
|
// Extended RW delay (+1)
|
|
Dtr3.field.tRWSR = TCL - 5 + 1;
|
|
}
|
|
else if(mrc_params->ddr_speed == DDRFREQ_1066)
|
|
{
|
|
// Extended RW delay (+1)
|
|
Dtr3.field.tRWSR = TCL - 5 + 1;
|
|
}
|
|
|
|
Dtr3.field.tWRSR = 4 + WL + TWTR - 11;
|
|
|
|
if (mrc_params->ddr_speed == DDRFREQ_800)
|
|
{
|
|
Dtr3.field.tXP = MMAX(0, 1 - Dtr1.field.tCMD);
|
|
}
|
|
else
|
|
{
|
|
Dtr3.field.tXP = MMAX(0, 2 - Dtr1.field.tCMD);
|
|
}
|
|
|
|
Dtr4.field.WRODTSTRT = Dtr1.field.tCMD;
|
|
Dtr4.field.WRODTSTOP = Dtr1.field.tCMD;
|
|
Dtr4.field.RDODTSTRT = Dtr1.field.tCMD + Dtr0.field.tCL - Dtr1.field.tWCL + 2; //Convert from WL (DRAM clocks) to VLV indx
|
|
Dtr4.field.RDODTSTOP = Dtr1.field.tCMD + Dtr0.field.tCL - Dtr1.field.tWCL + 2;
|
|
Dtr4.field.TRGSTRDIS = 0;
|
|
Dtr4.field.ODTDIS = 0;
|
|
|
|
isbW32m(MCU, DTR0, Dtr0.raw);
|
|
isbW32m(MCU, DTR1, Dtr1.raw);
|
|
isbW32m(MCU, DTR2, Dtr2.raw);
|
|
isbW32m(MCU, DTR3, Dtr3.raw);
|
|
isbW32m(MCU, DTR4, Dtr4.raw);
|
|
|
|
LEAVEFN();
|
|
}
|
|
|
|
// ddrphy_init:
|
|
// POST_CODE[major] == 0x03
|
|
//
|
|
// This function performs some initialisation on the DDRIO unit.
|
|
// This function is dependent on BOARD_ID, DDR_SPEED, and CHANNEL_ENABLES.
|
|
static void ddrphy_init(MRCParams_t *mrc_params)
|
|
{
|
|
uint32_t tempD; // temporary DWORD
|
|
uint8_t channel_i; // channel counter
|
|
uint8_t rank_i; // rank counter
|
|
uint8_t bl_grp_i; // byte lane group counter (2 BLs per module)
|
|
|
|
uint8_t bl_divisor = /*(mrc_params->channel_width==x16)?2:*/1; // byte lane divisor
|
|
uint8_t speed = mrc_params->ddr_speed & (BIT1|BIT0); // For DDR3 --> 0 == 800, 1 == 1066, 2 == 1333
|
|
uint8_t tCAS;
|
|
uint8_t tCWL;
|
|
|
|
ENTERFN();
|
|
|
|
tCAS = mrc_params->params.tCL;
|
|
tCWL = 5 + mrc_params->ddr_speed;
|
|
|
|
// ddrphy_init starts
|
|
post_code(0x03, 0x00);
|
|
|
|
// HSD#231531
|
|
// Make sure IOBUFACT is deasserted before initialising the DDR PHY.
|
|
// HSD#234845
|
|
// Make sure WRPTRENABLE is deasserted before initialising the DDR PHY.
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
// Deassert DDRPHY Initialisation Complete
|
|
isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT20, BIT20); // SPID_INIT_COMPLETE=0
|
|
// Deassert IOBUFACT
|
|
isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT2, BIT2); // IOBUFACTRST_N=0
|
|
// Disable WRPTR
|
|
isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT0, BIT0); // WRPTRENABLE=0
|
|
} // if channel enabled
|
|
} // channel_i loop
|
|
|
|
// Put PHY in reset
|
|
isbM32m(DDRPHY, MASTERRSTN, 0, BIT0); // PHYRSTN=0
|
|
|
|
// Initialise DQ01,DQ23,CMD,CLK-CTL,COMP modules
|
|
// STEP0:
|
|
post_code(0x03, 0x10);
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
|
|
// DQ01-DQ23
|
|
for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
|
|
isbM32m(DDRPHY, (DQOBSCKEBBCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i) ? (0x00) : (BIT22)), (BIT22)); // Analog MUX select - IO2xCLKSEL
|
|
|
|
// ODT Strength
|
|
switch (mrc_params->rd_odt_value) {
|
|
case 1: tempD = 0x3; break; // 60 ohm
|
|
case 2: tempD = 0x3; break; // 120 ohm
|
|
case 3: tempD = 0x3; break; // 180 ohm
|
|
default: tempD = 0x3; break; // 120 ohm
|
|
}
|
|
isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
|
|
isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
|
|
// Dynamic ODT/DIFFAMP
|
|
tempD = (((tCAS)<<24)|((tCAS)<<16)|((tCAS)<<8)|((tCAS)<<0));
|
|
switch (speed) {
|
|
case 0: tempD -= 0x01010101; break; // 800
|
|
case 1: tempD -= 0x02020202; break; // 1066
|
|
case 2: tempD -= 0x03030303; break; // 1333
|
|
case 3: tempD -= 0x04040404; break; // 1600
|
|
}
|
|
isbM32m(DDRPHY, (B01LATCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // Launch Time: ODT, DIFFAMP, ODT, DIFFAMP
|
|
switch (speed) {
|
|
// HSD#234715
|
|
case 0: tempD = ((0x06<<16)|(0x07<<8)); break; // 800
|
|
case 1: tempD = ((0x07<<16)|(0x08<<8)); break; // 1066
|
|
case 2: tempD = ((0x09<<16)|(0x0A<<8)); break; // 1333
|
|
case 3: tempD = ((0x0A<<16)|(0x0B<<8)); break; // 1600
|
|
}
|
|
isbM32m(DDRPHY, (B0ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
|
|
isbM32m(DDRPHY, (B1ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
|
|
|
|
switch (mrc_params->rd_odt_value) {
|
|
case 0: tempD = ((0x3F<<16)|(0x3f<<10)); break; // override DIFFAMP=on, ODT=off
|
|
default: tempD = ((0x3F<<16)|(0x2A<<10)); break; // override DIFFAMP=on, ODT=on
|
|
}
|
|
isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
|
|
isbM32m(DDRPHY, (B1OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
|
|
|
|
// DLL Setup
|
|
// 1xCLK Domain Timings: tEDP,RCVEN,WDQS (PO)
|
|
isbM32m(DDRPHY, (B0LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
|
|
isbM32m(DDRPHY, (B1LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
|
|
|
|
// RCVEN Bypass (PO)
|
|
isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
|
|
isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
|
|
// TX
|
|
isbM32m(DDRPHY, (DQCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT16), (BIT16)); // 0 means driving DQ during DQS-preamble
|
|
isbM32m(DDRPHY, (B01PTRCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT8), (BIT8)); // WR_LVL mode disable
|
|
// RX (PO)
|
|
isbM32m(DDRPHY, (B0VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
|
|
isbM32m(DDRPHY, (B1VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
|
|
isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
|
|
isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
|
|
}
|
|
// CLKEBB
|
|
isbM32m(DDRPHY, (CMDOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT23));
|
|
|
|
// Enable tristate control of cmd/address bus
|
|
isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT1|BIT0));
|
|
|
|
// ODT RCOMP
|
|
isbM32m(DDRPHY, (CMDRCOMPODT + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<5)|(0x03<<0)), ((BIT9|BIT8|BIT7|BIT6|BIT5)|(BIT4|BIT3|BIT2|BIT1|BIT0)));
|
|
|
|
// CMDPM* registers must be programmed in this order...
|
|
isbM32m(DDRPHY, (CMDPMDLYREG4 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFFFU<<16)|(0xFFFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: SFR (regulator), MPLL
|
|
isbM32m(DDRPHY, (CMDPMDLYREG3 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFU<<28)|(0xFFF<<16)|(0xF<<12)|(0x616<<0)), ((BIT31|BIT30|BIT29|BIT28)|(BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Delays: ASSERT_IOBUFACT_to_ALLON0_for_PM_MSG_3, VREG (MDLL) Turn On, ALLON0_to_DEASSERT_IOBUFACT_for_PM_MSG_gt0, MDLL Turn On
|
|
isbM32m(DDRPHY, (CMDPMDLYREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // MPLL Divider Reset Delays
|
|
isbM32m(DDRPHY, (CMDPMDLYREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn Off Delays: VREG, Staggered MDLL, MDLL, PI
|
|
isbM32m(DDRPHY, (CMDPMDLYREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT
|
|
isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x6<<8)|BIT6|(0x4<<0)), (BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|(BIT11|BIT10|BIT9|BIT8)|BIT6|(BIT3|BIT2|BIT1|BIT0))); // Allow PUnit signals
|
|
isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
|
|
// CLK-CTL
|
|
isbM32m(DDRPHY, (CCOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT24)); // CLKEBB
|
|
isbM32m(DDRPHY, (CCCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x0<<16)|(0x0<<12)|(0x0<<8)|(0xF<<4)|BIT0), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|BIT0)); // Buffer Enable: CS,CKE,ODT,CLK
|
|
isbM32m(DDRPHY, (CCRCOMPODT + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT RCOMP
|
|
isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
|
|
|
|
// COMP (RON channel specific)
|
|
// - DQ/DQS/DM RON: 32 Ohm
|
|
// - CTRL/CMD RON: 27 Ohm
|
|
// - CLK RON: 26 Ohm
|
|
isbM32m(DDRPHY, (DQVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
|
|
isbM32m(DDRPHY, (CMDVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
|
|
isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0F<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
|
|
isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
|
|
isbM32m(DDRPHY, (CTLVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
|
|
|
|
// DQS Swapped Input Enable
|
|
isbM32m(DDRPHY, (COMPEN1CH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT19|BIT17), ((BIT31|BIT30)|BIT19|BIT17|(BIT15|BIT14)));
|
|
|
|
// ODT VREF = 1.5 x 274/360+274 = 0.65V (code of ~50)
|
|
isbM32m(DDRPHY, (DQVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
|
|
isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
|
|
isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0E<<8)|(0x05<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
|
|
|
|
// Slew rate settings are frequency specific, numbers below are for 800Mhz (speed == 0)
|
|
// - DQ/DQS/DM/CLK SR: 4V/ns,
|
|
// - CTRL/CMD SR: 1.5V/ns
|
|
tempD = (0x0E<<16)|(0x0E<<12)|(0x08<<8)|(0x0B<<4)|(0x0B<<0);
|
|
isbM32m(DDRPHY, (DLYSELCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (tempD), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DCOMP Delay Select: CTL,CMD,CLK,DQS,DQ
|
|
isbM32m(DDRPHY, (TCOVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x05<<16)|(0x05<<8)|(0x05<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // TCO Vref CLK,DQS,DQ
|
|
isbM32m(DDRPHY, (CCBUFODTCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODTCOMP CMD/CTL PU/PD
|
|
isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), (0), ((BIT31|BIT30)|BIT8)); // COMP
|
|
|
|
#ifdef BACKUP_COMPS
|
|
// DQ COMP Overrides
|
|
isbM32m(DDRPHY, (DQDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
|
|
isbM32m(DDRPHY, (DQDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
|
|
isbM32m(DDRPHY, (DQDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
|
|
isbM32m(DDRPHY, (DQDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
|
|
isbM32m(DDRPHY, (DQODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
|
|
isbM32m(DDRPHY, (DQODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
|
|
isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
|
|
// DQS COMP Overrides
|
|
isbM32m(DDRPHY, (DQSDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
|
|
isbM32m(DDRPHY, (DQSDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
|
|
isbM32m(DDRPHY, (DQSDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
|
|
isbM32m(DDRPHY, (DQSDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
|
|
isbM32m(DDRPHY, (DQSODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
|
|
isbM32m(DDRPHY, (DQSODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
|
|
isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
|
|
// CLK COMP Overrides
|
|
isbM32m(DDRPHY, (CLKDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
|
|
isbM32m(DDRPHY, (CLKDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
|
|
isbM32m(DDRPHY, (CLKDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
|
|
isbM32m(DDRPHY, (CLKDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
|
|
isbM32m(DDRPHY, (CLKODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
|
|
isbM32m(DDRPHY, (CLKODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
|
|
isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
|
|
// CMD COMP Overrides
|
|
isbM32m(DDRPHY, (CMDDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
|
|
isbM32m(DDRPHY, (CMDDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
|
|
isbM32m(DDRPHY, (CMDDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
|
|
isbM32m(DDRPHY, (CMDDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
|
|
// CTL COMP Overrides
|
|
isbM32m(DDRPHY, (CTLDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
|
|
isbM32m(DDRPHY, (CTLDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
|
|
isbM32m(DDRPHY, (CTLDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
|
|
isbM32m(DDRPHY, (CTLDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
|
|
#else
|
|
// DQ TCOCOMP Overrides
|
|
isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
|
|
// DQS TCOCOMP Overrides
|
|
isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
|
|
// CLK TCOCOMP Overrides
|
|
isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
|
|
isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
|
|
#endif // BACKUP_COMPS
|
|
// program STATIC delays
|
|
#ifdef BACKUP_WCMD
|
|
set_wcmd(channel_i, ddr_wcmd[PLATFORM_ID]);
|
|
#else
|
|
set_wcmd(channel_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
|
|
#endif // BACKUP_WCMD
|
|
for (rank_i=0; rank_i<NUM_RANKS; rank_i++) {
|
|
if (mrc_params->rank_enables & (1<<rank_i)) {
|
|
set_wclk(channel_i, rank_i, ddr_wclk[PLATFORM_ID]);
|
|
#ifdef BACKUP_WCTL
|
|
set_wctl(channel_i, rank_i, ddr_wctl[PLATFORM_ID]);
|
|
#else
|
|
set_wctl(channel_i, rank_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
|
|
#endif // BACKUP_WCTL
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// COMP (non channel specific)
|
|
//isbM32m(DDRPHY, (), (), ());
|
|
isbM32m(DDRPHY, (DQANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CMDANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CMDANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CLKANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CLKANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQSANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQSANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CTLANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CTLANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
|
|
isbM32m(DDRPHY, (CLKANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
|
|
isbM32m(DDRPHY, (CLKANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQSANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQSANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CMDANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CMDANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CLKANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CLKANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQSANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQSANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (CTLANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
|
|
isbM32m(DDRPHY, (CTLANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
|
|
isbM32m(DDRPHY, (CLKANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
|
|
isbM32m(DDRPHY, (CLKANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
|
|
isbM32m(DDRPHY, (DQSANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
|
|
isbM32m(DDRPHY, (DQSANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
|
|
isbM32m(DDRPHY, (TCOCNTCTRL), (0x1<<0), (BIT1|BIT0)); // TCOCOMP: Pulse Count
|
|
isbM32m(DDRPHY, (CHNLBUFSTATIC), ((0x03<<24)|(0x03<<16)), ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODT: CMD/CTL PD/PU
|
|
isbM32m(DDRPHY, (MSCNTR), (0x64<<0), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0)); // Set 1us counter
|
|
isbM32m(DDRPHY, (LATCH1CTL), (0x1<<28), (BIT30|BIT29|BIT28)); // ???
|
|
|
|
// Release PHY from reset
|
|
isbM32m(DDRPHY, MASTERRSTN, BIT0, BIT0); // PHYRSTN=1
|
|
|
|
// STEP1:
|
|
post_code(0x03, 0x11);
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
// DQ01-DQ23
|
|
for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
|
|
isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
|
|
delay_n(3);
|
|
}
|
|
// ECC
|
|
isbM32m(DDRPHY, (ECCMDLLCTL), (BIT13), (BIT13)); // Enable VREG
|
|
delay_n(3);
|
|
// CMD
|
|
isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
|
|
delay_n(3);
|
|
// CLK-CTL
|
|
isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
|
|
delay_n(3);
|
|
}
|
|
}
|
|
|
|
// STEP2:
|
|
post_code(0x03, 0x12);
|
|
delay_n(200);
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
// DQ01-DQ23
|
|
for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
|
|
isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT17), (BIT17)); // Enable MCDLL
|
|
delay_n(50);
|
|
}
|
|
// ECC
|
|
isbM32m(DDRPHY, (ECCMDLLCTL), (BIT17), (BIT17)); // Enable MCDLL
|
|
delay_n(50);
|
|
// CMD
|
|
isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
|
|
delay_n(50);
|
|
// CLK-CTL
|
|
isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
|
|
delay_n(50);
|
|
}
|
|
}
|
|
|
|
// STEP3:
|
|
post_code(0x03, 0x13);
|
|
delay_n(100);
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
// DQ01-DQ23
|
|
for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
|
|
#ifdef FORCE_16BIT_DDRIO
|
|
tempD = ((bl_grp_i) && (mrc_params->channel_width == x16)) ? ((0x1<<12)|(0x1<<8)|(0xF<<4)|(0xF<<0)) : ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
|
|
#else
|
|
tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
|
|
#endif
|
|
isbM32m(DDRPHY, (DQDLLTXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
|
|
delay_n(3);
|
|
isbM32m(DDRPHY, (DQDLLRXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL
|
|
delay_n(3);
|
|
isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL Overrides BL0
|
|
}
|
|
|
|
// ECC
|
|
tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
|
|
isbM32m(DDRPHY, (ECCDLLTXCTL), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
|
|
delay_n(3);
|
|
|
|
// CMD (PO)
|
|
isbM32m(DDRPHY, (CMDDLLTXCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0)), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
|
|
delay_n(3);
|
|
}
|
|
}
|
|
|
|
|
|
// STEP4:
|
|
post_code(0x03, 0x14);
|
|
for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
|
|
if (mrc_params->channel_enables & (1<<channel_i)) {
|
|
// Host To Memory Clock Alignment (HMC) for 800/1066
|
|
for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
|
|
isbM32m(DDRPHY, (DQCLKALIGNREG2 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i)?(0x3):(0x1)), (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
|
|
}
|
|
isbM32m(DDRPHY, (ECCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
|
|
isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x0, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
|
|
isbM32m(DDRPHY, (CCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
|
|
isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), (0x2<<4), (BIT5|BIT4)); // CLK_ALIGN_MODE
|
|
isbM32m(DDRPHY, (CMDCLKALIGNREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x18<<16)|(0x10<<8)|(0x8<<2)|(0x1<<0)), ((BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|(BIT1|BIT0))); // NUM_SAMPLES, MAX_SAMPLES, MACRO_PI_STEP, MICRO_PI_STEP
|
|
isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x10<<16)|(0x4<<8)|(0x2<<4)), ((BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4))); // ???, TOTAL_NUM_MODULES, FIRST_U_PARTITION
|
|
#ifdef HMC_TEST
|
|
isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT24, BIT24); // START_CLK_ALIGN=1
|
|
while (isbR32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET))) & BIT24); // wait for START_CLK_ALIGN=0
|
|
#endif // HMC_TEST
|
|
|
|
// Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN
|
|
isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), BIT0, BIT0); // WRPTRENABLE=1
|
|
|
|
|
|
#ifdef SIM
|
|
// comp is not working on simulator
|
|
#else
|
|
// COMP initial
|
|
isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), BIT5, BIT5); // enable bypass for CLK buffer (PO)
|
|
isbM32m(DDRPHY, (CMPCTRL), (BIT0), (BIT0)); // Initial COMP Enable
|
|
while (isbR32m(DDRPHY, (CMPCTRL)) & BIT0); // wait for Initial COMP Enable = 0
|
|
isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), ~BIT5, BIT5); // disable bypass for CLK buffer (PO)
|
|
#endif
|
|
|
|
// IOBUFACT
|
|
// STEP4a
|
|
isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT2, BIT2); // IOBUFACTRST_N=1
|
|
|
|
// DDRPHY initialisation complete
|
|
isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT20, BIT20); // SPID_INIT_COMPLETE=1
|
|
}
|
|
}
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// jedec_init (aka PerformJedecInit):
|
|
// This function performs JEDEC initialisation on all enabled channels.
|
|
static void jedec_init(
|
|
MRCParams_t *mrc_params,
|
|
uint32_t silent)
|
|
{
|
|
uint8_t TWR, WL, Rank;
|
|
uint32_t TCK;
|
|
|
|
RegDTR0 DTR0reg;
|
|
|
|
DramInitDDR3MRS0 mrs0Command;
|
|
DramInitDDR3EMR1 emrs1Command;
|
|
DramInitDDR3EMR2 emrs2Command;
|
|
DramInitDDR3EMR3 emrs3Command;
|
|
|
|
ENTERFN();
|
|
|
|
// jedec_init starts
|
|
if (!silent)
|
|
{
|
|
post_code(0x04, 0x00);
|
|
}
|
|
|
|
// Assert RESET# for 200us
|
|
isbM32m(DDRPHY, CCDDR3RESETCTL, BIT1, (BIT8|BIT1)); // DDR3_RESET_SET=0, DDR3_RESET_RESET=1
|
|
#ifdef QUICKSIM
|
|
// Don't waste time during simulation
|
|
delay_u(2);
|
|
#else
|
|
delay_u(200);
|
|
#endif
|
|
isbM32m(DDRPHY, CCDDR3RESETCTL, BIT8, (BIT8|BIT1)); // DDR3_RESET_SET=1, DDR3_RESET_RESET=0
|
|
|
|
DTR0reg.raw = isbR32m(MCU, DTR0);
|
|
|
|
// Set CKEVAL for populated ranks
|
|
// then send NOP to each rank (#4550197)
|
|
{
|
|
uint32_t DRPbuffer;
|
|
uint32_t DRMCbuffer;
|
|
|
|
DRPbuffer = isbR32m(MCU, DRP);
|
|
DRPbuffer &= 0x3;
|
|
DRMCbuffer = isbR32m(MCU, DRMC);
|
|
DRMCbuffer &= 0xFFFFFFFC;
|
|
DRMCbuffer |= (BIT4 | DRPbuffer);
|
|
|
|
isbW32m(MCU, DRMC, DRMCbuffer);
|
|
|
|
for (Rank = 0; Rank < NUM_RANKS; Rank++)
|
|
{
|
|
// Skip to next populated rank
|
|
if ((mrc_params->rank_enables & (1 << Rank)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dram_init_command(DCMD_NOP(Rank));
|
|
}
|
|
|
|
isbW32m(MCU, DRMC, DRMC_DEFAULT);
|
|
}
|
|
|
|
// setup for emrs 2
|
|
// BIT[15:11] --> Always "0"
|
|
// BIT[10:09] --> Rtt_WR: want "Dynamic ODT Off" (0)
|
|
// BIT[08] --> Always "0"
|
|
// BIT[07] --> SRT: use sr_temp_range
|
|
// BIT[06] --> ASR: want "Manual SR Reference" (0)
|
|
// BIT[05:03] --> CWL: use oem_tCWL
|
|
// BIT[02:00] --> PASR: want "Full Array" (0)
|
|
emrs2Command.raw = 0;
|
|
emrs2Command.field.bankAddress = 2;
|
|
|
|
WL = 5 + mrc_params->ddr_speed;
|
|
emrs2Command.field.CWL = WL - 5;
|
|
emrs2Command.field.SRT = mrc_params->sr_temp_range;
|
|
|
|
// setup for emrs 3
|
|
// BIT[15:03] --> Always "0"
|
|
// BIT[02] --> MPR: want "Normal Operation" (0)
|
|
// BIT[01:00] --> MPR_Loc: want "Predefined Pattern" (0)
|
|
emrs3Command.raw = 0;
|
|
emrs3Command.field.bankAddress = 3;
|
|
|
|
// setup for emrs 1
|
|
// BIT[15:13] --> Always "0"
|
|
// BIT[12:12] --> Qoff: want "Output Buffer Enabled" (0)
|
|
// BIT[11:11] --> TDQS: want "Disabled" (0)
|
|
// BIT[10:10] --> Always "0"
|
|
// BIT[09,06,02] --> Rtt_nom: use rtt_nom_value
|
|
// BIT[08] --> Always "0"
|
|
// BIT[07] --> WR_LVL: want "Disabled" (0)
|
|
// BIT[05,01] --> DIC: use ron_value
|
|
// BIT[04:03] --> AL: additive latency want "0" (0)
|
|
// BIT[00] --> DLL: want "Enable" (0)
|
|
//
|
|
// (BIT5|BIT1) set Ron value
|
|
// 00 --> RZQ/6 (40ohm)
|
|
// 01 --> RZQ/7 (34ohm)
|
|
// 1* --> RESERVED
|
|
//
|
|
// (BIT9|BIT6|BIT2) set Rtt_nom value
|
|
// 000 --> Disabled
|
|
// 001 --> RZQ/4 ( 60ohm)
|
|
// 010 --> RZQ/2 (120ohm)
|
|
// 011 --> RZQ/6 ( 40ohm)
|
|
// 1** --> RESERVED
|
|
emrs1Command.raw = 0;
|
|
emrs1Command.field.bankAddress = 1;
|
|
emrs1Command.field.dllEnabled = 0; // 0 = Enable , 1 = Disable
|
|
|
|
if (mrc_params->ron_value == 0)
|
|
{
|
|
emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_34;
|
|
}
|
|
else
|
|
{
|
|
emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_40;
|
|
}
|
|
|
|
|
|
if (mrc_params->rtt_nom_value == 0)
|
|
{
|
|
emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_40 << 6);
|
|
}
|
|
else if (mrc_params->rtt_nom_value == 1)
|
|
{
|
|
emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_60 << 6);
|
|
}
|
|
else if (mrc_params->rtt_nom_value == 2)
|
|
{
|
|
emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_120 << 6);
|
|
}
|
|
|
|
// save MRS1 value (excluding control fields)
|
|
mrc_params->mrs1 = emrs1Command.raw >> 6;
|
|
|
|
// setup for mrs 0
|
|
// BIT[15:13] --> Always "0"
|
|
// BIT[12] --> PPD: for Quark (1)
|
|
// BIT[11:09] --> WR: use oem_tWR
|
|
// BIT[08] --> DLL: want "Reset" (1, self clearing)
|
|
// BIT[07] --> MODE: want "Normal" (0)
|
|
// BIT[06:04,02] --> CL: use oem_tCAS
|
|
// BIT[03] --> RD_BURST_TYPE: want "Interleave" (1)
|
|
// BIT[01:00] --> BL: want "8 Fixed" (0)
|
|
// WR:
|
|
// 0 --> 16
|
|
// 1 --> 5
|
|
// 2 --> 6
|
|
// 3 --> 7
|
|
// 4 --> 8
|
|
// 5 --> 10
|
|
// 6 --> 12
|
|
// 7 --> 14
|
|
// CL:
|
|
// BIT[02:02] "0" if oem_tCAS <= 11 (1866?)
|
|
// BIT[06:04] use oem_tCAS-4
|
|
mrs0Command.raw = 0;
|
|
mrs0Command.field.bankAddress = 0;
|
|
mrs0Command.field.dllReset = 1;
|
|
mrs0Command.field.BL = 0;
|
|
mrs0Command.field.PPD = 1;
|
|
mrs0Command.field.casLatency = DTR0reg.field.tCL + 1;
|
|
|
|
TCK = tCK[mrc_params->ddr_speed];
|
|
TWR = MCEIL(15000, TCK); // Per JEDEC: tWR=15000ps DDR2/3 from 800-1600
|
|
mrs0Command.field.writeRecovery = TWR - 4;
|
|
|
|
for (Rank = 0; Rank < NUM_RANKS; Rank++)
|
|
{
|
|
// Skip to next populated rank
|
|
if ((mrc_params->rank_enables & (1 << Rank)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
emrs2Command.field.rankSelect = Rank;
|
|
dram_init_command(emrs2Command.raw);
|
|
|
|
emrs3Command.field.rankSelect = Rank;
|
|
dram_init_command(emrs3Command.raw);
|
|
|
|
emrs1Command.field.rankSelect = Rank;
|
|
dram_init_command(emrs1Command.raw);
|
|
|
|
mrs0Command.field.rankSelect = Rank;
|
|
dram_init_command(mrs0Command.raw);
|
|
|
|
dram_init_command(DCMD_ZQCL(Rank));
|
|
}
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// rcvn_cal:
|
|
// POST_CODE[major] == 0x05
|
|
//
|
|
// This function will perform our RCVEN Calibration Algorithm.
|
|
// We will only use the 2xCLK domain timings to perform RCVEN Calibration.
|
|
// All byte lanes will be calibrated "simultaneously" per channel per rank.
|
|
static void rcvn_cal(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint8_t channel_i; // channel counter
|
|
uint8_t rank_i; // rank counter
|
|
uint8_t bl_i; // byte lane counter
|
|
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
|
|
|
|
#ifdef R2R_SHARING
|
|
uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
|
|
#ifndef BACKUP_RCVN
|
|
uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
|
|
#endif // BACKUP_RCVN
|
|
#endif // R2R_SHARING
|
|
|
|
#ifdef BACKUP_RCVN
|
|
#else
|
|
uint32_t tempD; // temporary DWORD
|
|
uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
|
|
RegDTR1 dtr1;
|
|
RegDTR1 dtr1save;
|
|
#endif // BACKUP_RCVN
|
|
ENTERFN();
|
|
|
|
// rcvn_cal starts
|
|
post_code(0x05, 0x00);
|
|
|
|
#ifndef BACKUP_RCVN
|
|
// need separate burst to sample DQS preamble
|
|
dtr1.raw = dtr1save.raw = isbR32m(MCU, DTR1);
|
|
dtr1.field.tCCD = 1;
|
|
isbW32m(MCU, DTR1, dtr1.raw);
|
|
#endif
|
|
|
|
#ifdef R2R_SHARING
|
|
// need to set "final_delay[][]" elements to "0"
|
|
memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
|
|
#endif // R2R_SHARING
|
|
|
|
// loop through each enabled channel
|
|
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
|
|
{
|
|
if (mrc_params->channel_enables & (1 << channel_i))
|
|
{
|
|
// perform RCVEN Calibration on a per rank basis
|
|
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
|
|
{
|
|
if (mrc_params->rank_enables & (1 << rank_i))
|
|
{
|
|
// POST_CODE here indicates the current channel and rank being calibrated
|
|
post_code(0x05, (0x10 + ((channel_i << 4) | rank_i)));
|
|
|
|
#ifdef BACKUP_RCVN
|
|
// set hard-coded timing values
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
set_rcvn(channel_i, rank_i, bl_i, ddr_rcvn[PLATFORM_ID]);
|
|
}
|
|
#else
|
|
// enable FIFORST
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
|
|
{
|
|
isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), 0,
|
|
BIT8); // 0 is enabled
|
|
} // bl_i loop
|
|
// initialise the starting delay to 128 PI (tCAS +1 CLK)
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
#ifdef SIM
|
|
// Original value was late at the end of DQS sequence
|
|
delay[bl_i] = 3 * FULL_CLK;
|
|
#else
|
|
delay[bl_i] = (4 + 1) * FULL_CLK; // 1x CLK domain timing is tCAS-4
|
|
#endif
|
|
|
|
set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
} // bl_i loop
|
|
|
|
// now find the rising edge
|
|
find_rising_edge(mrc_params, delay, channel_i, rank_i, true);
|
|
// Now increase delay by 32 PI (1/4 CLK) to place in center of high pulse.
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
delay[bl_i] += QRTR_CLK;
|
|
set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
} // bl_i loop
|
|
// Now decrement delay by 128 PI (1 CLK) until we sample a "0"
|
|
do
|
|
{
|
|
|
|
tempD = sample_dqs(mrc_params, channel_i, rank_i, true);
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
if (tempD & (1 << bl_i))
|
|
{
|
|
if (delay[bl_i] >= FULL_CLK)
|
|
{
|
|
delay[bl_i] -= FULL_CLK;
|
|
set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
}
|
|
else
|
|
{
|
|
// not enough delay
|
|
training_message(channel_i, rank_i, bl_i);
|
|
post_code(0xEE, 0x50);
|
|
}
|
|
}
|
|
} // bl_i loop
|
|
} while (tempD & 0xFF);
|
|
|
|
#ifdef R2R_SHARING
|
|
// increment "num_ranks_enabled"
|
|
num_ranks_enabled++;
|
|
// Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
delay[bl_i] += QRTR_CLK;
|
|
// add "delay[]" values to "final_delay[][]" for rolling average
|
|
final_delay[channel_i][bl_i] += delay[bl_i];
|
|
// set timing based on rolling average values
|
|
set_rcvn(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
|
|
} // bl_i loop
|
|
#else
|
|
// Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
delay[bl_i] += QRTR_CLK;
|
|
set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
} // bl_i loop
|
|
|
|
#endif // R2R_SHARING
|
|
|
|
// disable FIFORST
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
|
|
{
|
|
isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), BIT8,
|
|
BIT8); // 1 is disabled
|
|
} // bl_i loop
|
|
|
|
#endif // BACKUP_RCVN
|
|
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
#ifndef BACKUP_RCVN
|
|
// restore original
|
|
isbW32m(MCU, DTR1, dtr1save.raw);
|
|
#endif
|
|
|
|
#ifdef MRC_SV
|
|
if (mrc_params->tune_rcvn)
|
|
{
|
|
uint32_t rcven, val;
|
|
uint32_t rdcmd2rcven;
|
|
|
|
/*
|
|
Formulas for RDCMD2DATAVALID & DIFFAMP dynamic timings
|
|
|
|
1. Set after RCVEN training
|
|
|
|
//Tune RDCMD2DATAVALID
|
|
|
|
x80/x84[21:16]
|
|
MAX OF 2 RANKS : round up (rdcmd2rcven (rcven 1x) + 2x x 2 + PI/128) + 5
|
|
|
|
//rdcmd2rcven x80/84[12:8]
|
|
//rcven 2x x70[23:20] & [11:8]
|
|
|
|
//Tune DIFFAMP Timings
|
|
|
|
//diffampen launch x88[20:16] & [4:0] -- B01LATCTL1
|
|
MIN OF 2 RANKS : round down (rcven 1x + 2x x 2 + PI/128) - 1
|
|
|
|
//diffampen length x8C/x90 [13:8] -- B0ONDURCTL B1ONDURCTL
|
|
MAX OF 2 RANKS : roundup (rcven 1x + 2x x 2 + PI/128) + 5
|
|
|
|
|
|
2. need to do a fiforst after settings these values
|
|
*/
|
|
|
|
DPF(D_INFO, "BEFORE\n");
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
|
|
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
|
|
|
|
rcven = get_rcvn(0, 0, 0) / 128;
|
|
rdcmd2rcven = (isbR32m(DDRPHY, B0LATCTL0) >> 8) & 0x1F;
|
|
val = rdcmd2rcven + rcven + 6;
|
|
isbM32m(DDRPHY, B0LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
|
|
|
|
val = rdcmd2rcven + rcven - 1;
|
|
isbM32m(DDRPHY, B01LATCTL1, val << 0, (BIT4|BIT3|BIT2|BIT1|BIT0));
|
|
|
|
val = rdcmd2rcven + rcven + 5;
|
|
isbM32m(DDRPHY, B0ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
|
|
|
|
rcven = get_rcvn(0, 0, 1) / 128;
|
|
rdcmd2rcven = (isbR32m(DDRPHY, B1LATCTL0) >> 8) & 0x1F;
|
|
val = rdcmd2rcven + rcven + 6;
|
|
isbM32m(DDRPHY, B1LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
|
|
|
|
val = rdcmd2rcven + rcven - 1;
|
|
isbM32m(DDRPHY, B01LATCTL1, val << 16, (BIT20|BIT19|BIT18|BIT17|BIT16));
|
|
|
|
val = rdcmd2rcven + rcven + 5;
|
|
isbM32m(DDRPHY, B1ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
|
|
|
|
DPF(D_INFO, "AFTER\n");
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
|
|
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
|
|
DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
|
|
|
|
DPF(D_INFO, "\nPress a key\n");
|
|
mgetc();
|
|
|
|
// fifo reset
|
|
isbM32m(DDRPHY, B01PTRCTL1, 0, BIT8); // 0 is enabled
|
|
delay_n(3);
|
|
isbM32m(DDRPHY, B01PTRCTL1, BIT8, BIT8); // 1 is disabled
|
|
}
|
|
#endif
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// Check memory executing write/read/verify of many data patterns
|
|
// at the specified address. Bits in the result indicate failure
|
|
// on specific byte lane.
|
|
static uint32_t check_bls_ex(
|
|
MRCParams_t *mrc_params,
|
|
uint32_t address)
|
|
{
|
|
uint32_t result;
|
|
uint8_t first_run = 0;
|
|
|
|
if (mrc_params->hte_setup)
|
|
{
|
|
mrc_params->hte_setup = 0;
|
|
|
|
first_run = 1;
|
|
select_hte(mrc_params);
|
|
}
|
|
|
|
result = WriteStressBitLanesHTE(mrc_params, address, first_run);
|
|
|
|
DPF(D_TRN, "check_bls_ex result is %x\n", result);
|
|
return result;
|
|
}
|
|
|
|
// Check memory executing simple write/read/verify at
|
|
// the specified address. Bits in the result indicate failure
|
|
// on specific byte lane.
|
|
static uint32_t check_rw_coarse(
|
|
MRCParams_t *mrc_params,
|
|
uint32_t address)
|
|
{
|
|
uint32_t result = 0;
|
|
uint8_t first_run = 0;
|
|
|
|
if (mrc_params->hte_setup)
|
|
{
|
|
mrc_params->hte_setup = 0;
|
|
|
|
first_run = 1;
|
|
select_hte(mrc_params);
|
|
}
|
|
|
|
result = BasicWriteReadHTE(mrc_params, address, first_run, WRITE_TRAIN);
|
|
|
|
DPF(D_TRN, "check_rw_coarse result is %x\n", result);
|
|
return result;
|
|
}
|
|
|
|
// wr_level:
|
|
// POST_CODE[major] == 0x06
|
|
//
|
|
// This function will perform the Write Levelling algorithm (align WCLK and WDQS).
|
|
// This algorithm will act on each rank in each channel separately.
|
|
static void wr_level(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint8_t channel_i; // channel counter
|
|
uint8_t rank_i; // rank counter
|
|
uint8_t bl_i; // byte lane counter
|
|
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
|
|
|
|
#ifdef R2R_SHARING
|
|
uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
|
|
#ifndef BACKUP_WDQS
|
|
uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
|
|
#endif // BACKUP_WDQS
|
|
#endif // R2R_SHARING
|
|
|
|
#ifdef BACKUP_WDQS
|
|
#else
|
|
bool all_edges_found; // determines stop condition for CRS_WR_LVL
|
|
uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
|
|
// static makes it so the data is loaded in the heap once by shadow(), where
|
|
// non-static copies the data onto the stack every time this function is called.
|
|
|
|
uint32_t address; // address to be checked during COARSE_WR_LVL
|
|
RegDTR4 dtr4;
|
|
RegDTR4 dtr4save;
|
|
#endif // BACKUP_WDQS
|
|
|
|
ENTERFN();
|
|
|
|
// wr_level starts
|
|
post_code(0x06, 0x00);
|
|
|
|
#ifdef R2R_SHARING
|
|
// need to set "final_delay[][]" elements to "0"
|
|
memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
|
|
#endif // R2R_SHARING
|
|
// loop through each enabled channel
|
|
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
|
|
{
|
|
if (mrc_params->channel_enables & (1 << channel_i))
|
|
{
|
|
// perform WRITE LEVELING algorithm on a per rank basis
|
|
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
|
|
{
|
|
if (mrc_params->rank_enables & (1 << rank_i))
|
|
{
|
|
// POST_CODE here indicates the current rank and channel being calibrated
|
|
post_code(0x06, (0x10 + ((channel_i << 4) | rank_i)));
|
|
|
|
#ifdef BACKUP_WDQS
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
set_wdqs(channel_i, rank_i, bl_i, ddr_wdqs[PLATFORM_ID]);
|
|
set_wdq(channel_i, rank_i, bl_i, (ddr_wdqs[PLATFORM_ID] - QRTR_CLK));
|
|
}
|
|
#else
|
|
|
|
{ // Begin product specific code
|
|
|
|
// perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
|
|
dram_init_command(DCMD_PREA(rank_i));
|
|
|
|
// enable Write Levelling Mode (EMRS1 w/ Write Levelling Mode Enable)
|
|
dram_init_command(DCMD_MRS1(rank_i,0x0082));
|
|
|
|
// set ODT DRAM Full Time Termination disable in MCU
|
|
dtr4.raw = dtr4save.raw = isbR32m(MCU, DTR4);
|
|
dtr4.field.ODTDIS = 1;
|
|
isbW32m(MCU, DTR4, dtr4.raw);
|
|
|
|
for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
|
|
{
|
|
isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
|
|
(BIT28 | (0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
|
|
(BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Enable Sandy Bridge Mode (WDQ Tri-State) & Ensure 5 WDQS pulses during Write Leveling
|
|
}
|
|
|
|
isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (BIT16), (BIT16)); // Write Leveling Mode enabled in IO
|
|
} // End product specific code
|
|
// Initialise the starting delay to WCLK
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
{ // Begin product specific code
|
|
// CLK0 --> RK0
|
|
// CLK1 --> RK1
|
|
delay[bl_i] = get_wclk(channel_i, rank_i);
|
|
} // End product specific code
|
|
set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
} // bl_i loop
|
|
// now find the rising edge
|
|
find_rising_edge(mrc_params, delay, channel_i, rank_i, false);
|
|
{ // Begin product specific code
|
|
// disable Write Levelling Mode
|
|
isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (0), (BIT16)); // Write Leveling Mode disabled in IO
|
|
|
|
for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
|
|
{
|
|
isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
|
|
((0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
|
|
(BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Disable Sandy Bridge Mode & Ensure 4 WDQS pulses during normal operation
|
|
} // bl_i loop
|
|
|
|
// restore original DTR4
|
|
isbW32m(MCU, DTR4, dtr4save.raw);
|
|
|
|
// restore original value (Write Levelling Mode Disable)
|
|
dram_init_command(DCMD_MRS1(rank_i, mrc_params->mrs1));
|
|
|
|
// perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
|
|
dram_init_command(DCMD_PREA(rank_i));
|
|
} // End product specific code
|
|
|
|
post_code(0x06, (0x30 + ((channel_i << 4) | rank_i)));
|
|
|
|
// COARSE WRITE LEVEL:
|
|
// check that we're on the correct clock edge
|
|
|
|
// hte reconfiguration request
|
|
mrc_params->hte_setup = 1;
|
|
|
|
// start CRS_WR_LVL with WDQS = WDQS + 128 PI
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
delay[bl_i] = get_wdqs(channel_i, rank_i, bl_i) + FULL_CLK;
|
|
set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
// program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
|
|
set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
|
|
} // bl_i loop
|
|
|
|
// get an address in the targeted channel/rank
|
|
address = get_addr(mrc_params, channel_i, rank_i);
|
|
do
|
|
{
|
|
uint32_t coarse_result = 0x00;
|
|
uint32_t coarse_result_mask = byte_lane_mask(mrc_params);
|
|
all_edges_found = true; // assume pass
|
|
|
|
#ifdef SIM
|
|
// need restore memory to idle state as write can be in bad sync
|
|
dram_init_command (DCMD_PREA(rank_i));
|
|
#endif
|
|
|
|
mrc_params->hte_setup = 1;
|
|
coarse_result = check_rw_coarse(mrc_params, address);
|
|
|
|
// check for failures and margin the byte lane back 128 PI (1 CLK)
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
if (coarse_result & (coarse_result_mask << bl_i))
|
|
{
|
|
all_edges_found = false;
|
|
delay[bl_i] -= FULL_CLK;
|
|
set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
|
|
// program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
|
|
set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
|
|
}
|
|
} // bl_i loop
|
|
|
|
} while (!all_edges_found);
|
|
|
|
#ifdef R2R_SHARING
|
|
// increment "num_ranks_enabled"
|
|
num_ranks_enabled++;
|
|
// accumulate "final_delay[][]" values from "delay[]" values for rolling average
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
final_delay[channel_i][bl_i] += delay[bl_i];
|
|
set_wdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
|
|
// program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
|
|
set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled) - QRTR_CLK);
|
|
} // bl_i loop
|
|
#endif // R2R_SHARING
|
|
#endif // BACKUP_WDQS
|
|
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// rd_train:
|
|
// POST_CODE[major] == 0x07
|
|
//
|
|
// This function will perform the READ TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
|
|
// The idea here is to train the VREF and RDQS (and eventually RDQ) values to achieve maximum READ margins.
|
|
// The algorithm will first determine the X coordinate (RDQS setting).
|
|
// This is done by collapsing the VREF eye until we find a minimum required RDQS eye for VREF_MIN and VREF_MAX.
|
|
// Then we take the averages of the RDQS eye at VREF_MIN and VREF_MAX, then average those; this will be the final X coordinate.
|
|
// The algorithm will then determine the Y coordinate (VREF setting).
|
|
// This is done by collapsing the RDQS eye until we find a minimum required VREF eye for RDQS_MIN and RDQS_MAX.
|
|
// Then we take the averages of the VREF eye at RDQS_MIN and RDQS_MAX, then average those; this will be the final Y coordinate.
|
|
// NOTE: this algorithm assumes the eye curves have a one-to-one relationship, meaning for each X the curve has only one Y and vice-a-versa.
|
|
static void rd_train(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
|
|
#define MIN_RDQS_EYE 10 // in PI Codes
|
|
#define MIN_VREF_EYE 10 // in VREF Codes
|
|
#define RDQS_STEP 1 // how many RDQS codes to jump while margining
|
|
#define VREF_STEP 1 // how many VREF codes to jump while margining
|
|
#define VREF_MIN (0x00) // offset into "vref_codes[]" for minimum allowed VREF setting
|
|
#define VREF_MAX (0x3F) // offset into "vref_codes[]" for maximum allowed VREF setting
|
|
#define RDQS_MIN (0x00) // minimum RDQS delay value
|
|
#define RDQS_MAX (0x3F) // maximum RDQS delay value
|
|
#define B 0 // BOTTOM VREF
|
|
#define T 1 // TOP VREF
|
|
#define L 0 // LEFT RDQS
|
|
#define R 1 // RIGHT RDQS
|
|
|
|
uint8_t channel_i; // channel counter
|
|
uint8_t rank_i; // rank counter
|
|
uint8_t bl_i; // byte lane counter
|
|
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
|
|
#ifdef BACKUP_RDQS
|
|
#else
|
|
uint8_t side_x; // tracks LEFT/RIGHT approach vectors
|
|
uint8_t side_y; // tracks BOTTOM/TOP approach vectors
|
|
uint8_t x_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // X coordinate data (passing RDQS values) for approach vectors
|
|
uint8_t y_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_BYTE_LANES]; // Y coordinate data (passing VREF values) for approach vectors
|
|
uint8_t x_center[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // centered X (RDQS)
|
|
uint8_t y_center[NUM_CHANNELS][NUM_BYTE_LANES]; // centered Y (VREF)
|
|
uint32_t address; // target address for "check_bls_ex()"
|
|
uint32_t result; // result of "check_bls_ex()"
|
|
uint32_t bl_mask; // byte lane mask for "result" checking
|
|
#ifdef R2R_SHARING
|
|
uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
|
|
uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
|
|
#endif // R2R_SHARING
|
|
#endif // BACKUP_RDQS
|
|
// rd_train starts
|
|
post_code(0x07, 0x00);
|
|
|
|
ENTERFN();
|
|
|
|
#ifdef BACKUP_RDQS
|
|
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))
|
|
{
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
set_rdqs(channel_i, rank_i, bl_i, ddr_rdqs[PLATFORM_ID]);
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
#else
|
|
// initialise x/y_coordinate arrays
|
|
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))
|
|
{
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
// x_coordinate:
|
|
x_coordinate[L][B][channel_i][rank_i][bl_i] = RDQS_MIN;
|
|
x_coordinate[R][B][channel_i][rank_i][bl_i] = RDQS_MAX;
|
|
x_coordinate[L][T][channel_i][rank_i][bl_i] = RDQS_MIN;
|
|
x_coordinate[R][T][channel_i][rank_i][bl_i] = RDQS_MAX;
|
|
// y_coordinate:
|
|
y_coordinate[L][B][channel_i][bl_i] = VREF_MIN;
|
|
y_coordinate[R][B][channel_i][bl_i] = VREF_MIN;
|
|
y_coordinate[L][T][channel_i][bl_i] = VREF_MAX;
|
|
y_coordinate[R][T][channel_i][bl_i] = VREF_MAX;
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
// initialise other variables
|
|
bl_mask = byte_lane_mask(mrc_params);
|
|
address = get_addr(mrc_params, 0, 0);
|
|
|
|
#ifdef R2R_SHARING
|
|
// need to set "final_delay[][]" elements to "0"
|
|
memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
|
|
#endif // R2R_SHARING
|
|
|
|
// look for passing coordinates
|
|
for (side_y = B; side_y <= T; side_y++)
|
|
{
|
|
for (side_x = L; side_x <= R; side_x++)
|
|
{
|
|
|
|
post_code(0x07, (0x10 + (side_y * 2) + (side_x)));
|
|
|
|
// find passing values
|
|
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
|
|
{
|
|
if (mrc_params->channel_enables & (0x1 << channel_i))
|
|
{
|
|
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
|
|
{
|
|
|
|
if (mrc_params->rank_enables & (0x1 << rank_i))
|
|
{
|
|
// set x/y_coordinate search starting settings
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
|
|
set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
|
|
} // bl_i loop
|
|
// get an address in the target channel/rank
|
|
address = get_addr(mrc_params, channel_i, rank_i);
|
|
|
|
// request HTE reconfiguration
|
|
mrc_params->hte_setup = 1;
|
|
|
|
// test the settings
|
|
do
|
|
{
|
|
|
|
// result[07:00] == failing byte lane (MAX 8)
|
|
result = check_bls_ex( mrc_params, address);
|
|
|
|
// check for failures
|
|
if (result & 0xFF)
|
|
{
|
|
// at least 1 byte lane failed
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
if (result & (bl_mask << bl_i))
|
|
{
|
|
// adjust the RDQS values accordingly
|
|
if (side_x == L)
|
|
{
|
|
x_coordinate[L][side_y][channel_i][rank_i][bl_i] += RDQS_STEP;
|
|
}
|
|
else
|
|
{
|
|
x_coordinate[R][side_y][channel_i][rank_i][bl_i] -= RDQS_STEP;
|
|
}
|
|
// check that we haven't closed the RDQS_EYE too much
|
|
if ((x_coordinate[L][side_y][channel_i][rank_i][bl_i] > (RDQS_MAX - MIN_RDQS_EYE)) ||
|
|
(x_coordinate[R][side_y][channel_i][rank_i][bl_i] < (RDQS_MIN + MIN_RDQS_EYE))
|
|
||
|
|
(x_coordinate[L][side_y][channel_i][rank_i][bl_i]
|
|
== x_coordinate[R][side_y][channel_i][rank_i][bl_i]))
|
|
{
|
|
// not enough RDQS margin available at this VREF
|
|
// update VREF values accordingly
|
|
if (side_y == B)
|
|
{
|
|
y_coordinate[side_x][B][channel_i][bl_i] += VREF_STEP;
|
|
}
|
|
else
|
|
{
|
|
y_coordinate[side_x][T][channel_i][bl_i] -= VREF_STEP;
|
|
}
|
|
// check that we haven't closed the VREF_EYE too much
|
|
if ((y_coordinate[side_x][B][channel_i][bl_i] > (VREF_MAX - MIN_VREF_EYE)) ||
|
|
(y_coordinate[side_x][T][channel_i][bl_i] < (VREF_MIN + MIN_VREF_EYE)) ||
|
|
(y_coordinate[side_x][B][channel_i][bl_i] == y_coordinate[side_x][T][channel_i][bl_i]))
|
|
{
|
|
// VREF_EYE collapsed below MIN_VREF_EYE
|
|
training_message(channel_i, rank_i, bl_i);
|
|
post_code(0xEE, (0x70 + (side_y * 2) + (side_x)));
|
|
}
|
|
else
|
|
{
|
|
// update the VREF setting
|
|
set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
|
|
// reset the X coordinate to begin the search at the new VREF
|
|
x_coordinate[side_x][side_y][channel_i][rank_i][bl_i] =
|
|
(side_x == L) ? (RDQS_MIN) : (RDQS_MAX);
|
|
}
|
|
}
|
|
// update the RDQS setting
|
|
set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
|
|
} // if bl_i failed
|
|
} // bl_i loop
|
|
} // at least 1 byte lane failed
|
|
} while (result & 0xFF);
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
} // side_x loop
|
|
} // side_y loop
|
|
|
|
post_code(0x07, 0x20);
|
|
|
|
// find final RDQS (X coordinate) & final VREF (Y coordinate)
|
|
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))
|
|
{
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
uint32_t tempD1;
|
|
uint32_t tempD2;
|
|
|
|
// x_coordinate:
|
|
DPF(D_INFO, "RDQS T/B eye rank%d lane%d : %d-%d %d-%d\n", rank_i, bl_i,
|
|
x_coordinate[L][T][channel_i][rank_i][bl_i],
|
|
x_coordinate[R][T][channel_i][rank_i][bl_i],
|
|
x_coordinate[L][B][channel_i][rank_i][bl_i],
|
|
x_coordinate[R][B][channel_i][rank_i][bl_i]);
|
|
|
|
tempD1 = (x_coordinate[R][T][channel_i][rank_i][bl_i] + x_coordinate[L][T][channel_i][rank_i][bl_i]) / 2; // average the TOP side LEFT & RIGHT values
|
|
tempD2 = (x_coordinate[R][B][channel_i][rank_i][bl_i] + x_coordinate[L][B][channel_i][rank_i][bl_i]) / 2; // average the BOTTOM side LEFT & RIGHT values
|
|
x_center[channel_i][rank_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
|
|
|
|
// y_coordinate:
|
|
DPF(D_INFO, "VREF R/L eye lane%d : %d-%d %d-%d\n", bl_i,
|
|
y_coordinate[R][B][channel_i][bl_i],
|
|
y_coordinate[R][T][channel_i][bl_i],
|
|
y_coordinate[L][B][channel_i][bl_i],
|
|
y_coordinate[L][T][channel_i][bl_i]);
|
|
|
|
tempD1 = (y_coordinate[R][T][channel_i][bl_i] + y_coordinate[R][B][channel_i][bl_i]) / 2; // average the RIGHT side TOP & BOTTOM values
|
|
tempD2 = (y_coordinate[L][T][channel_i][bl_i] + y_coordinate[L][B][channel_i][bl_i]) / 2; // average the LEFT side TOP & BOTTOM values
|
|
y_center[channel_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
#ifdef RX_EYE_CHECK
|
|
// perform an eye check
|
|
for (side_y=B; side_y<=T; side_y++)
|
|
{
|
|
for (side_x=L; side_x<=R; side_x++)
|
|
{
|
|
|
|
post_code(0x07, (0x30 + (side_y * 2) + (side_x)));
|
|
|
|
// update the settings for the eye check
|
|
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))
|
|
{
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
if (side_x == L)
|
|
{
|
|
set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] - (MIN_RDQS_EYE / 2)));
|
|
}
|
|
else
|
|
{
|
|
set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] + (MIN_RDQS_EYE / 2)));
|
|
}
|
|
if (side_y == B)
|
|
{
|
|
set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] - (MIN_VREF_EYE / 2)));
|
|
}
|
|
else
|
|
{
|
|
set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] + (MIN_VREF_EYE / 2)));
|
|
}
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
// request HTE reconfiguration
|
|
mrc_params->hte_setup = 1;
|
|
|
|
// check the eye
|
|
if (check_bls_ex( mrc_params, address) & 0xFF)
|
|
{
|
|
// one or more byte lanes failed
|
|
post_code(0xEE, (0x74 + (side_x * 2) + (side_y)));
|
|
}
|
|
} // side_x loop
|
|
} // side_y loop
|
|
#endif // RX_EYE_CHECK
|
|
|
|
post_code(0x07, 0x40);
|
|
|
|
// set final placements
|
|
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))
|
|
{
|
|
#ifdef R2R_SHARING
|
|
// increment "num_ranks_enabled"
|
|
num_ranks_enabled++;
|
|
#endif // R2R_SHARING
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
// x_coordinate:
|
|
#ifdef R2R_SHARING
|
|
final_delay[channel_i][bl_i] += x_center[channel_i][rank_i][bl_i];
|
|
set_rdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
|
|
#else
|
|
set_rdqs(channel_i, rank_i, bl_i, x_center[channel_i][rank_i][bl_i]);
|
|
#endif // R2R_SHARING
|
|
// y_coordinate:
|
|
set_vref(channel_i, bl_i, y_center[channel_i][bl_i]);
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
#endif // BACKUP_RDQS
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// wr_train:
|
|
// POST_CODE[major] == 0x08
|
|
//
|
|
// This function will perform the WRITE TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
|
|
// The idea here is to train the WDQ timings to achieve maximum WRITE margins.
|
|
// The algorithm will start with WDQ at the current WDQ setting (tracks WDQS in WR_LVL) +/- 32 PIs (+/- 1/4 CLK) and collapse the eye until all data patterns pass.
|
|
// This is because WDQS will be aligned to WCLK by the Write Leveling algorithm and WDQ will only ever have a 1/2 CLK window of validity.
|
|
static void wr_train(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
|
|
#define WDQ_STEP 1 // how many WDQ codes to jump while margining
|
|
#define L 0 // LEFT side loop value definition
|
|
#define R 1 // RIGHT side loop value definition
|
|
|
|
uint8_t channel_i; // channel counter
|
|
uint8_t rank_i; // rank counter
|
|
uint8_t bl_i; // byte lane counter
|
|
uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
|
|
#ifdef BACKUP_WDQ
|
|
#else
|
|
uint8_t side_i; // LEFT/RIGHT side indicator (0=L, 1=R)
|
|
uint32_t tempD; // temporary DWORD
|
|
uint32_t delay[2/*side_i*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // 2 arrays, for L & R side passing delays
|
|
uint32_t address; // target address for "check_bls_ex()"
|
|
uint32_t result; // result of "check_bls_ex()"
|
|
uint32_t bl_mask; // byte lane mask for "result" checking
|
|
#ifdef R2R_SHARING
|
|
uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
|
|
uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
|
|
#endif // R2R_SHARING
|
|
#endif // BACKUP_WDQ
|
|
|
|
// wr_train starts
|
|
post_code(0x08, 0x00);
|
|
|
|
ENTERFN();
|
|
|
|
#ifdef BACKUP_WDQ
|
|
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))
|
|
{
|
|
for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
|
|
{
|
|
set_wdq(channel_i, rank_i, bl_i, ddr_wdq[PLATFORM_ID]);
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
#else
|
|
// initialise "delay"
|
|
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))
|
|
{
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
// want to start with WDQ = (WDQS - QRTR_CLK) +/- QRTR_CLK
|
|
tempD = get_wdqs(channel_i, rank_i, bl_i) - QRTR_CLK;
|
|
delay[L][channel_i][rank_i][bl_i] = tempD - QRTR_CLK;
|
|
delay[R][channel_i][rank_i][bl_i] = tempD + QRTR_CLK;
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
// initialise other variables
|
|
bl_mask = byte_lane_mask(mrc_params);
|
|
address = get_addr(mrc_params, 0, 0);
|
|
|
|
#ifdef R2R_SHARING
|
|
// need to set "final_delay[][]" elements to "0"
|
|
memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
|
|
#endif // R2R_SHARING
|
|
|
|
// start algorithm on the LEFT side and train each channel/bl until no failures are observed, then repeat for the RIGHT side.
|
|
for (side_i = L; side_i <= R; side_i++)
|
|
{
|
|
post_code(0x08, (0x10 + (side_i)));
|
|
|
|
// set starting values
|
|
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))
|
|
{
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
|
|
// find passing values
|
|
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
|
|
{
|
|
if (mrc_params->channel_enables & (0x1 << channel_i))
|
|
{
|
|
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
|
|
{
|
|
if (mrc_params->rank_enables & (0x1 << rank_i))
|
|
{
|
|
// get an address in the target channel/rank
|
|
address = get_addr(mrc_params, channel_i, rank_i);
|
|
|
|
// request HTE reconfiguration
|
|
mrc_params->hte_setup = 1;
|
|
|
|
// check the settings
|
|
do
|
|
{
|
|
|
|
#ifdef SIM
|
|
// need restore memory to idle state as write can be in bad sync
|
|
dram_init_command (DCMD_PREA(rank_i));
|
|
#endif
|
|
|
|
// result[07:00] == failing byte lane (MAX 8)
|
|
result = check_bls_ex( mrc_params, address);
|
|
// check for failures
|
|
if (result & 0xFF)
|
|
{
|
|
// at least 1 byte lane failed
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
if (result & (bl_mask << bl_i))
|
|
{
|
|
if (side_i == L)
|
|
{
|
|
delay[L][channel_i][rank_i][bl_i] += WDQ_STEP;
|
|
}
|
|
else
|
|
{
|
|
delay[R][channel_i][rank_i][bl_i] -= WDQ_STEP;
|
|
}
|
|
// check for algorithm failure
|
|
if (delay[L][channel_i][rank_i][bl_i] != delay[R][channel_i][rank_i][bl_i])
|
|
{
|
|
// margin available, update delay setting
|
|
set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
|
|
}
|
|
else
|
|
{
|
|
// no margin available, notify the user and halt
|
|
training_message(channel_i, rank_i, bl_i);
|
|
post_code(0xEE, (0x80 + side_i));
|
|
}
|
|
} // if bl_i failed
|
|
} // bl_i loop
|
|
} // at least 1 byte lane failed
|
|
} while (result & 0xFF); // stop when all byte lanes pass
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
} // side_i loop
|
|
|
|
// program WDQ to the middle of passing window
|
|
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))
|
|
{
|
|
#ifdef R2R_SHARING
|
|
// increment "num_ranks_enabled"
|
|
num_ranks_enabled++;
|
|
#endif // R2R_SHARING
|
|
for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
|
|
{
|
|
|
|
DPF(D_INFO, "WDQ eye rank%d lane%d : %d-%d\n", rank_i, bl_i,
|
|
delay[L][channel_i][rank_i][bl_i],
|
|
delay[R][channel_i][rank_i][bl_i]);
|
|
|
|
tempD = (delay[R][channel_i][rank_i][bl_i] + delay[L][channel_i][rank_i][bl_i]) / 2;
|
|
|
|
#ifdef R2R_SHARING
|
|
final_delay[channel_i][bl_i] += tempD;
|
|
set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
|
|
#else
|
|
set_wdq(channel_i, rank_i, bl_i, tempD);
|
|
#endif // R2R_SHARING
|
|
|
|
} // bl_i loop
|
|
} // if rank is enabled
|
|
} // rank_i loop
|
|
} // if channel is enabled
|
|
} // channel_i loop
|
|
#endif // BACKUP_WDQ
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// Wrapper for jedec initialisation routine
|
|
static void perform_jedec_init(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
jedec_init(mrc_params, 0);
|
|
}
|
|
|
|
// Configure DDRPHY for Auto-Refresh, Periodic Compensations,
|
|
// Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
|
|
static void set_auto_refresh(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint32_t channel_i;
|
|
uint32_t rank_i;
|
|
uint32_t bl_i;
|
|
uint32_t bl_divisor = /*(mrc_params->channel_width==x16)?2:*/1;
|
|
uint32_t tempD;
|
|
|
|
ENTERFN();
|
|
|
|
// enable Auto-Refresh, Periodic Compensations, Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
|
|
for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
|
|
{
|
|
if (mrc_params->channel_enables & (1 << channel_i))
|
|
{
|
|
// Enable Periodic RCOMPS
|
|
isbM32m(DDRPHY, CMPCTRL, (BIT1), (BIT1));
|
|
|
|
|
|
// Enable Dynamic DiffAmp & Set Read ODT Value
|
|
switch (mrc_params->rd_odt_value)
|
|
{
|
|
case 0: tempD = 0x3F; break; // OFF
|
|
default: tempD = 0x00; break; // Auto
|
|
} // rd_odt_value switch
|
|
|
|
for (bl_i=0; bl_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_i++)
|
|
{
|
|
isbM32m(DDRPHY, (B0OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
|
|
((0x00<<16)|(tempD<<10)),
|
|
((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
|
|
|
|
isbM32m(DDRPHY, (B1OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
|
|
((0x00<<16)|(tempD<<10)),
|
|
((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10)));// Override: DIFFAMP, ODT
|
|
} // bl_i loop
|
|
|
|
// Issue ZQCS command
|
|
for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
|
|
{
|
|
if (mrc_params->rank_enables & (1 << rank_i))
|
|
{
|
|
dram_init_command(DCMD_ZQCS(rank_i));
|
|
} // if rank_i enabled
|
|
} // rank_i loop
|
|
|
|
} // if channel_i enabled
|
|
} // channel_i loop
|
|
|
|
clear_pointers();
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// Depending on configuration enables ECC support.
|
|
// Available memory size is decresed, and updated with 0s
|
|
// in order to clear error status. Address mode 2 forced.
|
|
static void ecc_enable(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDRP Drp;
|
|
RegDSCH Dsch;
|
|
RegDECCCTRL Ctr;
|
|
|
|
if (mrc_params->ecc_enables == 0) return;
|
|
|
|
ENTERFN();
|
|
|
|
// Configuration required in ECC mode
|
|
Drp.raw = isbR32m(MCU, DRP);
|
|
Drp.field.addressMap = 2;
|
|
Drp.field.split64 = 1;
|
|
isbW32m(MCU, DRP, Drp.raw);
|
|
|
|
// Disable new request bypass
|
|
Dsch.raw = isbR32m(MCU, DSCH);
|
|
Dsch.field.NEWBYPDIS = 1;
|
|
isbW32m(MCU, DSCH, Dsch.raw);
|
|
|
|
// Enable ECC
|
|
Ctr.raw = 0;
|
|
Ctr.field.SBEEN = 1;
|
|
Ctr.field.DBEEN = 1;
|
|
Ctr.field.ENCBGEN = 1;
|
|
isbW32m(MCU, DECCCTRL, Ctr.raw);
|
|
|
|
#ifdef SIM
|
|
// Read back to be sure writing took place
|
|
Ctr.raw = isbR32m(MCU, DECCCTRL);
|
|
#endif
|
|
|
|
// Assume 8 bank memory, one bank is gone for ECC
|
|
mrc_params->mem_size -= mrc_params->mem_size / 8;
|
|
|
|
// For S3 resume memory content has to be preserved
|
|
if (mrc_params->boot_mode != bmS3)
|
|
{
|
|
select_hte(mrc_params);
|
|
HteMemInit(mrc_params, MrcMemInit, MrcHaltHteEngineOnError);
|
|
select_memory_manager(mrc_params);
|
|
}
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|
|
|
|
// Lock MCU registers at the end of initialisation sequence.
|
|
static void lock_registers(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
RegDCO Dco;
|
|
|
|
ENTERFN();
|
|
|
|
Dco.raw = isbR32m(MCU, DCO);
|
|
Dco.field.PMIDIS = 0; //0 - PRI enabled
|
|
Dco.field.PMICTL = 0; //0 - PRI owned by MEMORY_MANAGER
|
|
Dco.field.DRPLOCK = 1;
|
|
Dco.field.REUTLOCK = 1;
|
|
isbW32m(MCU, DCO, Dco.raw);
|
|
|
|
LEAVEFN();
|
|
|
|
}
|
|
|
|
#ifdef MRC_SV
|
|
|
|
// cache write back invalidate
|
|
static void asm_wbinvd(void)
|
|
{
|
|
#if defined (SIM) || defined (GCC)
|
|
asm(
|
|
"wbinvd;"
|
|
);
|
|
#else
|
|
__asm wbinvd;
|
|
#endif
|
|
}
|
|
|
|
// cache invalidate
|
|
static void asm_invd(void)
|
|
{
|
|
#if defined (SIM) || defined (GCC)
|
|
asm(
|
|
"invd;"
|
|
);
|
|
#else
|
|
__asm invd;
|
|
#endif
|
|
}
|
|
|
|
|
|
static void cpu_read(void)
|
|
{
|
|
uint32_t adr, dat, limit;
|
|
|
|
asm_invd();
|
|
|
|
limit = 8 * 1024;
|
|
for (adr = 0; adr < limit; adr += 4)
|
|
{
|
|
dat = *(uint32_t*) adr;
|
|
if ((adr & 0x0F) == 0)
|
|
{
|
|
DPF(D_INFO, "\n%x : ", adr);
|
|
}
|
|
DPF(D_INFO, "%x ", dat);
|
|
}
|
|
DPF(D_INFO, "\n");
|
|
|
|
DPF(D_INFO, "CPU read done\n");
|
|
}
|
|
|
|
|
|
static void cpu_write(void)
|
|
{
|
|
uint32_t adr, limit;
|
|
|
|
limit = 8 * 1024;
|
|
for (adr = 0; adr < limit; adr += 4)
|
|
{
|
|
*(uint32_t*) adr = 0xDEAD0000 + adr;
|
|
}
|
|
|
|
asm_wbinvd();
|
|
|
|
DPF(D_INFO, "CPU write done\n");
|
|
}
|
|
|
|
|
|
static void cpu_memory_test(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint32_t result = 0;
|
|
uint32_t val, dat, adr, adr0, step, limit;
|
|
uint64_t my_tsc;
|
|
|
|
ENTERFN();
|
|
|
|
asm_invd();
|
|
|
|
adr0 = 1 * 1024 * 1024;
|
|
limit = 256 * 1024 * 1024;
|
|
|
|
for (step = 0; step <= 4; step++)
|
|
{
|
|
DPF(D_INFO, "Mem test step %d starting from %xh\n", step, adr0);
|
|
|
|
my_tsc = read_tsc();
|
|
for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
|
|
{
|
|
if (step == 0) dat = adr;
|
|
else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
|
|
else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
|
|
else if (step == 3) dat = 0x5555AAAA;
|
|
else if (step == 4) dat = 0xAAAA5555;
|
|
|
|
*(uint32_t*) adr = dat;
|
|
}
|
|
DPF(D_INFO, "Write time %llXh\n", read_tsc() - my_tsc);
|
|
|
|
my_tsc = read_tsc();
|
|
for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
|
|
{
|
|
if (step == 0) dat = adr;
|
|
else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
|
|
else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
|
|
else if (step == 3) dat = 0x5555AAAA;
|
|
else if (step == 4) dat = 0xAAAA5555;
|
|
|
|
val = *(uint32_t*) adr;
|
|
|
|
if (val != dat)
|
|
{
|
|
DPF(D_INFO, "%x vs. %x@%x\n", dat, val, adr);
|
|
result = adr|BIT31;
|
|
}
|
|
}
|
|
DPF(D_INFO, "Read time %llXh\n", read_tsc() - my_tsc);
|
|
}
|
|
|
|
DPF( D_INFO, "Memory test result %x\n", result);
|
|
LEAVEFN();
|
|
}
|
|
#endif // MRC_SV
|
|
|
|
|
|
// Execute memory test, if error dtected it is
|
|
// indicated in mrc_params->status.
|
|
static void memory_test(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
uint32_t result = 0;
|
|
|
|
ENTERFN();
|
|
|
|
select_hte(mrc_params);
|
|
result = HteMemInit(mrc_params, MrcMemTest, MrcHaltHteEngineOnError);
|
|
select_memory_manager(mrc_params);
|
|
|
|
DPF(D_INFO, "Memory test result %x\n", result);
|
|
mrc_params->status = ((result == 0) ? MRC_SUCCESS : MRC_E_MEMTEST);
|
|
LEAVEFN();
|
|
}
|
|
|
|
|
|
// Force same timings as with backup settings
|
|
static void static_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_rcvn(ch, rk, bl, 498); // RCVN
|
|
set_rdqs(ch, rk, bl, 24); // RDQS
|
|
set_wdqs(ch, rk, bl, 292); // WDQS
|
|
set_wdq( ch, rk, bl, 260); // WDQ
|
|
if (rk == 0)
|
|
{
|
|
set_vref(ch, bl, 32); // VREF (RANK0 only)
|
|
}
|
|
}
|
|
set_wctl(ch, rk, 217); // WCTL
|
|
}
|
|
set_wcmd(ch, 220); // WCMD
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Initialise system memory.
|
|
//
|
|
void MemInit(
|
|
MRCParams_t *mrc_params)
|
|
{
|
|
static const MemInit_t init[] =
|
|
{
|
|
{ 0x0101, bmCold|bmFast|bmWarm|bmS3, clear_self_refresh }, //0
|
|
{ 0x0200, bmCold|bmFast|bmWarm|bmS3, prog_ddr_timing_control }, //1 initialise the MCU
|
|
{ 0x0103, bmCold|bmFast , prog_decode_before_jedec }, //2
|
|
{ 0x0104, bmCold|bmFast , perform_ddr_reset }, //3
|
|
{ 0x0300, bmCold|bmFast |bmS3, ddrphy_init }, //4 initialise the DDRPHY
|
|
{ 0x0400, bmCold|bmFast , perform_jedec_init }, //5 perform JEDEC initialisation of DRAMs
|
|
{ 0x0105, bmCold|bmFast , set_ddr_init_complete }, //6
|
|
{ 0x0106, bmFast|bmWarm|bmS3, restore_timings }, //7
|
|
{ 0x0106, bmCold , default_timings }, //8
|
|
{ 0x0500, bmCold , rcvn_cal }, //9 perform RCVN_CAL algorithm
|
|
{ 0x0600, bmCold , wr_level }, //10 perform WR_LEVEL algorithm
|
|
{ 0x0120, bmCold , prog_page_ctrl }, //11
|
|
{ 0x0700, bmCold , rd_train }, //12 perform RD_TRAIN algorithm
|
|
{ 0x0800, bmCold , wr_train }, //13 perform WR_TRAIN algorithm
|
|
{ 0x010B, bmCold , store_timings }, //14
|
|
{ 0x010C, bmCold|bmFast|bmWarm|bmS3, enable_scrambling }, //15
|
|
{ 0x010D, bmCold|bmFast|bmWarm|bmS3, prog_ddr_control }, //16
|
|
{ 0x010E, bmCold|bmFast|bmWarm|bmS3, prog_dra_drb }, //17
|
|
{ 0x010F, bmWarm|bmS3, perform_wake }, //18
|
|
{ 0x0110, bmCold|bmFast|bmWarm|bmS3, change_refresh_period }, //19
|
|
{ 0x0111, bmCold|bmFast|bmWarm|bmS3, set_auto_refresh }, //20
|
|
{ 0x0112, bmCold|bmFast|bmWarm|bmS3, ecc_enable }, //21
|
|
{ 0x0113, bmCold|bmFast , memory_test }, //22
|
|
{ 0x0114, bmCold|bmFast|bmWarm|bmS3, lock_registers } //23 set init done
|
|
};
|
|
|
|
uint32_t i;
|
|
|
|
ENTERFN();
|
|
|
|
DPF(D_INFO, "Meminit build %s %s\n", __DATE__, __TIME__);
|
|
|
|
// MRC started
|
|
post_code(0x01, 0x00);
|
|
|
|
if (mrc_params->boot_mode != bmCold)
|
|
{
|
|
if (mrc_params->ddr_speed != mrc_params->timings.ddr_speed)
|
|
{
|
|
// full training required as frequency changed
|
|
mrc_params->boot_mode = bmCold;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MCOUNT(init); i++)
|
|
{
|
|
uint64_t my_tsc;
|
|
|
|
#ifdef MRC_SV
|
|
if (mrc_params->menu_after_mrc && i > 14)
|
|
{
|
|
uint8_t ch;
|
|
|
|
mylop:
|
|
|
|
DPF(D_INFO, "-- c - continue --\n");
|
|
DPF(D_INFO, "-- j - move to jedec init --\n");
|
|
DPF(D_INFO, "-- m - memory test --\n");
|
|
DPF(D_INFO, "-- r - cpu read --\n");
|
|
DPF(D_INFO, "-- w - cpu write --\n");
|
|
DPF(D_INFO, "-- b - hte base test --\n");
|
|
DPF(D_INFO, "-- g - hte extended test --\n");
|
|
|
|
ch = mgetc();
|
|
switch (ch)
|
|
{
|
|
case 'c':
|
|
break;
|
|
case 'j': //move to jedec init
|
|
i = 5;
|
|
break;
|
|
|
|
case 'M':
|
|
case 'N':
|
|
{
|
|
uint32_t n, res, cnt=0;
|
|
|
|
for(n=0; mgetch()==0; n++)
|
|
{
|
|
if( ch == 'M' || n % 256 == 0)
|
|
{
|
|
DPF(D_INFO, "n=%d e=%d\n", n, cnt);
|
|
}
|
|
|
|
res = 0;
|
|
|
|
if( ch == 'M')
|
|
{
|
|
memory_test(mrc_params);
|
|
res |= mrc_params->status;
|
|
}
|
|
|
|
mrc_params->hte_setup = 1;
|
|
res |= check_bls_ex(mrc_params, 0x00000000);
|
|
res |= check_bls_ex(mrc_params, 0x00000000);
|
|
res |= check_bls_ex(mrc_params, 0x00000000);
|
|
res |= check_bls_ex(mrc_params, 0x00000000);
|
|
|
|
if( mrc_params->rank_enables & 2)
|
|
{
|
|
mrc_params->hte_setup = 1;
|
|
res |= check_bls_ex(mrc_params, 0x40000000);
|
|
res |= check_bls_ex(mrc_params, 0x40000000);
|
|
res |= check_bls_ex(mrc_params, 0x40000000);
|
|
res |= check_bls_ex(mrc_params, 0x40000000);
|
|
}
|
|
|
|
if( res != 0)
|
|
{
|
|
DPF(D_INFO, "###########\n");
|
|
DPF(D_INFO, "#\n");
|
|
DPF(D_INFO, "# Error count %d\n", ++cnt);
|
|
DPF(D_INFO, "#\n");
|
|
DPF(D_INFO, "###########\n");
|
|
}
|
|
|
|
} // for
|
|
|
|
select_memory_manager(mrc_params);
|
|
}
|
|
goto mylop;
|
|
case 'm':
|
|
memory_test(mrc_params);
|
|
goto mylop;
|
|
case 'n':
|
|
cpu_memory_test(mrc_params);
|
|
goto mylop;
|
|
|
|
case 'l':
|
|
ch = mgetc();
|
|
if (ch <= '9') DpfPrintMask ^= (ch - '0') << 3;
|
|
DPF(D_INFO, "Log mask %x\n", DpfPrintMask);
|
|
goto mylop;
|
|
case 'p':
|
|
print_timings(mrc_params);
|
|
goto mylop;
|
|
case 'R':
|
|
rd_train(mrc_params);
|
|
goto mylop;
|
|
case 'W':
|
|
wr_train(mrc_params);
|
|
goto mylop;
|
|
|
|
case 'r':
|
|
cpu_read();
|
|
goto mylop;
|
|
case 'w':
|
|
cpu_write();
|
|
goto mylop;
|
|
|
|
case 'g':
|
|
{
|
|
uint32_t result;
|
|
select_hte(mrc_params);
|
|
mrc_params->hte_setup = 1;
|
|
result = check_bls_ex(mrc_params, 0);
|
|
DPF(D_INFO, "Extended test result %x\n", result);
|
|
select_memory_manager(mrc_params);
|
|
}
|
|
goto mylop;
|
|
case 'b':
|
|
{
|
|
uint32_t result;
|
|
select_hte(mrc_params);
|
|
mrc_params->hte_setup = 1;
|
|
result = check_rw_coarse(mrc_params, 0);
|
|
DPF(D_INFO, "Base test result %x\n", result);
|
|
select_memory_manager(mrc_params);
|
|
}
|
|
goto mylop;
|
|
case 'B':
|
|
select_hte(mrc_params);
|
|
HteMemOp(0x2340, 1, 1);
|
|
select_memory_manager(mrc_params);
|
|
goto mylop;
|
|
|
|
case '3':
|
|
{
|
|
RegDPMC0 DPMC0reg;
|
|
|
|
DPF( D_INFO, "===>> Start suspend\n");
|
|
isbR32m(MCU, DSTAT);
|
|
|
|
DPMC0reg.raw = isbR32m(MCU, DPMC0);
|
|
DPMC0reg.field.DYNSREN = 0;
|
|
DPMC0reg.field.powerModeOpCode = 0x05; // Disable Master DLL
|
|
isbW32m(MCU, DPMC0, DPMC0reg.raw);
|
|
|
|
// Should be off for negative test case verification
|
|
#if 1
|
|
Wr32(MMIO, PCIADDR(0,0,0,SB_PACKET_REG),
|
|
(uint32_t)SB_COMMAND(SB_SUSPEND_CMND_OPCODE, MCU, 0));
|
|
#endif
|
|
|
|
DPF( D_INFO, "press key\n");
|
|
mgetc();
|
|
DPF( D_INFO, "===>> Start resume\n");
|
|
isbR32m(MCU, DSTAT);
|
|
|
|
mrc_params->boot_mode = bmS3;
|
|
i = 0;
|
|
}
|
|
|
|
} // switch
|
|
|
|
} // if( menu
|
|
#endif //MRC_SV
|
|
|
|
if (mrc_params->boot_mode & init[i].boot_path)
|
|
{
|
|
uint8_t major = init[i].post_code >> 8 & 0xFF;
|
|
uint8_t minor = init[i].post_code >> 0 & 0xFF;
|
|
post_code(major, minor);
|
|
|
|
my_tsc = read_tsc();
|
|
init[i].init_fn(mrc_params);
|
|
DPF(D_TIME, "Execution time %llX", read_tsc() - my_tsc);
|
|
}
|
|
}
|
|
|
|
// display the timings
|
|
print_timings(mrc_params);
|
|
|
|
// MRC is complete.
|
|
post_code(0x01, 0xFF);
|
|
|
|
LEAVEFN();
|
|
return;
|
|
}
|