2622 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			2622 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
/*
 | 
						|
 * Copyright (C) 2013, Intel Corporation
 | 
						|
 * Copyright (C) 2015, Bin Meng <bmeng.cn@gmail.com>
 | 
						|
 *
 | 
						|
 * Ported from Intel released Quark UEFI BIOS
 | 
						|
 * QuarkSocPkg/QuarkNorthCluster/MemoryInit/Pei
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier:	Intel
 | 
						|
 */
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <pci.h>
 | 
						|
#include <asm/arch/device.h>
 | 
						|
#include <asm/arch/mrc.h>
 | 
						|
#include <asm/arch/msg_port.h>
 | 
						|
#include "mrc_util.h"
 | 
						|
#include "hte.h"
 | 
						|
#include "smc.h"
 | 
						|
 | 
						|
/* t_rfc values (in picoseconds) per density */
 | 
						|
static const uint32_t t_rfc[5] = {
 | 
						|
	90000,	/* 512Mb */
 | 
						|
	110000,	/* 1Gb */
 | 
						|
	160000,	/* 2Gb */
 | 
						|
	300000,	/* 4Gb */
 | 
						|
	350000,	/* 8Gb */
 | 
						|
};
 | 
						|
 | 
						|
/* t_ck clock period in picoseconds per speed index 800, 1066, 1333 */
 | 
						|
static const uint32_t t_ck[3] = {
 | 
						|
	2500,
 | 
						|
	1875,
 | 
						|
	1500
 | 
						|
};
 | 
						|
 | 
						|
/* Global variables */
 | 
						|
static const uint16_t ddr_wclk[] = {193, 158};
 | 
						|
static const uint16_t ddr_wctl[] = {1, 217};
 | 
						|
static const uint16_t ddr_wcmd[] = {1, 220};
 | 
						|
 | 
						|
#ifdef BACKUP_RCVN
 | 
						|
static const uint16_t ddr_rcvn[] = {129, 498};
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BACKUP_WDQS
 | 
						|
static const uint16_t ddr_wdqs[] = {65, 289};
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BACKUP_RDQS
 | 
						|
static const uint8_t ddr_rdqs[] = {32, 24};
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BACKUP_WDQ
 | 
						|
static const uint16_t ddr_wdq[] = {32, 257};
 | 
						|
#endif
 | 
						|
 | 
						|
/* Stop self refresh driven by MCU */
 | 
						|
void clear_self_refresh(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* clear the PMSTS Channel Self Refresh bits */
 | 
						|
	mrc_write_mask(MEM_CTLR, PMSTS, PMSTS_DISR, PMSTS_DISR);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/* It will initialize timing registers in the MCU (DTR0..DTR4) */
 | 
						|
void prog_ddr_timing_control(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t tcl, wl;
 | 
						|
	uint8_t trp, trcd, tras, twr, twtr, trrd, trtp, tfaw;
 | 
						|
	uint32_t tck;
 | 
						|
	u32 dtr0, dtr1, dtr2, dtr3, dtr4;
 | 
						|
	u32 tmp1, tmp2;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* mcu_init starts */
 | 
						|
	mrc_post_code(0x02, 0x00);
 | 
						|
 | 
						|
	dtr0 = msg_port_read(MEM_CTLR, DTR0);
 | 
						|
	dtr1 = msg_port_read(MEM_CTLR, DTR1);
 | 
						|
	dtr2 = msg_port_read(MEM_CTLR, DTR2);
 | 
						|
	dtr3 = msg_port_read(MEM_CTLR, DTR3);
 | 
						|
	dtr4 = msg_port_read(MEM_CTLR, DTR4);
 | 
						|
 | 
						|
	tck = t_ck[mrc_params->ddr_speed];	/* Clock in picoseconds */
 | 
						|
	tcl = mrc_params->params.cl;		/* CAS latency in clocks */
 | 
						|
	trp = tcl;	/* Per CAT MRC */
 | 
						|
	trcd = tcl;	/* Per CAT MRC */
 | 
						|
	tras = MCEIL(mrc_params->params.ras, tck);
 | 
						|
 | 
						|
	/* Per JEDEC: tWR=15000ps DDR2/3 from 800-1600 */
 | 
						|
	twr = MCEIL(15000, tck);
 | 
						|
 | 
						|
	twtr = MCEIL(mrc_params->params.wtr, tck);
 | 
						|
	trrd = MCEIL(mrc_params->params.rrd, tck);
 | 
						|
	trtp = 4;	/* Valid for 800 and 1066, use 5 for 1333 */
 | 
						|
	tfaw = MCEIL(mrc_params->params.faw, tck);
 | 
						|
 | 
						|
	wl = 5 + mrc_params->ddr_speed;
 | 
						|
 | 
						|
	dtr0 &= ~DTR0_DFREQ_MASK;
 | 
						|
	dtr0 |= mrc_params->ddr_speed;
 | 
						|
	dtr0 &= ~DTR0_TCL_MASK;
 | 
						|
	tmp1 = tcl - 5;
 | 
						|
	dtr0 |= ((tcl - 5) << 12);
 | 
						|
	dtr0 &= ~DTR0_TRP_MASK;
 | 
						|
	dtr0 |= ((trp - 5) << 4);	/* 5 bit DRAM Clock */
 | 
						|
	dtr0 &= ~DTR0_TRCD_MASK;
 | 
						|
	dtr0 |= ((trcd - 5) << 8);	/* 5 bit DRAM Clock */
 | 
						|
 | 
						|
	dtr1 &= ~DTR1_TWCL_MASK;
 | 
						|
	tmp2 = wl - 3;
 | 
						|
	dtr1 |= (wl - 3);
 | 
						|
	dtr1 &= ~DTR1_TWTP_MASK;
 | 
						|
	dtr1 |= ((wl + 4 + twr - 14) << 8);	/* Change to tWTP */
 | 
						|
	dtr1 &= ~DTR1_TRTP_MASK;
 | 
						|
	dtr1 |= ((MMAX(trtp, 4) - 3) << 28);	/* 4 bit DRAM Clock */
 | 
						|
	dtr1 &= ~DTR1_TRRD_MASK;
 | 
						|
	dtr1 |= ((trrd - 4) << 24);		/* 4 bit DRAM Clock */
 | 
						|
	dtr1 &= ~DTR1_TCMD_MASK;
 | 
						|
	dtr1 |= (1 << 4);
 | 
						|
	dtr1 &= ~DTR1_TRAS_MASK;
 | 
						|
	dtr1 |= ((tras - 14) << 20);		/* 6 bit DRAM Clock */
 | 
						|
	dtr1 &= ~DTR1_TFAW_MASK;
 | 
						|
	dtr1 |= ((((tfaw + 1) >> 1) - 5) << 16);/* 4 bit DRAM Clock */
 | 
						|
	/* Set 4 Clock CAS to CAS delay (multi-burst) */
 | 
						|
	dtr1 &= ~DTR1_TCCD_MASK;
 | 
						|
 | 
						|
	dtr2 &= ~DTR2_TRRDR_MASK;
 | 
						|
	dtr2 |= 1;
 | 
						|
	dtr2 &= ~DTR2_TWWDR_MASK;
 | 
						|
	dtr2 |= (2 << 8);
 | 
						|
	dtr2 &= ~DTR2_TRWDR_MASK;
 | 
						|
	dtr2 |= (2 << 16);
 | 
						|
 | 
						|
	dtr3 &= ~DTR3_TWRDR_MASK;
 | 
						|
	dtr3 |= 2;
 | 
						|
	dtr3 &= ~DTR3_TXXXX_MASK;
 | 
						|
	dtr3 |= (2 << 4);
 | 
						|
 | 
						|
	dtr3 &= ~DTR3_TRWSR_MASK;
 | 
						|
	if (mrc_params->ddr_speed == DDRFREQ_800) {
 | 
						|
		/* Extended RW delay (+1) */
 | 
						|
		dtr3 |= ((tcl - 5 + 1) << 8);
 | 
						|
	} else if (mrc_params->ddr_speed == DDRFREQ_1066) {
 | 
						|
		/* Extended RW delay (+1) */
 | 
						|
		dtr3 |= ((tcl - 5 + 1) << 8);
 | 
						|
	}
 | 
						|
 | 
						|
	dtr3 &= ~DTR3_TWRSR_MASK;
 | 
						|
	dtr3 |= ((4 + wl + twtr - 11) << 13);
 | 
						|
 | 
						|
	dtr3 &= ~DTR3_TXP_MASK;
 | 
						|
	if (mrc_params->ddr_speed == DDRFREQ_800)
 | 
						|
		dtr3 |= ((MMAX(0, 1 - 1)) << 22);
 | 
						|
	else
 | 
						|
		dtr3 |= ((MMAX(0, 2 - 1)) << 22);
 | 
						|
 | 
						|
	dtr4 &= ~DTR4_WRODTSTRT_MASK;
 | 
						|
	dtr4 |= 1;
 | 
						|
	dtr4 &= ~DTR4_WRODTSTOP_MASK;
 | 
						|
	dtr4 |= (1 << 4);
 | 
						|
	dtr4 &= ~DTR4_XXXX1_MASK;
 | 
						|
	dtr4 |= ((1 + tmp1 - tmp2 + 2) << 8);
 | 
						|
	dtr4 &= ~DTR4_XXXX2_MASK;
 | 
						|
	dtr4 |= ((1 + tmp1 - tmp2 + 2) << 12);
 | 
						|
	dtr4 &= ~(DTR4_ODTDIS | DTR4_TRGSTRDIS);
 | 
						|
 | 
						|
	msg_port_write(MEM_CTLR, DTR0, dtr0);
 | 
						|
	msg_port_write(MEM_CTLR, DTR1, dtr1);
 | 
						|
	msg_port_write(MEM_CTLR, DTR2, dtr2);
 | 
						|
	msg_port_write(MEM_CTLR, DTR3, dtr3);
 | 
						|
	msg_port_write(MEM_CTLR, DTR4, dtr4);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/* Configure MCU before jedec init sequence */
 | 
						|
void prog_decode_before_jedec(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 drp;
 | 
						|
	u32 drfc;
 | 
						|
	u32 dcal;
 | 
						|
	u32 dsch;
 | 
						|
	u32 dpmc0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* Disable power saving features */
 | 
						|
	dpmc0 = msg_port_read(MEM_CTLR, DPMC0);
 | 
						|
	dpmc0 |= (DPMC0_CLKGTDIS | DPMC0_DISPWRDN);
 | 
						|
	dpmc0 &= ~DPMC0_PCLSTO_MASK;
 | 
						|
	dpmc0 &= ~DPMC0_DYNSREN;
 | 
						|
	msg_port_write(MEM_CTLR, DPMC0, dpmc0);
 | 
						|
 | 
						|
	/* Disable out of order transactions */
 | 
						|
	dsch = msg_port_read(MEM_CTLR, DSCH);
 | 
						|
	dsch |= (DSCH_OOODIS | DSCH_NEWBYPDIS);
 | 
						|
	msg_port_write(MEM_CTLR, DSCH, dsch);
 | 
						|
 | 
						|
	/* Disable issuing the REF command */
 | 
						|
	drfc = msg_port_read(MEM_CTLR, DRFC);
 | 
						|
	drfc &= ~DRFC_TREFI_MASK;
 | 
						|
	msg_port_write(MEM_CTLR, DRFC, drfc);
 | 
						|
 | 
						|
	/* Disable ZQ calibration short */
 | 
						|
	dcal = msg_port_read(MEM_CTLR, DCAL);
 | 
						|
	dcal &= ~DCAL_ZQCINT_MASK;
 | 
						|
	dcal &= ~DCAL_SRXZQCL_MASK;
 | 
						|
	msg_port_write(MEM_CTLR, DCAL, dcal);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Training performed in address mode 0, rank population has limited
 | 
						|
	 * impact, however simulator complains if enabled non-existing rank.
 | 
						|
	 */
 | 
						|
	drp = 0;
 | 
						|
	if (mrc_params->rank_enables & 1)
 | 
						|
		drp |= DRP_RKEN0;
 | 
						|
	if (mrc_params->rank_enables & 2)
 | 
						|
		drp |= DRP_RKEN1;
 | 
						|
	msg_port_write(MEM_CTLR, DRP, drp);
 | 
						|
 | 
						|
	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.
 | 
						|
 */
 | 
						|
void perform_ddr_reset(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* Set COLDWAKE bit before sending the WAKE message */
 | 
						|
	mrc_write_mask(MEM_CTLR, DRMC, DRMC_COLDWAKE, DRMC_COLDWAKE);
 | 
						|
 | 
						|
	/* Send wake command to DUNIT (MUST be done before JEDEC) */
 | 
						|
	dram_wake_command();
 | 
						|
 | 
						|
	/* Set default value */
 | 
						|
	msg_port_write(MEM_CTLR, DRMC,
 | 
						|
		       mrc_params->rd_odt_value == 0 ? DRMC_ODTMODE : 0);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * This function performs some initialization on the DDRIO unit.
 | 
						|
 * This function is dependent on BOARD_ID, DDR_SPEED, and CHANNEL_ENABLES.
 | 
						|
 */
 | 
						|
void ddrphy_init(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint32_t temp;
 | 
						|
	uint8_t ch;	/* channel counter */
 | 
						|
	uint8_t rk;	/* rank counter */
 | 
						|
	uint8_t bl_grp;	/*  byte lane group counter (2 BLs per module) */
 | 
						|
	uint8_t bl_divisor = 1;	/* byte lane divisor */
 | 
						|
	/* For DDR3 --> 0 == 800, 1 == 1066, 2 == 1333 */
 | 
						|
	uint8_t speed = mrc_params->ddr_speed & 3;
 | 
						|
	uint8_t cas;
 | 
						|
	uint8_t cwl;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	cas = mrc_params->params.cl;
 | 
						|
	cwl = 5 + mrc_params->ddr_speed;
 | 
						|
 | 
						|
	/* ddrphy_init starts */
 | 
						|
	mrc_post_code(0x03, 0x00);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * HSD#231531
 | 
						|
	 * Make sure IOBUFACT is deasserted before initializing the DDR PHY
 | 
						|
	 *
 | 
						|
	 * HSD#234845
 | 
						|
	 * Make sure WRPTRENABLE is deasserted before initializing the DDR PHY
 | 
						|
	 */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* Deassert DDRPHY Initialization Complete */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				~(1 << 20), 1 << 20);	/* SPID_INIT_COMPLETE=0 */
 | 
						|
			/* Deassert IOBUFACT */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				~(1 << 2), 1 << 2);	/* IOBUFACTRST_N=0 */
 | 
						|
			/* Disable WRPTR */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPTRREG + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				~(1 << 0), 1 << 0);	/* WRPTRENABLE=0 */
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* Put PHY in reset */
 | 
						|
	mrc_alt_write_mask(DDRPHY, MASTERRSTN, 0, 1);
 | 
						|
 | 
						|
	/* Initialize DQ01, DQ23, CMD, CLK-CTL, COMP modules */
 | 
						|
 | 
						|
	/* STEP0 */
 | 
						|
	mrc_post_code(0x03, 0x10);
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* DQ01-DQ23 */
 | 
						|
			for (bl_grp = 0;
 | 
						|
			     bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2;
 | 
						|
			     bl_grp++) {
 | 
						|
				/* Analog MUX select - IO2xCLKSEL */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQOBSCKEBBCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					bl_grp ? 0 : (1 << 22), 1 << 22);
 | 
						|
 | 
						|
				/* ODT Strength */
 | 
						|
				switch (mrc_params->rd_odt_value) {
 | 
						|
				case 1:
 | 
						|
					temp = 0x3;
 | 
						|
					break;	/* 60 ohm */
 | 
						|
				case 2:
 | 
						|
					temp = 0x3;
 | 
						|
					break;	/* 120 ohm */
 | 
						|
				case 3:
 | 
						|
					temp = 0x3;
 | 
						|
					break;	/* 180 ohm */
 | 
						|
				default:
 | 
						|
					temp = 0x3;
 | 
						|
					break;	/* 120 ohm */
 | 
						|
				}
 | 
						|
 | 
						|
				/* ODT strength */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp << 5, 0x60);
 | 
						|
				/* ODT strength */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp << 5, 0x60);
 | 
						|
 | 
						|
				/* Dynamic ODT/DIFFAMP */
 | 
						|
				temp = (cas << 24) | (cas << 16) |
 | 
						|
					(cas << 8) | (cas << 0);
 | 
						|
				switch (speed) {
 | 
						|
				case 0:
 | 
						|
					temp -= 0x01010101;
 | 
						|
					break;	/* 800 */
 | 
						|
				case 1:
 | 
						|
					temp -= 0x02020202;
 | 
						|
					break;	/* 1066 */
 | 
						|
				case 2:
 | 
						|
					temp -= 0x03030303;
 | 
						|
					break;	/* 1333 */
 | 
						|
				case 3:
 | 
						|
					temp -= 0x04040404;
 | 
						|
					break;	/* 1600 */
 | 
						|
				}
 | 
						|
 | 
						|
				/* Launch Time: ODT, DIFFAMP, ODT, DIFFAMP */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B01LATCTL1 +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0x1f1f1f1f);
 | 
						|
				switch (speed) {
 | 
						|
				/* HSD#234715 */
 | 
						|
				case 0:
 | 
						|
					temp = (0x06 << 16) | (0x07 << 8);
 | 
						|
					break;	/* 800 */
 | 
						|
				case 1:
 | 
						|
					temp = (0x07 << 16) | (0x08 << 8);
 | 
						|
					break;	/* 1066 */
 | 
						|
				case 2:
 | 
						|
					temp = (0x09 << 16) | (0x0a << 8);
 | 
						|
					break;	/* 1333 */
 | 
						|
				case 3:
 | 
						|
					temp = (0x0a << 16) | (0x0b << 8);
 | 
						|
					break;	/* 1600 */
 | 
						|
				}
 | 
						|
 | 
						|
				/* On Duration: ODT, DIFFAMP */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0ONDURCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0x003f3f00);
 | 
						|
				/* On Duration: ODT, DIFFAMP */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1ONDURCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0x003f3f00);
 | 
						|
 | 
						|
				switch (mrc_params->rd_odt_value) {
 | 
						|
				case 0:
 | 
						|
					/* override DIFFAMP=on, ODT=off */
 | 
						|
					temp = (0x3f << 16) | (0x3f << 10);
 | 
						|
					break;
 | 
						|
				default:
 | 
						|
					/* override DIFFAMP=on, ODT=on */
 | 
						|
					temp = (0x3f << 16) | (0x2a << 10);
 | 
						|
					break;
 | 
						|
				}
 | 
						|
 | 
						|
				/* Override: DIFFAMP, ODT */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0OVRCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0x003ffc00);
 | 
						|
				/* Override: DIFFAMP, ODT */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1OVRCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0x003ffc00);
 | 
						|
 | 
						|
				/* DLL Setup */
 | 
						|
 | 
						|
				/* 1xCLK Domain Timings: tEDP,RCVEN,WDQS (PO) */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0LATCTL0 +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					((cas + 7) << 16) | ((cas - 4) << 8) |
 | 
						|
					((cwl - 2) << 0), 0x003f1f1f);
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1LATCTL0 +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					((cas + 7) << 16) | ((cas - 4) << 8) |
 | 
						|
					((cwl - 2) << 0), 0x003f1f1f);
 | 
						|
 | 
						|
				/* RCVEN Bypass (PO) */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0, 0x81);
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0, 0x81);
 | 
						|
 | 
						|
				/* TX */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					1 << 16, 1 << 16);
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B01PTRCTL1 +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					1 << 8, 1 << 8);
 | 
						|
 | 
						|
				/* RX (PO) */
 | 
						|
				/* Internal Vref Code, Enable#, Ext_or_Int (1=Ext) */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0VREFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					(0x03 << 2) | (0x0 << 1) | (0x0 << 0),
 | 
						|
					0xff);
 | 
						|
				/* Internal Vref Code, Enable#, Ext_or_Int (1=Ext) */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1VREFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					(0x03 << 2) | (0x0 << 1) | (0x0 << 0),
 | 
						|
					0xff);
 | 
						|
				/* Per-Bit De-Skew Enable */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0, 0x10);
 | 
						|
				/* Per-Bit De-Skew Enable */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1RXIOBUFCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0, 0x10);
 | 
						|
			}
 | 
						|
 | 
						|
			/* CLKEBB */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDOBSCKEBBCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0, 1 << 23);
 | 
						|
 | 
						|
			/* Enable tristate control of cmd/address bus */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0, 0x03);
 | 
						|
 | 
						|
			/* ODT RCOMP */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDRCOMPODT + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x03 << 5) | (0x03 << 0), 0x3ff);
 | 
						|
 | 
						|
			/* CMDPM* registers must be programmed in this order */
 | 
						|
 | 
						|
			/* Turn On Delays: SFR (regulator), MPLL */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMDLYREG4 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0xffffffff, 0xffffffff);
 | 
						|
			/*
 | 
						|
			 * Delays: ASSERT_IOBUFACT_to_ALLON0_for_PM_MSG_3,
 | 
						|
			 * VREG (MDLL) Turn On, ALLON0_to_DEASSERT_IOBUFACT
 | 
						|
			 * for_PM_MSG_gt0, MDLL Turn On
 | 
						|
			 */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMDLYREG3 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0xfffff616, 0xffffffff);
 | 
						|
			/* MPLL Divider Reset Delays */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMDLYREG2 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0xffffffff, 0xffffffff);
 | 
						|
			/* Turn Off Delays: VREG, Staggered MDLL, MDLL, PI */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMDLYREG1 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0xffffffff, 0xffffffff);
 | 
						|
			/* Turn On Delays: MPLL, Staggered MDLL, PI, IOBUFACT */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMDLYREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0xffffffff, 0xffffffff);
 | 
						|
			/* Allow PUnit signals */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x6 << 8) | (0x1 << 6) | (0x4 << 0),
 | 
						|
				0xffe00f4f);
 | 
						|
			/* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x3 << 4) | (0x7 << 0), 0x7f);
 | 
						|
 | 
						|
			/* CLK-CTL */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCOBSCKEBBCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0, 1 << 24);	/* CLKEBB */
 | 
						|
			/* Buffer Enable: CS,CKE,ODT,CLK */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCCFGREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0x1f, 0x000ffff1);
 | 
						|
			/* ODT RCOMP */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCRCOMPODT + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x03 << 8) | (0x03 << 0), 0x00001f1f);
 | 
						|
			/* DLL_VREG Bias Trim, VREF Tuning for DLL_VREG */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x3 << 4) | (0x7 << 0), 0x7f);
 | 
						|
 | 
						|
			/*
 | 
						|
			 * COMP (RON channel specific)
 | 
						|
			 * - DQ/DQS/DM RON: 32 Ohm
 | 
						|
			 * - CTRL/CMD RON: 27 Ohm
 | 
						|
			 * - CLK RON: 26 Ohm
 | 
						|
			 */
 | 
						|
			/* RCOMP Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQVREFCH0 +  ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x08 << 24) | (0x03 << 16), 0x3f3f0000);
 | 
						|
			/* RCOMP Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x0C << 24) | (0x03 << 16), 0x3f3f0000);
 | 
						|
			/* RCOMP Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x0F << 24) | (0x03 << 16), 0x3f3f0000);
 | 
						|
			/* RCOMP Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x08 << 24) | (0x03 << 16), 0x3f3f0000);
 | 
						|
			/* RCOMP Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CTLVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x0C << 24) | (0x03 << 16), 0x3f3f0000);
 | 
						|
 | 
						|
			/* DQS Swapped Input Enable */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				COMPEN1CH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 19) | (1 << 17), 0xc00ac000);
 | 
						|
 | 
						|
			/* ODT VREF = 1.5 x 274/360+274 = 0.65V (code of ~50) */
 | 
						|
			/* ODT Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x32 << 8) | (0x03 << 0), 0x00003f3f);
 | 
						|
			/* ODT Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x32 << 8) | (0x03 << 0), 0x00003f3f);
 | 
						|
			/* ODT Vref PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x0E << 8) | (0x05 << 0), 0x00003f3f);
 | 
						|
 | 
						|
			/*
 | 
						|
			 * 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
 | 
						|
			 */
 | 
						|
			temp = (0x0e << 16) | (0x0e << 12) | (0x08 << 8) |
 | 
						|
				(0x0b << 4) | (0x0b << 0);
 | 
						|
			/* DCOMP Delay Select: CTL,CMD,CLK,DQS,DQ */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DLYSELCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				temp, 0x000fffff);
 | 
						|
			/* TCO Vref CLK,DQS,DQ */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				TCOVREFCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x05 << 16) | (0x05 << 8) | (0x05 << 0),
 | 
						|
				0x003f3f3f);
 | 
						|
			/* ODTCOMP CMD/CTL PU/PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCBUFODTCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(0x03 << 8) | (0x03 << 0),
 | 
						|
				0x00001f1f);
 | 
						|
			/* COMP */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				0, 0xc0000100);
 | 
						|
 | 
						|
#ifdef BACKUP_COMPS
 | 
						|
			/* DQ COMP Overrides */
 | 
						|
			/* RCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* RCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x10 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x10 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
 | 
						|
			/* DQS COMP Overrides */
 | 
						|
			/* RCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* RCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x10 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x10 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
 | 
						|
			/* CLK COMP Overrides */
 | 
						|
			/* RCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0c << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* RCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0c << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x07 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x07 << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKODTPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* ODTCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKODTPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0b << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 31, 1 << 31);
 | 
						|
 | 
						|
			/* CMD COMP Overrides */
 | 
						|
			/* RCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0d << 16),
 | 
						|
				0x803f0000);
 | 
						|
			/* RCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0d << 16),
 | 
						|
				0x803f0000);
 | 
						|
			/* DCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
 | 
						|
			/* CTL COMP Overrides */
 | 
						|
			/* RCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CTLDRVPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0d << 16),
 | 
						|
				0x803f0000);
 | 
						|
			/* RCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CTLDRVPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0d << 16),
 | 
						|
				0x803f0000);
 | 
						|
			/* DCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CTLDLYPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* DCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CTLDLYPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x0a << 16),
 | 
						|
				0x801f0000);
 | 
						|
#else
 | 
						|
			/* DQ TCOCOMP Overrides */
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
 | 
						|
			/* DQS TCOCOMP Overrides */
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				DQSTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
 | 
						|
			/* CLK TCOCOMP Overrides */
 | 
						|
			/* TCOCOMP PU */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKTCOPUCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
			/* TCOCOMP PD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CLKTCOPDCTLCH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				(1 << 31) | (0x1f << 16),
 | 
						|
				0x801f0000);
 | 
						|
#endif
 | 
						|
 | 
						|
			/* program STATIC delays */
 | 
						|
#ifdef BACKUP_WCMD
 | 
						|
			set_wcmd(ch, ddr_wcmd[PLATFORM_ID]);
 | 
						|
#else
 | 
						|
			set_wcmd(ch, ddr_wclk[PLATFORM_ID] + HALF_CLK);
 | 
						|
#endif
 | 
						|
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					set_wclk(ch, rk, ddr_wclk[PLATFORM_ID]);
 | 
						|
#ifdef BACKUP_WCTL
 | 
						|
					set_wctl(ch, rk, ddr_wctl[PLATFORM_ID]);
 | 
						|
#else
 | 
						|
					set_wctl(ch, rk, ddr_wclk[PLATFORM_ID] + HALF_CLK);
 | 
						|
#endif
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* COMP (non channel specific) */
 | 
						|
	/* RCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANADRVPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANADRVPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CMDANADRVPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CMDANADRVPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANADRVPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANADRVPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANADRVPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANADRVPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CTLANADRVPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* RCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CTLANADRVPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANAODTPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANAODTPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANAODTPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANAODTPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANAODTPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* ODT: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANAODTPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANADLYPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANADLYPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CMDANADLYPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CMDANADLYPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANADLYPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANADLYPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANADLYPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANADLYPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CTLANADLYPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* DCOMP: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CTLANADLYPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANATCOPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQANATCOPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANATCOPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CLKANATCOPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PU Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANATCOPUCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCO: Dither PD Enable */
 | 
						|
	mrc_alt_write_mask(DDRPHY, DQSANATCOPDCTL, 1 << 30, 1 << 30);
 | 
						|
	/* TCOCOMP: Pulse Count */
 | 
						|
	mrc_alt_write_mask(DDRPHY, TCOCNTCTRL, 1, 3);
 | 
						|
	/* ODT: CMD/CTL PD/PU */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CHNLBUFSTATIC,
 | 
						|
		(0x03 << 24) | (0x03 << 16), 0x1f1f0000);
 | 
						|
	/* Set 1us counter */
 | 
						|
	mrc_alt_write_mask(DDRPHY, MSCNTR, 0x64, 0xff);
 | 
						|
	mrc_alt_write_mask(DDRPHY, LATCH1CTL, 0x1 << 28, 0x70000000);
 | 
						|
 | 
						|
	/* Release PHY from reset */
 | 
						|
	mrc_alt_write_mask(DDRPHY, MASTERRSTN, 1, 1);
 | 
						|
 | 
						|
	/* STEP1 */
 | 
						|
	mrc_post_code(0x03, 0x11);
 | 
						|
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* DQ01-DQ23 */
 | 
						|
			for (bl_grp = 0;
 | 
						|
			     bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2;
 | 
						|
			     bl_grp++) {
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQMDLLCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					1 << 13,
 | 
						|
					1 << 13);	/* Enable VREG */
 | 
						|
				delay_n(3);
 | 
						|
			}
 | 
						|
 | 
						|
			/* ECC */
 | 
						|
			mrc_alt_write_mask(DDRPHY, ECCMDLLCTL,
 | 
						|
				1 << 13, 1 << 13);	/* Enable VREG */
 | 
						|
			delay_n(3);
 | 
						|
			/* CMD */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1 << 13, 1 << 13);	/* Enable VREG */
 | 
						|
			delay_n(3);
 | 
						|
			/* CLK-CTL */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1 << 13, 1 << 13);	/* Enable VREG */
 | 
						|
			delay_n(3);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* STEP2 */
 | 
						|
	mrc_post_code(0x03, 0x12);
 | 
						|
	delay_n(200);
 | 
						|
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* DQ01-DQ23 */
 | 
						|
			for (bl_grp = 0;
 | 
						|
			     bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2;
 | 
						|
			     bl_grp++) {
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQMDLLCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					1 << 17,
 | 
						|
					1 << 17);	/* Enable MCDLL */
 | 
						|
				delay_n(50);
 | 
						|
			}
 | 
						|
 | 
						|
		/* ECC */
 | 
						|
		mrc_alt_write_mask(DDRPHY, ECCMDLLCTL,
 | 
						|
			1 << 17, 1 << 17);	/* Enable MCDLL */
 | 
						|
		delay_n(50);
 | 
						|
		/* CMD */
 | 
						|
		mrc_alt_write_mask(DDRPHY,
 | 
						|
			CMDMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
			1 << 18, 1 << 18);	/* Enable MCDLL */
 | 
						|
		delay_n(50);
 | 
						|
		/* CLK-CTL */
 | 
						|
		mrc_alt_write_mask(DDRPHY,
 | 
						|
			CCMDLLCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
			1 << 18, 1 << 18);	/* Enable MCDLL */
 | 
						|
		delay_n(50);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* STEP3: */
 | 
						|
	mrc_post_code(0x03, 0x13);
 | 
						|
	delay_n(100);
 | 
						|
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* DQ01-DQ23 */
 | 
						|
			for (bl_grp = 0;
 | 
						|
			     bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2;
 | 
						|
			     bl_grp++) {
 | 
						|
#ifdef FORCE_16BIT_DDRIO
 | 
						|
				temp = (bl_grp &&
 | 
						|
					(mrc_params->channel_width == X16)) ?
 | 
						|
					0x11ff : 0xffff;
 | 
						|
#else
 | 
						|
				temp = 0xffff;
 | 
						|
#endif
 | 
						|
				/* Enable TXDLL */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQDLLTXCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					temp, 0xffff);
 | 
						|
				delay_n(3);
 | 
						|
				/* Enable RXDLL */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQDLLRXCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0xf, 0xf);
 | 
						|
				delay_n(3);
 | 
						|
				/* Enable RXDLL Overrides BL0 */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0OVRCTL +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					0xf, 0xf);
 | 
						|
			}
 | 
						|
 | 
						|
			/* ECC */
 | 
						|
			temp = 0xffff;
 | 
						|
			mrc_alt_write_mask(DDRPHY, ECCDLLTXCTL,
 | 
						|
				temp, 0xffff);
 | 
						|
			delay_n(3);
 | 
						|
 | 
						|
			/* CMD (PO) */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDDLLTXCTL + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				temp, 0xffff);
 | 
						|
			delay_n(3);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* STEP4 */
 | 
						|
	mrc_post_code(0x03, 0x14);
 | 
						|
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* Host To Memory Clock Alignment (HMC) for 800/1066 */
 | 
						|
			for (bl_grp = 0;
 | 
						|
			     bl_grp < (NUM_BYTE_LANES / bl_divisor) / 2;
 | 
						|
			     bl_grp++) {
 | 
						|
				/* CLK_ALIGN_MOD_ID */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					DQCLKALIGNREG2 +
 | 
						|
					bl_grp * DDRIODQ_BL_OFFSET +
 | 
						|
					ch * DDRIODQ_CH_OFFSET,
 | 
						|
					bl_grp ? 3 : 1,
 | 
						|
					0xf);
 | 
						|
			}
 | 
						|
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				ECCCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET,
 | 
						|
				0x2, 0xf);
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET,
 | 
						|
				0x0, 0xf);
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CCCLKALIGNREG2 + ch * DDRIODQ_CH_OFFSET,
 | 
						|
				0x2, 0xf);
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				0x20, 0x30);
 | 
						|
			/*
 | 
						|
			 * NUM_SAMPLES, MAX_SAMPLES,
 | 
						|
			 * MACRO_PI_STEP, MICRO_PI_STEP
 | 
						|
			 */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCLKALIGNREG1 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x18 << 16) | (0x10 << 8) |
 | 
						|
				(0x8 << 2) | (0x1 << 0),
 | 
						|
				0x007f7fff);
 | 
						|
			/* TOTAL_NUM_MODULES, FIRST_U_PARTITION */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCLKALIGNREG2 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				(0x10 << 16) | (0x4 << 8) | (0x2 << 4),
 | 
						|
				0x001f0ff0);
 | 
						|
#ifdef HMC_TEST
 | 
						|
			/* START_CLK_ALIGN=1 */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1 << 24, 1 << 24);
 | 
						|
			while (msg_port_alt_read(DDRPHY,
 | 
						|
				CMDCLKALIGNREG0 + ch * DDRIOCCC_CH_OFFSET) &
 | 
						|
				(1 << 24))
 | 
						|
				;	/* wait for START_CLK_ALIGN=0 */
 | 
						|
#endif
 | 
						|
 | 
						|
			/* Set RD/WR Pointer Seperation & COUNTEN & FIFOPTREN */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPTRREG + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1, 1);	/* WRPTRENABLE=1 */
 | 
						|
 | 
						|
			/* COMP initial */
 | 
						|
			/* enable bypass for CLK buffer (PO) */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				1 << 5, 1 << 5);
 | 
						|
			/* Initial COMP Enable */
 | 
						|
			mrc_alt_write_mask(DDRPHY, CMPCTRL, 1, 1);
 | 
						|
			/* wait for Initial COMP Enable = 0 */
 | 
						|
			while (msg_port_alt_read(DDRPHY, CMPCTRL) & 1)
 | 
						|
				;
 | 
						|
			/* disable bypass for CLK buffer (PO) */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				COMPEN0CH0 + ch * DDRCOMP_CH_OFFSET,
 | 
						|
				~(1 << 5), 1 << 5);
 | 
						|
 | 
						|
			/* IOBUFACT */
 | 
						|
 | 
						|
			/* STEP4a */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDCFGREG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1 << 2, 1 << 2);	/* IOBUFACTRST_N=1 */
 | 
						|
 | 
						|
			/* DDRPHY initialization complete */
 | 
						|
			mrc_alt_write_mask(DDRPHY,
 | 
						|
				CMDPMCONFIG0 + ch * DDRIOCCC_CH_OFFSET,
 | 
						|
				1 << 20, 1 << 20);	/* SPID_INIT_COMPLETE=1 */
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/* This function performs JEDEC initialization on all enabled channels */
 | 
						|
void perform_jedec_init(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t twr, wl, rank;
 | 
						|
	uint32_t tck;
 | 
						|
	u32 dtr0;
 | 
						|
	u32 drp;
 | 
						|
	u32 drmc;
 | 
						|
	u32 mrs0_cmd = 0;
 | 
						|
	u32 emrs1_cmd = 0;
 | 
						|
	u32 emrs2_cmd = 0;
 | 
						|
	u32 emrs3_cmd = 0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* jedec_init starts */
 | 
						|
	mrc_post_code(0x04, 0x00);
 | 
						|
 | 
						|
	/* DDR3_RESET_SET=0, DDR3_RESET_RESET=1 */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CCDDR3RESETCTL, 2, 0x102);
 | 
						|
 | 
						|
	/* Assert RESET# for 200us */
 | 
						|
	delay_u(200);
 | 
						|
 | 
						|
	/* DDR3_RESET_SET=1, DDR3_RESET_RESET=0 */
 | 
						|
	mrc_alt_write_mask(DDRPHY, CCDDR3RESETCTL, 0x100, 0x102);
 | 
						|
 | 
						|
	dtr0 = msg_port_read(MEM_CTLR, DTR0);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Set CKEVAL for populated ranks
 | 
						|
	 * then send NOP to each rank (#4550197)
 | 
						|
	 */
 | 
						|
 | 
						|
	drp = msg_port_read(MEM_CTLR, DRP);
 | 
						|
	drp &= 0x3;
 | 
						|
 | 
						|
	drmc = msg_port_read(MEM_CTLR, DRMC);
 | 
						|
	drmc &= 0xfffffffc;
 | 
						|
	drmc |= (DRMC_CKEMODE | drp);
 | 
						|
 | 
						|
	msg_port_write(MEM_CTLR, DRMC, drmc);
 | 
						|
 | 
						|
	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));
 | 
						|
	}
 | 
						|
 | 
						|
	msg_port_write(MEM_CTLR, DRMC,
 | 
						|
		(mrc_params->rd_odt_value == 0 ? DRMC_ODTMODE : 0));
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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)
 | 
						|
	 */
 | 
						|
	emrs2_cmd |= (2 << 3);
 | 
						|
	wl = 5 + mrc_params->ddr_speed;
 | 
						|
	emrs2_cmd |= ((wl - 5) << 9);
 | 
						|
	emrs2_cmd |= (mrc_params->sr_temp_range << 13);
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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)
 | 
						|
	 */
 | 
						|
	emrs3_cmd |= (3 << 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
 | 
						|
	 */
 | 
						|
	emrs1_cmd |= (1 << 3);
 | 
						|
	emrs1_cmd &= ~(1 << 6);
 | 
						|
 | 
						|
	if (mrc_params->ron_value == 0)
 | 
						|
		emrs1_cmd |= (1 << 7);
 | 
						|
	else
 | 
						|
		emrs1_cmd &= ~(1 << 7);
 | 
						|
 | 
						|
	if (mrc_params->rtt_nom_value == 0)
 | 
						|
		emrs1_cmd |= (DDR3_EMRS1_RTTNOM_40 << 6);
 | 
						|
	else if (mrc_params->rtt_nom_value == 1)
 | 
						|
		emrs1_cmd |= (DDR3_EMRS1_RTTNOM_60 << 6);
 | 
						|
	else if (mrc_params->rtt_nom_value == 2)
 | 
						|
		emrs1_cmd |= (DDR3_EMRS1_RTTNOM_120 << 6);
 | 
						|
 | 
						|
	/* save MRS1 value (excluding control fields) */
 | 
						|
	mrc_params->mrs1 = emrs1_cmd >> 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
 | 
						|
	 */
 | 
						|
	mrs0_cmd |= (1 << 14);
 | 
						|
	mrs0_cmd |= (1 << 18);
 | 
						|
	mrs0_cmd |= ((((dtr0 >> 12) & 7) + 1) << 10);
 | 
						|
 | 
						|
	tck = t_ck[mrc_params->ddr_speed];
 | 
						|
	/* Per JEDEC: tWR=15000ps DDR2/3 from 800-1600 */
 | 
						|
	twr = MCEIL(15000, tck);
 | 
						|
	mrs0_cmd |= ((twr - 4) << 15);
 | 
						|
 | 
						|
	for (rank = 0; rank < NUM_RANKS; rank++) {
 | 
						|
		/* Skip to next populated rank */
 | 
						|
		if ((mrc_params->rank_enables & (1 << rank)) == 0)
 | 
						|
			continue;
 | 
						|
 | 
						|
		emrs2_cmd |= (rank << 22);
 | 
						|
		dram_init_command(emrs2_cmd);
 | 
						|
 | 
						|
		emrs3_cmd |= (rank << 22);
 | 
						|
		dram_init_command(emrs3_cmd);
 | 
						|
 | 
						|
		emrs1_cmd |= (rank << 22);
 | 
						|
		dram_init_command(emrs1_cmd);
 | 
						|
 | 
						|
		mrs0_cmd |= (rank << 22);
 | 
						|
		dram_init_command(mrs0_cmd);
 | 
						|
 | 
						|
		dram_init_command(DCMD_ZQCL(rank));
 | 
						|
	}
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Dunit Initialization Complete
 | 
						|
 *
 | 
						|
 * Indicates that initialization 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.
 | 
						|
 */
 | 
						|
void set_ddr_init_complete(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 dco;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dco = msg_port_read(MEM_CTLR, DCO);
 | 
						|
	dco &= ~DCO_PMICTL;
 | 
						|
	dco |= DCO_IC;
 | 
						|
	msg_port_write(MEM_CTLR, DCO, dco);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch, rk, bl;
 | 
						|
	const struct mrc_timings *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]);
 | 
						|
				set_rdqs(ch, rk, bl, mt->rdqs[ch][rk][bl]);
 | 
						|
				set_wdqs(ch, rk, bl, mt->wdqs[ch][rk][bl]);
 | 
						|
				set_wdq(ch, rk, bl, mt->wdq[ch][rk][bl]);
 | 
						|
				if (rk == 0) {
 | 
						|
					/* VREF (RANK0 only) */
 | 
						|
					set_vref(ch, bl, mt->vref[ch][bl]);
 | 
						|
				}
 | 
						|
			}
 | 
						|
			set_wctl(ch, rk, mt->wctl[ch][rk]);
 | 
						|
		}
 | 
						|
		set_wcmd(ch, mt->wcmd[ch]);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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(struct mrc_params *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);
 | 
						|
				if (rk == 0) {
 | 
						|
					/* VREF (RANK0 only) */
 | 
						|
					set_vref(ch, bl, 32);
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
void rcvn_cal(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch;	/* channel counter */
 | 
						|
	uint8_t rk;	/* rank counter */
 | 
						|
	uint8_t bl;	/* byte lane counter */
 | 
						|
	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
#ifndef BACKUP_RCVN
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t num_ranks_enabled = 0;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BACKUP_RCVN
 | 
						|
#else
 | 
						|
	uint32_t temp;
 | 
						|
	/* absolute PI value to be programmed on the byte lane */
 | 
						|
	uint32_t delay[NUM_BYTE_LANES];
 | 
						|
	u32 dtr1, dtr1_save;
 | 
						|
#endif
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* rcvn_cal starts */
 | 
						|
	mrc_post_code(0x05, 0x00);
 | 
						|
 | 
						|
#ifndef BACKUP_RCVN
 | 
						|
	/* need separate burst to sample DQS preamble */
 | 
						|
	dtr1 = msg_port_read(MEM_CTLR, DTR1);
 | 
						|
	dtr1_save = dtr1;
 | 
						|
	dtr1 |= DTR1_TCCD_12CLK;
 | 
						|
	msg_port_write(MEM_CTLR, DTR1, dtr1);
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
	/* need to set "final_delay[][]" elements to "0" */
 | 
						|
	memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay));
 | 
						|
#endif
 | 
						|
 | 
						|
	/* loop through each enabled channel */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* perform RCVEN Calibration on a per rank basis */
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					/*
 | 
						|
					 * POST_CODE here indicates the current
 | 
						|
					 * channel and rank being calibrated
 | 
						|
					 */
 | 
						|
					mrc_post_code(0x05, 0x10 + ((ch << 4) | rk));
 | 
						|
 | 
						|
#ifdef BACKUP_RCVN
 | 
						|
					/* et hard-coded timing values */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++)
 | 
						|
						set_rcvn(ch, rk, bl, ddr_rcvn[PLATFORM_ID]);
 | 
						|
#else
 | 
						|
					/* enable FIFORST */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl += 2) {
 | 
						|
						mrc_alt_write_mask(DDRPHY,
 | 
						|
							B01PTRCTL1 +
 | 
						|
							(bl >> 1) * DDRIODQ_BL_OFFSET +
 | 
						|
							ch * DDRIODQ_CH_OFFSET,
 | 
						|
							0, 1 << 8);
 | 
						|
					}
 | 
						|
					/* initialize the starting delay to 128 PI (cas +1 CLK) */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						/* 1x CLK domain timing is cas-4 */
 | 
						|
						delay[bl] = (4 + 1) * FULL_CLK;
 | 
						|
 | 
						|
						set_rcvn(ch, rk, bl, delay[bl]);
 | 
						|
					}
 | 
						|
 | 
						|
					/* now find the rising edge */
 | 
						|
					find_rising_edge(mrc_params, delay, ch, rk, true);
 | 
						|
 | 
						|
					/* Now increase delay by 32 PI (1/4 CLK) to place in center of high pulse */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						delay[bl] += QRTR_CLK;
 | 
						|
						set_rcvn(ch, rk, bl, delay[bl]);
 | 
						|
					}
 | 
						|
					/* Now decrement delay by 128 PI (1 CLK) until we sample a "0" */
 | 
						|
					do {
 | 
						|
						temp = sample_dqs(mrc_params, ch, rk, true);
 | 
						|
						for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
							if (temp & (1 << bl)) {
 | 
						|
								if (delay[bl] >= FULL_CLK) {
 | 
						|
									delay[bl] -= FULL_CLK;
 | 
						|
									set_rcvn(ch, rk, bl, delay[bl]);
 | 
						|
								} else {
 | 
						|
									/* not enough delay */
 | 
						|
									training_message(ch, rk, bl);
 | 
						|
									mrc_post_code(0xee, 0x50);
 | 
						|
								}
 | 
						|
							}
 | 
						|
						}
 | 
						|
					} while (temp & 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 = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						delay[bl] += QRTR_CLK;
 | 
						|
						/* add "delay[]" values to "final_delay[][]" for rolling average */
 | 
						|
						final_delay[ch][bl] += delay[bl];
 | 
						|
						/* set timing based on rolling average values */
 | 
						|
						set_rcvn(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled);
 | 
						|
					}
 | 
						|
#else
 | 
						|
					/* Finally increment delay by 32 PI (1/4 CLK) to place in center of preamble */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						delay[bl] += QRTR_CLK;
 | 
						|
						set_rcvn(ch, rk, bl, delay[bl]);
 | 
						|
					}
 | 
						|
#endif
 | 
						|
 | 
						|
					/* disable FIFORST */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl += 2) {
 | 
						|
						mrc_alt_write_mask(DDRPHY,
 | 
						|
							B01PTRCTL1 +
 | 
						|
							(bl >> 1) * DDRIODQ_BL_OFFSET +
 | 
						|
							ch * DDRIODQ_CH_OFFSET,
 | 
						|
							1 << 8, 1 << 8);
 | 
						|
					}
 | 
						|
#endif
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef BACKUP_RCVN
 | 
						|
	/* restore original */
 | 
						|
	msg_port_write(MEM_CTLR, DTR1, dtr1_save);
 | 
						|
#endif
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * This function will perform the Write Levelling algorithm
 | 
						|
 * (align WCLK and WDQS).
 | 
						|
 *
 | 
						|
 * This algorithm will act on each rank in each channel separately.
 | 
						|
 */
 | 
						|
void wr_level(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch;	/* channel counter */
 | 
						|
	uint8_t rk;	/* rank counter */
 | 
						|
	uint8_t bl;	/* byte lane counter */
 | 
						|
	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
#ifndef BACKUP_WDQS
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t num_ranks_enabled = 0;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef BACKUP_WDQS
 | 
						|
#else
 | 
						|
	/* determines stop condition for CRS_WR_LVL */
 | 
						|
	bool all_edges_found;
 | 
						|
	/* absolute PI value to be programmed on the byte lane */
 | 
						|
	uint32_t delay[NUM_BYTE_LANES];
 | 
						|
	/*
 | 
						|
	 * 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 */
 | 
						|
	u32 dtr4, dtr4_save;
 | 
						|
#endif
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* wr_level starts */
 | 
						|
	mrc_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
 | 
						|
 | 
						|
	/* loop through each enabled channel */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			/* perform WRITE LEVELING algorithm on a per rank basis */
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					/*
 | 
						|
					 * POST_CODE here indicates the current
 | 
						|
					 * rank and channel being calibrated
 | 
						|
					 */
 | 
						|
					mrc_post_code(0x06, 0x10 + ((ch << 4) | rk));
 | 
						|
 | 
						|
#ifdef BACKUP_WDQS
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						set_wdqs(ch, rk, bl, ddr_wdqs[PLATFORM_ID]);
 | 
						|
						set_wdq(ch, rk, bl, ddr_wdqs[PLATFORM_ID] - QRTR_CLK);
 | 
						|
					}
 | 
						|
#else
 | 
						|
					/*
 | 
						|
					 * perform a single PRECHARGE_ALL command to
 | 
						|
					 * make DRAM state machine go to IDLE state
 | 
						|
					 */
 | 
						|
					dram_init_command(DCMD_PREA(rk));
 | 
						|
 | 
						|
					/*
 | 
						|
					 * enable Write Levelling Mode
 | 
						|
					 * (EMRS1 w/ Write Levelling Mode Enable)
 | 
						|
					 */
 | 
						|
					dram_init_command(DCMD_MRS1(rk, 0x82));
 | 
						|
 | 
						|
					/*
 | 
						|
					 * set ODT DRAM Full Time Termination
 | 
						|
					 * disable in MCU
 | 
						|
					 */
 | 
						|
 | 
						|
					dtr4 = msg_port_read(MEM_CTLR, DTR4);
 | 
						|
					dtr4_save = dtr4;
 | 
						|
					dtr4 |= DTR4_ODTDIS;
 | 
						|
					msg_port_write(MEM_CTLR, DTR4, dtr4);
 | 
						|
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) {
 | 
						|
						/*
 | 
						|
						 * Enable Sandy Bridge Mode (WDQ Tri-State) &
 | 
						|
						 * Ensure 5 WDQS pulses during Write Leveling
 | 
						|
						 */
 | 
						|
						mrc_alt_write_mask(DDRPHY,
 | 
						|
							DQCTL + DDRIODQ_BL_OFFSET * bl + DDRIODQ_CH_OFFSET * ch,
 | 
						|
							0x10000154,
 | 
						|
							0x100003fc);
 | 
						|
					}
 | 
						|
 | 
						|
					/* Write Leveling Mode enabled in IO */
 | 
						|
					mrc_alt_write_mask(DDRPHY,
 | 
						|
						CCDDR3RESETCTL + DDRIOCCC_CH_OFFSET * ch,
 | 
						|
						1 << 16, 1 << 16);
 | 
						|
 | 
						|
					/* Initialize the starting delay to WCLK */
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						/*
 | 
						|
						 * CLK0 --> RK0
 | 
						|
						 * CLK1 --> RK1
 | 
						|
						 */
 | 
						|
						delay[bl] = get_wclk(ch, rk);
 | 
						|
 | 
						|
						set_wdqs(ch, rk, bl, delay[bl]);
 | 
						|
					}
 | 
						|
 | 
						|
					/* now find the rising edge */
 | 
						|
					find_rising_edge(mrc_params, delay, ch, rk, false);
 | 
						|
 | 
						|
					/* disable Write Levelling Mode */
 | 
						|
					mrc_alt_write_mask(DDRPHY,
 | 
						|
						CCDDR3RESETCTL + DDRIOCCC_CH_OFFSET * ch,
 | 
						|
						0, 1 << 16);
 | 
						|
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) {
 | 
						|
						/* Disable Sandy Bridge Mode & Ensure 4 WDQS pulses during normal operation */
 | 
						|
						mrc_alt_write_mask(DDRPHY,
 | 
						|
							DQCTL + DDRIODQ_BL_OFFSET * bl + DDRIODQ_CH_OFFSET * ch,
 | 
						|
							0x00000154,
 | 
						|
							0x100003fc);
 | 
						|
					}
 | 
						|
 | 
						|
					/* restore original DTR4 */
 | 
						|
					msg_port_write(MEM_CTLR, DTR4, dtr4_save);
 | 
						|
 | 
						|
					/*
 | 
						|
					 * restore original value
 | 
						|
					 * (Write Levelling Mode Disable)
 | 
						|
					 */
 | 
						|
					dram_init_command(DCMD_MRS1(rk, mrc_params->mrs1));
 | 
						|
 | 
						|
					/*
 | 
						|
					 * perform a single PRECHARGE_ALL command to
 | 
						|
					 * make DRAM state machine go to IDLE state
 | 
						|
					 */
 | 
						|
					dram_init_command(DCMD_PREA(rk));
 | 
						|
 | 
						|
					mrc_post_code(0x06, 0x30 + ((ch << 4) | rk));
 | 
						|
 | 
						|
					/*
 | 
						|
					 * 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 = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						delay[bl] = get_wdqs(ch, rk, bl) + FULL_CLK;
 | 
						|
						set_wdqs(ch, rk, bl, delay[bl]);
 | 
						|
						/*
 | 
						|
						 * program WDQ timings based on WDQS
 | 
						|
						 * (WDQ = WDQS - 32 PI)
 | 
						|
						 */
 | 
						|
						set_wdq(ch, rk, bl, (delay[bl] - QRTR_CLK));
 | 
						|
					}
 | 
						|
 | 
						|
					/* get an address in the targeted channel/rank */
 | 
						|
					address = get_addr(ch, rk);
 | 
						|
					do {
 | 
						|
						uint32_t coarse_result = 0x00;
 | 
						|
						uint32_t coarse_result_mask = byte_lane_mask(mrc_params);
 | 
						|
						/* assume pass */
 | 
						|
						all_edges_found = true;
 | 
						|
 | 
						|
						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 = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
							if (coarse_result & (coarse_result_mask << bl)) {
 | 
						|
								all_edges_found = false;
 | 
						|
								delay[bl] -= FULL_CLK;
 | 
						|
								set_wdqs(ch, rk, bl, delay[bl]);
 | 
						|
								/* program WDQ timings based on WDQS (WDQ = WDQS - 32 PI) */
 | 
						|
								set_wdq(ch, rk, bl, delay[bl] - QRTR_CLK);
 | 
						|
							}
 | 
						|
						}
 | 
						|
					} 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 = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
						final_delay[ch][bl] += delay[bl];
 | 
						|
						set_wdqs(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled);
 | 
						|
						/* program WDQ timings based on WDQS (WDQ = WDQS - 32 PI) */
 | 
						|
						set_wdq(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled - QRTR_CLK);
 | 
						|
					}
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
void prog_page_ctrl(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 dpmc0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dpmc0 = msg_port_read(MEM_CTLR, DPMC0);
 | 
						|
	dpmc0 &= ~DPMC0_PCLSTO_MASK;
 | 
						|
	dpmc0 |= (4 << 16);
 | 
						|
	dpmc0 |= DPMC0_PREAPWDEN;
 | 
						|
	msg_port_write(MEM_CTLR, DPMC0, dpmc0);
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
void rd_train(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch;	/* channel counter */
 | 
						|
	uint8_t rk;	/* rank counter */
 | 
						|
	uint8_t bl;	/* byte lane counter */
 | 
						|
	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
 | 
						|
#ifdef BACKUP_RDQS
 | 
						|
#else
 | 
						|
	uint8_t side_x;	/* tracks LEFT/RIGHT approach vectors */
 | 
						|
	uint8_t side_y;	/* tracks BOTTOM/TOP approach vectors */
 | 
						|
	/* X coordinate data (passing RDQS values) for approach vectors */
 | 
						|
	uint8_t x_coordinate[2][2][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES];
 | 
						|
	/* Y coordinate data (passing VREF values) for approach vectors */
 | 
						|
	uint8_t y_coordinate[2][2][NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
	/* centered X (RDQS) */
 | 
						|
	uint8_t x_center[NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES];
 | 
						|
	/* centered Y (VREF) */
 | 
						|
	uint8_t y_center[NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
	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
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t num_ranks_enabled = 0;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
	/* rd_train starts */
 | 
						|
	mrc_post_code(0x07, 0x00);
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
#ifdef BACKUP_RDQS
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					for (bl = 0;
 | 
						|
					     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
					     bl++) {
 | 
						|
						set_rdqs(ch, rk, bl, ddr_rdqs[PLATFORM_ID]);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#else
 | 
						|
	/* initialize x/y_coordinate arrays */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					for (bl = 0;
 | 
						|
					     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
					     bl++) {
 | 
						|
						/* x_coordinate */
 | 
						|
						x_coordinate[L][B][ch][rk][bl] = RDQS_MIN;
 | 
						|
						x_coordinate[R][B][ch][rk][bl] = RDQS_MAX;
 | 
						|
						x_coordinate[L][T][ch][rk][bl] = RDQS_MIN;
 | 
						|
						x_coordinate[R][T][ch][rk][bl] = RDQS_MAX;
 | 
						|
						/* y_coordinate */
 | 
						|
						y_coordinate[L][B][ch][bl] = VREF_MIN;
 | 
						|
						y_coordinate[R][B][ch][bl] = VREF_MIN;
 | 
						|
						y_coordinate[L][T][ch][bl] = VREF_MAX;
 | 
						|
						y_coordinate[R][T][ch][bl] = VREF_MAX;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* initialize other variables */
 | 
						|
	bl_mask = byte_lane_mask(mrc_params);
 | 
						|
	address = get_addr(0, 0);
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
	/* need to set "final_delay[][]" elements to "0" */
 | 
						|
	memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay));
 | 
						|
#endif
 | 
						|
 | 
						|
	/* look for passing coordinates */
 | 
						|
	for (side_y = B; side_y <= T; side_y++) {
 | 
						|
		for (side_x = L; side_x <= R; side_x++) {
 | 
						|
			mrc_post_code(0x07, 0x10 + side_y * 2 + side_x);
 | 
						|
 | 
						|
			/* find passing values */
 | 
						|
			for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
				if (mrc_params->channel_enables & (0x1 << ch)) {
 | 
						|
					for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
						if (mrc_params->rank_enables &
 | 
						|
							(0x1 << rk)) {
 | 
						|
							/* set x/y_coordinate search starting settings */
 | 
						|
							for (bl = 0;
 | 
						|
							     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
							     bl++) {
 | 
						|
								set_rdqs(ch, rk, bl,
 | 
						|
									 x_coordinate[side_x][side_y][ch][rk][bl]);
 | 
						|
								set_vref(ch, bl,
 | 
						|
									 y_coordinate[side_x][side_y][ch][bl]);
 | 
						|
							}
 | 
						|
 | 
						|
							/* get an address in the target channel/rank */
 | 
						|
							address = get_addr(ch, rk);
 | 
						|
 | 
						|
							/* 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 = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
										if (result &
 | 
						|
											(bl_mask << bl)) {
 | 
						|
											/* adjust the RDQS values accordingly */
 | 
						|
											if (side_x == L)
 | 
						|
												x_coordinate[L][side_y][ch][rk][bl] += RDQS_STEP;
 | 
						|
											else
 | 
						|
												x_coordinate[R][side_y][ch][rk][bl] -= RDQS_STEP;
 | 
						|
 | 
						|
											/* check that we haven't closed the RDQS_EYE too much */
 | 
						|
											if ((x_coordinate[L][side_y][ch][rk][bl] > (RDQS_MAX - MIN_RDQS_EYE)) ||
 | 
						|
												(x_coordinate[R][side_y][ch][rk][bl] < (RDQS_MIN + MIN_RDQS_EYE)) ||
 | 
						|
												(x_coordinate[L][side_y][ch][rk][bl] ==
 | 
						|
												x_coordinate[R][side_y][ch][rk][bl])) {
 | 
						|
												/*
 | 
						|
												 * not enough RDQS margin available at this VREF
 | 
						|
												 * update VREF values accordingly
 | 
						|
												 */
 | 
						|
												if (side_y == B)
 | 
						|
													y_coordinate[side_x][B][ch][bl] += VREF_STEP;
 | 
						|
												else
 | 
						|
													y_coordinate[side_x][T][ch][bl] -= VREF_STEP;
 | 
						|
 | 
						|
												/* check that we haven't closed the VREF_EYE too much */
 | 
						|
												if ((y_coordinate[side_x][B][ch][bl] > (VREF_MAX - MIN_VREF_EYE)) ||
 | 
						|
													(y_coordinate[side_x][T][ch][bl] < (VREF_MIN + MIN_VREF_EYE)) ||
 | 
						|
													(y_coordinate[side_x][B][ch][bl] == y_coordinate[side_x][T][ch][bl])) {
 | 
						|
													/* VREF_EYE collapsed below MIN_VREF_EYE */
 | 
						|
													training_message(ch, rk, bl);
 | 
						|
													mrc_post_code(0xEE, 0x70 + side_y * 2 + side_x);
 | 
						|
												} else {
 | 
						|
													/* update the VREF setting */
 | 
						|
													set_vref(ch, bl, y_coordinate[side_x][side_y][ch][bl]);
 | 
						|
													/* reset the X coordinate to begin the search at the new VREF */
 | 
						|
													x_coordinate[side_x][side_y][ch][rk][bl] =
 | 
						|
														(side_x == L) ? RDQS_MIN : RDQS_MAX;
 | 
						|
												}
 | 
						|
											}
 | 
						|
 | 
						|
											/* update the RDQS setting */
 | 
						|
											set_rdqs(ch, rk, bl, x_coordinate[side_x][side_y][ch][rk][bl]);
 | 
						|
										}
 | 
						|
									}
 | 
						|
								}
 | 
						|
							} while (result & 0xff);
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	mrc_post_code(0x07, 0x20);
 | 
						|
 | 
						|
	/* find final RDQS (X coordinate) & final VREF (Y coordinate) */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						uint32_t temp1;
 | 
						|
						uint32_t temp2;
 | 
						|
 | 
						|
						/* x_coordinate */
 | 
						|
						DPF(D_INFO,
 | 
						|
						    "RDQS T/B eye rank%d lane%d : %d-%d %d-%d\n",
 | 
						|
						    rk, bl,
 | 
						|
						    x_coordinate[L][T][ch][rk][bl],
 | 
						|
						    x_coordinate[R][T][ch][rk][bl],
 | 
						|
						    x_coordinate[L][B][ch][rk][bl],
 | 
						|
						    x_coordinate[R][B][ch][rk][bl]);
 | 
						|
 | 
						|
						/* average the TOP side LEFT & RIGHT values */
 | 
						|
						temp1 = (x_coordinate[R][T][ch][rk][bl] + x_coordinate[L][T][ch][rk][bl]) / 2;
 | 
						|
						/* average the BOTTOM side LEFT & RIGHT values */
 | 
						|
						temp2 = (x_coordinate[R][B][ch][rk][bl] + x_coordinate[L][B][ch][rk][bl]) / 2;
 | 
						|
						/* average the above averages */
 | 
						|
						x_center[ch][rk][bl] = (uint8_t) ((temp1 + temp2) / 2);
 | 
						|
 | 
						|
						/* y_coordinate */
 | 
						|
						DPF(D_INFO,
 | 
						|
						    "VREF R/L eye lane%d : %d-%d %d-%d\n",
 | 
						|
						    bl,
 | 
						|
						    y_coordinate[R][B][ch][bl],
 | 
						|
						    y_coordinate[R][T][ch][bl],
 | 
						|
						    y_coordinate[L][B][ch][bl],
 | 
						|
						    y_coordinate[L][T][ch][bl]);
 | 
						|
 | 
						|
						/* average the RIGHT side TOP & BOTTOM values */
 | 
						|
						temp1 = (y_coordinate[R][T][ch][bl] + y_coordinate[R][B][ch][bl]) / 2;
 | 
						|
						/* average the LEFT side TOP & BOTTOM values */
 | 
						|
						temp2 = (y_coordinate[L][T][ch][bl] + y_coordinate[L][B][ch][bl]) / 2;
 | 
						|
						/* average the above averages */
 | 
						|
						y_center[ch][bl] = (uint8_t) ((temp1 + temp2) / 2);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
#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++) {
 | 
						|
			mrc_post_code(0x07, 0x30 + side_y * 2 + side_x);
 | 
						|
 | 
						|
			/* update the settings for the eye check */
 | 
						|
			for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
				if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
					for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
						if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
							for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
								if (side_x == L)
 | 
						|
									set_rdqs(ch, rk, bl, x_center[ch][rk][bl] - (MIN_RDQS_EYE / 2));
 | 
						|
								else
 | 
						|
									set_rdqs(ch, rk, bl, x_center[ch][rk][bl] + (MIN_RDQS_EYE / 2));
 | 
						|
 | 
						|
								if (side_y == B)
 | 
						|
									set_vref(ch, bl, y_center[ch][bl] - (MIN_VREF_EYE / 2));
 | 
						|
								else
 | 
						|
									set_vref(ch, bl, y_center[ch][bl] + (MIN_VREF_EYE / 2));
 | 
						|
							}
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			/* 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 */
 | 
						|
				mrc_post_code(0xee, 0x74 + side_x * 2 + side_y);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	mrc_post_code(0x07, 0x40);
 | 
						|
 | 
						|
	/* set final placements */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
#ifdef R2R_SHARING
 | 
						|
					/* increment "num_ranks_enabled" */
 | 
						|
					num_ranks_enabled++;
 | 
						|
#endif
 | 
						|
					for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor); bl++) {
 | 
						|
						/* x_coordinate */
 | 
						|
#ifdef R2R_SHARING
 | 
						|
						final_delay[ch][bl] += x_center[ch][rk][bl];
 | 
						|
						set_rdqs(ch, rk, bl, final_delay[ch][bl] / num_ranks_enabled);
 | 
						|
#else
 | 
						|
						set_rdqs(ch, rk, bl, x_center[ch][rk][bl]);
 | 
						|
#endif
 | 
						|
						/* y_coordinate */
 | 
						|
						set_vref(ch, bl, y_center[ch][bl]);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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.
 | 
						|
 */
 | 
						|
void wr_train(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch;	/* channel counter */
 | 
						|
	uint8_t rk;	/* rank counter */
 | 
						|
	uint8_t bl;	/* byte lane counter */
 | 
						|
	uint8_t bl_divisor = (mrc_params->channel_width == X16) ? 2 : 1;
 | 
						|
#ifdef BACKUP_WDQ
 | 
						|
#else
 | 
						|
	uint8_t side;		/* LEFT/RIGHT side indicator (0=L, 1=R) */
 | 
						|
	uint32_t temp;		/* temporary DWORD */
 | 
						|
	/* 2 arrays, for L & R side passing delays */
 | 
						|
	uint32_t delay[2][NUM_CHANNELS][NUM_RANKS][NUM_BYTE_LANES];
 | 
						|
	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
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t final_delay[NUM_CHANNELS][NUM_BYTE_LANES];
 | 
						|
	/* used to find placement for rank2rank sharing configs */
 | 
						|
	uint32_t num_ranks_enabled = 0;
 | 
						|
#endif
 | 
						|
#endif
 | 
						|
 | 
						|
	/* wr_train starts */
 | 
						|
	mrc_post_code(0x08, 0x00);
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
#ifdef BACKUP_WDQ
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					for (bl = 0;
 | 
						|
					     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
					     bl++) {
 | 
						|
						set_wdq(ch, rk, bl, ddr_wdq[PLATFORM_ID]);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#else
 | 
						|
	/* initialize "delay" */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
					for (bl = 0;
 | 
						|
					     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
					     bl++) {
 | 
						|
						/*
 | 
						|
						 * want to start with
 | 
						|
						 * WDQ = (WDQS - QRTR_CLK)
 | 
						|
						 * +/- QRTR_CLK
 | 
						|
						 */
 | 
						|
						temp = get_wdqs(ch, rk, bl) - QRTR_CLK;
 | 
						|
						delay[L][ch][rk][bl] = temp - QRTR_CLK;
 | 
						|
						delay[R][ch][rk][bl] = temp + QRTR_CLK;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* initialize other variables */
 | 
						|
	bl_mask = byte_lane_mask(mrc_params);
 | 
						|
	address = get_addr(0, 0);
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
	/* need to set "final_delay[][]" elements to "0" */
 | 
						|
	memset((void *)(final_delay), 0x00, (size_t)sizeof(final_delay));
 | 
						|
#endif
 | 
						|
 | 
						|
	/*
 | 
						|
	 * start algorithm on the LEFT side and train each channel/bl
 | 
						|
	 * until no failures are observed, then repeat for the RIGHT side.
 | 
						|
	 */
 | 
						|
	for (side = L; side <= R; side++) {
 | 
						|
		mrc_post_code(0x08, 0x10 + side);
 | 
						|
 | 
						|
		/* set starting values */
 | 
						|
		for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
			if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
				for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
					if (mrc_params->rank_enables &
 | 
						|
						(1 << rk)) {
 | 
						|
						for (bl = 0;
 | 
						|
						     bl < NUM_BYTE_LANES / bl_divisor;
 | 
						|
						     bl++) {
 | 
						|
							set_wdq(ch, rk, bl, delay[side][ch][rk][bl]);
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		/* find passing values */
 | 
						|
		for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
			if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
				for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
					if (mrc_params->rank_enables &
 | 
						|
						(1 << rk)) {
 | 
						|
						/* get an address in the target channel/rank */
 | 
						|
						address = get_addr(ch, rk);
 | 
						|
 | 
						|
						/* request HTE reconfiguration */
 | 
						|
						mrc_params->hte_setup = 1;
 | 
						|
 | 
						|
						/* check 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 = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
									if (result &
 | 
						|
										(bl_mask << bl)) {
 | 
						|
										if (side == L)
 | 
						|
											delay[L][ch][rk][bl] += WDQ_STEP;
 | 
						|
										else
 | 
						|
											delay[R][ch][rk][bl] -= WDQ_STEP;
 | 
						|
 | 
						|
										/* check for algorithm failure */
 | 
						|
										if (delay[L][ch][rk][bl] != delay[R][ch][rk][bl]) {
 | 
						|
											/*
 | 
						|
											 * margin available
 | 
						|
											 * update delay setting
 | 
						|
											 */
 | 
						|
											set_wdq(ch, rk, bl,
 | 
						|
												delay[side][ch][rk][bl]);
 | 
						|
										} else {
 | 
						|
											/*
 | 
						|
											 * no margin available
 | 
						|
											 * notify the user and halt
 | 
						|
											 */
 | 
						|
											training_message(ch, rk, bl);
 | 
						|
											mrc_post_code(0xee, 0x80 + side);
 | 
						|
										}
 | 
						|
									}
 | 
						|
								}
 | 
						|
							}
 | 
						|
						/* stop when all byte lanes pass */
 | 
						|
						} while (result & 0xff);
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	/* program WDQ to the middle of passing window */
 | 
						|
	for (ch = 0; ch < NUM_CHANNELS; ch++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << ch)) {
 | 
						|
			for (rk = 0; rk < NUM_RANKS; rk++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rk)) {
 | 
						|
#ifdef R2R_SHARING
 | 
						|
					/* increment "num_ranks_enabled" */
 | 
						|
					num_ranks_enabled++;
 | 
						|
#endif
 | 
						|
					for (bl = 0; bl < NUM_BYTE_LANES / bl_divisor; bl++) {
 | 
						|
						DPF(D_INFO,
 | 
						|
						    "WDQ eye rank%d lane%d : %d-%d\n",
 | 
						|
						    rk, bl,
 | 
						|
						    delay[L][ch][rk][bl],
 | 
						|
						    delay[R][ch][rk][bl]);
 | 
						|
 | 
						|
						temp = (delay[R][ch][rk][bl] + delay[L][ch][rk][bl]) / 2;
 | 
						|
 | 
						|
#ifdef R2R_SHARING
 | 
						|
						final_delay[ch][bl] += temp;
 | 
						|
						set_wdq(ch, rk, bl,
 | 
						|
							final_delay[ch][bl] / num_ranks_enabled);
 | 
						|
#else
 | 
						|
						set_wdq(ch, rk, bl, temp);
 | 
						|
#endif
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint8_t ch, rk, bl;
 | 
						|
	struct mrc_timings *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);
 | 
						|
				mt->rdqs[ch][rk][bl] = get_rdqs(ch, rk, bl);
 | 
						|
				mt->wdqs[ch][rk][bl] = get_wdqs(ch, rk, bl);
 | 
						|
				mt->wdq[ch][rk][bl] = get_wdq(ch, rk, bl);
 | 
						|
 | 
						|
				if (rk == 0)
 | 
						|
					mt->vref[ch][bl] = get_vref(ch, bl);
 | 
						|
			}
 | 
						|
 | 
						|
			mt->wctl[ch][rk] = get_wctl(ch, rk);
 | 
						|
		}
 | 
						|
 | 
						|
		mt->wcmd[ch] = get_wcmd(ch);
 | 
						|
	}
 | 
						|
 | 
						|
	/* need to save for a case of changing frequency after warm reset */
 | 
						|
	mt->ddr_speed = mrc_params->ddr_speed;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * 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(struct mrc_params *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 == BM_COLD) {
 | 
						|
		/*
 | 
						|
		 * factory value is 0 and in first boot,
 | 
						|
		 * a clock based seed is loaded.
 | 
						|
		 */
 | 
						|
		if (lfsr == 0) {
 | 
						|
			/*
 | 
						|
			 * get seed from system clock
 | 
						|
			 * and make sure it is not all 1's
 | 
						|
			 */
 | 
						|
			lfsr = rdtsc() & 0x0fffffff;
 | 
						|
		} else {
 | 
						|
			/*
 | 
						|
			 * Need to replace scrambler
 | 
						|
			 *
 | 
						|
			 * get next 32bit LFSR 16 times which is the last
 | 
						|
			 * part of the previous scrambler vector
 | 
						|
			 */
 | 
						|
			for (i = 0; i < 16; i++)
 | 
						|
				lfsr32(&lfsr);
 | 
						|
		}
 | 
						|
 | 
						|
		/* save new seed */
 | 
						|
		mrc_params->timings.scrambler_seed = lfsr;
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 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 */
 | 
						|
	msg_port_write(MEM_CTLR, SCRMSEED, (lfsr & 0x0003ffff));
 | 
						|
 | 
						|
	for (i = 0; i < 2; i++)
 | 
						|
		msg_port_write(MEM_CTLR, SCRMLO + i, (lfsr & 0xaaaaaaaa));
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Configure MCU Power Management Control Register
 | 
						|
 * and Scheduler Control Register
 | 
						|
 */
 | 
						|
void prog_ddr_control(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 dsch;
 | 
						|
	u32 dpmc0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dsch = msg_port_read(MEM_CTLR, DSCH);
 | 
						|
	dsch &= ~(DSCH_OOODIS | DSCH_OOOST3DIS | DSCH_NEWBYPDIS);
 | 
						|
	msg_port_write(MEM_CTLR, DSCH, dsch);
 | 
						|
 | 
						|
	dpmc0 = msg_port_read(MEM_CTLR, DPMC0);
 | 
						|
	dpmc0 &= ~DPMC0_DISPWRDN;
 | 
						|
	dpmc0 |= (mrc_params->power_down_disable << 25);
 | 
						|
	dpmc0 &= ~DPMC0_CLKGTDIS;
 | 
						|
	dpmc0 &= ~DPMC0_PCLSTO_MASK;
 | 
						|
	dpmc0 |= (4 << 16);
 | 
						|
	dpmc0 |= DPMC0_PREAPWDEN;
 | 
						|
	msg_port_write(MEM_CTLR, DPMC0, dpmc0);
 | 
						|
 | 
						|
	/* CMDTRIST = 2h - CMD/ADDR are tristated when no valid command */
 | 
						|
	mrc_write_mask(MEM_CTLR, DPMC1, 0x20, 0x30);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * After training complete configure MCU Rank Population Register
 | 
						|
 * specifying: ranks enabled, device width, density, address mode
 | 
						|
 */
 | 
						|
void prog_dra_drb(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 drp;
 | 
						|
	u32 dco;
 | 
						|
	u8 density = mrc_params->params.density;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dco = msg_port_read(MEM_CTLR, DCO);
 | 
						|
	dco &= ~DCO_IC;
 | 
						|
	msg_port_write(MEM_CTLR, DCO, dco);
 | 
						|
 | 
						|
	drp = 0;
 | 
						|
	if (mrc_params->rank_enables & 1)
 | 
						|
		drp |= DRP_RKEN0;
 | 
						|
	if (mrc_params->rank_enables & 2)
 | 
						|
		drp |= DRP_RKEN1;
 | 
						|
	if (mrc_params->dram_width == X16) {
 | 
						|
		drp |= (1 << 4);
 | 
						|
		drp |= (1 << 9);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Density encoding in struct dram_params: 0=512Mb, 1=Gb, 2=2Gb, 3=4Gb
 | 
						|
	 * has to be mapped RANKDENSx encoding (0=1Gb)
 | 
						|
	 */
 | 
						|
	if (density == 0)
 | 
						|
		density = 4;
 | 
						|
 | 
						|
	drp |= ((density - 1) << 6);
 | 
						|
	drp |= ((density - 1) << 11);
 | 
						|
 | 
						|
	/* Address mode can be overwritten if ECC enabled */
 | 
						|
	drp |= (mrc_params->address_mode << 14);
 | 
						|
 | 
						|
	msg_port_write(MEM_CTLR, DRP, drp);
 | 
						|
 | 
						|
	dco &= ~DCO_PMICTL;
 | 
						|
	dco |= DCO_IC;
 | 
						|
	msg_port_write(MEM_CTLR, DCO, dco);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/* Send DRAM wake command */
 | 
						|
void perform_wake(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dram_wake_command();
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Configure refresh rate and short ZQ calibration interval
 | 
						|
 * Activate dynamic self refresh
 | 
						|
 */
 | 
						|
void change_refresh_period(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 drfc;
 | 
						|
	u32 dcal;
 | 
						|
	u32 dpmc0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	drfc = msg_port_read(MEM_CTLR, DRFC);
 | 
						|
	drfc &= ~DRFC_TREFI_MASK;
 | 
						|
	drfc |= (mrc_params->refresh_rate << 12);
 | 
						|
	drfc |= DRFC_REFDBTCLR;
 | 
						|
	msg_port_write(MEM_CTLR, DRFC, drfc);
 | 
						|
 | 
						|
	dcal = msg_port_read(MEM_CTLR, DCAL);
 | 
						|
	dcal &= ~DCAL_ZQCINT_MASK;
 | 
						|
	dcal |= (3 << 8);	/* 63ms */
 | 
						|
	msg_port_write(MEM_CTLR, DCAL, dcal);
 | 
						|
 | 
						|
	dpmc0 = msg_port_read(MEM_CTLR, DPMC0);
 | 
						|
	dpmc0 |= (DPMC0_DYNSREN | DPMC0_ENPHYCLKGATE);
 | 
						|
	msg_port_write(MEM_CTLR, DPMC0, dpmc0);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Configure DDRPHY for Auto-Refresh, Periodic Compensations,
 | 
						|
 * Dynamic Diff-Amp, ZQSPERIOD, Auto-Precharge, CKE Power-Down
 | 
						|
 */
 | 
						|
void set_auto_refresh(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint32_t channel;
 | 
						|
	uint32_t rank;
 | 
						|
	uint32_t bl;
 | 
						|
	uint32_t bl_divisor = 1;
 | 
						|
	uint32_t temp;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Enable Auto-Refresh, Periodic Compensations, Dynamic Diff-Amp,
 | 
						|
	 * ZQSPERIOD, Auto-Precharge, CKE Power-Down
 | 
						|
	 */
 | 
						|
	for (channel = 0; channel < NUM_CHANNELS; channel++) {
 | 
						|
		if (mrc_params->channel_enables & (1 << channel)) {
 | 
						|
			/* Enable Periodic RCOMPS */
 | 
						|
			mrc_alt_write_mask(DDRPHY, CMPCTRL, 2, 2);
 | 
						|
 | 
						|
			/* Enable Dynamic DiffAmp & Set Read ODT Value */
 | 
						|
			switch (mrc_params->rd_odt_value) {
 | 
						|
			case 0:
 | 
						|
				temp = 0x3f;	/* OFF */
 | 
						|
				break;
 | 
						|
			default:
 | 
						|
				temp = 0x00;	/* Auto */
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			for (bl = 0; bl < (NUM_BYTE_LANES / bl_divisor) / 2; bl++) {
 | 
						|
				/* Override: DIFFAMP, ODT */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B0OVRCTL + bl * DDRIODQ_BL_OFFSET +
 | 
						|
					channel * DDRIODQ_CH_OFFSET,
 | 
						|
					temp << 10,
 | 
						|
					0x003ffc00);
 | 
						|
 | 
						|
				/* Override: DIFFAMP, ODT */
 | 
						|
				mrc_alt_write_mask(DDRPHY,
 | 
						|
					B1OVRCTL + bl * DDRIODQ_BL_OFFSET +
 | 
						|
					channel * DDRIODQ_CH_OFFSET,
 | 
						|
					temp << 10,
 | 
						|
					0x003ffc00);
 | 
						|
			}
 | 
						|
 | 
						|
			/* Issue ZQCS command */
 | 
						|
			for (rank = 0; rank < NUM_RANKS; rank++) {
 | 
						|
				if (mrc_params->rank_enables & (1 << rank))
 | 
						|
					dram_init_command(DCMD_ZQCS(rank));
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	clear_pointers();
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Depending on configuration enables ECC support
 | 
						|
 *
 | 
						|
 * Available memory size is decreased, and updated with 0s
 | 
						|
 * in order to clear error status. Address mode 2 forced.
 | 
						|
 */
 | 
						|
void ecc_enable(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 drp;
 | 
						|
	u32 dsch;
 | 
						|
	u32 ecc_ctrl;
 | 
						|
 | 
						|
	if (mrc_params->ecc_enables == 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	/* Configuration required in ECC mode */
 | 
						|
	drp = msg_port_read(MEM_CTLR, DRP);
 | 
						|
	drp &= ~DRP_ADDRMAP_MASK;
 | 
						|
	drp |= DRP_ADDRMAP_MAP1;
 | 
						|
	drp |= DRP_PRI64BSPLITEN;
 | 
						|
	msg_port_write(MEM_CTLR, DRP, drp);
 | 
						|
 | 
						|
	/* Disable new request bypass */
 | 
						|
	dsch = msg_port_read(MEM_CTLR, DSCH);
 | 
						|
	dsch |= DSCH_NEWBYPDIS;
 | 
						|
	msg_port_write(MEM_CTLR, DSCH, dsch);
 | 
						|
 | 
						|
	/* Enable ECC */
 | 
						|
	ecc_ctrl = (DECCCTRL_SBEEN | DECCCTRL_DBEEN | DECCCTRL_ENCBGEN);
 | 
						|
	msg_port_write(MEM_CTLR, DECCCTRL, ecc_ctrl);
 | 
						|
 | 
						|
	/* 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 != BM_S3) {
 | 
						|
		select_hte();
 | 
						|
		hte_mem_init(mrc_params, MRC_MEM_INIT);
 | 
						|
		select_mem_mgr();
 | 
						|
	}
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Execute memory test
 | 
						|
 * if error detected it is indicated in mrc_params->status
 | 
						|
 */
 | 
						|
void memory_test(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	uint32_t result = 0;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	select_hte();
 | 
						|
	result = hte_mem_init(mrc_params, MRC_MEM_TEST);
 | 
						|
	select_mem_mgr();
 | 
						|
 | 
						|
	DPF(D_INFO, "Memory test result %x\n", result);
 | 
						|
	mrc_params->status = ((result == 0) ? MRC_SUCCESS : MRC_E_MEMTEST);
 | 
						|
	LEAVEFN();
 | 
						|
}
 | 
						|
 | 
						|
/* Lock MCU registers at the end of initialization sequence */
 | 
						|
void lock_registers(struct mrc_params *mrc_params)
 | 
						|
{
 | 
						|
	u32 dco;
 | 
						|
 | 
						|
	ENTERFN();
 | 
						|
 | 
						|
	dco = msg_port_read(MEM_CTLR, DCO);
 | 
						|
	dco &= ~(DCO_PMICTL | DCO_PMIDIS);
 | 
						|
	dco |= (DCO_DRPLOCK | DCO_CPGCLOCK);
 | 
						|
	msg_port_write(MEM_CTLR, DCO, dco);
 | 
						|
 | 
						|
	LEAVEFN();
 | 
						|
}
 |