From 9b6bbcdbfdf5e54c6d1ed538ea8076d0858fb164 Mon Sep 17 00:00:00 2001 From: Michael Kinney Date: Tue, 15 Dec 2015 19:22:23 +0000 Subject: QuarkSocPkg: Add new package for Quark SoC X1000 Changes for V4 ============== 1) Remove Unicode character from C source file 2) Move delete of QuarkSocPkg\QuarkNorthCluster\Binary\QuarkMicrocode from QuarkPlatformPkg commit to QuarkSocPkg commit Changes for V2 ============== 1) Sync with new APIs in SmmCpuFeaturesLib class 2) Use new generic PCI serial driver PciSioSerialDxe in MdeModulePkg 3) Remove PCI serial driver from QuarkSocPkg 4) Apply optimizations to MtrrLib from MtrrLib in UefiCpuPkg 5) Convert all UNI files to utf-8 6) Replace tabs with spaces and remove trailing spaces 7) Add License.txt Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Michael Kinney Acked-by: Jordan Justen git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@19286 6f19259b-4bc3-4df7-8a09-765794883524 --- .../QuarkNorthCluster/MemoryInit/Pei/MemoryInit.c | 65 + .../QuarkNorthCluster/MemoryInit/Pei/MemoryInit.h | 41 + .../MemoryInit/Pei/MemoryInitPei.inf | 76 + .../QuarkNorthCluster/MemoryInit/Pei/core_types.h | 49 + .../MemoryInit/Pei/gen5_iosf_sb_definitions.h | 744 ++++++ .../MemoryInit/Pei/general_definitions.h | 90 + QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.c | 542 ++++ QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.h | 72 + QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/io.h | 138 + .../QuarkNorthCluster/MemoryInit/Pei/lprint.c | 388 +++ .../QuarkNorthCluster/MemoryInit/Pei/meminit.c | 2645 ++++++++++++++++++++ .../QuarkNorthCluster/MemoryInit/Pei/meminit.h | 28 + .../MemoryInit/Pei/meminit_utils.c | 1580 ++++++++++++ .../MemoryInit/Pei/meminit_utils.h | 97 + .../MemoryInit/Pei/memory_options.h | 83 + QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.c | 46 + QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.h | 166 ++ .../QuarkNorthCluster/MemoryInit/Pei/platform.c | 192 ++ .../QuarkNorthCluster/MemoryInit/Pei/prememinit.c | 193 ++ .../QuarkNorthCluster/MemoryInit/Pei/prememinit.h | 21 + 20 files changed, 7256 insertions(+) create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInitPei.inf create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/core_types.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/gen5_iosf_sb_definitions.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/general_definitions.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/io.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/lprint.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/memory_options.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.h create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/platform.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.c create mode 100644 QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.h (limited to 'QuarkSocPkg/QuarkNorthCluster/MemoryInit') diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.c new file mode 100644 index 0000000000..782a2d13a7 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.c @@ -0,0 +1,65 @@ +/** @file +Framework PEIM to initialize memory on a QuarkNcSocId Memory Controller. + +Copyright (c) 2013-2015 Intel Corporation. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +// +// Include common header file for this module. +// +#include "MemoryInit.h" + +static PEI_QNC_MEMORY_INIT_PPI mPeiQNCMemoryInitPpi = +{ MrcStart }; + +static EFI_PEI_PPI_DESCRIPTOR PpiListPeiQNCMemoryInit = +{ + (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), + &gQNCMemoryInitPpiGuid, + &mPeiQNCMemoryInitPpi +}; + +void Mrc( MRCParams_t *MrcData); + +/** + + Do memory initialization for QuarkNcSocId DDR3 SDRAM Controller + + @param FfsHeader Not used. + @param PeiServices General purpose services available to every PEIM. + + @return EFI_SUCCESS Memory initialization completed successfully. + All other error conditions encountered result in an ASSERT. + + **/ +EFI_STATUS +PeimMemoryInit( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + + Status = (**PeiServices).InstallPpi(PeiServices, &PpiListPeiQNCMemoryInit); + + return Status; +} + +VOID +EFIAPI +MrcStart( + IN OUT MRCParams_t *MrcData + ) +{ + + Mrc(MrcData); +} diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.h new file mode 100644 index 0000000000..5d61c4d71c --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInit.h @@ -0,0 +1,41 @@ +/** @file +Framework PEIM to initialize memory on an DDR2 SDRAM Memory Controller. + +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. + +**/ +#ifndef _PEI_QNC_MEMORY_INIT_H_ +#define _PEI_QNC_MEMORY_INIT_H_ + +// +// The package level header files this module uses +// +#include +#include +// +// The protocols, PPI and GUID defintions for this module +// +#include +// +// The Library classes this module consumes +// +#include +#include +#include + + +VOID +EFIAPI +MrcStart ( + IN OUT MRCParams_t *MrcData + ); + +#endif diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInitPei.inf b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInitPei.inf new file mode 100644 index 0000000000..e32768446e --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/MemoryInitPei.inf @@ -0,0 +1,76 @@ +## @file +# This is the Memory Initialization Driver for Quark +# +# 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. +# +## + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = MemoryInitPei + FILE_GUID = D2C69B26-82E1-4a1b-AD35-ED0261B9F347 + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = PeimMemoryInit + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 IPF EBC +# + +[BuildOptions] + GCC:DEBUG_*_*_CC_FLAGS = -DGCC -Wno-unused-function + GCC:RELEASE_*_*_CC_FLAGS = -DNDEBUG -DGCC -Wno-unused-function + INTEL:RELEASE_*_*_CC_FLAGS = /D NDEBUG + MSFT:RELEASE_*_*_CC_FLAGS = /D NDEBUG + +[Sources] + memory_options.h + platform.c + lprint.c + meminit.h + meminit.c + meminit_utils.h + meminit_utils.c + gen5_iosf_sb_definitions.h + general_definitions.h + io.h + core_types.h + prememinit.h + prememinit.c + mrc.h + mrc.c + hte.c + hte.h + MemoryInit.h + MemoryInit.c + +[Packages] + QuarkSocPkg/QuarkSocPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + PeimEntryPoint + DebugLib + BaseMemoryLib + +[Ppis] + gQNCMemoryInitPpiGuid # PPI ALWAYS_PRODUCED + +[Depex] + TRUE diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/core_types.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/core_types.h new file mode 100644 index 0000000000..78807a0958 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/core_types.h @@ -0,0 +1,49 @@ +/** @file +Core types used in Mrc. + +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. + +**/ +#ifndef __MRC_CORE_TYPES_H +#define __MRC_CORE_TYPES_H + +typedef char char_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +typedef int int32_t; +typedef unsigned int uint32_t; +typedef unsigned char bool; +typedef unsigned int size_t; + +#ifdef ASM_INC +// Unfortunately h2inc has issue with long long +typedef struct uint64_s +{ + uint32_t lo; + uint32_t hi; +}uint64_t; +#else +typedef unsigned long long uint64_t; +#endif + +#ifdef SIM +// Native word length is 64bit in simulation environment +typedef uint64_t uintn_t; +#else +// Quark is 32bit +typedef uint32_t uintn_t; +#endif + +#define PTR32(a) ((volatile uint32_t*)(uintn_t)(a)) + +#endif + diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/gen5_iosf_sb_definitions.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/gen5_iosf_sb_definitions.h new file mode 100644 index 0000000000..a8083a1f98 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/gen5_iosf_sb_definitions.h @@ -0,0 +1,744 @@ +/************************************************************************ + * + * 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. + * + * MCU register definition + * + ************************************************************************/ +#ifndef __IOSF_DEFINITIONS_H +#define __IOSF_DEFINITIONS_H + +// Define each of the IOSF-SB register offsets used by MRC. + + +// MCU registers (DUNIT): +// ==== +#define DRP 0x0000 +#define DTR0 0x0001 +#define DTR1 0x0002 +#define DTR2 0x0003 +#define DTR3 0x0004 +#define DTR4 0x0005 +#define DPMC0 0x0006 +#define DPMC1 0x0007 +#define DRFC 0x0008 +#define DSCH 0x0009 +#define DCAL 0x000A +#define DRMC 0x000B +#define PMSTS 0x000C +#define DCO 0x000F +#define DSTAT 0x0020 +#define DECCCTRL 0x0060 +#define DFUSESTAT 0x0070 +#define SCRMSEED 0x0080 +#define SCRMLO 0x0081 +#define SCRMHI 0x0082 + +#define MCU_CH_OFFSET 0x0040 +#define MCU_RK_OFFSET 0x0020 + +//// +// +// BEGIN DUnit register definition +// +#pragma pack(1) +typedef union { + uint32_t raw; + struct { + uint32_t rank0Enabled :1; /**< BIT [0] Rank 0 Enable */ + uint32_t rank1Enabled :1; /**< BIT [1] Rank 1 Enable */ + uint32_t reserved0 :2; + uint32_t dimm0DevWidth :2; /**< BIT [5:4] DIMM 0 Device Width (Rank0&1) */ + uint32_t dimm0DevDensity :2; /**< BIT [7:6] DIMM 0 Device Density */ + uint32_t reserved1 :1; + uint32_t dimm1DevWidth :2; /**< BIT [10:9] DIMM 1 Device Width (Rank2&3) */ + uint32_t dimm1DevDensity :2; /**< BIT [12:11] DIMM 1 Device Density */ + uint32_t split64 :1; /**< BIT [13] split 64B transactions */ + uint32_t addressMap :2; /**< BIT [15:14] Address Map select */ + uint32_t reserved3 :14; + uint32_t mode32 :1; /**< BIT [30] Select 32bit data interface*/ + uint32_t reserved4 :1; + } field; +} RegDRP; /**< DRAM Rank Population and Interface Register */ +#pragma pack() + + +#pragma pack(1) +typedef union { + uint32_t raw; + struct { + uint32_t dramFrequency :2; /**< DRAM Frequency (000=800,001=1033,010=1333) */ + uint32_t reserved1 :2; + uint32_t tRP :4; /**< bit [7:4] Precharge to Activate Delay */ + uint32_t tRCD :4; /**< bit [11:8] Activate to CAS Delay */ + uint32_t tCL :3; /**< bit [14:12] CAS Latency */ + uint32_t reserved4 :1; + uint32_t tXS :1; /**< SRX Delay */ + uint32_t reserved5 :1; + uint32_t tXSDLL :1; /**< SRX To DLL Delay */ + uint32_t reserved6 :1; + uint32_t tZQCS :1; /**< bit [20] ZQTS recovery Latncy */ + uint32_t reserved7 :1; + uint32_t tZQCL :1; /**< bit [22] ZQCL recovery Latncy */ + uint32_t reserved8 :1; + uint32_t pmeDelay :2; /**< bit [25:24] Power mode entry delay */ + uint32_t reserved9 :2; + uint32_t CKEDLY :4; /**< bit [31:28] */ + } field; +} RegDTR0; /**< DRAM Timing Register 0 */ +#pragma pack() + +#pragma pack(1) +typedef union { + uint32_t raw; + struct { + uint32_t tWCL :3; /**< bit [2:0] CAS Write Latency */ + uint32_t reserved1 :1; + uint32_t tCMD :2; /**< bit [5:4] Command transport duration */ + uint32_t reserved2 :2; + uint32_t tWTP :4; /**< Write to Precharge */ + uint32_t tCCD :2; /**< CAS to CAS delay */ + uint32_t reserved4 :2; + uint32_t tFAW :4; /**< Four bank Activation Window*/ + uint32_t tRAS :4; /**< Row Activation Period: */ + uint32_t tRRD :2; /**mem_size >> 6) - 1); +#endif + + isbW32m(HTE, 0x00020063, 0xAAAAAAAA); + isbW32m(HTE, 0x00020064, 0xCCCCCCCC); + isbW32m(HTE, 0x00020065, 0xF0F0F0F0); + isbW32m(HTE, 0x00020066, 0x03000000); + + switch (MemInitFlag) + { + case MrcMemInit: + TestNum = 1; // Only 1 write pass through memory is needed to initialize ECC. + break; + case MrcMemTest: + TestNum = 4; // Write/read then write/read with inverted pattern. + break; + default: + DPF(D_INFO, "Unknown parameter for MemInitFlag: %d\n", MemInitFlag); + return 0xFFFFFFFF; + break; + } + + DPF(D_INFO, "HteMemInit"); + for (i = 0; i < TestNum; i++) + { + DPF(D_INFO, "."); + + if (i == 0) + { + isbW32m(HTE, 0x00020061, 0x00000000); + isbW32m(HTE, 0x00020020, 0x00110010); + } + else if (i == 1) + { + isbW32m(HTE, 0x00020061, 0x00000000); + isbW32m(HTE, 0x00020020, 0x00010010); + } + else if (i == 2) + { + isbW32m(HTE, 0x00020061, 0x00010100); + isbW32m(HTE, 0x00020020, 0x00110010); + } + else + { + isbW32m(HTE, 0x00020061, 0x00010100); + isbW32m(HTE, 0x00020020, 0x00010010); + } + + isbW32m(HTE, 0x00020011, 0x00111000); + isbW32m(HTE, 0x00020011, 0x00111100); + + WaitForHteComplete(); + + // + // If this is a READ pass, check for errors at the end. + // + if ((i % 2) == 1) + { + // + // Return immediately if error. + // + if (CheckHteErrors()) + { + break; + } + } + } + + DPF(D_INFO, "done\n", i); + return CheckHteErrors(); +} + +STATIC UINT16 BasicDataCompareHte( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 FirstRun, + UINT8 Mode) +/*++ + + Routine Description: + + Execute basic single cache line memory write/read/verify test using simple constant + pattern (different for READ_RAIN and WRITE_TRAIN modes. + See BasicWriteReadHTE which is external visible wrapper. + + Arguments: + + CurrentMrcData: Host struture for all MRC global data. + Address: memory adress being tested (must hit specific channel/rank) + FirstRun: If set then hte registers are configured, otherwise + it is assumed configuration is done and just re-run the test. + Mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern) + + Returns: + Returns byte lane failure on each bit (for Quark only bit0 and bit1) + + --*/ +{ + UINT32 Pattern; + UINT32 Offset; + + if (FirstRun) + { + isbW32m(HTE, 0x00020020, 0x01B10021); + isbW32m(HTE, 0x00020021, 0x06000000); + isbW32m(HTE, 0x00020022, Address >> 6); + isbW32m(HTE, 0x00020062, 0x00800015); + isbW32m(HTE, 0x00020063, 0xAAAAAAAA); + isbW32m(HTE, 0x00020064, 0xCCCCCCCC); + isbW32m(HTE, 0x00020065, 0xF0F0F0F0); + isbW32m(HTE, 0x00020061, 0x00030008); + + if (Mode == WRITE_TRAIN) + { + Pattern = 0xC33C0000; + } + else // READ_TRAIN + { + Pattern = 0xAA5555AA; + } + + for (Offset = 0x80; Offset <= 0x8F; Offset++) + { + isbW32m(HTE, Offset, Pattern); + } + } + + isbW32m(HTE, 0x000200A1, 0xFFFF1000); + + isbW32m(HTE, 0x00020011, 0x00011000); + isbW32m(HTE, 0x00020011, 0x00011100); + + WaitForHteComplete(); + + // + // Return bits 15:8 of HTE_CH0_ERR_XSTAT to check for any bytelane errors. + // + return ((CheckHteErrors() >> 8) & 0xFF); +} + +STATIC UINT16 ReadWriteDataCompareHte( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 LoopCount, + UINT32 LfsrSeedVictim, + UINT32 LfsrSeedAggressor, + UINT8 VictimBit, + UINT8 FirstRun) +/*++ + + Routine Description: + + Examines single cache line memory with write/read/verify test using + multiple data patterns (victim-aggressor algorithm). + See WriteStressBitLanesHTE which is external visible wrapper. + + Arguments: + + CurrentMrcData: host struture for all MRC global data. + Address: memory adress being tested (must hit specific channel/rank) + LoopCount: number of test iterations + LfsrSeedXxx: victim aggressor data pattern seed + VictimBit: should be 0 as auto rotate feature is in use. + FirstRun: If set then hte registers are configured, otherwise + it is assumed configuration is done and just re-run the test. + + Returns: + Returns byte lane failure on each bit (for Quark only bit0 and bit1) + + --*/ +{ + UINT32 Offset; + UINT32 Tmp; + + if (FirstRun) + { + isbW32m(HTE, 0x00020020, 0x00910024); + isbW32m(HTE, 0x00020023, 0x00810024); + isbW32m(HTE, 0x00020021, 0x06070000); + isbW32m(HTE, 0x00020024, 0x06070000); + isbW32m(HTE, 0x00020022, Address >> 6); + isbW32m(HTE, 0x00020025, Address >> 6); + isbW32m(HTE, 0x00020062, 0x0000002A); + isbW32m(HTE, 0x00020063, LfsrSeedVictim); + isbW32m(HTE, 0x00020064, LfsrSeedAggressor); + isbW32m(HTE, 0x00020065, LfsrSeedVictim); + + // + // Write the pattern buffers to select the victim bit. Start with bit0. + // + for (Offset = 0x80; Offset <= 0x8F; Offset++) + { + if ((Offset % 8) == VictimBit) + { + isbW32m(HTE, Offset, 0x55555555); + } + else + { + isbW32m(HTE, Offset, 0xCCCCCCCC); + } + } + + isbW32m(HTE, 0x00020061, 0x00000000); + isbW32m(HTE, 0x00020066, 0x03440000); + isbW32m(HTE, 0x000200A1, 0xFFFF1000); + } + + Tmp = 0x10001000 | (LoopCount << 16); + isbW32m(HTE, 0x00020011, Tmp); + isbW32m(HTE, 0x00020011, Tmp | BIT8); + + WaitForHteComplete(); + + return (CheckHteErrors() >> 8) & 0xFF; +} + +UINT16 BasicWriteReadHTE( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 FirstRun, + UINT8 Mode) +/*++ + + Routine Description: + + Execute basic single cache line memory write/read/verify test using simple constant + pattern (different for READ_RAIN and WRITE_TRAIN modes. + + Arguments: + + CurrentMrcData: Host struture for all MRC global data. + Address: memory adress being tested (must hit specific channel/rank) + FirstRun: If set then hte registers are configured, otherwise + it is assumed configuration is done and just re-run the test. + Mode: READ_TRAIN or WRITE_TRAIN (the difference is in the pattern) + + Returns: + Returns byte lane failure on each bit (for Quark only bit0 and bit1) + + --*/ +{ + UINT16 ByteLaneErrors; + + ENTERFN(); + + // + // Enable all error reporting in preparation for HTE test. + // + EnableAllHteErrors(0xFF); + ClearHteErrorRegisters(); + + ByteLaneErrors = BasicDataCompareHte(CurrentMrcData, Address, FirstRun, + Mode); + + LEAVEFN(); + return ByteLaneErrors; +} + +UINT16 WriteStressBitLanesHTE( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 FirstRun) +/*++ + + Routine Description: + + Examines single cache line memory with write/read/verify test using + multiple data patterns (victim-aggressor algorithm). + + Arguments: + + CurrentMrcData: host struture for all MRC global data. + Address: memory adress being tested (must hit specific channel/rank) + FirstRun: If set then hte registers are configured, otherwise + it is assumed configuration is done and just re-run the test. + + Returns: + Returns byte lane failure on each bit (for Quark only bit0 and bit1) + + --*/ +{ + UINT16 ByteLaneErrors; + UINT8 VictimBit = 0; + + ENTERFN(); + + // + // Enable all error reporting in preparation for HTE test. + // + EnableAllHteErrors(0xFF); + ClearHteErrorRegisters(); + + // + // Loop through each bit in the bytelane. Each pass creates a victim bit + // while keeping all other bits the same - as aggressors. + // AVN HTE adds an auto-rotate feature which allows us to program the entire victim/aggressor + // sequence in 1 step. The victim bit rotates on each pass so no need to have software implement + // a victim bit loop like on VLV. + // + ByteLaneErrors = ReadWriteDataCompareHte(CurrentMrcData, Address, + HTE_LOOP_CNT, HTE_LFSR_VICTIM_SEED, HTE_LFSR_AGRESSOR_SEED, VictimBit, + FirstRun); + + LEAVEFN(); + return ByteLaneErrors; +} + +VOID HteMemOp( + UINT32 Address, + UINT8 FirstRun, + UINT8 IsWrite) +/*++ + + Routine Description: + + Execute basic single cache line memory write or read. + This is just for receive enable / fine write levelling purpose. + + Arguments: + + CurrentMrcData: Host structure for all MRC global data. + Address: memory address used (must hit specific channel/rank) + FirstRun: If set then hte registers are configured, otherwise + it is assumed configuration is done and just re-run the test. + IsWrite: When non-zero memory write operation executed, otherwise read + + Returns: + None + + --*/ +{ + UINT32 Offset; + UINT32 Tmp; + + EnableAllHteErrors(0xFF); + ClearHteErrorRegisters(); + + if (FirstRun) + { + Tmp = IsWrite ? 0x01110021 : 0x01010021; + isbW32m(HTE, 0x00020020, Tmp); + + isbW32m(HTE, 0x00020021, 0x06000000); + isbW32m(HTE, 0x00020022, Address >> 6); + isbW32m(HTE, 0x00020062, 0x00800015); + isbW32m(HTE, 0x00020063, 0xAAAAAAAA); + isbW32m(HTE, 0x00020064, 0xCCCCCCCC); + isbW32m(HTE, 0x00020065, 0xF0F0F0F0); + isbW32m(HTE, 0x00020061, 0x00030008); + + for (Offset = 0x80; Offset <= 0x8F; Offset++) + { + isbW32m(HTE, Offset, 0xC33C0000); + } + } + + isbW32m(HTE, 0x000200A1, 0xFFFF1000); + isbW32m(HTE, 0x00020011, 0x00011000); + isbW32m(HTE, 0x00020011, 0x00011100); + + WaitForHteComplete(); +} + diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.h new file mode 100644 index 0000000000..eeb6192ca0 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.h @@ -0,0 +1,72 @@ +/** @file +HTE handling routines for MRC use. + +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. + +**/ +#ifndef __HTE_H +#define __HTE_H + +#define STATIC static +#define VOID void + +#if !defined(__GNUC__) && (__STDC_VERSION__ < 199901L) +typedef uint32_t UINT32; +typedef uint16_t UINT16; +typedef uint8_t UINT8; +#endif + +typedef enum +{ + MrcNoHaltSystemOnError, + MrcHaltSystemOnError, + MrcHaltHteEngineOnError, + MrcNoHaltHteEngineOnError +} HALT_TYPE; + +typedef enum +{ + MrcMemInit, MrcMemTest +} MEM_INIT_OR_TEST; + +#define READ_TRAIN 1 +#define WRITE_TRAIN 2 + +#define HTE_MEMTEST_NUM 2 +#define HTE_LOOP_CNT 5 // EXP_LOOP_CNT field of HTE_CMD_CTL. This CANNOT be less than 4 +#define HTE_LFSR_VICTIM_SEED 0xF294BA21 // Random seed for victim. +#define HTE_LFSR_AGRESSOR_SEED 0xEBA7492D // Random seed for aggressor. +UINT32 +HteMemInit( + MRC_PARAMS *CurrentMrcData, + UINT8 MemInitFlag, + UINT8 HaltHteEngineOnError); + +UINT16 +BasicWriteReadHTE( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 FirstRun, + UINT8 Mode); + +UINT16 +WriteStressBitLanesHTE( + MRC_PARAMS *CurrentMrcData, + UINT32 Address, + UINT8 FirstRun); + +VOID +HteMemOp( + UINT32 Address, + UINT8 FirstRun, + UINT8 IsWrite); + +#endif diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/io.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/io.h new file mode 100644 index 0000000000..7419c593dc --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/io.h @@ -0,0 +1,138 @@ +/** @file +Declaration of IO handling routines. + +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. + +**/ +#ifndef __IO_H +#define __IO_H + +#include "core_types.h" + +#include "general_definitions.h" +#include "gen5_iosf_sb_definitions.h" + +// Instruction not present on Quark +#define SFENCE() + +#define DEAD_LOOP() for(;;); + +//// +// Define each of the IOSF_SB ports used by MRC +// + +// +// Has to be 0 because of emulation static data +// initialisation: +// Space_t EmuSpace[ SPACE_COUNT] = {0}; +// +#define FREE 0x000 + +// Pseudo side-band ports for access abstraction +// See Wr32/Rd32 functions +#define MEM 0x101 +#define MMIO 0x102 +#define DCMD 0x0A0 + +// Real side-band ports +// See Wr32/Rd32 functions +#define MCU 0x001 +#define HOST_BRIDGE 0x003 +#define MEMORY_MANAGER 0x005 +#define HTE 0x011 +#define DDRPHY 0x012 +#define FUSE 0x033 + +// End of IOSF_SB ports +//// + +// Pciexbar address +#define EC_BASE 0xE0000000 + +#define PCIADDR(bus,dev,fn,reg) ( \ + (EC_BASE) + \ + ((bus) << 20) + \ + ((dev) << 15) + \ + ((fn) << 12) + \ + (reg)) + +// Various offsets used in the building sideband commands. +#define SB_OPCODE_OFFSET 24 +#define SB_PORT_OFFSET 16 +#define SB_REG_OFFEST 8 + +// Sideband opcodes +#define SB_REG_READ_OPCODE 0x10 +#define SB_REG_WRITE_OPCODE 0x11 + +#define SB_FUSE_REG_READ_OPCODE 0x06 +#define SB_FUSE_REG_WRITE_OPCODE 0x07 + +#define SB_DDRIO_REG_READ_OPCODE 0x06 +#define SB_DDRIO_REG_WRITE_OPCODE 0x07 + +#define SB_DRAM_CMND_OPCODE 0x68 +#define SB_WAKE_CMND_OPCODE 0xCA +#define SB_SUSPEND_CMND_OPCODE 0xCC + +// Register addresses for sideband command and data. +#define SB_PACKET_REG 0x00D0 +#define SB_DATA_REG 0x00D4 +#define SB_HADR_REG 0x00D8 + +// We always flag all 4 bytes in the register reads/writes as required. +#define SB_ALL_BYTES_ENABLED 0xF0 + +#define SB_COMMAND(Opcode, Port, Reg) \ + ((Opcode << SB_OPCODE_OFFSET) | \ + (Port << SB_PORT_OFFSET) | \ + (Reg << SB_REG_OFFEST) | \ + SB_ALL_BYTES_ENABLED) + +// iosf +#define isbM32m WrMask32 +#define isbW32m Wr32 +#define isbR32m Rd32 + +// pci + +void pciwrite32( + uint32_t bus, + uint32_t dev, + uint32_t fn, + uint32_t reg, + uint32_t data); + +uint32_t pciread32( + uint32_t bus, + uint32_t dev, + uint32_t fn, + uint32_t reg); + +// general + +uint32_t Rd32( + uint32_t unit, + uint32_t addr); + +void Wr32( + uint32_t unit, + uint32_t addr, + uint32_t data); + +void WrMask32( + uint32_t unit, + uint32_t addr, + uint32_t data, + uint32_t mask); + + +#endif diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/lprint.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/lprint.c new file mode 100644 index 0000000000..f77db8b78b --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/lprint.c @@ -0,0 +1,388 @@ +/** @file +Serial conole output and string formating. + +Copyright (c) 2013-2015 Intel Corporation. + +This program and the accompanying materials +are licensed and made available under the terms and conditions of the BSD License +which accompanies this distribution. The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include "memory_options.h" +#include "general_definitions.h" + +// Resource programmed to PCI bridge, 1MB bound alignment is needed. +// The default value is overwritten by MRC parameter, assuming code +// relocated to eSRAM. +uint32_t UartMmioBase = 0; + +// Serial port registers based on SerialPortLib.c +#define R_UART_BAUD_THR 0 +#define R_UART_LSR 20 + +#define B_UART_LSR_RXRDY BIT0 +#define B_UART_LSR_TXRDY BIT5 +#define B_UART_LSR_TEMT BIT6 + +// Print mask see DPF and D_Xxxx +#define DPF_MASK DpfPrintMask + +// Select class of messages enabled for printing +uint32_t DpfPrintMask = + D_ERROR | + D_INFO | + // D_REGRD | + // D_REGWR | + // D_FCALL | + // D_TRN | + 0; + +#ifdef NDEBUG +// Don't generate debug code +void dpf( uint32_t mask, char_t* bla, ...) +{ + return; +} + +uint8_t mgetc(void) +{ + return 0; +} + +uint8_t mgetch(void) +{ + return 0; +} + +#else + +#ifdef SIM +// Use Vpi console in simulation environment +#include + +void dpf( uint32_t mask, char_t* bla, ...) +{ + va_list va; + + if( 0 == (mask & DPF_MASK)) return; + + va_start( va, bla); + vpi_vprintf( bla, va); + va_end(va); +} + +#else + +#ifdef EMU +// Use standard console in windows environment +#include +#endif + +// Read character from serial port +uint8_t mgetc(void) +{ +#ifdef EMU + + // Emulation in Windows environment uses console + getchar(); + +#else + uint8_t c; + + while ((*(volatile uint8_t*) (UartMmioBase + R_UART_LSR) & B_UART_LSR_RXRDY) == 0); + c = *(volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR); + + return c; +#endif +} + + +uint8_t mgetch(void) +{ +#ifdef EMU + return 0; +#else + uint8_t c = 0; + + if((*(volatile uint8_t*) (UartMmioBase + R_UART_LSR) & B_UART_LSR_RXRDY) != 0) + { + c = *(volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR); + } + + return c; +#endif +} + +// Print single character +static void printc( + uint8_t c) +{ +#ifdef EMU + + // Emulation in Windows environment uses console output + putchar(c); + +#else + + // + // Use MMIO access to serial port on PCI + // while( 0 == (0x20 & inp(0x3f8 + 5))); + // outp(0x3f8 + 0, c); + // + while (0 + == (B_UART_LSR_TEMT & *((volatile uint8_t*) (UartMmioBase + R_UART_LSR)))) + ; + *((volatile uint8_t*) (UartMmioBase + R_UART_BAUD_THR)) = c; +#endif +} + +// Print 0 terminated string on serial console +static void printstr( + char_t *str) +{ + while (*str) + { + printc(*str++); + } +} +// Print 64bit number as hex string on serial console +// the width parameters allows skipping leading zeros +static void printhexx( + uint64_t val, + uint32_t width) +{ + uint32_t i; + uint8_t c; + uint8_t empty = 1; + + // 64bit number has 16 characters in hex representation + for (i = 16; i > 0; i--) + { + c = *(((uint8_t *)&val) + ((i - 1) >> 1)); + if (((i - 1) & 1) != 0) + c = c >> 4; + c = c & 0x0F; + + if (c > 9) + c += 'A' - 10; + else + c += '0'; + + if (c != '0') + { + // end of leading zeros + empty = 0; + } + + // don't print leading zero + if (!empty || i <= width) + { + printc(c); + } + } +} +// Print 32bit number as hex string on serial console +// the width parameters allows skipping leading zeros +static void printhex( + uint32_t val, + uint32_t width) +{ + uint32_t i; + uint8_t c; + uint8_t empty = 1; + + // 32bit number has 8 characters in hex representation + for (i = 8; i > 0; i--) + { + c = (uint8_t) ((val >> 28) & 0x0F); + if (c > 9) + c += 'A' - 10; + else + c += '0'; + + val = val << 4; + + if (c != '0') + { + // end of leading zeros + empty = 0; + } + + // don't print leading zero + if (!empty || i <= width) + { + printc(c); + } + } +} +// Print 32bit number as decimal string on serial console +// the width parameters allows skipping leading zeros +static void printdec( + uint32_t val, + uint32_t width) +{ + uint32_t i; + uint8_t c = 0; + uint8_t empty = 1; + + // Ten digits is enough for 32bit number in decimal + uint8_t buf[10]; + + for (i = 0; i < sizeof(buf); i++) + { + c = (uint8_t) (val % 10); + buf[i] = c + '0'; + val = val / 10; + } + + while (i > 0) + { + c = buf[--i]; + + if (c != '0') + { + // end of leading zeros + empty = 0; + } + + // don't print leading zero + if (!empty || i < width) + { + printc(c); + } + } +} + +// Consume numeric substring leading the given string +// Return pointer to the first non-numeric character +// Buffer reference by width is updated with number +// converted from the numeric substring. +static char_t *getwidth( + char_t *bla, + uint32_t *width) +{ + uint32_t val = 0; + + while (*bla >= '0' && *bla <= '9') + { + val = val * 10 + *bla - '0'; + bla += 1; + } + + if (val > 0) + { + *width = val; + } + return bla; +} + +// Consume print format designator from the head of given string +// Return pointer to first character after format designator +// input fmt +// ----- --- +// s -> s +// d -> d +// X -> X +// llX -> L +static char_t *getformat( + char_t *bla, + uint8_t *fmt) +{ + if (bla[0] == 's') + { + bla += 1; + *fmt = 's'; + } + else if (bla[0] == 'd') + { + bla += 1; + *fmt = 'd'; + } + else if (bla[0] == 'X' || bla[0] == 'x') + { + bla += 1; + *fmt = 'X'; + } + else if (bla[0] == 'l' && bla[1] == 'l' && bla[2] == 'X') + { + bla += 3; + *fmt = 'L'; + } + + return bla; +} + +// Simplified implementation of standard printf function +// The output is directed to serial console. Only selected +// class of messages is printed (mask has to match DpfPrintMask) +// Supported print formats: %[n]s,%[n]d,%[n]X,,%[n]llX +// The width is ignored for %s format. +void dpf( + uint32_t mask, + char_t* bla, + ...) +{ + uint32_t* arg = (uint32_t*) (&bla + 1); + + // Check UART MMIO base configured + if (0 == UartMmioBase) + return; + + // Check event not masked + if (0 == (mask & DPF_MASK)) + return; + + for (;;) + { + uint8_t x = *bla++; + if (x == 0) + break; + + if (x == '\n') + { + printc('\r'); + printc('\n'); + } + else if (x == '%') + { + uint8_t fmt = 0; + uint32_t width = 1; + + bla = getwidth(bla, &width); + bla = getformat(bla, &fmt); + + // Print value + if (fmt == 'd') + { + printdec(*arg, width); + arg += 1; + } + else if (fmt == 'X') + { + printhex(*arg, width); + arg += 1; + } + else if (fmt == 'L') + { + printhexx(*(uint64_t*) arg, width); + arg += 2; + } + else if (fmt == 's') + { + printstr(*(char**) arg); + arg += 1; + } + } + else + { + printc(x); + } + } +} + +#endif //SIM +#endif //NDEBUG diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.c new file mode 100644 index 0000000000..26c56e6037 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.c @@ -0,0 +1,2645 @@ +/************************************************************************ + * + * 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_ichannel_enables & (1<channel_enables & (1<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_irank_enables & (1<channel_enables & (1<channel_enables & (1<channel_enables & (1<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_ichannel_enables & (1<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_ichannel_enables & (1<rank_enables & (1<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_ichannel_enables & (1<rank_enables & (1<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_ichannel_enables & (1<rank_enables & (1<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; +} diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.h new file mode 100644 index 0000000000..e2c126672f --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit.h @@ -0,0 +1,28 @@ +/************************************************************************ + * + * 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. + * + ************************************************************************/ +#ifndef _MEMINIT_H_ +#define _MEMINIT_H_ + +// function prototypes +void MemInit(MRCParams_t *mrc_params); + +typedef void (*MemInitFn_t)(MRCParams_t *mrc_params); + +typedef struct MemInit_s { + uint16_t post_code; + uint16_t boot_path; + MemInitFn_t init_fn; +} MemInit_t; + +#endif // _MEMINIT_H_ diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c new file mode 100644 index 0000000000..f0c8757b22 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.c @@ -0,0 +1,1580 @@ +/************************************************************************ + * + * Copyright (c) 2013-2015 Intel Corporation. + * +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the BSD License +* which accompanies this distribution. The full text of the license may be found at +* http://opensource.org/licenses/bsd-license.php +* +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + ***************************************************************************/ + +#include "mrc.h" +#include "memory_options.h" + +#include "meminit_utils.h" +#include "hte.h" +#include "io.h" + +void select_hte( + MRCParams_t *mrc_params); + +static uint8_t first_run = 0; + +const uint8_t vref_codes[64] = +{ // lowest to highest + 0x3F, 0x3E, 0x3D, 0x3C, 0x3B, 0x3A, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, // 00 - 15 + 0x2F, 0x2E, 0x2D, 0x2C, 0x2B, 0x2A, 0x29, 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, // 16 - 31 + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // 32 - 47 + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F // 48 - 63 +}; + +#ifdef EMU +// Track current post code for debugging purpose +uint32_t PostCode; +#endif + +// set_rcvn: +// +// This function will program the RCVEN delays. +// (currently doesn't comprehend rank) +void set_rcvn( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + DPF(D_TRN, "Rcvn ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[11:08] (0x0-0xF) + // BL1 -> B01PTRCTL0[23:20] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = (byte_lane & BIT0) ? (BIT23 | BIT22 | BIT21 | BIT20) : (BIT11 | BIT10 | BIT9 | BIT8); + tempD = (byte_lane & BIT0) ? ((pi_count / HALF_CLK) << 20) : ((pi_count / HALF_CLK) << 8); + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24); + tempD = pi_count << 24; + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // BL0/1 -> B01DBCTL1[08/11] (+1 select) + // BL0/1 -> B01DBCTL1[02/05] (enable) + reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= (byte_lane & BIT0) ? (BIT5) : (BIT2); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= (byte_lane & BIT0) ? (BIT11) : (BIT8); + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + training_message(channel, rank, byte_lane); + post_code(0xEE, 0xE0); + } + + LEAVEFN(); + return; +} + +// get_rcvn: +// +// This function will return the current RCVEN delay on the given channel, rank, byte_lane as an absolute PI count. +// (currently doesn't comprehend rank) +uint32_t get_rcvn( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[11:08] (0x0-0xF) + // BL1 -> B01PTRCTL0[23:20] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= (byte_lane & BIT0) ? (20) : (8); + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = tempD * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[29:24] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[29:24] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 24; + tempD &= 0x3F; + + // Adjust PI_COUNT + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_rdqs: +// +// This function will program the RDQS delays based on an absolute amount of PIs. +// (currently doesn't comprehend rank) +void set_rdqs( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + DPF(D_TRN, "Rdqs ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count); + + // PI (1/128 MCLK) + // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) + // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) + reg = (byte_lane & BIT0) ? (B1RXDQSPICODE) : (B0RXDQSPICODE); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT6 | BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0); + tempD = pi_count << 0; + isbM32m(DDRPHY, reg, tempD, msk); + + // error check (shouldn't go above 0x3F) + if (pi_count > 0x47) + { + training_message(channel, rank, byte_lane); + post_code(0xEE, 0xE1); + } + + LEAVEFN(); + return; +} + +// get_rdqs: +// +// This function will return the current RDQS delay on the given channel, rank, byte_lane as an absolute PI count. +// (currently doesn't comprehend rank) +uint32_t get_rdqs( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + + // PI (1/128 MCLK) + // BL0 -> B0RXDQSPICODE[06:00] (0x00-0x47) + // BL1 -> B1RXDQSPICODE[06:00] (0x00-0x47) + reg = (byte_lane & BIT0) ? (B1RXDQSPICODE) : (B0RXDQSPICODE); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + tempD = isbR32m(DDRPHY, reg); + + // Adjust PI_COUNT + pi_count = tempD & 0x7F; + + LEAVEFN(); + return pi_count; +} + +// set_wdqs: +// +// This function will program the WDQS delays based on an absolute amount of PIs. +// (currently doesn't comprehend rank) +void set_wdqs( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + DPF(D_TRN, "Wdqs ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[07:04] (0x0-0xF) + // BL1 -> B01PTRCTL0[19:16] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = (byte_lane & BIT0) ? (BIT19 | BIT18 | BIT17 | BIT16) : (BIT7 | BIT6 | BIT5 | BIT4); + tempD = pi_count / HALF_CLK; + tempD <<= (byte_lane & BIT0) ? (16) : (4); + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16); + tempD = pi_count << 16; + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // BL0/1 -> B01DBCTL1[07/10] (+1 select) + // BL0/1 -> B01DBCTL1[01/04] (enable) + reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= (byte_lane & BIT0) ? (BIT4) : (BIT1); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= (byte_lane & BIT0) ? (BIT10) : (BIT7); + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + training_message(channel, rank, byte_lane); + post_code(0xEE, 0xE2); + } + + LEAVEFN(); + return; +} + +// get_wdqs: +// +// This function will return the amount of WDQS delay on the given channel, rank, byte_lane as an absolute PI count. +// (currently doesn't comprehend rank) +uint32_t get_wdqs( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[07:04] (0x0-0xF) + // BL1 -> B01PTRCTL0[19:16] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= (byte_lane & BIT0) ? (16) : (4); + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = (tempD * HALF_CLK); + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[21:16] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[21:16] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 16; + tempD &= 0x3F; + + // Adjust PI_COUNT + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_wdq: +// +// This function will program the WDQ delays based on an absolute number of PIs. +// (currently doesn't comprehend rank) +void set_wdq( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + DPF(D_TRN, "Wdq ch%d rnk%d ln%d : pi=%03X\n", channel, rank, byte_lane, pi_count); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[03:00] (0x0-0xF) + // BL1 -> B01PTRCTL0[15:12] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = (byte_lane & BIT0) ? (BIT15 | BIT14 | BIT13 | BIT12) : (BIT3 | BIT2 | BIT1 | BIT0); + tempD = pi_count / HALF_CLK; + tempD <<= (byte_lane & BIT0) ? (12) : (0); + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + msk = (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8); + tempD = pi_count << 8; + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // BL0/1 -> B01DBCTL1[06/09] (+1 select) + // BL0/1 -> B01DBCTL1[00/03] (enable) + reg = B01DBCTL1 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= (byte_lane & BIT0) ? (BIT3) : (BIT0); + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= (byte_lane & BIT0) ? (BIT9) : (BIT6); + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + training_message(channel, rank, byte_lane); + post_code(0xEE, 0xE3); + } + + LEAVEFN(); + return; +} + +// get_wdq: +// +// This function will return the amount of WDQ delay on the given channel, rank, byte_lane as an absolute PI count. +// (currently doesn't comprehend rank) +uint32_t get_wdq( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + + // RDPTR (1/2 MCLK, 64 PIs) + // BL0 -> B01PTRCTL0[03:00] (0x0-0xF) + // BL1 -> B01PTRCTL0[15:12] (0x0-0xF) + reg = B01PTRCTL0 + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= (byte_lane & BIT0) ? (12) : (0); + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = (tempD * HALF_CLK); + + // PI (1/64 MCLK, 1 PIs) + // BL0 -> B0DLLPICODER0[13:08] (0x00-0x3F) + // BL1 -> B1DLLPICODER0[13:08] (0x00-0x3F) + reg = (byte_lane & BIT0) ? (B1DLLPICODER0) : (B0DLLPICODER0); + reg += (((byte_lane >> 1) * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET)); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 8; + tempD &= 0x3F; + + // Adjust PI_COUNT + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_wcmd: +// +// This function will program the WCMD delays based on an absolute number of PIs. +void set_wcmd( + uint8_t channel, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + // RDPTR (1/2 MCLK, 64 PIs) + // CMDPTRREG[11:08] (0x0-0xF) + reg = CMDPTRREG + (channel * DDRIOCCC_CH_OFFSET); + msk = (BIT11 | BIT10 | BIT9 | BIT8); + tempD = pi_count / HALF_CLK; + tempD <<= 8; + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) + // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) + // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) + // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) + // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) + // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) + // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) + // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) + reg = CMDDLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET); + + msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24) | (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16) + | (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8) | (BIT5 | BIT4 | BIT3 | BIT2 | BIT1 | BIT0); + + tempD = (pi_count << 24) | (pi_count << 16) | (pi_count << 8) | (pi_count << 0); + + isbM32m(DDRPHY, reg, tempD, msk); + reg = CMDDLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET); // PO + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // CMDCFGREG0[17] (+1 select) + // CMDCFGREG0[16] (enable) + reg = CMDCFGREG0 + (channel * DDRIOCCC_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= BIT16; + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= BIT17; + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + post_code(0xEE, 0xE4); + } + + LEAVEFN(); + return; +} + +// get_wcmd: +// +// This function will return the amount of WCMD delay on the given channel as an absolute PI count. +uint32_t get_wcmd( + uint8_t channel) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + // RDPTR (1/2 MCLK, 64 PIs) + // CMDPTRREG[11:08] (0x0-0xF) + reg = CMDPTRREG + (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 8; + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = tempD * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // CMDDLLPICODER0[29:24] -> CMDSLICE R3 (unused) + // CMDDLLPICODER0[21:16] -> CMDSLICE L3 (unused) + // CMDDLLPICODER0[13:08] -> CMDSLICE R2 (unused) + // CMDDLLPICODER0[05:00] -> CMDSLICE L2 (unused) + // CMDDLLPICODER1[29:24] -> CMDSLICE R1 (unused) + // CMDDLLPICODER1[21:16] -> CMDSLICE L1 (0x00-0x3F) + // CMDDLLPICODER1[13:08] -> CMDSLICE R0 (unused) + // CMDDLLPICODER1[05:00] -> CMDSLICE L0 (unused) + reg = CMDDLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 16; + tempD &= 0x3F; + + // Adjust PI_COUNT + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_wclk: +// +// This function will program the WCLK delays based on an absolute number of PIs. +void set_wclk( + uint8_t channel, + uint8_t rank, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + // RDPTR (1/2 MCLK, 64 PIs) + // CCPTRREG[15:12] -> CLK1 (0x0-0xF) + // CCPTRREG[11:08] -> CLK0 (0x0-0xF) + reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET); + msk = (BIT15 | BIT14 | BIT13 | BIT12) | (BIT11 | BIT10 | BIT9 | BIT8); + tempD = ((pi_count / HALF_CLK) << 12) | ((pi_count / HALF_CLK) << 8); + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) + // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) + reg = (rank) ? (ECCB1DLLPICODER0) : (ECCB1DLLPICODER0); + reg += (channel * DDRIOCCC_CH_OFFSET); + msk = (BIT21 | BIT20 | BIT19 | BIT18 | BIT17 | BIT16) | (BIT13 | BIT12 | BIT11 | BIT10 | BIT9 | BIT8); + tempD = (pi_count << 16) | (pi_count << 8); + isbM32m(DDRPHY, reg, tempD, msk); + reg = (rank) ? (ECCB1DLLPICODER1) : (ECCB1DLLPICODER1); + reg += (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + reg = (rank) ? (ECCB1DLLPICODER2) : (ECCB1DLLPICODER2); + reg += (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + reg = (rank) ? (ECCB1DLLPICODER3) : (ECCB1DLLPICODER3); + reg += (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // CCCFGREG1[11:08] (+1 select) + // CCCFGREG1[03:00] (enable) + reg = CCCFGREG1 + (channel * DDRIOCCC_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= (BIT3 | BIT2 | BIT1 | BIT0); // only ??? matters + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= (BIT11 | BIT10 | BIT9 | BIT8); // only ??? matters + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + post_code(0xEE, 0xE5); + } + + LEAVEFN(); + return; +} + +// get_wclk: +// +// This function will return the amout of WCLK delay on the given channel, rank as an absolute PI count. +uint32_t get_wclk( + uint8_t channel, + uint8_t rank) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + // RDPTR (1/2 MCLK, 64 PIs) + // CCPTRREG[15:12] -> CLK1 (0x0-0xF) + // CCPTRREG[11:08] -> CLK0 (0x0-0xF) + reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= (rank) ? (12) : (8); + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = tempD * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // ECCB1DLLPICODER0[13:08] -> CLK0 (0x00-0x3F) + // ECCB1DLLPICODER0[21:16] -> CLK1 (0x00-0x3F) + reg = (rank) ? (ECCB1DLLPICODER0) : (ECCB1DLLPICODER0); + reg += (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= (rank) ? (16) : (8); + tempD &= 0x3F; + + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_wctl: +// +// This function will program the WCTL delays based on an absolute number of PIs. +// (currently doesn't comprehend rank) +void set_wctl( + uint8_t channel, + uint8_t rank, + uint32_t pi_count) +{ + uint32_t reg; + uint32_t msk; + uint32_t tempD; + + ENTERFN(); + + // RDPTR (1/2 MCLK, 64 PIs) + // CCPTRREG[31:28] (0x0-0xF) + // CCPTRREG[27:24] (0x0-0xF) + reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET); + msk = (BIT31 | BIT30 | BIT29 | BIT28) | (BIT27 | BIT26 | BIT25 | BIT24); + tempD = ((pi_count / HALF_CLK) << 28) | ((pi_count / HALF_CLK) << 24); + isbM32m(DDRPHY, reg, tempD, msk); + + // Adjust PI_COUNT + pi_count -= ((pi_count / HALF_CLK) & 0xF) * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // ECCB1DLLPICODER?[29:24] (0x00-0x3F) + // ECCB1DLLPICODER?[29:24] (0x00-0x3F) + reg = ECCB1DLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET); + msk = (BIT29 | BIT28 | BIT27 | BIT26 | BIT25 | BIT24); + tempD = (pi_count << 24); + isbM32m(DDRPHY, reg, tempD, msk); + reg = ECCB1DLLPICODER1 + (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + reg = ECCB1DLLPICODER2 + (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + reg = ECCB1DLLPICODER3 + (channel * DDRIOCCC_CH_OFFSET); + isbM32m(DDRPHY, reg, tempD, msk); + + // DEADBAND + // CCCFGREG1[13:12] (+1 select) + // CCCFGREG1[05:04] (enable) + reg = CCCFGREG1 + (channel * DDRIOCCC_CH_OFFSET); + msk = 0x00; + tempD = 0x00; + // enable + msk |= (BIT5 | BIT4); // only ??? matters + if ((pi_count < EARLY_DB) || (pi_count > LATE_DB)) + { + tempD |= msk; + } + // select + msk |= (BIT13 | BIT12); // only ??? matters + if (pi_count < EARLY_DB) + { + tempD |= msk; + } + isbM32m(DDRPHY, reg, tempD, msk); + + // error check + if (pi_count > 0x3F) + { + post_code(0xEE, 0xE6); + } + + LEAVEFN(); + return; +} + +// get_wctl: +// +// This function will return the amount of WCTL delay on the given channel, rank as an absolute PI count. +// (currently doesn't comprehend rank) +uint32_t get_wctl( + uint8_t channel, + uint8_t rank) +{ + uint32_t reg; + uint32_t tempD; + uint32_t pi_count; + + ENTERFN(); + + // RDPTR (1/2 MCLK, 64 PIs) + // CCPTRREG[31:28] (0x0-0xF) + // CCPTRREG[27:24] (0x0-0xF) + reg = CCPTRREG + (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 24; + tempD &= 0xF; + + // Adjust PI_COUNT + pi_count = tempD * HALF_CLK; + + // PI (1/64 MCLK, 1 PIs) + // ECCB1DLLPICODER?[29:24] (0x00-0x3F) + // ECCB1DLLPICODER?[29:24] (0x00-0x3F) + reg = ECCB1DLLPICODER0 + (channel * DDRIOCCC_CH_OFFSET); + tempD = isbR32m(DDRPHY, reg); + tempD >>= 24; + tempD &= 0x3F; + + // Adjust PI_COUNT + pi_count += tempD; + + LEAVEFN(); + return pi_count; +} + +// set_vref: +// +// This function will program the internal Vref setting in a given byte lane in a given channel. +void set_vref( + uint8_t channel, + uint8_t byte_lane, + uint32_t setting) +{ + uint32_t reg = (byte_lane & 0x1) ? (B1VREFCTL) : (B0VREFCTL); + + ENTERFN(); + DPF(D_TRN, "Vref ch%d ln%d : val=%03X\n", channel, byte_lane, setting); + + isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)), + (vref_codes[setting] << 2), (BIT7 | BIT6 | BIT5 | BIT4 | BIT3 | BIT2)); + //isbM32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET)), (setting<<2), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)); + // need to wait ~300ns for Vref to settle (check that this is necessary) + delay_n(300); + // ??? may need to clear pointers ??? + LEAVEFN(); + return; +} + +// get_vref: +// +// This function will return the internal Vref setting for the given channel, byte_lane; +uint32_t get_vref( + uint8_t channel, + uint8_t byte_lane) +{ + uint8_t j; + uint32_t ret_val = sizeof(vref_codes) / 2; + uint32_t reg = (byte_lane & 0x1) ? (B1VREFCTL) : (B0VREFCTL); + + uint32_t tempD; + + ENTERFN(); + tempD = isbR32m(DDRPHY, (reg + (channel * DDRIODQ_CH_OFFSET) + ((byte_lane >> 1) * DDRIODQ_BL_OFFSET))); + tempD >>= 2; + tempD &= 0x3F; + for (j = 0; j < sizeof(vref_codes); j++) + { + if (vref_codes[j] == tempD) + { + ret_val = j; + break; + } + } + LEAVEFN(); + return ret_val; +} + +// clear_pointers: +// +// This function will be used to clear the pointers in a given byte lane in a given channel. +void clear_pointers( + void) +{ + uint8_t channel_i; + uint8_t bl_i; + + ENTERFN(); + for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++) + { + for (bl_i = 0; bl_i < NUM_BYTE_LANES; bl_i++) + { + isbM32m(DDRPHY, (B01PTRCTL1 + (channel_i * DDRIODQ_CH_OFFSET) + ((bl_i >> 1) * DDRIODQ_BL_OFFSET)), ~(BIT8), + (BIT8)); + //delay_m(1); // DEBUG + isbM32m(DDRPHY, (B01PTRCTL1 + (channel_i * DDRIODQ_CH_OFFSET) + ((bl_i >> 1) * DDRIODQ_BL_OFFSET)), (BIT8), + (BIT8)); + } + } + LEAVEFN(); + return; +} + +// void enable_cache: +void enable_cache( + void) +{ + // Cache control not used in Quark MRC + return; +} + +// void disable_cache: +void disable_cache( + void) +{ + // Cache control not used in Quark MRC + return; +} + +// Send DRAM command, data should be formated +// using DCMD_Xxxx macro or emrsXCommand structure. +static void dram_init_command( + uint32_t data) +{ + Wr32(DCMD, 0, data); +} + +// find_rising_edge: +// +// This function will find the rising edge transition on RCVN or WDQS. +void find_rising_edge( + MRCParams_t *mrc_params, + uint32_t delay[], + uint8_t channel, + uint8_t rank, + bool rcvn) +{ + +#define SAMPLE_CNT 3 // number of sample points +#define SAMPLE_DLY 26 // number of PIs to increment per sample +#define FORWARD true // indicates to increase delays when looking for edge +#define BACKWARD false // indicates to decrease delays when looking for edge + + bool all_edges_found; // determines stop condition + bool direction[NUM_BYTE_LANES]; // direction indicator + uint8_t sample_i; // sample counter + uint8_t bl_i; // byte lane counter + uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor + uint32_t sample_result[SAMPLE_CNT]; // results of "sample_dqs()" + uint32_t tempD; // temporary DWORD + uint32_t transition_pattern; + + ENTERFN(); + + // select hte and request initial configuration + select_hte(mrc_params); + first_run = 1; + + // Take 3 sample points (T1,T2,T3) to obtain a transition pattern. + for (sample_i = 0; sample_i < SAMPLE_CNT; sample_i++) + { + // program the desired delays for sample + for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++) + { + // increase sample delay by 26 PI (0.2 CLK) + if (rcvn) + { + set_rcvn(channel, rank, bl_i, delay[bl_i] + (sample_i * SAMPLE_DLY)); + } + else + { + set_wdqs(channel, rank, bl_i, delay[bl_i] + (sample_i * SAMPLE_DLY)); + } + } // bl_i loop + // take samples (Tsample_i) + sample_result[sample_i] = sample_dqs(mrc_params, channel, rank, rcvn); + + DPF(D_TRN, "Find rising edge %s ch%d rnk%d: #%d dly=%d dqs=%02X\n", + (rcvn ? "RCVN" : "WDQS"), channel, rank, + sample_i, sample_i * SAMPLE_DLY, sample_result[sample_i]); + + } // sample_i loop + + // This pattern will help determine where we landed and ultimately how to place RCVEN/WDQS. + for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++) + { + // build "transition_pattern" (MSB is 1st sample) + transition_pattern = 0x00; + for (sample_i = 0; sample_i < SAMPLE_CNT; sample_i++) + { + transition_pattern |= ((sample_result[sample_i] & (1 << bl_i)) >> bl_i) << (SAMPLE_CNT - 1 - sample_i); + } // sample_i loop + + DPF(D_TRN, "=== transition pattern %d\n", transition_pattern); + + // set up to look for rising edge based on "transition_pattern" + switch (transition_pattern) + { + case 0x00: // sampled 0->0->0 + // move forward from T3 looking for 0->1 + delay[bl_i] += 2 * SAMPLE_DLY; + direction[bl_i] = FORWARD; + break; + case 0x01: // sampled 0->0->1 + case 0x05: // sampled 1->0->1 (bad duty cycle) *HSD#237503* + // move forward from T2 looking for 0->1 + delay[bl_i] += 1 * SAMPLE_DLY; + direction[bl_i] = FORWARD; + break; +// HSD#237503 +// case 0x02: // sampled 0->1->0 (bad duty cycle) +// training_message(channel, rank, bl_i); +// post_code(0xEE, 0xE8); +// break; + case 0x02: // sampled 0->1->0 (bad duty cycle) *HSD#237503* + case 0x03: // sampled 0->1->1 + // move forward from T1 looking for 0->1 + delay[bl_i] += 0 * SAMPLE_DLY; + direction[bl_i] = FORWARD; + break; + case 0x04: // sampled 1->0->0 (assumes BL8, HSD#234975) + // move forward from T3 looking for 0->1 + delay[bl_i] += 2 * SAMPLE_DLY; + direction[bl_i] = FORWARD; + break; +// HSD#237503 +// case 0x05: // sampled 1->0->1 (bad duty cycle) +// training_message(channel, rank, bl_i); +// post_code(0xEE, 0xE9); +// break; + case 0x06: // sampled 1->1->0 + case 0x07: // sampled 1->1->1 + // move backward from T1 looking for 1->0 + delay[bl_i] += 0 * SAMPLE_DLY; + direction[bl_i] = BACKWARD; + break; + default: + post_code(0xEE, 0xEE); + break; + } // transition_pattern switch + // program delays + if (rcvn) + { + set_rcvn(channel, rank, bl_i, delay[bl_i]); + } + else + { + set_wdqs(channel, rank, bl_i, delay[bl_i]); + } + } // bl_i loop + + // Based on the observed transition pattern on the byte lane, + // begin looking for a rising edge with single PI granularity. + do + { + all_edges_found = true; // assume all byte lanes passed + tempD = sample_dqs(mrc_params, channel, rank, rcvn); // take a sample + // check all each byte lane for proper edge + for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++) + { + if (tempD & (1 << bl_i)) + { + // sampled "1" + if (direction[bl_i] == BACKWARD) + { + // keep looking for edge on this byte lane + all_edges_found = false; + delay[bl_i] -= 1; + if (rcvn) + { + set_rcvn(channel, rank, bl_i, delay[bl_i]); + } + else + { + set_wdqs(channel, rank, bl_i, delay[bl_i]); + } + } + } + else + { + // sampled "0" + if (direction[bl_i] == FORWARD) + { + // keep looking for edge on this byte lane + all_edges_found = false; + delay[bl_i] += 1; + if (rcvn) + { + set_rcvn(channel, rank, bl_i, delay[bl_i]); + } + else + { + set_wdqs(channel, rank, bl_i, delay[bl_i]); + } + } + } + } // bl_i loop + } while (!all_edges_found); + + // restore DDR idle state + dram_init_command(DCMD_PREA(rank)); + + DPF(D_TRN, "Delay %03X %03X %03X %03X\n", + delay[0], delay[1], delay[2], delay[3]); + + LEAVEFN(); + return; +} + +// sample_dqs: +// +// This function will sample the DQTRAINSTS registers in the given channel/rank SAMPLE_SIZE times looking for a valid '0' or '1'. +// It will return an encoded DWORD in which each bit corresponds to the sampled value on the byte lane. +uint32_t sample_dqs( + MRCParams_t *mrc_params, + uint8_t channel, + uint8_t rank, + bool rcvn) +{ + uint8_t j; // just a counter + uint8_t bl_i; // which BL in the module (always 2 per module) + uint8_t bl_grp; // which BL module + uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor + uint32_t msk[2]; // BLx in module + uint32_t sampled_val[SAMPLE_SIZE]; // DQTRAINSTS register contents for each sample + uint32_t num_0s; // tracks the number of '0' samples + uint32_t num_1s; // tracks the number of '1' samples + uint32_t ret_val = 0x00; // assume all '0' samples + uint32_t address = get_addr(mrc_params, channel, rank); + + // initialise "msk[]" + msk[0] = (rcvn) ? (BIT1) : (BIT9); // BL0 + msk[1] = (rcvn) ? (BIT0) : (BIT8); // BL1 + + + // cycle through each byte lane group + for (bl_grp = 0; bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2; bl_grp++) + { + // take SAMPLE_SIZE samples + for (j = 0; j < SAMPLE_SIZE; j++) + { + HteMemOp(address, first_run, rcvn?0:1); + first_run = 0; + + // record the contents of the proper DQTRAINSTS register + sampled_val[j] = isbR32m(DDRPHY, (DQTRAINSTS + (bl_grp * DDRIODQ_BL_OFFSET) + (channel * DDRIODQ_CH_OFFSET))); + } + // look for a majority value ( (SAMPLE_SIZE/2)+1 ) on the byte lane + // and set that value in the corresponding "ret_val" bit + for (bl_i = 0; bl_i < 2; bl_i++) + { + num_0s = 0x00; // reset '0' tracker for byte lane + num_1s = 0x00; // reset '1' tracker for byte lane + for (j = 0; j < SAMPLE_SIZE; j++) + { + if (sampled_val[j] & msk[bl_i]) + { + num_1s++; + } + else + { + num_0s++; + } + } + if (num_1s > num_0s) + { + ret_val |= (1 << (bl_i + (bl_grp * 2))); + } + } + } + + // "ret_val.0" contains the status of BL0 + // "ret_val.1" contains the status of BL1 + // "ret_val.2" contains the status of BL2 + // etc. + return ret_val; +} + +// get_addr: +// +// This function will return a 32 bit address in the desired channel and rank. +uint32_t get_addr( + MRCParams_t *mrc_params, + uint8_t channel, + uint8_t rank) +{ + uint32_t offset = 0x02000000; // 32MB + + // Begin product specific code + if (channel > 0) + { + DPF(D_ERROR, "ILLEGAL CHANNEL\n"); + DEAD_LOOP(); + } + + if (rank > 1) + { + DPF(D_ERROR, "ILLEGAL RANK\n"); + DEAD_LOOP(); + } + + // use 256MB lowest density as per DRP == 0x0003 + offset += rank * (256 * 1024 * 1024); + + return offset; +} + +// byte_lane_mask: +// +// This function will return a 32 bit mask that will be used to check for byte lane failures. +uint32_t byte_lane_mask( + MRCParams_t *mrc_params) +{ + uint32_t j; + uint32_t ret_val = 0x00; + + // set "ret_val" based on NUM_BYTE_LANES such that you will check only BL0 in "result" + // (each bit in "result" represents a byte lane) + for (j = 0; j < MAX_BYTE_LANES; j += NUM_BYTE_LANES) + { + ret_val |= (1 << ((j / NUM_BYTE_LANES) * NUM_BYTE_LANES)); + } + + // HSD#235037 + // need to adjust the mask for 16-bit mode + if (mrc_params->channel_width == x16) + { + ret_val |= (ret_val << 2); + } + + return ret_val; +} + + +// read_tsc: +// +// This function will do some assembly to return TSC register contents as a uint64_t. +uint64_t read_tsc( + void) +{ + volatile uint64_t tsc; // EDX:EAX + +#if defined (SIM) || defined (GCC) + volatile uint32_t tscH; // EDX + volatile uint32_t tscL;// EAX + + asm("rdtsc":"=a"(tscL),"=d"(tscH)); + tsc = tscH; + tsc = (tsc<<32)|tscL; +#else + tsc = __rdtsc(); +#endif + + return tsc; +} + +// get_tsc_freq: +// +// This function returns the TSC frequency in MHz +uint32_t get_tsc_freq( + void) +{ + static uint32_t freq[] = + { 533, 400, 200, 100 }; + uint32_t fuse; +#if 0 + fuse = (isbR32m(FUSE, 0) >> 12) & (BIT1|BIT0); +#else + // todo!!! Fixed 533MHz for emulation or debugging + fuse = 0; +#endif + return freq[fuse]; +} + +#ifndef SIM +// delay_n: +// +// This is a simple delay function. +// It takes "nanoseconds" as a parameter. +void delay_n( + uint32_t nanoseconds) +{ + // 1000 MHz clock has 1ns period --> no conversion required + uint64_t final_tsc = read_tsc(); + final_tsc += ((get_tsc_freq() * (nanoseconds)) / 1000); + + while (read_tsc() < final_tsc) + ; + return; +} +#endif + +// delay_u: +// +// This is a simple delay function. +// It takes "microseconds as a parameter. +void delay_u( + uint32_t microseconds) +{ + // 64 bit math is not an option, just use loops + while (microseconds--) + { + delay_n(1000); + } + return; +} + +// delay_m: +// +// This is a simple delay function. +// It takes "milliseconds" as a parameter. +void delay_m( + uint32_t milliseconds) +{ + // 64 bit math is not an option, just use loops + while (milliseconds--) + { + delay_u(1000); + } + return; +} + +// delay_s: +// +// This is a simple delay function. +// It takes "seconds" as a parameter. +void delay_s( + uint32_t seconds) +{ + // 64 bit math is not an option, just use loops + while (seconds--) + { + delay_m(1000); + } + return; +} + +// post_code: +// +// This function will output the POST CODE to the four 7-Segment LED displays. +void post_code( + uint8_t major, + uint8_t minor) +{ +#ifdef EMU + // Update global variable for execution tracking in debug env + PostCode = ((major << 8) | minor); +#endif + + // send message to UART + DPF(D_INFO, "POST: 0x%01X%02X\n", major, minor); + + // error check: + if (major == 0xEE) + { + // todo!!! Consider updating error status and exit MRC +#ifdef SIM + // enable Ctrl-C handling + for(;;) delay_n(100); +#else + DEAD_LOOP(); +#endif + } +} + +void training_message( + uint8_t channel, + uint8_t rank, + uint8_t byte_lane) +{ + // send message to UART + DPF(D_INFO, "CH%01X RK%01X BL%01X\n", channel, rank, byte_lane); + return; +} + +void print_timings( + MRCParams_t *mrc_params) +{ + uint8_t algo_i; + uint8_t channel_i; + uint8_t rank_i; + uint8_t bl_i; + uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; + + DPF(D_INFO, "\n---------------------------"); + DPF(D_INFO, "\nALGO[CH:RK] BL0 BL1 BL2 BL3"); + DPF(D_INFO, "\n==========================="); + for (algo_i = 0; algo_i < eMAX_ALGOS; algo_i++) + { + for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++) + { + if (mrc_params->channel_enables & (1 << channel_i)) + { + for (rank_i = 0; rank_i < NUM_RANKS; rank_i++) + { + if (mrc_params->rank_enables & (1 << rank_i)) + { + switch (algo_i) + { + case eRCVN: + DPF(D_INFO, "\nRCVN[%02d:%02d]", channel_i, rank_i); + break; + case eWDQS: + DPF(D_INFO, "\nWDQS[%02d:%02d]", channel_i, rank_i); + break; + case eWDQx: + DPF(D_INFO, "\nWDQx[%02d:%02d]", channel_i, rank_i); + break; + case eRDQS: + DPF(D_INFO, "\nRDQS[%02d:%02d]", channel_i, rank_i); + break; + case eVREF: + DPF(D_INFO, "\nVREF[%02d:%02d]", channel_i, rank_i); + break; + case eWCMD: + DPF(D_INFO, "\nWCMD[%02d:%02d]", channel_i, rank_i); + break; + case eWCTL: + DPF(D_INFO, "\nWCTL[%02d:%02d]", channel_i, rank_i); + break; + case eWCLK: + DPF(D_INFO, "\nWCLK[%02d:%02d]", channel_i, rank_i); + break; + default: + break; + } // algo_i switch + for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++) + { + switch (algo_i) + { + case eRCVN: + DPF(D_INFO, " %03d", get_rcvn(channel_i, rank_i, bl_i)); + break; + case eWDQS: + DPF(D_INFO, " %03d", get_wdqs(channel_i, rank_i, bl_i)); + break; + case eWDQx: + DPF(D_INFO, " %03d", get_wdq(channel_i, rank_i, bl_i)); + break; + case eRDQS: + DPF(D_INFO, " %03d", get_rdqs(channel_i, rank_i, bl_i)); + break; + case eVREF: + DPF(D_INFO, " %03d", get_vref(channel_i, bl_i)); + break; + case eWCMD: + DPF(D_INFO, " %03d", get_wcmd(channel_i)); + break; + case eWCTL: + DPF(D_INFO, " %03d", get_wctl(channel_i, rank_i)); + break; + case eWCLK: + DPF(D_INFO, " %03d", get_wclk(channel_i, rank_i)); + break; + default: + break; + } // algo_i switch + } // bl_i loop + } // if rank_i enabled + } // rank_i loop + } // if channel_i enabled + } // channel_i loop + } // algo_i loop + DPF(D_INFO, "\n---------------------------"); + DPF(D_INFO, "\n"); + return; +} + +// 32 bit LFSR with characteristic polynomial: X^32 + X^22 +X^2 + X^1 +// The function takes pointer to previous 32 bit value and modifies it to next value. +void lfsr32( + uint32_t *lfsr_ptr) +{ + uint32_t bit; + uint32_t lfsr; + uint32_t i; + + lfsr = *lfsr_ptr; + + for (i = 0; i < 32; i++) + { + bit = 1 ^ (lfsr & BIT0); + bit = bit ^ ((lfsr & BIT1) >> 1); + bit = bit ^ ((lfsr & BIT2) >> 2); + bit = bit ^ ((lfsr & BIT22) >> 22); + + lfsr = ((lfsr >> 1) | (bit << 31)); + } + + *lfsr_ptr = lfsr; + return; +} + +// The purpose of this function is to ensure the SEC comes out of reset +// and IA initiates the SEC enabling Memory Scrambling. +void enable_scrambling( + MRCParams_t *mrc_params) +{ + uint32_t lfsr = 0; + uint8_t i; + + if (mrc_params->scrambling_enables == 0) + return; + + ENTERFN(); + + // 32 bit seed is always stored in BIOS NVM. + lfsr = mrc_params->timings.scrambler_seed; + + if (mrc_params->boot_mode == bmCold) + { + // factory value is 0 and in first boot, a clock based seed is loaded. + if (lfsr == 0) + { + lfsr = read_tsc() & 0x0FFFFFFF; // get seed from system clock and make sure it is not all 1's + } + // need to replace scrambler + // get next 32bit LFSR 16 times which is the last part of the previous scrambler vector. + else + { + for (i = 0; i < 16; i++) + { + lfsr32(&lfsr); + } + } + mrc_params->timings.scrambler_seed = lfsr; // save new seed. + } // if (cold_boot) + + // In warm boot or S3 exit, we have the previous seed. + // In cold boot, we have the last 32bit LFSR which is the new seed. + lfsr32(&lfsr); // shift to next value + isbW32m(MCU, SCRMSEED, (lfsr & 0x0003FFFF)); + for (i = 0; i < 2; i++) + { + isbW32m(MCU, SCRMLO + i, (lfsr & 0xAAAAAAAA)); + } + + LEAVEFN(); + return; +} + +// This function will store relevant timing data +// This data will be used on subsequent boots to speed up boot times +// and is required for Suspend To RAM capabilities. +void store_timings( + MRCParams_t *mrc_params) +{ + uint8_t ch, rk, bl; + MrcTimings_t *mt = &mrc_params->timings; + + for (ch = 0; ch < NUM_CHANNELS; ch++) + { + for (rk = 0; rk < NUM_RANKS; rk++) + { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) + { + mt->rcvn[ch][rk][bl] = get_rcvn(ch, rk, bl); // RCVN + mt->rdqs[ch][rk][bl] = get_rdqs(ch, rk, bl); // RDQS + mt->wdqs[ch][rk][bl] = get_wdqs(ch, rk, bl); // WDQS + mt->wdq[ch][rk][bl] = get_wdq(ch, rk, bl); // WDQ + if (rk == 0) + { + mt->vref[ch][bl] = get_vref(ch, bl); // VREF (RANK0 only) + } + } + mt->wctl[ch][rk] = get_wctl(ch, rk); // WCTL + } + mt->wcmd[ch] = get_wcmd(ch); // WCMD + } + + // need to save for a case of changing frequency after warm reset + mt->ddr_speed = mrc_params->ddr_speed; + + return; +} + +// This function will retrieve relevant timing data +// This data will be used on subsequent boots to speed up boot times +// and is required for Suspend To RAM capabilities. +void restore_timings( + MRCParams_t *mrc_params) +{ + uint8_t ch, rk, bl; + const MrcTimings_t *mt = &mrc_params->timings; + + for (ch = 0; ch < NUM_CHANNELS; ch++) + { + for (rk = 0; rk < NUM_RANKS; rk++) + { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) + { + set_rcvn(ch, rk, bl, mt->rcvn[ch][rk][bl]); // RCVN + set_rdqs(ch, rk, bl, mt->rdqs[ch][rk][bl]); // RDQS + set_wdqs(ch, rk, bl, mt->wdqs[ch][rk][bl]); // WDQS + set_wdq(ch, rk, bl, mt->wdq[ch][rk][bl]); // WDQ + if (rk == 0) + { + set_vref(ch, bl, mt->vref[ch][bl]); // VREF (RANK0 only) + } + } + set_wctl(ch, rk, mt->wctl[ch][rk]); // WCTL + } + set_wcmd(ch, mt->wcmd[ch]); // WCMD + } + + return; +} + +// Configure default settings normally set as part of read training +// Some defaults have to be set earlier as they may affect earlier +// training steps. +void default_timings( + MRCParams_t *mrc_params) +{ + uint8_t ch, rk, bl; + + for (ch = 0; ch < NUM_CHANNELS; ch++) + { + for (rk = 0; rk < NUM_RANKS; rk++) + { + for (bl = 0; bl < NUM_BYTE_LANES; bl++) + { + set_rdqs(ch, rk, bl, 24); // RDQS + if (rk == 0) + { + set_vref(ch, bl, 32); // VREF (RANK0 only) + } + } + } + } + + return; +} + diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.h new file mode 100644 index 0000000000..04c59f5af0 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/meminit_utils.h @@ -0,0 +1,97 @@ +/************************************************************************ + * + * 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. + * + ***************************************************************************/ +#ifndef _MEMINIT_UTILS_H_ +#define _MEMINIT_UTILS_H_ + +// General Definitions: +#ifdef QUICKSIM +#define SAMPLE_SIZE 4 // reduce number of training samples in simulation env +#else +#define SAMPLE_SIZE 6 // must be odd number +#endif + +#define EARLY_DB (0x12) // must be less than this number to enable early deadband +#define LATE_DB (0x34) // must be greater than this number to enable late deadband +#define CHX_REGS (11*4) +#define FULL_CLK 128 +#define HALF_CLK 64 +#define QRTR_CLK 32 + + + +#define MCEIL(num,den) ((uint8_t)((num+den-1)/den)) +#define MMAX(a,b) ((((int32_t)(a))>((int32_t)(b)))?(a):(b)) +#define MCOUNT(a) (sizeof(a)/sizeof(*a)) + +typedef enum ALGOS_enum { + eRCVN = 0, + eWDQS, + eWDQx, + eRDQS, + eVREF, + eWCMD, + eWCTL, + eWCLK, + eMAX_ALGOS, +} ALGOs_t; + + +// Prototypes: +void set_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane, uint32_t pi_count); +void set_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane, uint32_t pi_count); +void set_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane, uint32_t pi_count); +void set_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane, uint32_t pi_count); +void set_wcmd(uint8_t channel, uint32_t pi_count); +void set_wclk(uint8_t channel, uint8_t grp, uint32_t pi_count); +void set_wctl(uint8_t channel, uint8_t rank, uint32_t pi_count); +void set_vref(uint8_t channel, uint8_t byte_lane, uint32_t setting); +uint32_t get_rcvn(uint8_t channel, uint8_t rank, uint8_t byte_lane); +uint32_t get_rdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane); +uint32_t get_wdqs(uint8_t channel, uint8_t rank, uint8_t byte_lane); +uint32_t get_wdq(uint8_t channel, uint8_t rank, uint8_t byte_lane); +uint32_t get_wcmd(uint8_t channel); +uint32_t get_wclk(uint8_t channel, uint8_t group); +uint32_t get_wctl(uint8_t channel, uint8_t rank); +uint32_t get_vref(uint8_t channel, uint8_t byte_lane); + +void clear_pointers(void); +void enable_cache(void); +void disable_cache(void); +void find_rising_edge(MRCParams_t *mrc_params, uint32_t delay[], uint8_t channel, uint8_t rank, bool rcvn); +uint32_t sample_dqs(MRCParams_t *mrc_params, uint8_t channel, uint8_t rank, bool rcvn); +uint32_t get_addr(MRCParams_t *mrc_params, uint8_t channel, uint8_t rank); +uint32_t byte_lane_mask(MRCParams_t *mrc_params); + +uint64_t read_tsc(void); +uint32_t get_tsc_freq(void); +void delay_n(uint32_t nanoseconds); +void delay_u(uint32_t microseconds); +void delay_m(uint32_t milliseconds); +void delay_s(uint32_t seconds); + +void post_code(uint8_t major, uint8_t minor); +void training_message(uint8_t channel, uint8_t rank, uint8_t byte_lane); +void print_timings(MRCParams_t *mrc_params); + +void enable_scrambling(MRCParams_t *mrc_params); +void store_timings(MRCParams_t *mrc_params); +void restore_timings(MRCParams_t *mrc_params); +void default_timings(MRCParams_t *mrc_params); + +#ifndef SIM +void *memset(void *d, int c, size_t n); +void *memcpy(void *d, const void *s, size_t n); +#endif + +#endif // _MEMINIT_UTILS_H_ diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/memory_options.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/memory_options.h new file mode 100644 index 0000000000..8452b98814 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/memory_options.h @@ -0,0 +1,83 @@ +/** @file +Common definitions and compilation switches for MRC + +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. + +**/ +#ifndef __MEMORY_OPTIONS_H +#define __MEMORY_OPTIONS_H + +#include "core_types.h" + +// MRC COMPILE TIME SWITCHES: +// ========================== + + + +//#define MRC_SV // enable some validation opitons + +#if defined (SIM) || defined(EMU) +#define QUICKSIM // reduce execution time using shorter rd/wr sequences +#endif + +#define CLT // required for Quark project + + + +//#define BACKUP_RCVN // enable STATIC timing settings for RCVN (BACKUP_MODE) +//#define BACKUP_WDQS // enable STATIC timing settings for WDQS (BACKUP_MODE) +//#define BACKUP_RDQS // enable STATIC timing settings for RDQS (BACKUP_MODE) +//#define BACKUP_WDQ // enable STATIC timing settings for WDQ (BACKUP_MODE) + + + +//#define BACKUP_COMPS // enable *COMP overrides (BACKUP_MODE) +//#define RX_EYE_CHECK // enable the RD_TRAIN eye check +#define HMC_TEST // enable Host to Memory Clock Alignment +#define R2R_SHARING // enable multi-rank support via rank2rank sharing + +#define FORCE_16BIT_DDRIO // disable signals not used in 16bit mode of DDRIO + + + +// +// Debug support +// + +#ifdef NDEBUG +#define DPF if(0) dpf +#else +#define DPF dpf +#endif + +void dpf( uint32_t mask, char_t *bla, ...); + + +uint8_t mgetc(void); +uint8_t mgetch(void); + + +// Debug print type +#define D_ERROR 0x0001 +#define D_INFO 0x0002 +#define D_REGRD 0x0004 +#define D_REGWR 0x0008 +#define D_FCALL 0x0010 +#define D_TRN 0x0020 +#define D_TIME 0x0040 + +#define ENTERFN() DPF(D_FCALL, "<%s>\n", __FUNCTION__) +#define LEAVEFN() DPF(D_FCALL, "\n", __FUNCTION__) +#define REPORTFN() DPF(D_FCALL, "<%s/>\n", __FUNCTION__) + +extern uint32_t DpfPrintMask; + +#endif diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.c new file mode 100644 index 0000000000..ae7e239c8c --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.c @@ -0,0 +1,46 @@ +/************************************************************************ + * + * Copyright (c) 2013-2015 Intel Corporation. + * +* This program and the accompanying materials +* are licensed and made available under the terms and conditions of the BSD License +* which accompanies this distribution. The full text of the license may be found at +* http://opensource.org/licenses/bsd-license.php +* +* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + * + ************************************************************************/ +#include "mrc.h" +#include "memory_options.h" + +#include "meminit.h" +#include "meminit_utils.h" +#include "prememinit.h" +#include "io.h" + +// Base address for UART registers +extern uint32_t UartMmioBase; + +// +// Memory Reference Code entry point when executing from BIOS +// +void Mrc( MRCParams_t *mrc_params) +{ + // configure uart base address assuming code relocated to eSRAM + UartMmioBase = mrc_params->uart_mmio_base; + + ENTERFN(); + + DPF(D_INFO, "MRC Version %04X %s %s\n", MRC_VERSION, __DATE__, __TIME__); + + // this will set up the data structures used by MemInit() + PreMemInit(mrc_params); + + // this will initialize system memory + MemInit(mrc_params); + + LEAVEFN(); + return; +} + diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.h new file mode 100644 index 0000000000..05055db57c --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/mrc.h @@ -0,0 +1,166 @@ +/************************************************************************ + * + * 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. + * + ************************************************************************/ +#ifndef _MRC_H_ +#define _MRC_H_ + +#include "core_types.h" + +// define the MRC Version +#define MRC_VERSION 0x0112 + + +// architectural definitions +#define NUM_CHANNELS 1 // number of channels +#define NUM_RANKS 2 // number of ranks per channel +#define NUM_BYTE_LANES 4 // number of byte lanes per channel + +// software limitations +#define MAX_CHANNELS 1 +#define MAX_RANKS 2 +#define MAX_BYTE_LANES 4 + +// only to mock MrcWrapper +#define MAX_SOCKETS 1 +#define MAX_SIDES 1 +#define MAX_ROWS (MAX_SIDES * MAX_SOCKETS) +// end + + +// Specify DRAM of nenory channel width +enum { + x8, // DRAM width + x16, // DRAM width & Channel Width + x32 // Channel Width +}; + +// Specify DRAM speed +enum { + DDRFREQ_800, + DDRFREQ_1066 +}; + +// Specify DRAM type +enum { + DDR3, + DDR3L +}; + +// Delay configuration for individual signals +// Vref setting +// Scrambler seed +typedef struct MrcTimings_s +{ + uint32_t rcvn[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + uint32_t rdqs[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + uint32_t wdqs[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + uint32_t wdq [NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; + uint32_t vref[NUM_CHANNELS][NUM_BYTE_LANES]; + uint32_t wctl[NUM_CHANNELS][NUM_RANKS]; + uint32_t wcmd[NUM_CHANNELS]; + + uint32_t scrambler_seed; + uint8_t ddr_speed; // need to save for the case of frequency change +} MrcTimings_t; + + +// DENSITY: 0=512Mb, 1=Gb, 2=2Gb, 3=4Gb +// tCL is DRAM CAS Latency in clocks. +// All other timings are in picoseconds. +// Refer to JEDEC spec (or DRAM datasheet) when changing these values. +typedef struct DRAMParams_s { + uint8_t DENSITY; + uint8_t tCL; // CAS latency in clocks + uint32_t tRAS; // ACT to PRE command period + uint32_t tWTR; // Delay from start of internal write transaction to internal read command + uint32_t tRRD; // ACT to ACT command period (JESD79 specific to page size 1K/2K) + uint32_t tFAW; // Four activate window (JESD79 specific to page size 1K/2K) +} DRAMParams_t; + + +// Boot mode defined as bit mask (1<boot_mode); + DPF(D_INFO, "- r - rank enable [%d]\n", mrc_params->rank_enables); + DPF(D_INFO, "- e - ecc switch [%d]\n", mrc_params->ecc_enables); + DPF(D_INFO, "- b - scrambling switch [%d]\n", mrc_params->scrambling_enables); + DPF(D_INFO, "- a - adr mode [%d]\n", mrc_params->address_mode); + DPF(D_INFO, "- m - menu after mrc [%d]\n", mrc_params->menu_after_mrc); + DPF(D_INFO, "- t - tune to rcvn [%d]\n", mrc_params->tune_rcvn); + DPF(D_INFO, "- o - odt switch [%d]\n", mrc_params->rd_odt_value); + DPF(D_INFO, "- d - dram density [%d]\n", mrc_params->params.DENSITY); + DPF(D_INFO, "- p - power down disable [%d]\n", mrc_params->power_down_disable); + DPF(D_INFO, "- l - log switch 0x%x\n", DpfPrintMask); + ch = mgetc(); + + switch (ch) + { + case 'f': + mrc_params->boot_mode >>= 1; + if(mrc_params->boot_mode == bmUnknown) + { + mrc_params->boot_mode = bmWarm; + } + DPF(D_INFO, "Boot mode %d\n", mrc_params->boot_mode); + break; + + case 'p': + mrc_params->power_down_disable ^= 1; + DPF(D_INFO, "Power down disable %d\n", mrc_params->power_down_disable); + break; + + case 'r': + mrc_params->rank_enables ^= 2; + DPF(D_INFO, "Rank enable %d\n", mrc_params->rank_enables); + break; + + case 'e': + mrc_params->ecc_enables ^= 1; + DPF(D_INFO, "Ecc enable %d\n", mrc_params->ecc_enables); + break; + + case 'b': + mrc_params->scrambling_enables ^= 1; + DPF(D_INFO, "Scrambler enable %d\n", mrc_params->scrambling_enables); + break; + + case 'a': + mrc_params->address_mode = (mrc_params->address_mode + 1) % 3; + DPF(D_INFO, "Adr mode %d\n", mrc_params->address_mode); + break; + + case 'm': + mrc_params->menu_after_mrc ^= 1; + DPF(D_INFO, "Menu after mrc %d\n", mrc_params->menu_after_mrc); + break; + + case 't': + mrc_params->tune_rcvn ^= 1; + DPF(D_INFO, "Tune to rcvn %d\n", mrc_params->tune_rcvn); + break; + + case 'o': + mrc_params->rd_odt_value = (mrc_params->rd_odt_value + 1) % 4; + DPF(D_INFO, "Rd_odt_value %d\n", mrc_params->rd_odt_value); + break; + + case 'd': + mrc_params->params.DENSITY = (mrc_params->params.DENSITY + 1) % 4; + DPF(D_INFO, "Dram density %d\n", mrc_params->params.DENSITY); + break; + + case 'l': + DpfPrintMask ^= 0x30; + DPF(D_INFO, "Log mask %x\n", DpfPrintMask); + break; + + default: + break; + } + + if (ch != 'c') + goto myloop; + + } +#endif + + // initially expect success + mrc_params->status = MRC_SUCCESS; + + // todo!!! Setup board layout (must be reviewed as is selecting static timings) + // 0 == R0 (DDR3 x16), 1 == R1 (DDR3 x16), 2 == DV (DDR3 x8), 3 == SV (DDR3 x8) + if (mrc_params->dram_width == x8) + { + mrc_params->board_id = 2; // select x8 layout + } + else + { + mrc_params->board_id = 0; // select x16 layout + } + + // initially no memory + mrc_params->mem_size = 0; + channel_i = 0; + + // begin of channel settings + dram_width = mrc_params->dram_width; + dram_params = &mrc_params->params; + dram_cfg_index = 0; + + // Determine Column & Row Bits: + // Column: + // 11 for 8Gbx8, else 10 + mrc_params->column_bits[channel_i] = ((dram_params[dram_cfg_index].DENSITY == 4) && (dram_width == x8)) ? (11) : (10); + + // Row: + // 512Mbx16=12 512Mbx8=13 + // 1Gbx16=13 1Gbx8=14 + // 2Gbx16=14 2Gbx8=15 + // 4Gbx16=15 4Gbx8=16 + // 8Gbx16=16 8Gbx8=16 + mrc_params->row_bits[channel_i] = 12 + (dram_params[dram_cfg_index].DENSITY) + + (((dram_params[dram_cfg_index].DENSITY < 4) && (dram_width == x8)) ? (1) : (0)); + + // Determine Per Channel Memory Size: + // (For 2 RANKs, multiply by 2) + // (For 16 bit data bus, divide by 2) + // DENSITY WIDTH MEM_AVAILABLE + // 512Mb x16 0x008000000 ( 128MB) + // 512Mb x8 0x010000000 ( 256MB) + // 1Gb x16 0x010000000 ( 256MB) + // 1Gb x8 0x020000000 ( 512MB) + // 2Gb x16 0x020000000 ( 512MB) + // 2Gb x8 0x040000000 (1024MB) + // 4Gb x16 0x040000000 (1024MB) + // 4Gb x8 0x080000000 (2048MB) + mrc_params->channel_size[channel_i] = (1 << dram_params[dram_cfg_index].DENSITY); + mrc_params->channel_size[channel_i] *= ((dram_width == x8) ? (2) : (1)); + mrc_params->channel_size[channel_i] *= (mrc_params->rank_enables == 0x3) ? (2) : (1); + mrc_params->channel_size[channel_i] *= (mrc_params->channel_width == x16) ? (1) : (2); + + // Determine memory size (convert number of 64MB/512Mb units) + mrc_params->mem_size += mrc_params->channel_size[channel_i] << 26; + + // end of channel settings + + LEAVEFN(); + return; +} + diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.h new file mode 100644 index 0000000000..78cca36f75 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.h @@ -0,0 +1,21 @@ +/************************************************************************ + * + * 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. + * + ************************************************************************/ +#ifndef __PREMEMINIT_H_ +#define __PREMEMINIT_H_ + +// Function prototypes +void PreMemInit(MRCParams_t *mrc_params); + + +#endif // _PREMEMINIT_H_ -- cgit v1.2.3