diff options
Diffstat (limited to 'QuarkSocPkg/QuarkNorthCluster/MemoryInit')
20 files changed, 7256 insertions, 0 deletions
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 <PiPei.h>
+#include <IntelQNCPeim.h>
+//
+// The protocols, PPI and GUID defintions for this module
+//
+#include <Ppi/QNCMemoryInit.h>
+//
+// The Library classes this module consumes
+//
+#include <Library/DebugLib.h>
+#include <Library/PeimEntryPoint.h>
+#include <Library/BaseMemoryLib.h>
+
+
+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; /**<Row activation to Row activation Delay */
+ uint32_t reserved5 :2;
+ uint32_t tRTP :3; /**<Read to Precharge Delay */
+ uint32_t reserved6 :1;
+ } field;
+} RegDTR1; /**< DRAM Timing Register 1 */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t tRRDR :3; /**< RD to RD from different ranks, same DIMM */
+ uint32_t reserved1 :5;
+ uint32_t tWWDR :3; /**< WR to WR from different ranks, same DIMM. */
+ uint32_t reserved3 :5;
+ uint32_t tRWDR :4; /**< bit [19:16] RD to WR from different ranks, same DIMM. */
+ uint32_t reserved5 :12;
+ } field;
+} RegDTR2; /**< DRAM Timing Register 2 */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t tWRDR :3; /**< WR to RD from different ranks, same DIMM. */
+ uint32_t reserved1 :1;
+ uint32_t tWRDD :3; /**< WR to RD from different DIMM. */
+ uint32_t reserved2 :1;
+ uint32_t tRWSR :4; /**< RD to WR Same Rank. */
+ uint32_t reserved3 :1;
+ uint32_t tWRSR :4; /**< WR to RD Same Rank. */
+ uint32_t reserved4 :5;
+ uint32_t tXP :2; /**< Time from CKE set on to any command. */
+ uint32_t PWD_DLY :4; /**< Extended Power-Down Delay. */
+ uint32_t EnDeRate :1;
+ uint32_t DeRateOvr :1;
+ uint32_t DeRateStat :1;
+ uint32_t reserved5 :1;
+ } field;
+} RegDTR3; /**< DRAM Timing Register 3 */
+#pragma pack()
+
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t WRODTSTRT :2; /**< WR command to ODT assert delay */
+ uint32_t reserved1 :2;
+ uint32_t WRODTSTOP :3; /**< Write command to ODT de-assert delay. */
+ uint32_t reserved2 :1;
+ uint32_t RDODTSTRT :3; /**< Read command to ODT assert delay */
+ uint32_t reserved3 :1;
+ uint32_t RDODTSTOP :3; /**< Read command to ODT de-assert delay */
+ uint32_t ODTDIS :1; /**< ODT disable */
+ uint32_t TRGSTRDIS :1; /**< Write target rank is not stretched */
+ uint32_t RDODTDIS :1; /**< Disable Read ODT */
+ uint32_t WRBODTDIS :1; /**< Disable Write ODT */
+ uint32_t reserved5 :13;
+ } field;
+} RegDTR4; /**< DRAM Timing Register 3 */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t SREntryDelay :8; /**< Self-Refresh Entry Delay: */
+ uint32_t powerModeOpCode :5; /**< SPID Power Mode Opcode */
+ uint32_t reserved1 :3;
+ uint32_t PCLSTO :3; /**< Page Close Timeout Period */
+ uint32_t reserved2 :1;
+ uint32_t PCLSWKOK :1; /**< Wake Allowed For Page Close Timeout */
+ uint32_t PREAPWDEN :1; /**< Send Precharge All to rank before entering Power-Down mode. */
+ uint32_t reserved3 :1;
+ uint32_t DYNSREN :1; /**< Dynamic Self-Refresh */
+ uint32_t CLKGTDIS :1; /**< Clock Gating Disabled*/
+ uint32_t DISPWRDN :1; /**< Disable Power Down*/
+ uint32_t reserved4 :2;
+ uint32_t REUTCLKGTDIS :1;
+ uint32_t ENPHYCLKGATE :1;
+ uint32_t reserved5 :2;
+ } field;
+} RegDPMC0; /**< DRAM Power Management Control Register 0 */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t REFWMLO :4; /**< Refresh Opportunistic Watermark */
+ uint32_t REFWMHI :4; /**< Refresh High Watermark*/
+ uint32_t REFWMPNC :4; /**< Refresh Panic Watermark */
+ uint32_t tREFI :3; /**< bit [14:12] Refresh Period */
+ uint32_t reserved1 :1;
+ uint32_t REFCNTMAX :2; /**< Refresh Max tREFI Interval */
+ uint32_t reserved2 :2;
+ uint32_t REFSKEWDIS :1; /**< tREFI counters */
+ uint32_t REFDBTCLR :1;
+ uint32_t reserved3 :2;
+ uint32_t CuRefRate :3;
+ uint32_t DisRefBW :1;
+ uint32_t reserved4 :4;
+ } field;
+} RegDRCF; /**< DRAM Refresh Control Register*/
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t reserved1 :8;
+ uint32_t ZQCINT :3; /**< ZQ Calibration Short Interval: */
+ uint32_t reserved2 :1;
+ uint32_t SRXZQCL :2; /** < ZQ Calibration Length */
+ uint32_t ZQCalType :1;
+ uint32_t ZQCalStart :1;
+ uint32_t TQPollStart :1;
+ uint32_t TQPollRS :2;
+ uint32_t reserved3 :5;
+ uint32_t MRRData :8; /**< bit[31:24] */
+ } field;
+} RegDCAL; /**< DRAM Calibration Control*/
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t OOOAGETRH :5; /**< Out-of-Order Aging Threshold */
+ uint32_t reserved1 :3;
+ uint32_t OOODIS :1; /**< Out-of-Order Disable */
+ uint32_t OOOST3DIS :1; /**< Out-of-Order Disabled when RequestBD_Status is 3. */
+ uint32_t reserved2 :2;
+ uint32_t NEWBYPDIS :1;
+ uint32_t reserved3 :3;
+ uint32_t IPREQMAX :3; /** < Max In-Progress Requests stored in MC */
+ uint32_t reserved4 :13;
+ } field;
+} RegDSCH; /**< DRAM Scheduler Control Register */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t DRPLOCK :1; /**< DRP lock bit */
+ uint32_t reserved1 :7;
+ uint32_t REUTLOCK :1; /**< REUT lock bit */
+ uint32_t reserved2 :19;
+ uint32_t PMICTL :1; /**< PRI Control Select: 0-memory_manager, 1-hte */
+ uint32_t PMIDIS :1; /**< PMIDIS Should be set is using IOSF-SB RW */
+ uint32_t DIOIC :1; /**< DDRIO initialization is complete */
+ uint32_t IC :1; /**< D-unit Initialization Complete */
+ } field;
+} RegDCO; /**< DRAM Controller Operation Register*/
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t SBEEN :1; /**< Enable Single Bit Error Detection and Correction */
+ uint32_t DBEEN :1; /**< Enable Double Bit Error Detection */
+ uint32_t CBOEN :3; /**< Enable ECC Check Bits Override */
+ uint32_t SYNSEL :2; /**< ECC Syndrome Bits Select for Observation */
+ uint32_t CLRSBECNT :1; /**< Clear ECC Single Bit Error Count */
+ uint32_t CBOV :8; /**< ECC Check Bits Override Value */
+ uint32_t reserved1 :1; /**< */
+ uint32_t ENCBGEN :1; /**< Enable Generation of ECC Check Bits */
+ uint32_t ENCBGESWIZ :1; /**< Enable Same Chip ECC Byte Lane Swizzle */
+
+ } field;
+} RegDECCCTRL; /**< DRAM ECC Control Register */
+#pragma pack()
+
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t FUS_DUN_ECC_DIS :1;
+ uint32_t FUS_DUN_MAX_SUPPORTED_MEMORY :3;
+ uint32_t FUS_DUN_MAX_DEVDEN :2;
+ uint32_t RESERVED1 :1;
+ uint32_t FUS_DUN_RANK2_DIS :1;
+ uint32_t FUS_DUN_OOO_DIS :1;
+ uint32_t FUS_DUN_MEMX8_DIS :1;
+ uint32_t FUS_DUN_MEMX16_DIS :1;
+ uint32_t RESERVED2 :1;
+ uint32_t FUS_DUN_1N_DIS :1;
+ uint32_t FUS_DUN_DQ_SCRAMBLER_DIS :1;
+ uint32_t RESERVED3 :1;
+ uint32_t FUS_DUN_32BIT_DRAM_IFC :1;
+ } field;
+} RegDFUSESTAT;
+#pragma pack()
+
+//
+// END DUnit register definition
+//
+////
+
+
+
+////
+//
+// DRAM Initialization Structures used in JEDEC Message Bus Commands
+//
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ unsigned command :3; /**< Command: 000-MRS,001-Refresh,010-Pre-charge,011-Activate,110-ZQ,111-NOP */
+ unsigned bankAddress :3; /**< Bank Address (BA[2:0]) */
+ unsigned BL :2; /**< Burst Length, CDV:1*/
+ unsigned CL :1; /**< CL Reserved CDV:0 */
+ unsigned RBT :1; /**< Read Burst Type */
+ unsigned casLatency :3; /**< cas Latency */
+ unsigned TM :1; /**< Test mode */
+ unsigned dllReset :1; /**< DLL Reset */
+ unsigned writeRecovery :3; /**< Write Recovery for Auto Pre-Charge: 001=2,010=3,011=4,100=5,101=6 */
+ unsigned PPD :1; /**< DLL Control for Precharge Power-Down CDV:1 */
+ unsigned reserved1 :3;
+ unsigned rankSelect :4; /**< Rank Select */
+ unsigned reserved2 :6;
+ } field;
+} DramInitDDR3MRS0; /**< DDR3 Mode Register Set (MRS) Command */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ unsigned command :3; /**< Command: 000-MRS,001-Refresh,010-Pre-charge,011-Activate,110-ZQ,111-NOP */
+ unsigned bankAddress :3; /**< Bank Address (BA[2:0]) */
+ unsigned dllEnabled :1; /**< CDV=0 */
+ unsigned DIC0 :1; /**< Output Driver Impedance Control */
+ unsigned rttNom0 :1; /**< RTT_nom[0] */
+ unsigned MRC_AL :2; /**< Additive Latency = 0 */
+ unsigned DIC1 :1; /**< Reserved */
+ unsigned rttNom1 :1; /**< RTT_nom[1] */
+ unsigned wlEnabled :1; /**< Write Leveling Enable */
+ unsigned reserved1 :1;
+ unsigned rttNom2 :1; /** < RTT_nom[2] */
+ unsigned reserved2 :1;
+ unsigned TDQS :1; /**< TDQS Enable */
+ unsigned Qoff :1; /**< Output Buffers Disabled */
+ unsigned reserved3 :3;
+ unsigned rankSelect :4; /**< Rank Select */
+ unsigned reserved4 :6;
+ } field;
+} DramInitDDR3EMR1; /**< DDR3 Extended Mode Register 1 Set (EMRS1) Command */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t command :3; /**< Command: 000-MRS,001-Refresh,010-Pre-charge,011-Activate,110-ZQ,111-NOP */
+ uint32_t bankAddress :3; /**< Bank Address (BA[2:0]) */
+ uint32_t PASR :3; /**< Partial Array Self-Refresh */
+ uint32_t CWL :3; /**< CAS Write Latency */
+ uint32_t ASR :1; /**< Auto Self-Refresh */
+ uint32_t SRT :1; /**< SR Temperature Range = 0*/
+ uint32_t reserved1 :1;
+ uint32_t rtt_WR :2; /**< Rtt_WR */
+ uint32_t reserved2 :5;
+ uint32_t rankSelect :4; /**< Rank Select */
+ uint32_t reserved3 :6;
+ } field;
+} DramInitDDR3EMR2; /**< DDR3 Extended Mode Register 2 Set (EMRS2) Command */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t command :3; /**< Command: 000-MRS,001-Refresh,010-Pre-charge,011-Activate,110-ZQ,111-NOP */
+ uint32_t bankAddress :3; /**< Bank Address (BA[2:0]) */
+ uint32_t MPR_Location :2; /**< MPR Location */
+ uint32_t MPR :1; /**< MPR: Multi Purpose Register */
+ uint32_t reserved1 :13;
+ uint32_t rankSelect :4; /**< Rank Select */
+ uint32_t reserved2 :6;
+ } field;
+} DramInitDDR3EMR3; /**< DDR3 Extended Mode Register 2 Set (EMRS2) Command */
+#pragma pack()
+
+#pragma pack(1)
+typedef union {
+ uint32_t raw;
+ struct {
+ uint32_t command :3; /**< Command: 000-MRS,001-Refresh,010-Pre-charge,011-Activate,110 - ZQ Calibration,111-NOP */
+ uint32_t bankAddress :3; /**< Bank Address (BA[2:0]) */
+ uint32_t multAddress :16; /**< Multiplexed Address (MA[14:0]) */
+ uint32_t rankSelect :2; /**< Rank Select */
+ uint32_t reserved3 :8;
+ } field;
+} DramInitMisc; /**< Miscellaneous DDRx Initialization Command */
+#pragma pack()
+
+//
+// Construct DRAM init command using DramInitXxxx pattern
+//
+#define DCMD_MRS1(rnk,dat) (0 | ((rnk)<<22) | (1<<3) | ((dat)<<6))
+#define DCMD_REF(rnk) (1 | ((rnk)<<22))
+#define DCMD_PRE(rnk) (2 | ((rnk)<<22))
+#define DCMD_PREA(rnk) (2 | ((rnk)<<22) | (BIT10<<6))
+#define DCMD_ACT(rnk,row) (3 | ((rnk)<<22) | ((row)<<6))
+#define DCMD_WR(rnk,col) (4 | ((rnk)<<22) | ((col)<<6))
+#define DCMD_RD(rnk,col) (5 | ((rnk)<<22) | ((col)<<6))
+#define DCMD_ZQCS(rnk) (6 | ((rnk)<<22))
+#define DCMD_ZQCL(rnk) (6 | ((rnk)<<22) | (BIT10<<6))
+#define DCMD_NOP(rnk) (7 | ((rnk)<<22))
+
+
+
+
+#define DDR3_EMRS1_DIC_40 (0)
+#define DDR3_EMRS1_DIC_34 (1)
+
+#define DDR3_EMRS2_RTTWR_60 (BIT9)
+#define DDR3_EMRS2_RTTWR_120 (BIT10)
+
+#define DDR3_EMRS1_RTTNOM_0 (0)
+#define DDR3_EMRS1_RTTNOM_60 (BIT2)
+#define DDR3_EMRS1_RTTNOM_120 (BIT6)
+#define DDR3_EMRS1_RTTNOM_40 (BIT6|BIT2)
+#define DDR3_EMRS1_RTTNOM_20 (BIT9)
+#define DDR3_EMRS1_RTTNOM_30 (BIT9|BIT2)
+
+
+//
+// END DRAM Init...
+//
+////
+
+
+// HOST_BRIDGE registers:
+#define HMBOUND 0x0020 //ok
+
+// MEMORY_MANAGER registers:
+#define BCTRL 0x0004
+#define BWFLUSH 0x0008
+#define BDEBUG1 0x00C4
+
+////
+//
+// BEGIN DDRIO registers
+//
+
+// DDR IOs & COMPs:
+#define DDRIODQ_BL_OFFSET 0x0800
+#define DDRIODQ_CH_OFFSET ((NUM_BYTE_LANES/2) * DDRIODQ_BL_OFFSET)
+#define DDRIOCCC_CH_OFFSET 0x0800
+#define DDRCOMP_CH_OFFSET 0x0100
+
+// CH0-BL01-DQ
+#define DQOBSCKEBBCTL 0x0000
+#define DQDLLTXCTL 0x0004
+#define DQDLLRXCTL 0x0008
+#define DQMDLLCTL 0x000C
+#define B0RXIOBUFCTL 0x0010
+#define B0VREFCTL 0x0014
+#define B0RXOFFSET1 0x0018
+#define B0RXOFFSET0 0x001C
+#define B1RXIOBUFCTL 0x0020
+#define B1VREFCTL 0x0024
+#define B1RXOFFSET1 0x0028
+#define B1RXOFFSET0 0x002C
+#define DQDFTCTL 0x0030
+#define DQTRAINSTS 0x0034
+#define B1DLLPICODER0 0x0038
+#define B0DLLPICODER0 0x003C
+#define B1DLLPICODER1 0x0040
+#define B0DLLPICODER1 0x0044
+#define B1DLLPICODER2 0x0048
+#define B0DLLPICODER2 0x004C
+#define B1DLLPICODER3 0x0050
+#define B0DLLPICODER3 0x0054
+#define B1RXDQSPICODE 0x0058
+#define B0RXDQSPICODE 0x005C
+#define B1RXDQPICODER32 0x0060
+#define B1RXDQPICODER10 0x0064
+#define B0RXDQPICODER32 0x0068
+#define B0RXDQPICODER10 0x006C
+#define B01PTRCTL0 0x0070
+#define B01PTRCTL1 0x0074
+#define B01DBCTL0 0x0078
+#define B01DBCTL1 0x007C
+#define B0LATCTL0 0x0080
+#define B1LATCTL0 0x0084
+#define B01LATCTL1 0x0088
+#define B0ONDURCTL 0x008C
+#define B1ONDURCTL 0x0090
+#define B0OVRCTL 0x0094
+#define B1OVRCTL 0x0098
+#define DQCTL 0x009C
+#define B0RK2RKCHGPTRCTRL 0x00A0
+#define B1RK2RKCHGPTRCTRL 0x00A4
+#define DQRK2RKCTL 0x00A8
+#define DQRK2RKPTRCTL 0x00AC
+#define B0RK2RKLAT 0x00B0
+#define B1RK2RKLAT 0x00B4
+#define DQCLKALIGNREG0 0x00B8
+#define DQCLKALIGNREG1 0x00BC
+#define DQCLKALIGNREG2 0x00C0
+#define DQCLKALIGNSTS0 0x00C4
+#define DQCLKALIGNSTS1 0x00C8
+#define DQCLKGATE 0x00CC
+#define B0COMPSLV1 0x00D0
+#define B1COMPSLV1 0x00D4
+#define B0COMPSLV2 0x00D8
+#define B1COMPSLV2 0x00DC
+#define B0COMPSLV3 0x00E0
+#define B1COMPSLV3 0x00E4
+#define DQVISALANECR0TOP 0x00E8
+#define DQVISALANECR1TOP 0x00EC
+#define DQVISACONTROLCRTOP 0x00F0
+#define DQVISALANECR0BL 0x00F4
+#define DQVISALANECR1BL 0x00F8
+#define DQVISACONTROLCRBL 0x00FC
+#define DQTIMINGCTRL 0x010C
+// CH0-ECC
+#define ECCDLLTXCTL 0x2004
+#define ECCDLLRXCTL 0x2008
+#define ECCMDLLCTL 0x200C
+#define ECCB1DLLPICODER0 0x2038
+#define ECCB1DLLPICODER1 0x2040
+#define ECCB1DLLPICODER2 0x2048
+#define ECCB1DLLPICODER3 0x2050
+#define ECCB01DBCTL0 0x2078
+#define ECCB01DBCTL1 0x207C
+#define ECCCLKALIGNREG0 0x20B8
+#define ECCCLKALIGNREG1 0x20BC
+#define ECCCLKALIGNREG2 0x20C0
+// CH0-CMD
+#define CMDOBSCKEBBCTL 0x4800
+#define CMDDLLTXCTL 0x4808
+#define CMDDLLRXCTL 0x480C
+#define CMDMDLLCTL 0x4810
+#define CMDRCOMPODT 0x4814
+#define CMDDLLPICODER0 0x4820
+#define CMDDLLPICODER1 0x4824
+#define CMDCFGREG0 0x4840
+#define CMDPTRREG 0x4844
+#define CMDCLKALIGNREG0 0x4850
+#define CMDCLKALIGNREG1 0x4854
+#define CMDCLKALIGNREG2 0x4858
+#define CMDPMCONFIG0 0x485C
+#define CMDPMDLYREG0 0x4860
+#define CMDPMDLYREG1 0x4864
+#define CMDPMDLYREG2 0x4868
+#define CMDPMDLYREG3 0x486C
+#define CMDPMDLYREG4 0x4870
+#define CMDCLKALIGNSTS0 0x4874
+#define CMDCLKALIGNSTS1 0x4878
+#define CMDPMSTS0 0x487C
+#define CMDPMSTS1 0x4880
+#define CMDCOMPSLV 0x4884
+#define CMDBONUS0 0x488C
+#define CMDBONUS1 0x4890
+#define CMDVISALANECR0 0x4894
+#define CMDVISALANECR1 0x4898
+#define CMDVISACONTROLCR 0x489C
+#define CMDCLKGATE 0x48A0
+#define CMDTIMINGCTRL 0x48A4
+// CH0-CLK-CTL
+#define CCOBSCKEBBCTL 0x5800
+#define CCRCOMPIO 0x5804
+#define CCDLLTXCTL 0x5808
+#define CCDLLRXCTL 0x580C
+#define CCMDLLCTL 0x5810
+#define CCRCOMPODT 0x5814
+#define CCDLLPICODER0 0x5820
+#define CCDLLPICODER1 0x5824
+#define CCDDR3RESETCTL 0x5830
+#define CCCFGREG0 0x5838
+#define CCCFGREG1 0x5840
+#define CCPTRREG 0x5844
+#define CCCLKALIGNREG0 0x5850
+#define CCCLKALIGNREG1 0x5854
+#define CCCLKALIGNREG2 0x5858
+#define CCPMCONFIG0 0x585C
+#define CCPMDLYREG0 0x5860
+#define CCPMDLYREG1 0x5864
+#define CCPMDLYREG2 0x5868
+#define CCPMDLYREG3 0x586C
+#define CCPMDLYREG4 0x5870
+#define CCCLKALIGNSTS0 0x5874
+#define CCCLKALIGNSTS1 0x5878
+#define CCPMSTS0 0x587C
+#define CCPMSTS1 0x5880
+#define CCCOMPSLV1 0x5884
+#define CCCOMPSLV2 0x5888
+#define CCCOMPSLV3 0x588C
+#define CCBONUS0 0x5894
+#define CCBONUS1 0x5898
+#define CCVISALANECR0 0x589C
+#define CCVISALANECR1 0x58A0
+#define CCVISACONTROLCR 0x58A4
+#define CCCLKGATE 0x58A8
+#define CCTIMINGCTL 0x58AC
+// COMP
+#define CMPCTRL 0x6800
+#define SOFTRSTCNTL 0x6804
+#define MSCNTR 0x6808
+#define NMSCNTRL 0x680C
+#define LATCH1CTL 0x6814
+#define COMPVISALANECR0 0x681C
+#define COMPVISALANECR1 0x6820
+#define COMPVISACONTROLCR 0x6824
+#define COMPBONUS0 0x6830
+#define TCOCNTCTRL 0x683C
+#define DQANAODTPUCTL 0x6840
+#define DQANAODTPDCTL 0x6844
+#define DQANADRVPUCTL 0x6848
+#define DQANADRVPDCTL 0x684C
+#define DQANADLYPUCTL 0x6850
+#define DQANADLYPDCTL 0x6854
+#define DQANATCOPUCTL 0x6858
+#define DQANATCOPDCTL 0x685C
+#define CMDANADRVPUCTL 0x6868
+#define CMDANADRVPDCTL 0x686C
+#define CMDANADLYPUCTL 0x6870
+#define CMDANADLYPDCTL 0x6874
+#define CLKANAODTPUCTL 0x6880
+#define CLKANAODTPDCTL 0x6884
+#define CLKANADRVPUCTL 0x6888
+#define CLKANADRVPDCTL 0x688C
+#define CLKANADLYPUCTL 0x6890
+#define CLKANADLYPDCTL 0x6894
+#define CLKANATCOPUCTL 0x6898
+#define CLKANATCOPDCTL 0x689C
+#define DQSANAODTPUCTL 0x68A0
+#define DQSANAODTPDCTL 0x68A4
+#define DQSANADRVPUCTL 0x68A8
+#define DQSANADRVPDCTL 0x68AC
+#define DQSANADLYPUCTL 0x68B0
+#define DQSANADLYPDCTL 0x68B4
+#define DQSANATCOPUCTL 0x68B8
+#define DQSANATCOPDCTL 0x68BC
+#define CTLANADRVPUCTL 0x68C8
+#define CTLANADRVPDCTL 0x68CC
+#define CTLANADLYPUCTL 0x68D0
+#define CTLANADLYPDCTL 0x68D4
+#define CHNLBUFSTATIC 0x68F0
+#define COMPOBSCNTRL 0x68F4
+#define COMPBUFFDBG0 0x68F8
+#define COMPBUFFDBG1 0x68FC
+#define CFGMISCCH0 0x6900
+#define COMPEN0CH0 0x6904
+#define COMPEN1CH0 0x6908
+#define COMPEN2CH0 0x690C
+#define STATLEGEN0CH0 0x6910
+#define STATLEGEN1CH0 0x6914
+#define DQVREFCH0 0x6918
+#define CMDVREFCH0 0x691C
+#define CLKVREFCH0 0x6920
+#define DQSVREFCH0 0x6924
+#define CTLVREFCH0 0x6928
+#define TCOVREFCH0 0x692C
+#define DLYSELCH0 0x6930
+#define TCODRAMBUFODTCH0 0x6934
+#define CCBUFODTCH0 0x6938
+#define RXOFFSETCH0 0x693C
+#define DQODTPUCTLCH0 0x6940
+#define DQODTPDCTLCH0 0x6944
+#define DQDRVPUCTLCH0 0x6948
+#define DQDRVPDCTLCH0 0x694C
+#define DQDLYPUCTLCH0 0x6950
+#define DQDLYPDCTLCH0 0x6954
+#define DQTCOPUCTLCH0 0x6958
+#define DQTCOPDCTLCH0 0x695C
+#define CMDDRVPUCTLCH0 0x6968
+#define CMDDRVPDCTLCH0 0x696C
+#define CMDDLYPUCTLCH0 0x6970
+#define CMDDLYPDCTLCH0 0x6974
+#define CLKODTPUCTLCH0 0x6980
+#define CLKODTPDCTLCH0 0x6984
+#define CLKDRVPUCTLCH0 0x6988
+#define CLKDRVPDCTLCH0 0x698C
+#define CLKDLYPUCTLCH0 0x6990
+#define CLKDLYPDCTLCH0 0x6994
+#define CLKTCOPUCTLCH0 0x6998
+#define CLKTCOPDCTLCH0 0x699C
+#define DQSODTPUCTLCH0 0x69A0
+#define DQSODTPDCTLCH0 0x69A4
+#define DQSDRVPUCTLCH0 0x69A8
+#define DQSDRVPDCTLCH0 0x69AC
+#define DQSDLYPUCTLCH0 0x69B0
+#define DQSDLYPDCTLCH0 0x69B4
+#define DQSTCOPUCTLCH0 0x69B8
+#define DQSTCOPDCTLCH0 0x69BC
+#define CTLDRVPUCTLCH0 0x69C8
+#define CTLDRVPDCTLCH0 0x69CC
+#define CTLDLYPUCTLCH0 0x69D0
+#define CTLDLYPDCTLCH0 0x69D4
+#define FNLUPDTCTLCH0 0x69F0
+// PLL
+#define MPLLCTRL0 0x7800
+#define MPLLCTRL1 0x7808
+#define MPLLCSR0 0x7810
+#define MPLLCSR1 0x7814
+#define MPLLCSR2 0x7820
+#define MPLLDFT 0x7828
+#define MPLLMON0CTL 0x7830
+#define MPLLMON1CTL 0x7838
+#define MPLLMON2CTL 0x783C
+#define SFRTRIM 0x7850
+#define MPLLDFTOUT0 0x7858
+#define MPLLDFTOUT1 0x785C
+#define MASTERRSTN 0x7880
+#define PLLLOCKDEL 0x7884
+#define SFRDEL 0x7888
+#define CRUVISALANECR0 0x78F0
+#define CRUVISALANECR1 0x78F4
+#define CRUVISACONTROLCR 0x78F8
+#define IOSFVISALANECR0 0x78FC
+#define IOSFVISALANECR1 0x7900
+#define IOSFVISACONTROLCR 0x7904
+
+//
+// END DDRIO registers
+//
+////
+
+
+#endif
diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/general_definitions.h b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/general_definitions.h new file mode 100644 index 0000000000..c5f92b3b5d --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/general_definitions.h @@ -0,0 +1,90 @@ +/************************************************************************
+ *
+ * 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 __GENERAL_DEFINITIONS_H
+#define __GENERAL_DEFINITIONS_H
+
+#undef BIT0
+#undef BIT1
+#undef BIT2
+#undef BIT3
+#undef BIT4
+#undef BIT5
+#undef BIT6
+#undef BIT7
+#undef BIT8
+#undef BIT9
+#undef BIT10
+#undef BIT11
+#undef BIT12
+#undef BIT13
+#undef BIT14
+#undef BIT15
+#undef BIT16
+#undef BIT17
+#undef BIT18
+#undef BIT19
+#undef BIT20
+#undef BIT21
+#undef BIT22
+#undef BIT23
+#undef BIT24
+#undef BIT25
+#undef BIT26
+#undef BIT27
+#undef BIT28
+#undef BIT29
+#undef BIT30
+#undef BIT31
+
+
+
+// defines
+#define BIT0 0x00000001U
+#define BIT1 0x00000002U
+#define BIT2 0x00000004U
+#define BIT3 0x00000008U
+#define BIT4 0x00000010U
+#define BIT5 0x00000020U
+#define BIT6 0x00000040U
+#define BIT7 0x00000080U
+#define BIT8 0x00000100U
+#define BIT9 0x00000200U
+#define BIT10 0x00000400U
+#define BIT11 0x00000800U
+#define BIT12 0x00001000U
+#define BIT13 0x00002000U
+#define BIT14 0x00004000U
+#define BIT15 0x00008000U
+#define BIT16 0x00010000U
+#define BIT17 0x00020000U
+#define BIT18 0x00040000U
+#define BIT19 0x00080000U
+#define BIT20 0x00100000U
+#define BIT21 0x00200000U
+#define BIT22 0x00400000U
+#define BIT23 0x00800000U
+#define BIT24 0x01000000U
+#define BIT25 0x02000000U
+#define BIT26 0x04000000U
+#define BIT27 0x08000000U
+#define BIT28 0x10000000U
+#define BIT29 0x20000000U
+#define BIT30 0x40000000U
+#define BIT31 0x80000000U
+
+
+#define true 0x01
+#define false 0x00
+
+#endif
diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.c new file mode 100644 index 0000000000..92ec4ba1f6 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/hte.c @@ -0,0 +1,542 @@ +/** @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.
+
+**/
+
+#include "mrc.h"
+#include "memory_options.h"
+#include "io.h"
+
+#include "hte.h"
+
+
+#ifdef SIM
+VOID delay_n(UINT32 nanoseconds);
+#define MySimStall(a) delay_n(a/1000)
+#endif
+
+STATIC VOID EnableAllHteErrors(
+ UINT8 Mask)
+/*++
+
+ Routine Description:
+
+ This function enables to HTE to detect all possible errors for
+ the given training parameters (per-bit or full byte lane).
+
+ Returns:
+
+ None
+
+ --*/
+{
+ isbW32m(HTE, 0x000200A2, 0xFFFFFFFF);
+ isbW32m(HTE, 0x000200A3, 0x000000FF);
+ isbW32m(HTE, 0x000200A4, 0x00000000);
+}
+
+STATIC UINT32 CheckHteErrors(
+ VOID)
+/*++
+
+ Routine Description:
+
+ This function goes and reads the HTE register in order to find any error
+
+ Returns:
+
+ The errors detected in the HTE status register
+
+ --*/
+{
+ return isbR32m(HTE, 0x000200A7);
+}
+
+STATIC VOID WaitForHteComplete(
+ VOID)
+/*++
+
+ Routine Description:
+
+ This function waits until HTE finishes
+
+ Returns:
+
+ None
+
+ --*/
+{
+ UINT32 Tmp;
+
+ ENTERFN();
+
+ //
+ // Is the test done?
+ //
+ do
+ {
+#ifdef SIM
+ MySimStall (35000); // 35 ns delay
+#endif
+ } while (0 != (isbR32m(HTE, 0x00020012) & BIT30));
+
+ Tmp = isbR32m(HTE, 0x00020011);
+ Tmp = Tmp | BIT9;
+ Tmp = Tmp & ~(BIT13 | BIT12);
+ isbW32m(HTE, 0x00020011, Tmp);
+
+ LEAVEFN();
+}
+
+STATIC VOID ClearHteErrorRegisters(
+ VOID)
+/*++
+
+ Routine Description:
+
+ Clears registers related with errors in the HTE.
+
+ Returns:
+
+ None
+
+ --*/
+{
+ UINT32 Tmp;
+
+ //
+ // Clear all HTE errors and enable error checking
+ // for burst and chunk.
+ //
+ Tmp = isbR32m(HTE, 0x000200A1);
+ Tmp |= BIT8;
+ isbW32m(HTE, 0x000200A1, Tmp);
+}
+
+UINT32 HteMemInit(
+ MRC_PARAMS *CurrentMrcData,
+ UINT8 MemInitFlag,
+ UINT8 HaltHteEngineOnError)
+
+/*++
+
+ Routine Description:
+
+ Uses HW HTE engine to initialize or test all memory attached to a given DUNIT.
+ If MemInitFlag is 1, this routine writes 0s to all memory locations to initialize
+ ECC.
+ If MemInitFlag is 0, this routine will send an 5AA55AA5 pattern to all memory
+ locations on the RankMask and then read it back. Then it sends an A55AA55A
+ pattern to all memory locations on the RankMask and reads it back.
+
+ Arguments:
+
+ CurrentMrcData: Host struture for all MRC global data.
+ MemInitFlag: 0 for memtest, 1 for meminit.
+ HaltHteEngineOnError: Halt the HTE engine on first error observed, or keep
+ running to see how many errors are found.
+
+ Returns:
+ Errors register showing HTE failures.
+ Also prints out which rank failed the HTE test if failure occurs.
+ For rank detection to work, the address map must be left in its default
+ state. If MRC changes the address map, this function must be modified
+ to change it back to default at the beginning, then restore it at the end.
+
+ --*/
+{
+ UINT32 Offset;
+ UINT8 TestNum;
+ UINT8 i;
+
+ //
+ // Clear out the error registers at the start of each memory
+ // init or memory test run.
+ //
+ ClearHteErrorRegisters();
+
+ isbW32m(HTE, 0x00020062, 0x00000015);
+
+ for (Offset = 0x80; Offset <= 0x8F; Offset++)
+ {
+ isbW32m(HTE, Offset, ((Offset & 1) ? 0xA55A : 0x5AA5));
+ }
+
+ isbW32m(HTE, 0x00020021, 0x00000000);
+#ifdef QUICKSIM
+ // Just do 4 cache lines for simulation memtest to save time.
+ isbW32m(HTE, 0x00020022, 4-1);
+#else
+ isbW32m(HTE, 0x00020022, (CurrentMrcData->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 <vpi_user.h>
+
+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 <stdio.h>
+#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_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+ // Deassert DDRPHY Initialisation Complete
+ isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT20, BIT20); // SPID_INIT_COMPLETE=0
+ // Deassert IOBUFACT
+ isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT2, BIT2); // IOBUFACTRST_N=0
+ // Disable WRPTR
+ isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), ~BIT0, BIT0); // WRPTRENABLE=0
+ } // if channel enabled
+ } // channel_i loop
+
+ // Put PHY in reset
+ isbM32m(DDRPHY, MASTERRSTN, 0, BIT0); // PHYRSTN=0
+
+ // Initialise DQ01,DQ23,CMD,CLK-CTL,COMP modules
+ // STEP0:
+ post_code(0x03, 0x10);
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+
+ // DQ01-DQ23
+ for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
+ isbM32m(DDRPHY, (DQOBSCKEBBCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i) ? (0x00) : (BIT22)), (BIT22)); // Analog MUX select - IO2xCLKSEL
+
+ // ODT Strength
+ switch (mrc_params->rd_odt_value) {
+ case 1: tempD = 0x3; break; // 60 ohm
+ case 2: tempD = 0x3; break; // 120 ohm
+ case 3: tempD = 0x3; break; // 180 ohm
+ default: tempD = 0x3; break; // 120 ohm
+ }
+ isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
+ isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD<<5), (BIT6|BIT5)); // ODT strength
+ // Dynamic ODT/DIFFAMP
+ tempD = (((tCAS)<<24)|((tCAS)<<16)|((tCAS)<<8)|((tCAS)<<0));
+ switch (speed) {
+ case 0: tempD -= 0x01010101; break; // 800
+ case 1: tempD -= 0x02020202; break; // 1066
+ case 2: tempD -= 0x03030303; break; // 1333
+ case 3: tempD -= 0x04040404; break; // 1600
+ }
+ isbM32m(DDRPHY, (B01LATCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // Launch Time: ODT, DIFFAMP, ODT, DIFFAMP
+ switch (speed) {
+ // HSD#234715
+ case 0: tempD = ((0x06<<16)|(0x07<<8)); break; // 800
+ case 1: tempD = ((0x07<<16)|(0x08<<8)); break; // 1066
+ case 2: tempD = ((0x09<<16)|(0x0A<<8)); break; // 1333
+ case 3: tempD = ((0x0A<<16)|(0x0B<<8)); break; // 1600
+ }
+ isbM32m(DDRPHY, (B0ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
+ isbM32m(DDRPHY, (B1ONDURCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8))); // On Duration: ODT, DIFFAMP
+
+ switch (mrc_params->rd_odt_value) {
+ case 0: tempD = ((0x3F<<16)|(0x3f<<10)); break; // override DIFFAMP=on, ODT=off
+ default: tempD = ((0x3F<<16)|(0x2A<<10)); break; // override DIFFAMP=on, ODT=on
+ }
+ isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
+ isbM32m(DDRPHY, (B1OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), tempD, ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
+
+ // DLL Setup
+ // 1xCLK Domain Timings: tEDP,RCVEN,WDQS (PO)
+ isbM32m(DDRPHY, (B0LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
+ isbM32m(DDRPHY, (B1LATCTL0 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (((tCAS+7)<<16)|((tCAS-4)<<8)|((tCWL-2)<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // 1xCLK: tEDP, RCVEN, WDQS
+
+ // RCVEN Bypass (PO)
+ isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
+ isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x0<<7)|(0x0<<0)), (BIT7|BIT0)); // AFE Bypass, RCVEN DIFFAMP
+ // TX
+ isbM32m(DDRPHY, (DQCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT16), (BIT16)); // 0 means driving DQ during DQS-preamble
+ isbM32m(DDRPHY, (B01PTRCTL1 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT8), (BIT8)); // WR_LVL mode disable
+ // RX (PO)
+ isbM32m(DDRPHY, (B0VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
+ isbM32m(DDRPHY, (B1VREFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((0x03<<2)|(0x0<<1)|(0x0<<0)), ((BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|BIT1|BIT0)); // Internal Vref Code, Enable#, Ext_or_Int (1=Ext)
+ isbM32m(DDRPHY, (B0RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
+ isbM32m(DDRPHY, (B1RXIOBUFCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (0), (BIT4)); // Per-Bit De-Skew Enable
+ }
+ // CLKEBB
+ isbM32m(DDRPHY, (CMDOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT23));
+
+ // Enable tristate control of cmd/address bus
+ isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT1|BIT0));
+
+ // ODT RCOMP
+ isbM32m(DDRPHY, (CMDRCOMPODT + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<5)|(0x03<<0)), ((BIT9|BIT8|BIT7|BIT6|BIT5)|(BIT4|BIT3|BIT2|BIT1|BIT0)));
+
+ // CMDPM* registers must be programmed in this order...
+ isbM32m(DDRPHY, (CMDPMDLYREG4 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFFFU<<16)|(0xFFFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: SFR (regulator), MPLL
+ isbM32m(DDRPHY, (CMDPMDLYREG3 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFU<<28)|(0xFFF<<16)|(0xF<<12)|(0x616<<0)), ((BIT31|BIT30|BIT29|BIT28)|(BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8|BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Delays: ASSERT_IOBUFACT_to_ALLON0_for_PM_MSG_3, VREG (MDLL) Turn On, ALLON0_to_DEASSERT_IOBUFACT_for_PM_MSG_gt0, MDLL Turn On
+ isbM32m(DDRPHY, (CMDPMDLYREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // MPLL Divider Reset Delays
+ isbM32m(DDRPHY, (CMDPMDLYREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn Off Delays: VREG, Staggered MDLL, MDLL, PI
+ isbM32m(DDRPHY, (CMDPMDLYREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xFFU<<24)|(0xFF<<16)|(0xFF<<8)|(0xFF<<0)), ((BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT23|BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT
+ isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x6<<8)|BIT6|(0x4<<0)), (BIT31|BIT30|BIT29|BIT28|BIT27|BIT26|BIT25|BIT24|BIT23|BIT22|BIT21|(BIT11|BIT10|BIT9|BIT8)|BIT6|(BIT3|BIT2|BIT1|BIT0))); // Allow PUnit signals
+ isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
+ // CLK-CTL
+ isbM32m(DDRPHY, (CCOBSCKEBBCTL + (channel_i * DDRIOCCC_CH_OFFSET)), 0, (BIT24)); // CLKEBB
+ isbM32m(DDRPHY, (CCCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x0<<16)|(0x0<<12)|(0x0<<8)|(0xF<<4)|BIT0), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|BIT0)); // Buffer Enable: CS,CKE,ODT,CLK
+ isbM32m(DDRPHY, (CCRCOMPODT + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT RCOMP
+ isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x3<<4)|(0x7<<0)), ((BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DLL_VREG Bias Trim, VREF Tuning for DLL_VREG
+
+ // COMP (RON channel specific)
+ // - DQ/DQS/DM RON: 32 Ohm
+ // - CTRL/CMD RON: 27 Ohm
+ // - CLK RON: 26 Ohm
+ isbM32m(DDRPHY, (DQVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
+ isbM32m(DDRPHY, (CMDVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
+ isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0F<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
+ isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x08<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
+ isbM32m(DDRPHY, (CTLVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0C<<24)|(0x03<<16)), ((BIT29|BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP Vref PU/PD
+
+ // DQS Swapped Input Enable
+ isbM32m(DDRPHY, (COMPEN1CH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT19|BIT17), ((BIT31|BIT30)|BIT19|BIT17|(BIT15|BIT14)));
+
+ // ODT VREF = 1.5 x 274/360+274 = 0.65V (code of ~50)
+ isbM32m(DDRPHY, (DQVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
+ isbM32m(DDRPHY, (DQSVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x32<<8)|(0x03<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
+ isbM32m(DDRPHY, (CLKVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x0E<<8)|(0x05<<0)), ((BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // ODT Vref PU/PD
+
+ // Slew rate settings are frequency specific, numbers below are for 800Mhz (speed == 0)
+ // - DQ/DQS/DM/CLK SR: 4V/ns,
+ // - CTRL/CMD SR: 1.5V/ns
+ tempD = (0x0E<<16)|(0x0E<<12)|(0x08<<8)|(0x0B<<4)|(0x0B<<0);
+ isbM32m(DDRPHY, (DLYSELCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (tempD), ((BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // DCOMP Delay Select: CTL,CMD,CLK,DQS,DQ
+ isbM32m(DDRPHY, (TCOVREFCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x05<<16)|(0x05<<8)|(0x05<<0)), ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT5|BIT4|BIT3|BIT2|BIT1|BIT0))); // TCO Vref CLK,DQS,DQ
+ isbM32m(DDRPHY, (CCBUFODTCH0 + (channel_i * DDRCOMP_CH_OFFSET)), ((0x03<<8)|(0x03<<0)), ((BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT4|BIT3|BIT2|BIT1|BIT0))); // ODTCOMP CMD/CTL PU/PD
+ isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), (0), ((BIT31|BIT30)|BIT8)); // COMP
+
+ #ifdef BACKUP_COMPS
+ // DQ COMP Overrides
+ isbM32m(DDRPHY, (DQDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
+ isbM32m(DDRPHY, (DQDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
+ isbM32m(DDRPHY, (DQDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
+ isbM32m(DDRPHY, (DQDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
+ isbM32m(DDRPHY, (DQODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
+ isbM32m(DDRPHY, (DQODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
+ isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
+ isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
+ // DQS COMP Overrides
+ isbM32m(DDRPHY, (DQSDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
+ isbM32m(DDRPHY, (DQSDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
+ isbM32m(DDRPHY, (DQSDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
+ isbM32m(DDRPHY, (DQSDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x10<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
+ isbM32m(DDRPHY, (DQSODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
+ isbM32m(DDRPHY, (DQSODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
+ isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
+ isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
+ // CLK COMP Overrides
+ isbM32m(DDRPHY, (CLKDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
+ isbM32m(DDRPHY, (CLKDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0C<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
+ isbM32m(DDRPHY, (CLKDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
+ isbM32m(DDRPHY, (CLKDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x07<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
+ isbM32m(DDRPHY, (CLKODTPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PU
+ isbM32m(DDRPHY, (CLKODTPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0B<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODTCOMP PD
+ isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PU
+ isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31), (BIT31)); // TCOCOMP PD
+ // CMD COMP Overrides
+ isbM32m(DDRPHY, (CMDDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
+ isbM32m(DDRPHY, (CMDDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
+ isbM32m(DDRPHY, (CMDDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
+ isbM32m(DDRPHY, (CMDDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
+ // CTL COMP Overrides
+ isbM32m(DDRPHY, (CTLDRVPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PU
+ isbM32m(DDRPHY, (CTLDRVPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0D<<16)), (BIT31|(BIT21|BIT20|BIT19|BIT18|BIT17|BIT16))); // RCOMP PD
+ isbM32m(DDRPHY, (CTLDLYPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PU
+ isbM32m(DDRPHY, (CTLDLYPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x0A<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // DCOMP PD
+ #else
+ // DQ TCOCOMP Overrides
+ isbM32m(DDRPHY, (DQTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
+ isbM32m(DDRPHY, (DQTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
+ // DQS TCOCOMP Overrides
+ isbM32m(DDRPHY, (DQSTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
+ isbM32m(DDRPHY, (DQSTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
+ // CLK TCOCOMP Overrides
+ isbM32m(DDRPHY, (CLKTCOPUCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PU
+ isbM32m(DDRPHY, (CLKTCOPDCTLCH0 + (channel_i * DDRCOMP_CH_OFFSET)), (BIT31|(0x1F<<16)), (BIT31|(BIT20|BIT19|BIT18|BIT17|BIT16))); // TCOCOMP PD
+ #endif // BACKUP_COMPS
+ // program STATIC delays
+ #ifdef BACKUP_WCMD
+ set_wcmd(channel_i, ddr_wcmd[PLATFORM_ID]);
+ #else
+ set_wcmd(channel_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
+ #endif // BACKUP_WCMD
+ for (rank_i=0; rank_i<NUM_RANKS; rank_i++) {
+ if (mrc_params->rank_enables & (1<<rank_i)) {
+ set_wclk(channel_i, rank_i, ddr_wclk[PLATFORM_ID]);
+ #ifdef BACKUP_WCTL
+ set_wctl(channel_i, rank_i, ddr_wctl[PLATFORM_ID]);
+ #else
+ set_wctl(channel_i, rank_i, ddr_wclk[PLATFORM_ID] + HALF_CLK);
+ #endif // BACKUP_WCTL
+ }
+ }
+ }
+ }
+ // COMP (non channel specific)
+ //isbM32m(DDRPHY, (), (), ());
+ isbM32m(DDRPHY, (DQANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (DQANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CMDANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CMDANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CLKANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CLKANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (DQSANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (DQSANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CTLANADRVPUCTL), (BIT30), (BIT30)); // RCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CTLANADRVPDCTL), (BIT30), (BIT30)); // RCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (DQANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
+ isbM32m(DDRPHY, (DQANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
+ isbM32m(DDRPHY, (CLKANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
+ isbM32m(DDRPHY, (CLKANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
+ isbM32m(DDRPHY, (DQSANAODTPUCTL), (BIT30), (BIT30)); // ODT: Dither PU Enable
+ isbM32m(DDRPHY, (DQSANAODTPDCTL), (BIT30), (BIT30)); // ODT: Dither PD Enable
+ isbM32m(DDRPHY, (DQANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (DQANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CMDANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CMDANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CLKANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CLKANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (DQSANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (DQSANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (CTLANADLYPUCTL), (BIT30), (BIT30)); // DCOMP: Dither PU Enable
+ isbM32m(DDRPHY, (CTLANADLYPDCTL), (BIT30), (BIT30)); // DCOMP: Dither PD Enable
+ isbM32m(DDRPHY, (DQANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
+ isbM32m(DDRPHY, (DQANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
+ isbM32m(DDRPHY, (CLKANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
+ isbM32m(DDRPHY, (CLKANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
+ isbM32m(DDRPHY, (DQSANATCOPUCTL), (BIT30), (BIT30)); // TCO: Dither PU Enable
+ isbM32m(DDRPHY, (DQSANATCOPDCTL), (BIT30), (BIT30)); // TCO: Dither PD Enable
+ isbM32m(DDRPHY, (TCOCNTCTRL), (0x1<<0), (BIT1|BIT0)); // TCOCOMP: Pulse Count
+ isbM32m(DDRPHY, (CHNLBUFSTATIC), ((0x03<<24)|(0x03<<16)), ((BIT28|BIT27|BIT26|BIT25|BIT24)|(BIT20|BIT19|BIT18|BIT17|BIT16))); // ODT: CMD/CTL PD/PU
+ isbM32m(DDRPHY, (MSCNTR), (0x64<<0), (BIT7|BIT6|BIT5|BIT4|BIT3|BIT2|BIT1|BIT0)); // Set 1us counter
+ isbM32m(DDRPHY, (LATCH1CTL), (0x1<<28), (BIT30|BIT29|BIT28)); // ???
+
+ // Release PHY from reset
+ isbM32m(DDRPHY, MASTERRSTN, BIT0, BIT0); // PHYRSTN=1
+
+ // STEP1:
+ post_code(0x03, 0x11);
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+ // DQ01-DQ23
+ for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
+ isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
+ delay_n(3);
+ }
+ // ECC
+ isbM32m(DDRPHY, (ECCMDLLCTL), (BIT13), (BIT13)); // Enable VREG
+ delay_n(3);
+ // CMD
+ isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
+ delay_n(3);
+ // CLK-CTL
+ isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT13), (BIT13)); // Enable VREG
+ delay_n(3);
+ }
+ }
+
+ // STEP2:
+ post_code(0x03, 0x12);
+ delay_n(200);
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+ // DQ01-DQ23
+ for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
+ isbM32m(DDRPHY, (DQMDLLCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT17), (BIT17)); // Enable MCDLL
+ delay_n(50);
+ }
+ // ECC
+ isbM32m(DDRPHY, (ECCMDLLCTL), (BIT17), (BIT17)); // Enable MCDLL
+ delay_n(50);
+ // CMD
+ isbM32m(DDRPHY, (CMDMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
+ delay_n(50);
+ // CLK-CTL
+ isbM32m(DDRPHY, (CCMDLLCTL + (channel_i * DDRIOCCC_CH_OFFSET)), (BIT18), (BIT18)); // Enable MCDLL
+ delay_n(50);
+ }
+ }
+
+ // STEP3:
+ post_code(0x03, 0x13);
+ delay_n(100);
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+ // DQ01-DQ23
+ for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
+#ifdef FORCE_16BIT_DDRIO
+ tempD = ((bl_grp_i) && (mrc_params->channel_width == x16)) ? ((0x1<<12)|(0x1<<8)|(0xF<<4)|(0xF<<0)) : ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
+#else
+ tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
+#endif
+ isbM32m(DDRPHY, (DQDLLTXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
+ delay_n(3);
+ isbM32m(DDRPHY, (DQDLLRXCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL
+ delay_n(3);
+ isbM32m(DDRPHY, (B0OVRCTL + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), (BIT3|BIT2|BIT1|BIT0), (BIT3|BIT2|BIT1|BIT0)); // Enable RXDLL Overrides BL0
+ }
+
+ // ECC
+ tempD = ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0));
+ isbM32m(DDRPHY, (ECCDLLTXCTL), (tempD), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
+ delay_n(3);
+
+ // CMD (PO)
+ isbM32m(DDRPHY, (CMDDLLTXCTL + (channel_i * DDRIOCCC_CH_OFFSET)), ((0xF<<12)|(0xF<<8)|(0xF<<4)|(0xF<<0)), ((BIT15|BIT14|BIT13|BIT12)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4)|(BIT3|BIT2|BIT1|BIT0))); // Enable TXDLL
+ delay_n(3);
+ }
+ }
+
+
+ // STEP4:
+ post_code(0x03, 0x14);
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++) {
+ if (mrc_params->channel_enables & (1<<channel_i)) {
+ // Host To Memory Clock Alignment (HMC) for 800/1066
+ for (bl_grp_i=0; bl_grp_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_grp_i++) {
+ isbM32m(DDRPHY, (DQCLKALIGNREG2 + (bl_grp_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), ((bl_grp_i)?(0x3):(0x1)), (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
+ }
+ isbM32m(DDRPHY, (ECCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
+ isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x0, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
+ isbM32m(DDRPHY, (CCCLKALIGNREG2 + (channel_i * DDRIODQ_CH_OFFSET)), 0x2, (BIT3|BIT2|BIT1|BIT0)); // CLK_ALIGN_MOD_ID
+ isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), (0x2<<4), (BIT5|BIT4)); // CLK_ALIGN_MODE
+ isbM32m(DDRPHY, (CMDCLKALIGNREG1 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x18<<16)|(0x10<<8)|(0x8<<2)|(0x1<<0)), ((BIT22|BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT14|BIT13|BIT12|BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4|BIT3|BIT2)|(BIT1|BIT0))); // NUM_SAMPLES, MAX_SAMPLES, MACRO_PI_STEP, MICRO_PI_STEP
+ isbM32m(DDRPHY, (CMDCLKALIGNREG2 + (channel_i * DDRIOCCC_CH_OFFSET)), ((0x10<<16)|(0x4<<8)|(0x2<<4)), ((BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT11|BIT10|BIT9|BIT8)|(BIT7|BIT6|BIT5|BIT4))); // ???, TOTAL_NUM_MODULES, FIRST_U_PARTITION
+ #ifdef HMC_TEST
+ isbM32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT24, BIT24); // START_CLK_ALIGN=1
+ while (isbR32m(DDRPHY, (CMDCLKALIGNREG0 + (channel_i * DDRIOCCC_CH_OFFSET))) & BIT24); // wait for START_CLK_ALIGN=0
+ #endif // HMC_TEST
+
+ // Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN
+ isbM32m(DDRPHY, (CMDPTRREG + (channel_i * DDRIOCCC_CH_OFFSET)), BIT0, BIT0); // WRPTRENABLE=1
+
+
+#ifdef SIM
+ // comp is not working on simulator
+#else
+ // COMP initial
+ isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), BIT5, BIT5); // enable bypass for CLK buffer (PO)
+ isbM32m(DDRPHY, (CMPCTRL), (BIT0), (BIT0)); // Initial COMP Enable
+ while (isbR32m(DDRPHY, (CMPCTRL)) & BIT0); // wait for Initial COMP Enable = 0
+ isbM32m(DDRPHY, (COMPEN0CH0 + (channel_i * DDRCOMP_CH_OFFSET)), ~BIT5, BIT5); // disable bypass for CLK buffer (PO)
+#endif
+
+ // IOBUFACT
+ // STEP4a
+ isbM32m(DDRPHY, (CMDCFGREG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT2, BIT2); // IOBUFACTRST_N=1
+
+ // DDRPHY initialisation complete
+ isbM32m(DDRPHY, (CMDPMCONFIG0 + (channel_i * DDRIOCCC_CH_OFFSET)), BIT20, BIT20); // SPID_INIT_COMPLETE=1
+ }
+ }
+
+ LEAVEFN();
+ return;
+}
+
+// jedec_init (aka PerformJedecInit):
+// This function performs JEDEC initialisation on all enabled channels.
+static void jedec_init(
+ MRCParams_t *mrc_params,
+ uint32_t silent)
+{
+ uint8_t TWR, WL, Rank;
+ uint32_t TCK;
+
+ RegDTR0 DTR0reg;
+
+ DramInitDDR3MRS0 mrs0Command;
+ DramInitDDR3EMR1 emrs1Command;
+ DramInitDDR3EMR2 emrs2Command;
+ DramInitDDR3EMR3 emrs3Command;
+
+ ENTERFN();
+
+ // jedec_init starts
+ if (!silent)
+ {
+ post_code(0x04, 0x00);
+ }
+
+ // Assert RESET# for 200us
+ isbM32m(DDRPHY, CCDDR3RESETCTL, BIT1, (BIT8|BIT1)); // DDR3_RESET_SET=0, DDR3_RESET_RESET=1
+#ifdef QUICKSIM
+ // Don't waste time during simulation
+ delay_u(2);
+#else
+ delay_u(200);
+#endif
+ isbM32m(DDRPHY, CCDDR3RESETCTL, BIT8, (BIT8|BIT1)); // DDR3_RESET_SET=1, DDR3_RESET_RESET=0
+
+ DTR0reg.raw = isbR32m(MCU, DTR0);
+
+ // Set CKEVAL for populated ranks
+ // then send NOP to each rank (#4550197)
+ {
+ uint32_t DRPbuffer;
+ uint32_t DRMCbuffer;
+
+ DRPbuffer = isbR32m(MCU, DRP);
+ DRPbuffer &= 0x3;
+ DRMCbuffer = isbR32m(MCU, DRMC);
+ DRMCbuffer &= 0xFFFFFFFC;
+ DRMCbuffer |= (BIT4 | DRPbuffer);
+
+ isbW32m(MCU, DRMC, DRMCbuffer);
+
+ for (Rank = 0; Rank < NUM_RANKS; Rank++)
+ {
+ // Skip to next populated rank
+ if ((mrc_params->rank_enables & (1 << Rank)) == 0)
+ {
+ continue;
+ }
+
+ dram_init_command(DCMD_NOP(Rank));
+ }
+
+ isbW32m(MCU, DRMC, DRMC_DEFAULT);
+ }
+
+ // setup for emrs 2
+ // BIT[15:11] --> Always "0"
+ // BIT[10:09] --> Rtt_WR: want "Dynamic ODT Off" (0)
+ // BIT[08] --> Always "0"
+ // BIT[07] --> SRT: use sr_temp_range
+ // BIT[06] --> ASR: want "Manual SR Reference" (0)
+ // BIT[05:03] --> CWL: use oem_tCWL
+ // BIT[02:00] --> PASR: want "Full Array" (0)
+ emrs2Command.raw = 0;
+ emrs2Command.field.bankAddress = 2;
+
+ WL = 5 + mrc_params->ddr_speed;
+ emrs2Command.field.CWL = WL - 5;
+ emrs2Command.field.SRT = mrc_params->sr_temp_range;
+
+ // setup for emrs 3
+ // BIT[15:03] --> Always "0"
+ // BIT[02] --> MPR: want "Normal Operation" (0)
+ // BIT[01:00] --> MPR_Loc: want "Predefined Pattern" (0)
+ emrs3Command.raw = 0;
+ emrs3Command.field.bankAddress = 3;
+
+ // setup for emrs 1
+ // BIT[15:13] --> Always "0"
+ // BIT[12:12] --> Qoff: want "Output Buffer Enabled" (0)
+ // BIT[11:11] --> TDQS: want "Disabled" (0)
+ // BIT[10:10] --> Always "0"
+ // BIT[09,06,02] --> Rtt_nom: use rtt_nom_value
+ // BIT[08] --> Always "0"
+ // BIT[07] --> WR_LVL: want "Disabled" (0)
+ // BIT[05,01] --> DIC: use ron_value
+ // BIT[04:03] --> AL: additive latency want "0" (0)
+ // BIT[00] --> DLL: want "Enable" (0)
+ //
+ // (BIT5|BIT1) set Ron value
+ // 00 --> RZQ/6 (40ohm)
+ // 01 --> RZQ/7 (34ohm)
+ // 1* --> RESERVED
+ //
+ // (BIT9|BIT6|BIT2) set Rtt_nom value
+ // 000 --> Disabled
+ // 001 --> RZQ/4 ( 60ohm)
+ // 010 --> RZQ/2 (120ohm)
+ // 011 --> RZQ/6 ( 40ohm)
+ // 1** --> RESERVED
+ emrs1Command.raw = 0;
+ emrs1Command.field.bankAddress = 1;
+ emrs1Command.field.dllEnabled = 0; // 0 = Enable , 1 = Disable
+
+ if (mrc_params->ron_value == 0)
+ {
+ emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_34;
+ }
+ else
+ {
+ emrs1Command.field.DIC0 = DDR3_EMRS1_DIC_40;
+ }
+
+
+ if (mrc_params->rtt_nom_value == 0)
+ {
+ emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_40 << 6);
+ }
+ else if (mrc_params->rtt_nom_value == 1)
+ {
+ emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_60 << 6);
+ }
+ else if (mrc_params->rtt_nom_value == 2)
+ {
+ emrs1Command.raw |= (DDR3_EMRS1_RTTNOM_120 << 6);
+ }
+
+ // save MRS1 value (excluding control fields)
+ mrc_params->mrs1 = emrs1Command.raw >> 6;
+
+ // setup for mrs 0
+ // BIT[15:13] --> Always "0"
+ // BIT[12] --> PPD: for Quark (1)
+ // BIT[11:09] --> WR: use oem_tWR
+ // BIT[08] --> DLL: want "Reset" (1, self clearing)
+ // BIT[07] --> MODE: want "Normal" (0)
+ // BIT[06:04,02] --> CL: use oem_tCAS
+ // BIT[03] --> RD_BURST_TYPE: want "Interleave" (1)
+ // BIT[01:00] --> BL: want "8 Fixed" (0)
+ // WR:
+ // 0 --> 16
+ // 1 --> 5
+ // 2 --> 6
+ // 3 --> 7
+ // 4 --> 8
+ // 5 --> 10
+ // 6 --> 12
+ // 7 --> 14
+ // CL:
+ // BIT[02:02] "0" if oem_tCAS <= 11 (1866?)
+ // BIT[06:04] use oem_tCAS-4
+ mrs0Command.raw = 0;
+ mrs0Command.field.bankAddress = 0;
+ mrs0Command.field.dllReset = 1;
+ mrs0Command.field.BL = 0;
+ mrs0Command.field.PPD = 1;
+ mrs0Command.field.casLatency = DTR0reg.field.tCL + 1;
+
+ TCK = tCK[mrc_params->ddr_speed];
+ TWR = MCEIL(15000, TCK); // Per JEDEC: tWR=15000ps DDR2/3 from 800-1600
+ mrs0Command.field.writeRecovery = TWR - 4;
+
+ for (Rank = 0; Rank < NUM_RANKS; Rank++)
+ {
+ // Skip to next populated rank
+ if ((mrc_params->rank_enables & (1 << Rank)) == 0)
+ {
+ continue;
+ }
+
+ emrs2Command.field.rankSelect = Rank;
+ dram_init_command(emrs2Command.raw);
+
+ emrs3Command.field.rankSelect = Rank;
+ dram_init_command(emrs3Command.raw);
+
+ emrs1Command.field.rankSelect = Rank;
+ dram_init_command(emrs1Command.raw);
+
+ mrs0Command.field.rankSelect = Rank;
+ dram_init_command(mrs0Command.raw);
+
+ dram_init_command(DCMD_ZQCL(Rank));
+ }
+
+ LEAVEFN();
+ return;
+}
+
+// rcvn_cal:
+// POST_CODE[major] == 0x05
+//
+// This function will perform our RCVEN Calibration Algorithm.
+// We will only use the 2xCLK domain timings to perform RCVEN Calibration.
+// All byte lanes will be calibrated "simultaneously" per channel per rank.
+static void rcvn_cal(
+ MRCParams_t *mrc_params)
+{
+ uint8_t channel_i; // channel counter
+ uint8_t rank_i; // rank counter
+ uint8_t bl_i; // byte lane counter
+ uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
+
+#ifdef R2R_SHARING
+ uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
+#ifndef BACKUP_RCVN
+ uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
+#endif // BACKUP_RCVN
+#endif // R2R_SHARING
+
+#ifdef BACKUP_RCVN
+#else
+ uint32_t tempD; // temporary DWORD
+ uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
+ RegDTR1 dtr1;
+ RegDTR1 dtr1save;
+#endif // BACKUP_RCVN
+ ENTERFN();
+
+ // rcvn_cal starts
+ post_code(0x05, 0x00);
+
+#ifndef BACKUP_RCVN
+ // need separate burst to sample DQS preamble
+ dtr1.raw = dtr1save.raw = isbR32m(MCU, DTR1);
+ dtr1.field.tCCD = 1;
+ isbW32m(MCU, DTR1, dtr1.raw);
+#endif
+
+#ifdef R2R_SHARING
+ // need to set "final_delay[][]" elements to "0"
+ memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
+#endif // R2R_SHARING
+
+ // loop through each enabled channel
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ // perform RCVEN Calibration on a per rank basis
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ // POST_CODE here indicates the current channel and rank being calibrated
+ post_code(0x05, (0x10 + ((channel_i << 4) | rank_i)));
+
+#ifdef BACKUP_RCVN
+ // set hard-coded timing values
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ set_rcvn(channel_i, rank_i, bl_i, ddr_rcvn[PLATFORM_ID]);
+ }
+#else
+ // enable FIFORST
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
+ {
+ isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), 0,
+ BIT8); // 0 is enabled
+ } // bl_i loop
+ // initialise the starting delay to 128 PI (tCAS +1 CLK)
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+#ifdef SIM
+ // Original value was late at the end of DQS sequence
+ delay[bl_i] = 3 * FULL_CLK;
+#else
+ delay[bl_i] = (4 + 1) * FULL_CLK; // 1x CLK domain timing is tCAS-4
+#endif
+
+ set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
+ } // bl_i loop
+
+ // now find the rising edge
+ find_rising_edge(mrc_params, delay, channel_i, rank_i, true);
+ // Now increase delay by 32 PI (1/4 CLK) to place in center of high pulse.
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ delay[bl_i] += QRTR_CLK;
+ set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
+ } // bl_i loop
+ // Now decrement delay by 128 PI (1 CLK) until we sample a "0"
+ do
+ {
+
+ tempD = sample_dqs(mrc_params, channel_i, rank_i, true);
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ if (tempD & (1 << bl_i))
+ {
+ if (delay[bl_i] >= FULL_CLK)
+ {
+ delay[bl_i] -= FULL_CLK;
+ set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
+ }
+ else
+ {
+ // not enough delay
+ training_message(channel_i, rank_i, bl_i);
+ post_code(0xEE, 0x50);
+ }
+ }
+ } // bl_i loop
+ } while (tempD & 0xFF);
+
+#ifdef R2R_SHARING
+ // increment "num_ranks_enabled"
+ num_ranks_enabled++;
+ // Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ delay[bl_i] += QRTR_CLK;
+ // add "delay[]" values to "final_delay[][]" for rolling average
+ final_delay[channel_i][bl_i] += delay[bl_i];
+ // set timing based on rolling average values
+ set_rcvn(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
+ } // bl_i loop
+#else
+ // Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble.
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ delay[bl_i] += QRTR_CLK;
+ set_rcvn(channel_i, rank_i, bl_i, delay[bl_i]);
+ } // bl_i loop
+
+#endif // R2R_SHARING
+
+ // disable FIFORST
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i += 2)
+ {
+ isbM32m(DDRPHY, (B01PTRCTL1 + ((bl_i >> 1) * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)), BIT8,
+ BIT8); // 1 is disabled
+ } // bl_i loop
+
+#endif // BACKUP_RCVN
+
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+#ifndef BACKUP_RCVN
+ // restore original
+ isbW32m(MCU, DTR1, dtr1save.raw);
+#endif
+
+#ifdef MRC_SV
+ if (mrc_params->tune_rcvn)
+ {
+ uint32_t rcven, val;
+ uint32_t rdcmd2rcven;
+
+ /*
+ Formulas for RDCMD2DATAVALID & DIFFAMP dynamic timings
+
+ 1. Set after RCVEN training
+
+ //Tune RDCMD2DATAVALID
+
+ x80/x84[21:16]
+ MAX OF 2 RANKS : round up (rdcmd2rcven (rcven 1x) + 2x x 2 + PI/128) + 5
+
+ //rdcmd2rcven x80/84[12:8]
+ //rcven 2x x70[23:20] & [11:8]
+
+ //Tune DIFFAMP Timings
+
+ //diffampen launch x88[20:16] & [4:0] -- B01LATCTL1
+ MIN OF 2 RANKS : round down (rcven 1x + 2x x 2 + PI/128) - 1
+
+ //diffampen length x8C/x90 [13:8] -- B0ONDURCTL B1ONDURCTL
+ MAX OF 2 RANKS : roundup (rcven 1x + 2x x 2 + PI/128) + 5
+
+
+ 2. need to do a fiforst after settings these values
+ */
+
+ DPF(D_INFO, "BEFORE\n");
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
+
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
+
+ rcven = get_rcvn(0, 0, 0) / 128;
+ rdcmd2rcven = (isbR32m(DDRPHY, B0LATCTL0) >> 8) & 0x1F;
+ val = rdcmd2rcven + rcven + 6;
+ isbM32m(DDRPHY, B0LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
+
+ val = rdcmd2rcven + rcven - 1;
+ isbM32m(DDRPHY, B01LATCTL1, val << 0, (BIT4|BIT3|BIT2|BIT1|BIT0));
+
+ val = rdcmd2rcven + rcven + 5;
+ isbM32m(DDRPHY, B0ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
+
+ rcven = get_rcvn(0, 0, 1) / 128;
+ rdcmd2rcven = (isbR32m(DDRPHY, B1LATCTL0) >> 8) & 0x1F;
+ val = rdcmd2rcven + rcven + 6;
+ isbM32m(DDRPHY, B1LATCTL0, val << 16, (BIT21|BIT20|BIT19|BIT18|BIT17|BIT16));
+
+ val = rdcmd2rcven + rcven - 1;
+ isbM32m(DDRPHY, B01LATCTL1, val << 16, (BIT20|BIT19|BIT18|BIT17|BIT16));
+
+ val = rdcmd2rcven + rcven + 5;
+ isbM32m(DDRPHY, B1ONDURCTL, val << 8, (BIT13|BIT12|BIT11|BIT10|BIT9|BIT8));
+
+ DPF(D_INFO, "AFTER\n");
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0LATCTL0));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B01LATCTL1));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B0ONDURCTL));
+
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1LATCTL0));
+ DPF(D_INFO, "### %x\n", isbR32m(DDRPHY, B1ONDURCTL));
+
+ DPF(D_INFO, "\nPress a key\n");
+ mgetc();
+
+ // fifo reset
+ isbM32m(DDRPHY, B01PTRCTL1, 0, BIT8); // 0 is enabled
+ delay_n(3);
+ isbM32m(DDRPHY, B01PTRCTL1, BIT8, BIT8); // 1 is disabled
+ }
+#endif
+
+ LEAVEFN();
+ return;
+}
+
+// Check memory executing write/read/verify of many data patterns
+// at the specified address. Bits in the result indicate failure
+// on specific byte lane.
+static uint32_t check_bls_ex(
+ MRCParams_t *mrc_params,
+ uint32_t address)
+{
+ uint32_t result;
+ uint8_t first_run = 0;
+
+ if (mrc_params->hte_setup)
+ {
+ mrc_params->hte_setup = 0;
+
+ first_run = 1;
+ select_hte(mrc_params);
+ }
+
+ result = WriteStressBitLanesHTE(mrc_params, address, first_run);
+
+ DPF(D_TRN, "check_bls_ex result is %x\n", result);
+ return result;
+}
+
+// Check memory executing simple write/read/verify at
+// the specified address. Bits in the result indicate failure
+// on specific byte lane.
+static uint32_t check_rw_coarse(
+ MRCParams_t *mrc_params,
+ uint32_t address)
+{
+ uint32_t result = 0;
+ uint8_t first_run = 0;
+
+ if (mrc_params->hte_setup)
+ {
+ mrc_params->hte_setup = 0;
+
+ first_run = 1;
+ select_hte(mrc_params);
+ }
+
+ result = BasicWriteReadHTE(mrc_params, address, first_run, WRITE_TRAIN);
+
+ DPF(D_TRN, "check_rw_coarse result is %x\n", result);
+ return result;
+}
+
+// wr_level:
+// POST_CODE[major] == 0x06
+//
+// This function will perform the Write Levelling algorithm (align WCLK and WDQS).
+// This algorithm will act on each rank in each channel separately.
+static void wr_level(
+ MRCParams_t *mrc_params)
+{
+ uint8_t channel_i; // channel counter
+ uint8_t rank_i; // rank counter
+ uint8_t bl_i; // byte lane counter
+ uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
+
+#ifdef R2R_SHARING
+ uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
+#ifndef BACKUP_WDQS
+ uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
+#endif // BACKUP_WDQS
+#endif // R2R_SHARING
+
+#ifdef BACKUP_WDQS
+#else
+ bool all_edges_found; // determines stop condition for CRS_WR_LVL
+ uint32_t delay[NUM_BYTE_LANES]; // absolute PI value to be programmed on the byte lane
+ // static makes it so the data is loaded in the heap once by shadow(), where
+ // non-static copies the data onto the stack every time this function is called.
+
+ uint32_t address; // address to be checked during COARSE_WR_LVL
+ RegDTR4 dtr4;
+ RegDTR4 dtr4save;
+#endif // BACKUP_WDQS
+
+ ENTERFN();
+
+ // wr_level starts
+ post_code(0x06, 0x00);
+
+#ifdef R2R_SHARING
+ // need to set "final_delay[][]" elements to "0"
+ memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
+#endif // R2R_SHARING
+ // loop through each enabled channel
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ // perform WRITE LEVELING algorithm on a per rank basis
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ // POST_CODE here indicates the current rank and channel being calibrated
+ post_code(0x06, (0x10 + ((channel_i << 4) | rank_i)));
+
+#ifdef BACKUP_WDQS
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ set_wdqs(channel_i, rank_i, bl_i, ddr_wdqs[PLATFORM_ID]);
+ set_wdq(channel_i, rank_i, bl_i, (ddr_wdqs[PLATFORM_ID] - QRTR_CLK));
+ }
+#else
+
+ { // Begin product specific code
+
+ // perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
+ dram_init_command(DCMD_PREA(rank_i));
+
+ // enable Write Levelling Mode (EMRS1 w/ Write Levelling Mode Enable)
+ dram_init_command(DCMD_MRS1(rank_i,0x0082));
+
+ // set ODT DRAM Full Time Termination disable in MCU
+ dtr4.raw = dtr4save.raw = isbR32m(MCU, DTR4);
+ dtr4.field.ODTDIS = 1;
+ isbW32m(MCU, DTR4, dtr4.raw);
+
+ for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
+ {
+ isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
+ (BIT28 | (0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
+ (BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Enable Sandy Bridge Mode (WDQ Tri-State) & Ensure 5 WDQS pulses during Write Leveling
+ }
+
+ isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (BIT16), (BIT16)); // Write Leveling Mode enabled in IO
+ } // End product specific code
+ // Initialise the starting delay to WCLK
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ { // Begin product specific code
+ // CLK0 --> RK0
+ // CLK1 --> RK1
+ delay[bl_i] = get_wclk(channel_i, rank_i);
+ } // End product specific code
+ set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
+ } // bl_i loop
+ // now find the rising edge
+ find_rising_edge(mrc_params, delay, channel_i, rank_i, false);
+ { // Begin product specific code
+ // disable Write Levelling Mode
+ isbM32m(DDRPHY, CCDDR3RESETCTL + (DDRIOCCC_CH_OFFSET * channel_i), (0), (BIT16)); // Write Leveling Mode disabled in IO
+
+ for (bl_i = 0; bl_i < ((NUM_BYTE_LANES / bl_divisor) / 2); bl_i++)
+ {
+ isbM32m(DDRPHY, DQCTL + (DDRIODQ_BL_OFFSET * bl_i) + (DDRIODQ_CH_OFFSET * channel_i),
+ ((0x1 << 8) | (0x1 << 6) | (0x1 << 4) | (0x1 << 2)),
+ (BIT28 | (BIT9|BIT8) | (BIT7|BIT6) | (BIT5|BIT4) | (BIT3|BIT2))); // Disable Sandy Bridge Mode & Ensure 4 WDQS pulses during normal operation
+ } // bl_i loop
+
+ // restore original DTR4
+ isbW32m(MCU, DTR4, dtr4save.raw);
+
+ // restore original value (Write Levelling Mode Disable)
+ dram_init_command(DCMD_MRS1(rank_i, mrc_params->mrs1));
+
+ // perform a single PRECHARGE_ALL command to make DRAM state machine go to IDLE state
+ dram_init_command(DCMD_PREA(rank_i));
+ } // End product specific code
+
+ post_code(0x06, (0x30 + ((channel_i << 4) | rank_i)));
+
+ // COARSE WRITE LEVEL:
+ // check that we're on the correct clock edge
+
+ // hte reconfiguration request
+ mrc_params->hte_setup = 1;
+
+ // start CRS_WR_LVL with WDQS = WDQS + 128 PI
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ delay[bl_i] = get_wdqs(channel_i, rank_i, bl_i) + FULL_CLK;
+ set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
+ // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
+ set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
+ } // bl_i loop
+
+ // get an address in the targeted channel/rank
+ address = get_addr(mrc_params, channel_i, rank_i);
+ do
+ {
+ uint32_t coarse_result = 0x00;
+ uint32_t coarse_result_mask = byte_lane_mask(mrc_params);
+ all_edges_found = true; // assume pass
+
+#ifdef SIM
+ // need restore memory to idle state as write can be in bad sync
+ dram_init_command (DCMD_PREA(rank_i));
+#endif
+
+ mrc_params->hte_setup = 1;
+ coarse_result = check_rw_coarse(mrc_params, address);
+
+ // check for failures and margin the byte lane back 128 PI (1 CLK)
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ if (coarse_result & (coarse_result_mask << bl_i))
+ {
+ all_edges_found = false;
+ delay[bl_i] -= FULL_CLK;
+ set_wdqs(channel_i, rank_i, bl_i, delay[bl_i]);
+ // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
+ set_wdq(channel_i, rank_i, bl_i, (delay[bl_i] - QRTR_CLK));
+ }
+ } // bl_i loop
+
+ } while (!all_edges_found);
+
+#ifdef R2R_SHARING
+ // increment "num_ranks_enabled"
+ num_ranks_enabled++;
+ // accumulate "final_delay[][]" values from "delay[]" values for rolling average
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ final_delay[channel_i][bl_i] += delay[bl_i];
+ set_wdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
+ // program WDQ timings based on WDQS (WDQ = WDQS - 32 PI)
+ set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled) - QRTR_CLK);
+ } // bl_i loop
+#endif // R2R_SHARING
+#endif // BACKUP_WDQS
+
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+ LEAVEFN();
+ return;
+}
+
+// rd_train:
+// POST_CODE[major] == 0x07
+//
+// This function will perform the READ TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
+// The idea here is to train the VREF and RDQS (and eventually RDQ) values to achieve maximum READ margins.
+// The algorithm will first determine the X coordinate (RDQS setting).
+// This is done by collapsing the VREF eye until we find a minimum required RDQS eye for VREF_MIN and VREF_MAX.
+// Then we take the averages of the RDQS eye at VREF_MIN and VREF_MAX, then average those; this will be the final X coordinate.
+// The algorithm will then determine the Y coordinate (VREF setting).
+// This is done by collapsing the RDQS eye until we find a minimum required VREF eye for RDQS_MIN and RDQS_MAX.
+// Then we take the averages of the VREF eye at RDQS_MIN and RDQS_MAX, then average those; this will be the final Y coordinate.
+// NOTE: this algorithm assumes the eye curves have a one-to-one relationship, meaning for each X the curve has only one Y and vice-a-versa.
+static void rd_train(
+ MRCParams_t *mrc_params)
+{
+
+#define MIN_RDQS_EYE 10 // in PI Codes
+#define MIN_VREF_EYE 10 // in VREF Codes
+#define RDQS_STEP 1 // how many RDQS codes to jump while margining
+#define VREF_STEP 1 // how many VREF codes to jump while margining
+#define VREF_MIN (0x00) // offset into "vref_codes[]" for minimum allowed VREF setting
+#define VREF_MAX (0x3F) // offset into "vref_codes[]" for maximum allowed VREF setting
+#define RDQS_MIN (0x00) // minimum RDQS delay value
+#define RDQS_MAX (0x3F) // maximum RDQS delay value
+#define B 0 // BOTTOM VREF
+#define T 1 // TOP VREF
+#define L 0 // LEFT RDQS
+#define R 1 // RIGHT RDQS
+
+ uint8_t channel_i; // channel counter
+ uint8_t rank_i; // rank counter
+ uint8_t bl_i; // byte lane counter
+ uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
+#ifdef BACKUP_RDQS
+#else
+ uint8_t side_x; // tracks LEFT/RIGHT approach vectors
+ uint8_t side_y; // tracks BOTTOM/TOP approach vectors
+ uint8_t x_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // X coordinate data (passing RDQS values) for approach vectors
+ uint8_t y_coordinate[2/*side_x*/][2/*side_y*/][NUM_CHANNELS][NUM_BYTE_LANES]; // Y coordinate data (passing VREF values) for approach vectors
+ uint8_t x_center[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // centered X (RDQS)
+ uint8_t y_center[NUM_CHANNELS][NUM_BYTE_LANES]; // centered Y (VREF)
+ uint32_t address; // target address for "check_bls_ex()"
+ uint32_t result; // result of "check_bls_ex()"
+ uint32_t bl_mask; // byte lane mask for "result" checking
+#ifdef R2R_SHARING
+ uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
+ uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
+#endif // R2R_SHARING
+#endif // BACKUP_RDQS
+ // rd_train starts
+ post_code(0x07, 0x00);
+
+ ENTERFN();
+
+#ifdef BACKUP_RDQS
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1<<channel_i))
+ {
+ for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1<<rank_i))
+ {
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ set_rdqs(channel_i, rank_i, bl_i, ddr_rdqs[PLATFORM_ID]);
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+#else
+ // initialise x/y_coordinate arrays
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ // x_coordinate:
+ x_coordinate[L][B][channel_i][rank_i][bl_i] = RDQS_MIN;
+ x_coordinate[R][B][channel_i][rank_i][bl_i] = RDQS_MAX;
+ x_coordinate[L][T][channel_i][rank_i][bl_i] = RDQS_MIN;
+ x_coordinate[R][T][channel_i][rank_i][bl_i] = RDQS_MAX;
+ // y_coordinate:
+ y_coordinate[L][B][channel_i][bl_i] = VREF_MIN;
+ y_coordinate[R][B][channel_i][bl_i] = VREF_MIN;
+ y_coordinate[L][T][channel_i][bl_i] = VREF_MAX;
+ y_coordinate[R][T][channel_i][bl_i] = VREF_MAX;
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+ // initialise other variables
+ bl_mask = byte_lane_mask(mrc_params);
+ address = get_addr(mrc_params, 0, 0);
+
+#ifdef R2R_SHARING
+ // need to set "final_delay[][]" elements to "0"
+ memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
+#endif // R2R_SHARING
+
+ // look for passing coordinates
+ for (side_y = B; side_y <= T; side_y++)
+ {
+ for (side_x = L; side_x <= R; side_x++)
+ {
+
+ post_code(0x07, (0x10 + (side_y * 2) + (side_x)));
+
+ // find passing values
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (0x1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+
+ if (mrc_params->rank_enables & (0x1 << rank_i))
+ {
+ // set x/y_coordinate search starting settings
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
+ set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
+ } // bl_i loop
+ // get an address in the target channel/rank
+ address = get_addr(mrc_params, channel_i, rank_i);
+
+ // request HTE reconfiguration
+ mrc_params->hte_setup = 1;
+
+ // test the settings
+ do
+ {
+
+ // result[07:00] == failing byte lane (MAX 8)
+ result = check_bls_ex( mrc_params, address);
+
+ // check for failures
+ if (result & 0xFF)
+ {
+ // at least 1 byte lane failed
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ if (result & (bl_mask << bl_i))
+ {
+ // adjust the RDQS values accordingly
+ if (side_x == L)
+ {
+ x_coordinate[L][side_y][channel_i][rank_i][bl_i] += RDQS_STEP;
+ }
+ else
+ {
+ x_coordinate[R][side_y][channel_i][rank_i][bl_i] -= RDQS_STEP;
+ }
+ // check that we haven't closed the RDQS_EYE too much
+ if ((x_coordinate[L][side_y][channel_i][rank_i][bl_i] > (RDQS_MAX - MIN_RDQS_EYE)) ||
+ (x_coordinate[R][side_y][channel_i][rank_i][bl_i] < (RDQS_MIN + MIN_RDQS_EYE))
+ ||
+ (x_coordinate[L][side_y][channel_i][rank_i][bl_i]
+ == x_coordinate[R][side_y][channel_i][rank_i][bl_i]))
+ {
+ // not enough RDQS margin available at this VREF
+ // update VREF values accordingly
+ if (side_y == B)
+ {
+ y_coordinate[side_x][B][channel_i][bl_i] += VREF_STEP;
+ }
+ else
+ {
+ y_coordinate[side_x][T][channel_i][bl_i] -= VREF_STEP;
+ }
+ // check that we haven't closed the VREF_EYE too much
+ if ((y_coordinate[side_x][B][channel_i][bl_i] > (VREF_MAX - MIN_VREF_EYE)) ||
+ (y_coordinate[side_x][T][channel_i][bl_i] < (VREF_MIN + MIN_VREF_EYE)) ||
+ (y_coordinate[side_x][B][channel_i][bl_i] == y_coordinate[side_x][T][channel_i][bl_i]))
+ {
+ // VREF_EYE collapsed below MIN_VREF_EYE
+ training_message(channel_i, rank_i, bl_i);
+ post_code(0xEE, (0x70 + (side_y * 2) + (side_x)));
+ }
+ else
+ {
+ // update the VREF setting
+ set_vref(channel_i, bl_i, y_coordinate[side_x][side_y][channel_i][bl_i]);
+ // reset the X coordinate to begin the search at the new VREF
+ x_coordinate[side_x][side_y][channel_i][rank_i][bl_i] =
+ (side_x == L) ? (RDQS_MIN) : (RDQS_MAX);
+ }
+ }
+ // update the RDQS setting
+ set_rdqs(channel_i, rank_i, bl_i, x_coordinate[side_x][side_y][channel_i][rank_i][bl_i]);
+ } // if bl_i failed
+ } // bl_i loop
+ } // at least 1 byte lane failed
+ } while (result & 0xFF);
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+ } // side_x loop
+ } // side_y loop
+
+ post_code(0x07, 0x20);
+
+ // find final RDQS (X coordinate) & final VREF (Y coordinate)
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ uint32_t tempD1;
+ uint32_t tempD2;
+
+ // x_coordinate:
+ DPF(D_INFO, "RDQS T/B eye rank%d lane%d : %d-%d %d-%d\n", rank_i, bl_i,
+ x_coordinate[L][T][channel_i][rank_i][bl_i],
+ x_coordinate[R][T][channel_i][rank_i][bl_i],
+ x_coordinate[L][B][channel_i][rank_i][bl_i],
+ x_coordinate[R][B][channel_i][rank_i][bl_i]);
+
+ tempD1 = (x_coordinate[R][T][channel_i][rank_i][bl_i] + x_coordinate[L][T][channel_i][rank_i][bl_i]) / 2; // average the TOP side LEFT & RIGHT values
+ tempD2 = (x_coordinate[R][B][channel_i][rank_i][bl_i] + x_coordinate[L][B][channel_i][rank_i][bl_i]) / 2; // average the BOTTOM side LEFT & RIGHT values
+ x_center[channel_i][rank_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
+
+ // y_coordinate:
+ DPF(D_INFO, "VREF R/L eye lane%d : %d-%d %d-%d\n", bl_i,
+ y_coordinate[R][B][channel_i][bl_i],
+ y_coordinate[R][T][channel_i][bl_i],
+ y_coordinate[L][B][channel_i][bl_i],
+ y_coordinate[L][T][channel_i][bl_i]);
+
+ tempD1 = (y_coordinate[R][T][channel_i][bl_i] + y_coordinate[R][B][channel_i][bl_i]) / 2; // average the RIGHT side TOP & BOTTOM values
+ tempD2 = (y_coordinate[L][T][channel_i][bl_i] + y_coordinate[L][B][channel_i][bl_i]) / 2; // average the LEFT side TOP & BOTTOM values
+ y_center[channel_i][bl_i] = (uint8_t) ((tempD1 + tempD2) / 2); // average the above averages
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+#ifdef RX_EYE_CHECK
+ // perform an eye check
+ for (side_y=B; side_y<=T; side_y++)
+ {
+ for (side_x=L; side_x<=R; side_x++)
+ {
+
+ post_code(0x07, (0x30 + (side_y * 2) + (side_x)));
+
+ // update the settings for the eye check
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1<<channel_i))
+ {
+ for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1<<rank_i))
+ {
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ if (side_x == L)
+ {
+ set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] - (MIN_RDQS_EYE / 2)));
+ }
+ else
+ {
+ set_rdqs(channel_i, rank_i, bl_i, (x_center[channel_i][rank_i][bl_i] + (MIN_RDQS_EYE / 2)));
+ }
+ if (side_y == B)
+ {
+ set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] - (MIN_VREF_EYE / 2)));
+ }
+ else
+ {
+ set_vref(channel_i, bl_i, (y_center[channel_i][bl_i] + (MIN_VREF_EYE / 2)));
+ }
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+ // request HTE reconfiguration
+ mrc_params->hte_setup = 1;
+
+ // check the eye
+ if (check_bls_ex( mrc_params, address) & 0xFF)
+ {
+ // one or more byte lanes failed
+ post_code(0xEE, (0x74 + (side_x * 2) + (side_y)));
+ }
+ } // side_x loop
+ } // side_y loop
+#endif // RX_EYE_CHECK
+
+ post_code(0x07, 0x40);
+
+ // set final placements
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+#ifdef R2R_SHARING
+ // increment "num_ranks_enabled"
+ num_ranks_enabled++;
+#endif // R2R_SHARING
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ // x_coordinate:
+#ifdef R2R_SHARING
+ final_delay[channel_i][bl_i] += x_center[channel_i][rank_i][bl_i];
+ set_rdqs(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
+#else
+ set_rdqs(channel_i, rank_i, bl_i, x_center[channel_i][rank_i][bl_i]);
+#endif // R2R_SHARING
+ // y_coordinate:
+ set_vref(channel_i, bl_i, y_center[channel_i][bl_i]);
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+#endif // BACKUP_RDQS
+ LEAVEFN();
+ return;
+}
+
+// wr_train:
+// POST_CODE[major] == 0x08
+//
+// This function will perform the WRITE TRAINING Algorithm on all channels/ranks/byte_lanes simultaneously to minimize execution time.
+// The idea here is to train the WDQ timings to achieve maximum WRITE margins.
+// The algorithm will start with WDQ at the current WDQ setting (tracks WDQS in WR_LVL) +/- 32 PIs (+/- 1/4 CLK) and collapse the eye until all data patterns pass.
+// This is because WDQS will be aligned to WCLK by the Write Leveling algorithm and WDQ will only ever have a 1/2 CLK window of validity.
+static void wr_train(
+ MRCParams_t *mrc_params)
+{
+
+#define WDQ_STEP 1 // how many WDQ codes to jump while margining
+#define L 0 // LEFT side loop value definition
+#define R 1 // RIGHT side loop value definition
+
+ uint8_t channel_i; // channel counter
+ uint8_t rank_i; // rank counter
+ uint8_t bl_i; // byte lane counter
+ uint8_t bl_divisor = (mrc_params->channel_width == x16) ? 2 : 1; // byte lane divisor
+#ifdef BACKUP_WDQ
+#else
+ uint8_t side_i; // LEFT/RIGHT side indicator (0=L, 1=R)
+ uint32_t tempD; // temporary DWORD
+ uint32_t delay[2/*side_i*/][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES]; // 2 arrays, for L & R side passing delays
+ uint32_t address; // target address for "check_bls_ex()"
+ uint32_t result; // result of "check_bls_ex()"
+ uint32_t bl_mask; // byte lane mask for "result" checking
+#ifdef R2R_SHARING
+ uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES]; // used to find placement for rank2rank sharing configs
+ uint32_t num_ranks_enabled = 0; // used to find placement for rank2rank sharing configs
+#endif // R2R_SHARING
+#endif // BACKUP_WDQ
+
+ // wr_train starts
+ post_code(0x08, 0x00);
+
+ ENTERFN();
+
+#ifdef BACKUP_WDQ
+ for (channel_i=0; channel_i<NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1<<channel_i))
+ {
+ for (rank_i=0; rank_i<NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1<<rank_i))
+ {
+ for (bl_i=0; bl_i<(NUM_BYTE_LANES/bl_divisor); bl_i++)
+ {
+ set_wdq(channel_i, rank_i, bl_i, ddr_wdq[PLATFORM_ID]);
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+#else
+ // initialise "delay"
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ // want to start with WDQ = (WDQS - QRTR_CLK) +/- QRTR_CLK
+ tempD = get_wdqs(channel_i, rank_i, bl_i) - QRTR_CLK;
+ delay[L][channel_i][rank_i][bl_i] = tempD - QRTR_CLK;
+ delay[R][channel_i][rank_i][bl_i] = tempD + QRTR_CLK;
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+ // initialise other variables
+ bl_mask = byte_lane_mask(mrc_params);
+ address = get_addr(mrc_params, 0, 0);
+
+#ifdef R2R_SHARING
+ // need to set "final_delay[][]" elements to "0"
+ memset((void *) (final_delay), 0x00, (size_t) sizeof(final_delay));
+#endif // R2R_SHARING
+
+ // start algorithm on the LEFT side and train each channel/bl until no failures are observed, then repeat for the RIGHT side.
+ for (side_i = L; side_i <= R; side_i++)
+ {
+ post_code(0x08, (0x10 + (side_i)));
+
+ // set starting values
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+
+ // find passing values
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (0x1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (0x1 << rank_i))
+ {
+ // get an address in the target channel/rank
+ address = get_addr(mrc_params, channel_i, rank_i);
+
+ // request HTE reconfiguration
+ mrc_params->hte_setup = 1;
+
+ // check the settings
+ do
+ {
+
+#ifdef SIM
+ // need restore memory to idle state as write can be in bad sync
+ dram_init_command (DCMD_PREA(rank_i));
+#endif
+
+ // result[07:00] == failing byte lane (MAX 8)
+ result = check_bls_ex( mrc_params, address);
+ // check for failures
+ if (result & 0xFF)
+ {
+ // at least 1 byte lane failed
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+ if (result & (bl_mask << bl_i))
+ {
+ if (side_i == L)
+ {
+ delay[L][channel_i][rank_i][bl_i] += WDQ_STEP;
+ }
+ else
+ {
+ delay[R][channel_i][rank_i][bl_i] -= WDQ_STEP;
+ }
+ // check for algorithm failure
+ if (delay[L][channel_i][rank_i][bl_i] != delay[R][channel_i][rank_i][bl_i])
+ {
+ // margin available, update delay setting
+ set_wdq(channel_i, rank_i, bl_i, delay[side_i][channel_i][rank_i][bl_i]);
+ }
+ else
+ {
+ // no margin available, notify the user and halt
+ training_message(channel_i, rank_i, bl_i);
+ post_code(0xEE, (0x80 + side_i));
+ }
+ } // if bl_i failed
+ } // bl_i loop
+ } // at least 1 byte lane failed
+ } while (result & 0xFF); // stop when all byte lanes pass
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+ } // side_i loop
+
+ // program WDQ to the middle of passing window
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+#ifdef R2R_SHARING
+ // increment "num_ranks_enabled"
+ num_ranks_enabled++;
+#endif // R2R_SHARING
+ for (bl_i = 0; bl_i < (NUM_BYTE_LANES / bl_divisor); bl_i++)
+ {
+
+ DPF(D_INFO, "WDQ eye rank%d lane%d : %d-%d\n", rank_i, bl_i,
+ delay[L][channel_i][rank_i][bl_i],
+ delay[R][channel_i][rank_i][bl_i]);
+
+ tempD = (delay[R][channel_i][rank_i][bl_i] + delay[L][channel_i][rank_i][bl_i]) / 2;
+
+#ifdef R2R_SHARING
+ final_delay[channel_i][bl_i] += tempD;
+ set_wdq(channel_i, rank_i, bl_i, ((final_delay[channel_i][bl_i]) / num_ranks_enabled));
+#else
+ set_wdq(channel_i, rank_i, bl_i, tempD);
+#endif // R2R_SHARING
+
+ } // bl_i loop
+ } // if rank is enabled
+ } // rank_i loop
+ } // if channel is enabled
+ } // channel_i loop
+#endif // BACKUP_WDQ
+ LEAVEFN();
+ return;
+}
+
+// Wrapper for jedec initialisation routine
+static void perform_jedec_init(
+ MRCParams_t *mrc_params)
+{
+ jedec_init(mrc_params, 0);
+}
+
+// Configure DDRPHY for Auto-Refresh, Periodic Compensations,
+// Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
+static void set_auto_refresh(
+ MRCParams_t *mrc_params)
+{
+ uint32_t channel_i;
+ uint32_t rank_i;
+ uint32_t bl_i;
+ uint32_t bl_divisor = /*(mrc_params->channel_width==x16)?2:*/1;
+ uint32_t tempD;
+
+ ENTERFN();
+
+ // enable Auto-Refresh, Periodic Compensations, Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
+ for (channel_i = 0; channel_i < NUM_CHANNELS; channel_i++)
+ {
+ if (mrc_params->channel_enables & (1 << channel_i))
+ {
+ // Enable Periodic RCOMPS
+ isbM32m(DDRPHY, CMPCTRL, (BIT1), (BIT1));
+
+
+ // Enable Dynamic DiffAmp & Set Read ODT Value
+ switch (mrc_params->rd_odt_value)
+ {
+ case 0: tempD = 0x3F; break; // OFF
+ default: tempD = 0x00; break; // Auto
+ } // rd_odt_value switch
+
+ for (bl_i=0; bl_i<((NUM_BYTE_LANES/bl_divisor)/2); bl_i++)
+ {
+ isbM32m(DDRPHY, (B0OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
+ ((0x00<<16)|(tempD<<10)),
+ ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10))); // Override: DIFFAMP, ODT
+
+ isbM32m(DDRPHY, (B1OVRCTL + (bl_i * DDRIODQ_BL_OFFSET) + (channel_i * DDRIODQ_CH_OFFSET)),
+ ((0x00<<16)|(tempD<<10)),
+ ((BIT21|BIT20|BIT19|BIT18|BIT17|BIT16)|(BIT15|BIT14|BIT13|BIT12|BIT11|BIT10)));// Override: DIFFAMP, ODT
+ } // bl_i loop
+
+ // Issue ZQCS command
+ for (rank_i = 0; rank_i < NUM_RANKS; rank_i++)
+ {
+ if (mrc_params->rank_enables & (1 << rank_i))
+ {
+ dram_init_command(DCMD_ZQCS(rank_i));
+ } // if rank_i enabled
+ } // rank_i loop
+
+ } // if channel_i enabled
+ } // channel_i loop
+
+ clear_pointers();
+
+ LEAVEFN();
+ return;
+}
+
+// Depending on configuration enables ECC support.
+// Available memory size is decresed, and updated with 0s
+// in order to clear error status. Address mode 2 forced.
+static void ecc_enable(
+ MRCParams_t *mrc_params)
+{
+ RegDRP Drp;
+ RegDSCH Dsch;
+ RegDECCCTRL Ctr;
+
+ if (mrc_params->ecc_enables == 0) return;
+
+ ENTERFN();
+
+ // Configuration required in ECC mode
+ Drp.raw = isbR32m(MCU, DRP);
+ Drp.field.addressMap = 2;
+ Drp.field.split64 = 1;
+ isbW32m(MCU, DRP, Drp.raw);
+
+ // Disable new request bypass
+ Dsch.raw = isbR32m(MCU, DSCH);
+ Dsch.field.NEWBYPDIS = 1;
+ isbW32m(MCU, DSCH, Dsch.raw);
+
+ // Enable ECC
+ Ctr.raw = 0;
+ Ctr.field.SBEEN = 1;
+ Ctr.field.DBEEN = 1;
+ Ctr.field.ENCBGEN = 1;
+ isbW32m(MCU, DECCCTRL, Ctr.raw);
+
+#ifdef SIM
+ // Read back to be sure writing took place
+ Ctr.raw = isbR32m(MCU, DECCCTRL);
+#endif
+
+ // Assume 8 bank memory, one bank is gone for ECC
+ mrc_params->mem_size -= mrc_params->mem_size / 8;
+
+ // For S3 resume memory content has to be preserved
+ if (mrc_params->boot_mode != bmS3)
+ {
+ select_hte(mrc_params);
+ HteMemInit(mrc_params, MrcMemInit, MrcHaltHteEngineOnError);
+ select_memory_manager(mrc_params);
+ }
+
+ LEAVEFN();
+ return;
+}
+
+// Lock MCU registers at the end of initialisation sequence.
+static void lock_registers(
+ MRCParams_t *mrc_params)
+{
+ RegDCO Dco;
+
+ ENTERFN();
+
+ Dco.raw = isbR32m(MCU, DCO);
+ Dco.field.PMIDIS = 0; //0 - PRI enabled
+ Dco.field.PMICTL = 0; //0 - PRI owned by MEMORY_MANAGER
+ Dco.field.DRPLOCK = 1;
+ Dco.field.REUTLOCK = 1;
+ isbW32m(MCU, DCO, Dco.raw);
+
+ LEAVEFN();
+
+}
+
+#ifdef MRC_SV
+
+// cache write back invalidate
+static void asm_wbinvd(void)
+{
+#if defined (SIM) || defined (GCC)
+ asm(
+ "wbinvd;"
+ );
+#else
+ __asm wbinvd;
+#endif
+}
+
+// cache invalidate
+static void asm_invd(void)
+{
+#if defined (SIM) || defined (GCC)
+ asm(
+ "invd;"
+ );
+#else
+ __asm invd;
+#endif
+}
+
+
+static void cpu_read(void)
+{
+ uint32_t adr, dat, limit;
+
+ asm_invd();
+
+ limit = 8 * 1024;
+ for (adr = 0; adr < limit; adr += 4)
+ {
+ dat = *(uint32_t*) adr;
+ if ((adr & 0x0F) == 0)
+ {
+ DPF(D_INFO, "\n%x : ", adr);
+ }
+ DPF(D_INFO, "%x ", dat);
+ }
+ DPF(D_INFO, "\n");
+
+ DPF(D_INFO, "CPU read done\n");
+}
+
+
+static void cpu_write(void)
+{
+ uint32_t adr, limit;
+
+ limit = 8 * 1024;
+ for (adr = 0; adr < limit; adr += 4)
+ {
+ *(uint32_t*) adr = 0xDEAD0000 + adr;
+ }
+
+ asm_wbinvd();
+
+ DPF(D_INFO, "CPU write done\n");
+}
+
+
+static void cpu_memory_test(
+ MRCParams_t *mrc_params)
+{
+ uint32_t result = 0;
+ uint32_t val, dat, adr, adr0, step, limit;
+ uint64_t my_tsc;
+
+ ENTERFN();
+
+ asm_invd();
+
+ adr0 = 1 * 1024 * 1024;
+ limit = 256 * 1024 * 1024;
+
+ for (step = 0; step <= 4; step++)
+ {
+ DPF(D_INFO, "Mem test step %d starting from %xh\n", step, adr0);
+
+ my_tsc = read_tsc();
+ for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
+ {
+ if (step == 0) dat = adr;
+ else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
+ else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
+ else if (step == 3) dat = 0x5555AAAA;
+ else if (step == 4) dat = 0xAAAA5555;
+
+ *(uint32_t*) adr = dat;
+ }
+ DPF(D_INFO, "Write time %llXh\n", read_tsc() - my_tsc);
+
+ my_tsc = read_tsc();
+ for (adr = adr0; adr < limit; adr += sizeof(uint32_t))
+ {
+ if (step == 0) dat = adr;
+ else if (step == 1) dat = (1 << ((adr >> 2) & 0x1f));
+ else if (step == 2) dat = ~(1 << ((adr >> 2) & 0x1f));
+ else if (step == 3) dat = 0x5555AAAA;
+ else if (step == 4) dat = 0xAAAA5555;
+
+ val = *(uint32_t*) adr;
+
+ if (val != dat)
+ {
+ DPF(D_INFO, "%x vs. %x@%x\n", dat, val, adr);
+ result = adr|BIT31;
+ }
+ }
+ DPF(D_INFO, "Read time %llXh\n", read_tsc() - my_tsc);
+ }
+
+ DPF( D_INFO, "Memory test result %x\n", result);
+ LEAVEFN();
+}
+#endif // MRC_SV
+
+
+// Execute memory test, if error dtected it is
+// indicated in mrc_params->status.
+static void memory_test(
+ MRCParams_t *mrc_params)
+{
+ uint32_t result = 0;
+
+ ENTERFN();
+
+ select_hte(mrc_params);
+ result = HteMemInit(mrc_params, MrcMemTest, MrcHaltHteEngineOnError);
+ select_memory_manager(mrc_params);
+
+ DPF(D_INFO, "Memory test result %x\n", result);
+ mrc_params->status = ((result == 0) ? MRC_SUCCESS : MRC_E_MEMTEST);
+ LEAVEFN();
+}
+
+
+// Force same timings as with backup settings
+static void static_timings(
+ MRCParams_t *mrc_params)
+
+{
+ uint8_t ch, rk, bl;
+
+ for (ch = 0; ch < NUM_CHANNELS; ch++)
+ {
+ for (rk = 0; rk < NUM_RANKS; rk++)
+ {
+ for (bl = 0; bl < NUM_BYTE_LANES; bl++)
+ {
+ set_rcvn(ch, rk, bl, 498); // RCVN
+ set_rdqs(ch, rk, bl, 24); // RDQS
+ set_wdqs(ch, rk, bl, 292); // WDQS
+ set_wdq( ch, rk, bl, 260); // WDQ
+ if (rk == 0)
+ {
+ set_vref(ch, bl, 32); // VREF (RANK0 only)
+ }
+ }
+ set_wctl(ch, rk, 217); // WCTL
+ }
+ set_wcmd(ch, 220); // WCMD
+ }
+
+ return;
+}
+
+//
+// Initialise system memory.
+//
+void MemInit(
+ MRCParams_t *mrc_params)
+{
+ static const MemInit_t init[] =
+ {
+ { 0x0101, bmCold|bmFast|bmWarm|bmS3, clear_self_refresh }, //0
+ { 0x0200, bmCold|bmFast|bmWarm|bmS3, prog_ddr_timing_control }, //1 initialise the MCU
+ { 0x0103, bmCold|bmFast , prog_decode_before_jedec }, //2
+ { 0x0104, bmCold|bmFast , perform_ddr_reset }, //3
+ { 0x0300, bmCold|bmFast |bmS3, ddrphy_init }, //4 initialise the DDRPHY
+ { 0x0400, bmCold|bmFast , perform_jedec_init }, //5 perform JEDEC initialisation of DRAMs
+ { 0x0105, bmCold|bmFast , set_ddr_init_complete }, //6
+ { 0x0106, bmFast|bmWarm|bmS3, restore_timings }, //7
+ { 0x0106, bmCold , default_timings }, //8
+ { 0x0500, bmCold , rcvn_cal }, //9 perform RCVN_CAL algorithm
+ { 0x0600, bmCold , wr_level }, //10 perform WR_LEVEL algorithm
+ { 0x0120, bmCold , prog_page_ctrl }, //11
+ { 0x0700, bmCold , rd_train }, //12 perform RD_TRAIN algorithm
+ { 0x0800, bmCold , wr_train }, //13 perform WR_TRAIN algorithm
+ { 0x010B, bmCold , store_timings }, //14
+ { 0x010C, bmCold|bmFast|bmWarm|bmS3, enable_scrambling }, //15
+ { 0x010D, bmCold|bmFast|bmWarm|bmS3, prog_ddr_control }, //16
+ { 0x010E, bmCold|bmFast|bmWarm|bmS3, prog_dra_drb }, //17
+ { 0x010F, bmWarm|bmS3, perform_wake }, //18
+ { 0x0110, bmCold|bmFast|bmWarm|bmS3, change_refresh_period }, //19
+ { 0x0111, bmCold|bmFast|bmWarm|bmS3, set_auto_refresh }, //20
+ { 0x0112, bmCold|bmFast|bmWarm|bmS3, ecc_enable }, //21
+ { 0x0113, bmCold|bmFast , memory_test }, //22
+ { 0x0114, bmCold|bmFast|bmWarm|bmS3, lock_registers } //23 set init done
+ };
+
+ uint32_t i;
+
+ ENTERFN();
+
+ DPF(D_INFO, "Meminit build %s %s\n", __DATE__, __TIME__);
+
+ // MRC started
+ post_code(0x01, 0x00);
+
+ if (mrc_params->boot_mode != bmCold)
+ {
+ if (mrc_params->ddr_speed != mrc_params->timings.ddr_speed)
+ {
+ // full training required as frequency changed
+ mrc_params->boot_mode = bmCold;
+ }
+ }
+
+ for (i = 0; i < MCOUNT(init); i++)
+ {
+ uint64_t my_tsc;
+
+#ifdef MRC_SV
+ if (mrc_params->menu_after_mrc && i > 14)
+ {
+ uint8_t ch;
+
+ mylop:
+
+ DPF(D_INFO, "-- c - continue --\n");
+ DPF(D_INFO, "-- j - move to jedec init --\n");
+ DPF(D_INFO, "-- m - memory test --\n");
+ DPF(D_INFO, "-- r - cpu read --\n");
+ DPF(D_INFO, "-- w - cpu write --\n");
+ DPF(D_INFO, "-- b - hte base test --\n");
+ DPF(D_INFO, "-- g - hte extended test --\n");
+
+ ch = mgetc();
+ switch (ch)
+ {
+ case 'c':
+ break;
+ case 'j': //move to jedec init
+ i = 5;
+ break;
+
+ case 'M':
+ case 'N':
+ {
+ uint32_t n, res, cnt=0;
+
+ for(n=0; mgetch()==0; n++)
+ {
+ if( ch == 'M' || n % 256 == 0)
+ {
+ DPF(D_INFO, "n=%d e=%d\n", n, cnt);
+ }
+
+ res = 0;
+
+ if( ch == 'M')
+ {
+ memory_test(mrc_params);
+ res |= mrc_params->status;
+ }
+
+ mrc_params->hte_setup = 1;
+ res |= check_bls_ex(mrc_params, 0x00000000);
+ res |= check_bls_ex(mrc_params, 0x00000000);
+ res |= check_bls_ex(mrc_params, 0x00000000);
+ res |= check_bls_ex(mrc_params, 0x00000000);
+
+ if( mrc_params->rank_enables & 2)
+ {
+ mrc_params->hte_setup = 1;
+ res |= check_bls_ex(mrc_params, 0x40000000);
+ res |= check_bls_ex(mrc_params, 0x40000000);
+ res |= check_bls_ex(mrc_params, 0x40000000);
+ res |= check_bls_ex(mrc_params, 0x40000000);
+ }
+
+ if( res != 0)
+ {
+ DPF(D_INFO, "###########\n");
+ DPF(D_INFO, "#\n");
+ DPF(D_INFO, "# Error count %d\n", ++cnt);
+ DPF(D_INFO, "#\n");
+ DPF(D_INFO, "###########\n");
+ }
+
+ } // for
+
+ select_memory_manager(mrc_params);
+ }
+ goto mylop;
+ case 'm':
+ memory_test(mrc_params);
+ goto mylop;
+ case 'n':
+ cpu_memory_test(mrc_params);
+ goto mylop;
+
+ case 'l':
+ ch = mgetc();
+ if (ch <= '9') DpfPrintMask ^= (ch - '0') << 3;
+ DPF(D_INFO, "Log mask %x\n", DpfPrintMask);
+ goto mylop;
+ case 'p':
+ print_timings(mrc_params);
+ goto mylop;
+ case 'R':
+ rd_train(mrc_params);
+ goto mylop;
+ case 'W':
+ wr_train(mrc_params);
+ goto mylop;
+
+ case 'r':
+ cpu_read();
+ goto mylop;
+ case 'w':
+ cpu_write();
+ goto mylop;
+
+ case 'g':
+ {
+ uint32_t result;
+ select_hte(mrc_params);
+ mrc_params->hte_setup = 1;
+ result = check_bls_ex(mrc_params, 0);
+ DPF(D_INFO, "Extended test result %x\n", result);
+ select_memory_manager(mrc_params);
+ }
+ goto mylop;
+ case 'b':
+ {
+ uint32_t result;
+ select_hte(mrc_params);
+ mrc_params->hte_setup = 1;
+ result = check_rw_coarse(mrc_params, 0);
+ DPF(D_INFO, "Base test result %x\n", result);
+ select_memory_manager(mrc_params);
+ }
+ goto mylop;
+ case 'B':
+ select_hte(mrc_params);
+ HteMemOp(0x2340, 1, 1);
+ select_memory_manager(mrc_params);
+ goto mylop;
+
+ case '3':
+ {
+ RegDPMC0 DPMC0reg;
+
+ DPF( D_INFO, "===>> Start suspend\n");
+ isbR32m(MCU, DSTAT);
+
+ DPMC0reg.raw = isbR32m(MCU, DPMC0);
+ DPMC0reg.field.DYNSREN = 0;
+ DPMC0reg.field.powerModeOpCode = 0x05; // Disable Master DLL
+ isbW32m(MCU, DPMC0, DPMC0reg.raw);
+
+ // Should be off for negative test case verification
+ #if 1
+ Wr32(MMIO, PCIADDR(0,0,0,SB_PACKET_REG),
+ (uint32_t)SB_COMMAND(SB_SUSPEND_CMND_OPCODE, MCU, 0));
+ #endif
+
+ DPF( D_INFO, "press key\n");
+ mgetc();
+ DPF( D_INFO, "===>> Start resume\n");
+ isbR32m(MCU, DSTAT);
+
+ mrc_params->boot_mode = bmS3;
+ i = 0;
+ }
+
+ } // switch
+
+ } // if( menu
+#endif //MRC_SV
+
+ if (mrc_params->boot_mode & init[i].boot_path)
+ {
+ uint8_t major = init[i].post_code >> 8 & 0xFF;
+ uint8_t minor = init[i].post_code >> 0 & 0xFF;
+ post_code(major, minor);
+
+ my_tsc = read_tsc();
+ init[i].init_fn(mrc_params);
+ DPF(D_TIME, "Execution time %llX", read_tsc() - my_tsc);
+ }
+ }
+
+ // display the timings
+ print_timings(mrc_params);
+
+ // MRC is complete.
+ post_code(0x01, 0xFF);
+
+ LEAVEFN();
+ return;
+}
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, "</%s>\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<<n)
+#define bmCold 1 // full training
+#define bmFast 2 // restore timing parameters
+#define bmS3 4 // resume from S3
+#define bmWarm 8
+#define bmUnknown 0
+
+
+// MRC execution status
+#define MRC_SUCCESS 0 // initialization ok
+#define MRC_E_MEMTEST 1 // memtest failed
+
+
+//
+// Input/output/context parameters for Memory Reference Code
+//
+typedef struct MRCParams_s
+{
+ //
+ // Global settings
+ //
+
+ uint32_t boot_mode; // bmCold, bmFast, bmWarm, bmS3
+ uint32_t uart_mmio_base; // pcie serial port base address (force 0 to disable debug)
+
+ uint8_t dram_width; // x8, x16
+ uint8_t ddr_speed; // DDRFREQ_800, DDRFREQ_1066
+ uint8_t ddr_type; // DDR3, DDR3L
+ uint8_t ecc_enables; // 0, 1 (memory size reduced to 7/8)
+ uint8_t scrambling_enables; // 0, 1
+ uint32_t rank_enables; // 1, 3 (1'st rank has to be populated if 2'nd rank present)
+ uint32_t channel_enables; // 1 only
+ uint32_t channel_width; // x16 only
+ uint32_t address_mode; // 0, 1, 2 (mode 2 forced if ecc enabled)
+
+ // memConfig_t begin
+ uint8_t refresh_rate; // REFRESH_RATE : 1=1.95us, 2=3.9us, 3=7.8us, others=RESERVED
+ uint8_t sr_temp_range; // SR_TEMP_RANGE : 0=normal, 1=extended, others=RESERVED
+ uint8_t ron_value; // RON_VALUE : 0=34ohm, 1=40ohm, others=RESERVED (select MRS1.DIC driver impedance control)
+ uint8_t rtt_nom_value; // RTT_NOM_VALUE : 0=40ohm, 1=60ohm, 2=120ohm, others=RESERVED
+ uint8_t rd_odt_value; // RD_ODT_VALUE : 0=off, 1=60ohm, 2=120ohm, 3=180ohm, others=RESERVED
+ // memConfig_t end
+
+ DRAMParams_t params;
+
+ //
+ // Internally used
+ //
+
+ uint32_t board_id; // internally used for board layout (use x8 or x16 memory)
+ uint32_t hte_setup : 1; // when set hte reconfiguration requested
+ uint32_t menu_after_mrc : 1;
+ uint32_t power_down_disable :1;
+ uint32_t tune_rcvn :1;
+
+ uint32_t channel_size[NUM_CHANNELS];
+ uint32_t column_bits[NUM_CHANNELS];
+ uint32_t row_bits[NUM_CHANNELS];
+
+ uint32_t mrs1; // register content saved during training
+
+ //
+ // Output
+ //
+
+ uint32_t status; // initialization result (non zero specifies error code)
+ uint32_t mem_size; // total memory size in bytes (excludes ECC banks)
+
+ MrcTimings_t timings; // training results (also used on input)
+
+} MRCParams_t;
+
+// Alternative type name for consistent naming convention
+#define MRC_PARAMS MRCParams_t
+
+#endif // _MRC_H_
diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/platform.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/platform.c new file mode 100644 index 0000000000..cb6eb99f74 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/platform.c @@ -0,0 +1,192 @@ +/** @file
+The interface layer for memory controller access.
+It is supporting both real hardware platform and simulation environment.
+
+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 "io.h"
+
+#ifdef SIM
+
+void SimMmio32Write (
+ uint32_t be,
+ uint32_t address,
+ uint32_t data );
+
+void SimMmio32Read (
+ uint32_t be,
+ uint32_t address,
+ uint32_t *data );
+
+void SimDelayClk (
+ uint32_t x2clk );
+
+// This is a simple delay function.
+// It takes "nanoseconds" as a parameter.
+void delay_n(uint32_t nanoseconds)
+{
+ SimDelayClk( 800*nanoseconds/1000);
+}
+#endif
+
+/****
+ *
+ ***/
+uint32_t Rd32(
+ uint32_t unit,
+ uint32_t addr)
+{
+ uint32_t data;
+
+ switch (unit)
+ {
+ case MEM:
+ case MMIO:
+#ifdef SIM
+ SimMmio32Read( 1, addr, &data);
+#else
+ data = *PTR32(addr);
+#endif
+ break;
+
+ case MCU:
+ case HOST_BRIDGE:
+ case MEMORY_MANAGER:
+ case HTE:
+ // Handle case addr bigger than 8bit
+ pciwrite32(0, 0, 0, SB_HADR_REG, addr & 0xFFF00);
+ addr &= 0x00FF;
+
+ pciwrite32(0, 0, 0, SB_PACKET_REG,
+ SB_COMMAND(SB_REG_READ_OPCODE, unit, addr));
+ data = pciread32(0, 0, 0, SB_DATA_REG);
+ break;
+
+ case DDRPHY:
+ // Handle case addr bigger than 8bit
+ pciwrite32(0, 0, 0, SB_HADR_REG, addr & 0xFFF00);
+ addr &= 0x00FF;
+
+ pciwrite32(0, 0, 0, SB_PACKET_REG,
+ SB_COMMAND(SB_DDRIO_REG_READ_OPCODE, unit, addr));
+ data = pciread32(0, 0, 0, SB_DATA_REG);
+ break;
+
+ default:
+ DEAD_LOOP()
+ ;
+ }
+
+ if (unit < MEM)
+ DPF(D_REGRD, "RD32 %03X %08X %08X\n", unit, addr, data);
+
+ return data;
+}
+
+/****
+ *
+ ***/
+void Wr32(
+ uint32_t unit,
+ uint32_t addr,
+ uint32_t data)
+{
+ if (unit < MEM)
+ DPF(D_REGWR, "WR32 %03X %08X %08X\n", unit, addr, data);
+
+ switch (unit)
+ {
+ case MEM:
+ case MMIO:
+#ifdef SIM
+ SimMmio32Write( 1, addr, data);
+#else
+ *PTR32(addr) = data;
+#endif
+ break;
+
+ case MCU:
+ case HOST_BRIDGE:
+ case MEMORY_MANAGER:
+ case HTE:
+ // Handle case addr bigger than 8bit
+ pciwrite32(0, 0, 0, SB_HADR_REG, addr & 0xFFF00);
+ addr &= 0x00FF;
+
+ pciwrite32(0, 0, 0, SB_DATA_REG, data);
+ pciwrite32(0, 0, 0, SB_PACKET_REG,
+ SB_COMMAND(SB_REG_WRITE_OPCODE, unit, addr));
+ break;
+
+ case DDRPHY:
+ // Handle case addr bigger than 8bit
+ pciwrite32(0, 0, 0, SB_HADR_REG, addr & 0xFFF00);
+ addr &= 0x00FF;
+
+ pciwrite32(0, 0, 0, SB_DATA_REG, data);
+ pciwrite32(0, 0, 0, SB_PACKET_REG,
+ SB_COMMAND(SB_DDRIO_REG_WRITE_OPCODE, unit, addr));
+ break;
+
+ case DCMD:
+ pciwrite32(0, 0, 0, SB_HADR_REG, 0);
+ pciwrite32(0, 0, 0, SB_DATA_REG, data);
+ pciwrite32(0, 0, 0, SB_PACKET_REG,
+ SB_COMMAND(SB_DRAM_CMND_OPCODE, MCU, 0));
+ break;
+
+ default:
+ DEAD_LOOP()
+ ;
+ }
+}
+
+/****
+ *
+ ***/
+void WrMask32(
+ uint32_t unit,
+ uint32_t addr,
+ uint32_t data,
+ uint32_t mask)
+{
+ Wr32(unit, addr, ((Rd32(unit, addr) & ~mask) | (data & mask)));
+}
+
+/****
+ *
+ ***/
+void pciwrite32(
+ uint32_t bus,
+ uint32_t dev,
+ uint32_t fn,
+ uint32_t reg,
+ uint32_t data)
+{
+ Wr32(MMIO, PCIADDR(bus,dev,fn,reg), data);
+}
+
+/****
+ *
+ ***/
+uint32_t pciread32(
+ uint32_t bus,
+ uint32_t dev,
+ uint32_t fn,
+ uint32_t reg)
+{
+ return Rd32(MMIO, PCIADDR(bus,dev,fn,reg));
+}
+
diff --git a/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.c b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.c new file mode 100644 index 0000000000..f34eb4a6c1 --- /dev/null +++ b/QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei/prememinit.c @@ -0,0 +1,193 @@ +/************************************************************************
+ *
+ * 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 "prememinit.h"
+#include "io.h"
+
+// Read character from serial console
+uint8_t mgetc(void);
+
+extern uint32_t DpfPrintMask;
+
+// Adjust configuration parameters before initialisation
+// sequence.
+void PreMemInit(
+ MRCParams_t *mrc_params)
+{
+ const DRAMParams_t *dram_params;
+
+ uint8_t dram_width;
+ uint32_t dram_cfg_index;
+ uint32_t channel_i;
+
+ ENTERFN();
+
+#ifdef MRC_SV
+ {
+ uint8_t ch;
+
+ myloop:
+
+ DPF(D_INFO, "- c - continue\n");
+ DPF(D_INFO, "- f - boot mode [%d]\n", mrc_params->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_
|