/*
 * Copyright (C) 2014-2015 Apple Inc. All rights reserved.
 *
 * This document is the property of Apple Inc.
 * It is considered confidential and proprietary.
 *
 * This document may not be reproduced or transmitted in any form,
 * in whole or in part, without the express written permission of
 * Apple Inc.
 */

/*
 * Init Sequence Library for use in:
 * Driver for the LPDDR4 compatible DRAM Control Subsystem (DCS) block
 */

/*
Summary of Steps from the Spec (Spreadsheet)

  + (Aa) Steps 0,1,2,3a     	   [need to identify parameterization]
  + (Ab) Step  3b		   [PMGR: set Freq to 50MHz & delay-wait]
  + (Ac) Steps 3c,4,5   	   [need to identify parameterization]
  + (B) Step   6		   [Change Clk Speed]
  + (C) Step   7		   [include delay-wait from early Step 8]
  + (D) Step   8a		   [Cal: CA  800MHz Vmin]
  + (E) Step   8b		   [Write Regs]
  + (F) Step   8c		   [Cal: CA  800MHz Vmin]   (...again? should it be CS?)
  + (G) Step   8d		   [Write Regs]
  + (H) Step   9		   [Write Regs]
  + (I) Step  10		   [Cal: WrLvl, RdDq, WrDq  800MHz Vmin]
  + (J) Step  11		   [Write Regs]
  + (K) Step  12		   [Cal: CA 1200MHz Vnom]
  + (L) Step  13		   [Write Regs]
  + (M) Step  14		   [Cal: WrLvl, RdDq, WrDq 1200MHz Vmin]
  + (N) Steps 15,16,17,18,19,20    [need to identify parameterization]

Between these steps there is possibly a need/desire for:
- Frequency Changes (PMGR)
- PMU save/restore
- debug messages
- register dumps (DV, SiVal)
- anything else!

CONFIG Table:
    Compile-Time Known:
	Type of Execution Environment	CoreOS, DV, SiVal
	Type of Target			SoC, FPGA, Palladium
	ChipID				0x8000, 0x8001, etc.
	Revision			A0, B0, B1, etc.
	Num DCS				4, 8, etc.			(Derived...!)
	Variant Flags			Work-arounds, RADAR Ref., et al.

	(Config Info know at Compile time can be subject to #if)

    Run-Time Known:
	Type of Init			ColdBoot, AOP_DDR, AOP_AWAKE
	Debug Flags			Low-Level, Milestone, et al.

RegValue Table
	Much like today's:  various values for various registers
				Sensitive to ChipID (also Target type?)
	(Internal static alloc (Complete Table Fill-in within Init function):
		Fill in Values (per ChipID/Rev & Target-type)
		return pointer to caller
			(Allows customization by caller before use)

TODO:
- Convert to using SPDS for register names, et al

*/

//================================================================================
// API Definitions
#include <drivers/dcs/dcs_init_lib.h>

extern dcs_config_t dcs_cfg;
extern dcs_config_params_t dcs_params;

#if izIBOOT
extern int debug_level_dcs;
#endif

void dcs_enable_autorefresh(void)
{
	uint32_t arefen_freq0 = 0x1011013f;
	uint32_t arefen_freq1 = 0x10010000;

	arefen_freq1 = 0x10110000;
 
	// Configure auto-refresh.  Freq0 has the auto-refresh enable, so do it last.
	dcs_reg_write_all_chan(rDCS_MCU_AREFEN_FREQ(3), 0x10100000);
	dcs_reg_write_all_chan(rDCS_MCU_AREFEN_FREQ(2), 0x10000000);
	dcs_reg_write_all_chan(rDCS_MCU_AREFEN_FREQ(1), arefen_freq1);
	dcs_reg_write_all_chan(rDCS_MCU_AREFEN_FREQ(0), arefen_freq0);
}


//================================================================================
// Implementing the Various Steps of the DCS Init, per Specification
// Based on AMC Init Spec (Spreadsheet)
// - Supporting Maui:  Spec (v??)
//================================================================================
// Step  0: AMC Prolog
//================================================================================
void dcs_init_Prolog(void)
{
	// NOTE: between the Compiler and the Optimization, this should vanish for non-FPGA builds
	if (izFPGA && izColdBoot) {
		dcs_reg_write_all_chan(rDCS_AMP_DRAM_RESETN(AMP_CA), 1);
		delay_for_us(200);
		dcs_reg_write_all_chan(rDCS_AMP_DRAM_RESETN(AMP_CA), 0);
		delay_for_us(200);
		dcs_reg_write_all_chan(rDCS_AMP_DRAM_RESETN(AMP_CA), 1);
		delay_for_us(200);
	}
}

//================================================================================
// Step  1: AMC Initial Configuration
//================================================================================
void dcs_init_AMC_Initial_Config(void)
{
	uint32_t i,n;

// rdar://problem/19137387: this applies to Maui A1 and B0, as well as other SoCs before Cayman

	dcs_reg_write_all_chan(rDCS_SPLLCTRL_MODEREG, 0x1);
	dcs_reg_write_all_chan(rDCS_SPLLCTRL_VCO, dcs_params.spllctrl_vco_1);
	



	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++) {
		for (n = 0; n < dcs_params.num_freqchngctl_regs; n++)
			dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL_FREQ(n, i), dcs_params.freq[i].freqchngctl[n]);

		// This register write to be skipped for Maui A0/A1, but written to for B0 & later, and all other SoCs
		if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0))
			dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGTIM_FREQ(i), dcs_params.freq[i].freqchngtim);
	}
	

	dcs_reg_write_all_chan(rDCS_AMP_WRDQCALVREFCODECONTROL, dcs_params.wrdqcalvrefcodecontrol);
	dcs_reg_write_all_chan(rDCS_AMP_RDDQCALVREFCODECONTROL, dcs_params.rddqcalvrefcodecontrol);
	dcs_reg_write_all_chan(rDCS_MCU_MODEREG, 0x120c90b8);

	dcs_reg_write_all_chan(rDCS_MCU_LAT_FREQ(0), dcs_params.freq[0].lat);
	dcs_reg_write_all_chan(rDCS_MCU_LAT_FREQ(1), dcs_params.freq[1].lat);
	
	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++)
		dcs_reg_write_all_chan(rDCS_MCU_PHYRDWRTIM_FREQ(i), dcs_params.freq[i].phyrdwrtim);
	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++) {
		dcs_reg_write_all_chan(rDCS_MCU_CASPCH_FREQ(i),  dcs_params.freq[i].caspch);
		dcs_reg_write_all_chan(rDCS_MCU_ACT_FREQ(i),     dcs_params.freq[i].act);
		dcs_reg_write_all_chan(rDCS_MCU_AUTOREF_FREQ(i), dcs_params.freq[i].autoref);
		dcs_reg_write_all_chan(rDCS_MCU_SELFREF_FREQ(i), dcs_params.freq[i].selfref);

		if (i == 0) dcs_reg_write_all_chan(rDCS_MCU_MODEREG, dcs_params.modereg);

		dcs_reg_write_all_chan(rDCS_MCU_PDN_FREQ(i), dcs_params.freq[i].pdn);
		if (i == 0) {
			dcs_reg_write_all_chan(rDCS_MCU_PDN1, dcs_params.pdn1);
			dcs_reg_write_all_chan(rDCS_MCU_MODEREG1, dcs_params.modereg1);
		}
	}

	dcs_reg_write_all_chan(rDCS_MCU_AUTOREF_PARAMS, dcs_params.autoref_params);
	dcs_reg_write_all_chan(rDCS_MCU_PDN, dcs_params.pdn);
	dcs_reg_write_all_chan(rDCS_MCU_DERATE_FREQ(0), dcs_params.freq[0].derate);

	// These register writes to be skipped for Maui A0/A1, but written to for B0 & later, and all other SoCs
	if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0))
		for(i = 1; i < DCS_FREQUENCY_SLOTS; i++)
			dcs_reg_write_all_chan(rDCS_MCU_DERATE_FREQ(i), dcs_params.freq[i].derate);

	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++)
		dcs_reg_write_all_chan(rDCS_MCU_LAT_FREQ(i), dcs_params.freq[i].lat2);
	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++)
		dcs_reg_write_all_chan(rDCS_MCU_TAT_FREQ(i), dcs_params.freq[i].tat);
	dcs_reg_write_all_chan(rDCS_MCU_RNKCFG, dcs_params.rnkcfg);
	dcs_reg_write_all_chan(rDCS_MCU_MIFQMAXCTRL_FREQ(0), dcs_params.freq[0].mifqmaxctrl);
	dcs_reg_write_all_chan(rDCS_MCU_MIFQMAXCTRL_FREQ(3), dcs_params.freq[3].mifqmaxctrl);
	dcs_reg_write_all_chan(rDCS_MCU_PWRMNGTEN, 0);
	dcs_reg_write_all_chan(rDCS_MCU_ODTSZQC, 0x00002000);
	dcs_reg_write_all_chan(rDCS_MCU_AMCCTRL, 0x00000002);

	dcs_reg_write_all_chan(rDCS_MCU_LS3B_QUIES_CTRL, 0x00000015);
	dcs_reg_write_all_chan(rDCS_MCU_MCPHYUPDTPARAM, 0x15030000);
	
}

//================================================================================
// Step  2: AMP Initial Configuration
//================================================================================
void dcs_init_AMP_Initial_Config(void)
{
	int i;

	for(i = AMP_CA ; i < DCS_AMP_NUM_PHYS; i++)
		dcs_reg_write_all_chan(rDCS_AMP_AMPEN(i), 0x00000001);

	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++)
		dcs_reg_write_all_chan(rDCS_AMP_VREF_F(AMP_CA, i), dcs_params.freq[i].cavref);	

	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++) {
		dcs_reg_write_all_chan(rDCS_AMP_VREF_F(AMP_DQ0, i), dcs_params.freq[i].dqvref);
		dcs_reg_write_all_chan(rDCS_AMP_VREF_F(AMP_DQ1, i), dcs_params.freq[i].dqvref);
	}

	dcs_reg_write_all_chan(rDCS_AMP_SDLL_UPDATE_CNTL(AMP_CA), dcs_params.casdllupdatectrl);
	dcs_reg_write_all_chan(rDCS_AMP_SDLL_UPDATE_CNTL(AMP_DQ0), dcs_params.dqsdllupdatectrl);
	dcs_reg_write_all_chan(rDCS_AMP_SDLL_UPDATE_CNTL(AMP_DQ1), dcs_params.dqsdllupdatectrl);
	dcs_reg_write_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ0), dcs_params.rdsdllctrl_step2);
	dcs_reg_write_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ1), dcs_params.rdsdllctrl_step2);
	dcs_reg_poll_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ0), dcs_params.rdsdllctrl_step2 & 0x7, 0x0);
	dcs_reg_poll_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ1), dcs_params.rdsdllctrl_step2 & 0x7, 0x0);

	dcs_reg_write_all_chan(rDCS_AMP_WRDQDQSSDLLCTRL(AMP_DQ0), dcs_params.wrdqdqssdllctrl_step2);
	dcs_reg_write_all_chan(rDCS_AMP_WRDQDQSSDLLCTRL(AMP_DQ1), dcs_params.wrdqdqssdllctrl_step2);
	dcs_reg_poll_all_chan(rDCS_AMP_WRDQDQSSDLLCTRL(AMP_DQ0), dcs_params.wrdqdqssdllctrl_step2 & 0x7, 0x0);
	dcs_reg_poll_all_chan(rDCS_AMP_WRDQDQSSDLLCTRL(AMP_DQ1), dcs_params.wrdqdqssdllctrl_step2 & 0x7, 0x0);

	dcs_reg_write_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, dcs_params.cawrlvlsdllcode);
	dcs_reg_poll_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, dcs_params.cawrlvlsdllcode & 0x300, 0x000);
	
	if (!izFPGA)
		for(i = AMP_CA ; i < DCS_AMP_NUM_PHYS; i++)
			dcs_reg_write_all_chan(rDCS_AMP_DLLLOCKTIM(i), dcs_params.dlllocktim);

	if(dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0) {
		dcs_reg_write_all_chan(rDCS_AMP_DQSPDENALWYSON(AMP_DQ0), 0x00000001);
		dcs_reg_write_all_chan(rDCS_AMP_DQSPDENALWYSON(AMP_DQ1), 0x00000001);
	}

	// These register writes to be skipped for Maui A0/A1
	if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {
		for(i = 1; i < DCS_FREQUENCY_SLOTS; i++)
			dcs_reg_write_all_chan(rDCS_AMPCA_DFICALTIMING_F(i), dcs_params.freq[i].cadficaltiming);

		for(i = 0; i < DCS_FREQUENCY_SLOTS; i++) {
			dcs_reg_write_all_chan(rDCS_AMPDQ_DFICALTIMING_F(AMP_DQ0, i), dcs_params.freq[i].dqdficaltiming);
			dcs_reg_write_all_chan(rDCS_AMPDQ_DFICALTIMING_F(AMP_DQ1, i), dcs_params.freq[i].dqdficaltiming);
		}
	}
	
	dcs_reg_write_all_chan(rDCS_AMP_RDWRDQCALTIMING_F(0), dcs_params.rdwrdqcaltiming_f0);
	dcs_reg_write_all_chan(rDCS_AMP_RDWRDQCALSEGLEN_F(0), dcs_params.rdwrdqcalseglen_f0);
	dcs_reg_write_all_chan(rDCS_AMP_RDWRDQCALTIMING_F(1), dcs_params.rdwrdqcaltiming_f1);
	dcs_reg_write_all_chan(rDCS_AMP_RDWRDQCALSEGLEN_F(1), dcs_params.rdwrdqcalseglen_f1);
	dcs_reg_write_all_chan(rDCS_AMP_RDWRDQCALTIMING_F(3), dcs_params.rdwrdqcaltiming_f3);

	dcs_reg_write_all_chan(rDCS_AMP_HWRDDQCALPATPRBS4I, dcs_params.rddqcalpatprbs4i);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATPRBS4I, dcs_params.wrdqcalpatprbs4i);

	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATPRBS7(0), 0x87654321);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATPRBS7(1), 0xcdedcba9);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATPRBS7(2), 0x456789ab);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATPRBS7(3), 0x5fa63123);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALPATINVERTMASK, 0x55550000);

	dcs_reg_write_all_chan(rDCS_AMP_RDDQCALWINDOW_F(0), dcs_params.freq[0].rddqcalwindow);
	dcs_reg_write_all_chan(rDCS_AMP_WRDQCALWINDOW_F(0), dcs_params.freq[0].wrdqcalwindow);
	dcs_reg_write_all_chan(rDCS_AMP_RDDQCALWINDOW_F(1), dcs_params.freq[1].rddqcalwindow);
	dcs_reg_write_all_chan(rDCS_AMP_WRDQCALWINDOW_F(1), dcs_params.freq[1].wrdqcalwindow);
	dcs_reg_write_all_chan(rDCS_AMP_MAXRDDQS_SDLL_MULFACTOR, dcs_params.maxrddqssdllmulfactor);

	dcs_reg_write_all_chan(rDCS_AMP_MAXWRDQS_SDLL_MULFACTOR, dcs_params.maxwrdqssdllmulfactor);

	// These register writes to be skipped for Maui A0/A1
	if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {
		dcs_reg_write_all_chan(rDCS_AMPDQ_RDDQSMULFACTOR(AMP_DQ0), 0x20181000);
		dcs_reg_write_all_chan(rDCS_AMPDQ_RDDQSMULFACTOR(AMP_DQ1), 0x20181000);
	}
	
	for(i = 0; i < DCS_FREQUENCY_SLOTS; i++) {
		dcs_reg_write_all_chan(rDCS_AMP_RDCAPCFG_FREQ(AMP_DQ0, i), dcs_params.freq[i].rdcapcfg);
		dcs_reg_write_all_chan(rDCS_AMP_RDCAPCFG_FREQ(AMP_DQ1, i), dcs_params.freq[i].rdcapcfg);
	}
	
	if (!izFPGA) {
		for(i = AMP_CA ; i < DCS_AMP_NUM_PHYS; i++)
			dcs_reg_write_all_chan(rDCS_AMP_DLLUPDTCTRL(i), dcs_params.dllupdtctrl1);
	}
	
	dcs_reg_write_all_chan(rDCS_AMP_DLLUPDTINTVL(AMP_CA), dcs_params.dllupdtintvl);

	dcs_reg_write_all_chan(rDCS_AMP_DLLUPDTINTVL(AMP_DQ0), 0);
	dcs_reg_write_all_chan(rDCS_AMP_DLLUPDTINTVL(AMP_DQ1), 0);

	for(i = AMP_DQ0 ; i < DCS_AMP_NUM_PHYS; i++)
		dcs_reg_write_all_chan(rDCS_AMP_DLLEN(i), 0x00000100);

	dcs_reg_write_all_chan(rDCS_AMP_DCCCONTROL, dcs_params.dcccontrol);
	dcs_reg_write_all_chan(rDCS_AMP_DCCTIMER, 0x00000190);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALDYNHALFCLKDLYCTRL, 0x00000001);


// AMPH configuration


	dcs_reg_write_all_chan(rDCS_AMPH_CB_DRIVESTR,     dcs_params.cbdrivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_CB_IOCTL,        dcs_params.cbioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_CK_DRIVESTR,     dcs_params.ckdrivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_CK_IOCTL,        dcs_params.ckioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_B0_DRIVESTR,     dcs_params.b0drivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_B0_IOCTL,        dcs_params.b0ioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_B0_ODT,          dcs_params.b0odt);
	dcs_reg_write_all_chan(rDCS_AMPH_B0_ODTCTRL,      dcs_params.b0odtctrl);
	dcs_reg_write_all_chan(rDCS_AMPH_B1_DRIVESTR,     dcs_params.b1drivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_B1_ODTCTRL,      dcs_params.b1odtctrl);
	dcs_reg_write_all_chan(rDCS_AMPH_B1_IOCTL,        dcs_params.b1ioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_B1_ODT,          dcs_params.b1odt);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS0_DRIVESTR,   dcs_params.dqs0drivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS0_IOCTL,      dcs_params.dqs0ioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS0_ODT,        dcs_params.dqs0odt);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS0_ZDETBIASEN, dcs_params.dqs0zdetbiasen);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS0_ODTCTRL,    dcs_params.dqs0odtctrl);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS1_DRIVESTR,   dcs_params.dqs1drivestr);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS1_IOCTL,      dcs_params.dqs1ioctl);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS1_ODT,        dcs_params.dqs1odt);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS1_ZDETBIASEN, dcs_params.dqs1zdetbiasen);
	dcs_reg_write_all_chan(rDCS_AMPH_DQS1_ODTCTRL,    dcs_params.dqs1odtctrl);
	dcs_reg_write_all_chan(rDCS_AMPH_DBG_REG0,        0x00000000);
	dcs_reg_write_all_chan(rDCS_AMPH_ZCAL_FSM(1),     dcs_params.zcalfsm1);
	dcs_reg_write_all_chan(rDCS_AMPH_ZCAL_FSM(0),     dcs_params.zcalfsm0);
	dcs_reg_write_all_chan(rDCS_AMPH_SPARE(0),        dcs_params.spare0);
	dcs_reg_write_all_chan(rDCS_AMP_HWRDDQCALTVREF, dcs_params.hwrddqcaltvref);
	dcs_reg_write_all_chan(rDCS_AMP_HWWRDQCALTVREF, dcs_params.hwwrdqcaltvref);

}

//================================================================================
// Step  3: Self-Refresh Exit
//================================================================================
void dcs_init_Self_Refresh_Exit_a(void)
{
	// Wait 5 us after Impedence Calibration to avoid McPhyPending
	// preventing the SRFSM from exiting SR.
	dcs_spin(5, "after Imp. Cal.");
	
	dcs_reg_write_all_chan(rDCS_MCU_AREFPARAM, dcs_params.arefparam);
	
	for(int i = 0; i < DCS_FREQUENCY_SLOTS; i++)
		dcs_reg_write_all_chan(rDCS_MCU_AUTOREF_FREQ(i), dcs_params.freq[i].autoref2);
	
	dcs_reg_write_all_chan(rDCS_MCU_AUTOREF_PARAMS, dcs_params.autoref_params2);
	
	dcs_reg_write_all_chan(rDCS_MCU_ODTSZQC, dcs_params.odtszqc);
	// M8 Bug radar #20346417, two writes to ensure propogation
	dcs_reg_write_all_chan(rDCS_MCU_LONGSR, dcs_params.longsr);
	dcs_reg_write_all_chan(rDCS_MCU_LONGSR, dcs_params.longsr);
	dcs_reg_write_all_chan(rDCS_MCU_MCPHYUPDTPARAM, 0x15030004);
	
}

//========================================
// Caller Code should:
// - Change Frequency to 50MHz
//========================================

void dcs_init_Self_Refresh_Exit_b(void)
{
	dcs_reg_write_all_chan(rDCS_MCU_AMCCTRL, 0x00000003);

	for(uint32_t i = AMP_CA ; i < DCS_AMP_NUM_PHYS; i++)
		dcs_reg_write_all_chan(rDCS_AMP_AMPINIT(i), 0x00000001);

	if (!izFPGA) {
		dcs_reg_write_all_chan(rDCS_AMP_IMPCALCMD, 0x00000001);
		dcs_reg_poll_all_chan(rDCS_AMP_IMPCALCMD, 0x01, 0x00);
	}
	
	if (izResume)
		dcs_enable_autorefresh();

	dcs_spin(2200, "After Enable Auto-Refresh");

	if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {
		dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL, dcs_params.freqchngctl_step3);
		dcs_reg_poll_all_chan(rDCS_MCU_FREQCHNGCTL, 0x10000, 0x00000);
		dcs_spin(2, "After SoC Update");
	}

	// Resume-Boot and Cold-Boot use different Exit commands for Self-Refresh
	// Issue self-refresh exit cmd for resume boot with MPC
	int n = (izColdBoot)? 0x00000000:0x00004000;
	// Issue self-refresh exit cmd for resume boot with MPC
	dcs_reg_write_all_chan(rDCS_MCU_MRINITCMD_0, n);
	dcs_reg_write_all_chan(rDCS_MCU_MRINITCMD_0, n | 0x1);
	dcs_reg_poll_all_chan(rDCS_MCU_MRINITCMD_0, 0x01, 0x00);

	dcs_spin(2, NULL);
	
	dcs_reg_write(rGLBTIMER_CHEN, (1 << dcs_cfg.num_dcs) - 1);
}

//================================================================================
// Step  4: DRAM Reset, ZQ Calibration & Configuration (cold boot only)
//================================================================================
void dcs_init_ZQ_Cal_Cfg(void)
{
	// ZQ calibration START MPC cmd
	dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0, 0x4f);
	// wait 1us for tZQCAL
	dcs_spin(1, "tZQCAL");
	
	// ZQ calibration LATCH MPC cmd
	dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0, 0x51);
	// wait 10ns for tZQLAT
	dcs_spin(1, "tZQLAT");

	if (izColdBoot) {
		// MR2 register (read/write latency)
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x2, 0x0);
		// MR1 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x1, 0x8e);
		// MR3 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x3, dcs_params.mr3cmd);
		// MR22 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x16, 0x0);
		// MR11 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xb, 0x0);
		// MR13 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xd, dcs_params.mr13cmd);
		// MR12 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xc, 0x59);
		// MR14 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xe, 0x59);
		// MR23 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x17, 0x80);

		uint32_t patinvertmask = (DCS_REG_READ_CH(0, rDCS_AMP_HWRDDQCALPATPRBS4I) >> 16) & 0xffff;
		uint32_t prbs = DCS_REG_READ_CH(0, rDCS_AMP_HWWRDQCALPATPRBS4I);

		// MR15 register
		dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xf, patinvertmask & 0xff);
		// MR20 register
		dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x14, (patinvertmask >> 8) & 0xff);
		// MR32 register
		dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x20, prbs & 0xff);
		// MR40 register
		dcs_mrcmd(MR_MPC, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x28, (prbs >> 8) & 0xff);
	}
}	

//================================================================================
// Step  5a: AddrConfig Configuration
//================================================================================
void dcs_init_AddrCfg_Cfg(void)
{

	dcs_reg_write(rAMCC_ADDRCFG_ADDR, dcs_params.addrcfg);
#if NUM_AMCCS == 2
	dcs_reg_write(rAMCC1_ADDRCFG_ADDR, dcs_params.addrcfg);
#endif

}

//========================================
// Caller Code should:
// - Use MR Cmds to Query the Device
//========================================

//================================================================================
// Step  5b: Total Mem Size Config
//================================================================================
void dcs_init_TotalSize_Cfg(uint32_t total_size_Mbytes)
{

	dbgprintf(DCS_DBG_ASSESS_CONFIG, "Memory_size: %llu bytes\n",
					(((uint64_t)total_size_Mbytes) * MEG * NUM_AMCCS));
	// (Units of 128 MBs in the register)
	dcs_reg_write(rAMCC_DRAMACCCTRL_ADDR, ((total_size_Mbytes / NUM_AMCCS) / DCS_NUM_MEG_IN_DRAM_BLK) - 1);
	dcs_reg_write(rAMCC_MCCCHNLDEC_ADDR, dcs_params.chnldec);

#if NUM_AMCCS == 2
	dcs_reg_write(rAMCC1_DRAMACCCTRL_ADDR, ((total_size_Mbytes / NUM_AMCCS) / DCS_NUM_MEG_IN_DRAM_BLK) - 1);
	dcs_reg_write(rAMCC1_MCCCHNLDEC_ADDR, dcs_params.chnldec);
#endif
	dbgprintf(DCS_DBG_ASSESS_CONFIG, "rAMCC_DRAMACCCTRL: 0x%08x\n", rAMCC_DRAMACCCTRL);
}

//================================================================================
// Step  6: Switch from boot-clock speed to normal operation speed
//================================================================================
void dcs_init_Reenable_AMC_Scheduler(void)
{
	// Wait 5 us before freq change to make sure all refreshes have been flushed
	dcs_spin(5, "After Refresh");

	// Enable (again) the AMC Scheduler
	dcs_reg_write_all_chan(rDCS_MCU_AMCCTRL, 0x00000003);
}

//================================================================================
// Step  7: Setup registers for CA calibration for bucket 1
//================================================================================
void dcs_init_CA_Cal_Setup_Freq1(void)
{
	// MR13 register
	dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xd, dcs_params.mr13cmd_step7);
	
	if (izColdBoot) {
		if (izFPGA) {
			// MR11 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xb, 0x00);
		} else {
			// MR2 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x2, 0x52);
			// MR1 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x1, 0xae);
			// MR3 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x3, dcs_params.mr3cmd_step7);
			// MR22 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x16, dcs_params.mr22cmd);
			// MR11 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xb, dcs_params.mr11cmd);
			// MR12 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xc, 0x11);
			// MR14 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xe, 0x11);
		}
		dcs_reg_write_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, 0x00000200);
		dcs_reg_poll_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, 0x0200, 0x0000);
	}
}

//================================================================================
// Step  8: AMP Dynamic CA Vref Timing Calibration at Vmin 800MHz
//================================================================================
void dcs_init_CA_Cal_Freq1(void)
{
	// Nothing to be done at this step for cold boot
}

//================================================================================
// Step  9: Setup registers for DQ calibration for bucket 1
//================================================================================
void dcs_init_DQ_Cal_Setup_Freq1_a(void)
{
	int i;

	if (izColdBoot) {
		// MR13 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0xd, dcs_params.mr13cmd_step9);
	}
	
	dcs_spin(1, NULL);
	
	if(izResume) {
		dcs_reg_write_all_chan(rDCS_MCU_PWRMNGTEN, 0x00000002);
		dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL, 0x01008888);

		for(i = AMP_DQ0 ; i < DCS_AMP_NUM_PHYS; i++)
			dcs_reg_write_all_chan(rDCS_AMP_WRDQDQSSDLLCTRL(i), 0xFF00000C);		
		for(i = AMP_CA ; i < DCS_AMP_NUM_PHYS; i++)
			dcs_reg_write_all_chan(rDCS_AMP_SDLL_UPDATE_DEFER_EN(i), 0x00000000);
		for(i = AMP_DQ0; i < DCS_AMP_NUM_PHYS; i++)
			dcs_reg_write_all_chan(rDCS_AMP_MDLLOVRRD(i),  0x00000000);
	}
}

// Caller code should change frequency to bucket 1
void dcs_init_DQ_Cal_Setup_Freq1_b(void)
{
	if (izColdBoot) {
		if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {
			dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL, dcs_params.freqchngctl_step9);
			dcs_reg_poll_all_chan(rDCS_MCU_FREQCHNGCTL, 0x10000, 0x00000);

			// Wait 2us for the soc update to finish
			dcs_spin(2, NULL);
		}
	}
}

//================================================================================
// Step 10: PHY write DQ calibration
//================================================================================
void dcs_init_wrdq_skew(void)
{
	dcs_reg_write_all_chan(rDCS_AMP_DQSDQ_SKEWCTL(AMP_DQ0), 0x06000000);
	dcs_reg_write_all_chan(rDCS_AMP_DQSDQ_SKEWCTL(AMP_DQ1), 0x06000000);
}

void dcs_init_post_wrlvl(void)
{
	if(dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0) {
		dcs_reg_write_all_chan(rDCS_AMP_DQFLTCTRL(AMP_DQ0), 0x00000010);
		dcs_reg_write_all_chan(rDCS_AMP_DQFLTCTRL(AMP_DQ1), 0x00000010);
	}

}

//================================================================================
// Step 11: Setup registers for CA calibration for bucket 0
//================================================================================
void dcs_init_CA_Cal_Setup_Freq0(void)
{
	if (izColdBoot) {

		if(dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0) {
			dcs_reg_write_all_chan(rDCS_AMP_DQFLTCTRL(AMP_DQ0), 0x00000000);
			dcs_reg_write_all_chan(rDCS_AMP_DQFLTCTRL(AMP_DQ1), 0x00000000);
		}

		// MR13 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0d, dcs_params.mr13cmd_step11);
		if (!izFPGA) {
			// MR2 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x02, dcs_params.mr2cmd_step11);
			// MR1 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x01, dcs_params.mr1cmd_step11);
			// MR3 register
			dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x03, dcs_params.mr3cmd_step11);
		}
		
		// MR22 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x16, dcs_params.mr22cmd_step11);
		// MR11 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0b, (izFPGA) ? 0x00 : dcs_params.mr11cmd_step11);
		// MR12 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0c, dcs_params.mr12cmd_step11);
		// MR14 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0e, dcs_params.mr14cmd_step11);
	
		if((dcs_params.supported_freqs & DCS_FREQ(1)) == 0) {
			dcs_reg_write_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, 0x00000200);
			dcs_reg_poll_all_chan(rDCS_AMP_CAWRLVLSDLLCODE, 0x200, 0x000);
		}
	}
}

//================================================================================
// Step 12: AMP Dynamic CA Timing Calibration
//================================================================================

//================================================================================
// Step 13: Setup registers for DQ calibration for bucket 0
//================================================================================
void dcs_init_DQ_Cal_Setup_Freq0_a(void)
{
	if (izColdBoot) {
		// MR13 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0d, dcs_params.mr13cmd_step13);

		// Wait 1 us
		dcs_spin(1, NULL);
	}
}

void dcs_init_DQ_Cal_Setup_Freq0_b(void)
{
	if (izColdBoot) {
		if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {
			dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL, 0x00010000);
			dcs_reg_poll_all_chan(rDCS_MCU_FREQCHNGCTL, 0x10000, 0x00000);

			// Wait 2us for the soc update to finish
			dcs_spin(2, NULL);
		}
	}
}

//================================================================================
// Step 14: AMP Dynamic DQ Calibration
//================================================================================

//================================================================================
// Step 15: Setup Registers for Boot
//================================================================================
void dcs_init_Reg_for_Boot(void)
{

#ifndef DCS_RUN_AT_50MHZ
	if (izColdBoot) {
		// MR13 register
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0d, dcs_params.mr13cmd_step15);
	}
#endif
	if(izFPGA && !(dcs_params.supported_freqs & DCS_FREQ(1)))
		dcs_mrcmd(MR_WRITE, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x0d, 0x50);

	// Wait 1 us
	dcs_spin(1, NULL);

	if (izColdBoot) {
		dcs_reg_write_all_chan(rDCS_MCU_FREQCHNGCTL, dcs_params.freqchngctl_step15);

#if !DCS_DO_CALIBRATION
		// skip these for all silicon targets, where calibration is performed, also skip for any Maui A1 target
		if((dcs_cfg.chipID != 0x8000) || (dcs_cfg.chipID == 0x8000 && dcs_cfg.chipRev >= CHIP_REVISION_B0)) {

			dcs_reg_write_all_chan(rDCS_AMP_RDDQSDLL_DLYSEL(AMP_DQ0), 0x00000000);
			dcs_reg_write_all_chan(rDCS_AMP_RDDQSDLL_DLYSEL(AMP_DQ1), 0x00000000);

			dcs_reg_write_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ0), dcs_params.rdsdllctrl_step15);
			dcs_reg_write_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ1), dcs_params.rdsdllctrl_step15);
			dcs_reg_poll_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ0), dcs_params.rdsdllctrl_step15 & 0x7, 0x0);
			dcs_reg_poll_all_chan(rDCS_AMP_RDSDLLCTRL(AMP_DQ1), dcs_params.rdsdllctrl_step15 & 0x7, 0x0);
		}
#endif
	}


	if(!izFPGA)
	{
		dcs_reg_write_all_chan(rDCS_AMP_HWRDWRDQCALFULLSCANEN, 0x00000000);
		dcs_reg_write_all_chan(rDCS_AMP_HWRDWRDQCALFULLSCANEN, 0x00000003);	
	}

}

//================================================================================
// Step 16: Enable Other Features
//================================================================================
void dcs_init_MoreFeatures(void)
{
	dcs_reg_write_all_chan(rDCS_MCU_AREFPARAM, dcs_params.arefparam);
	dcs_reg_write_all_chan(rDCS_MCU_ODTSZQC, dcs_params.odtszqc2);
	
	dcs_reg_write_all_chan(rDCS_MCU_QBREN, dcs_params.qbren_step16);
	dcs_reg_write_all_chan(rDCS_MCU_PSQWQCTL0, 0x00000100);
	
	if (izColdBoot)
		dcs_enable_autorefresh();

	dcs_reg_write_all_chan(rDCS_AMPH_B0_DYN_ISEL_ASRTIME, dcs_params.b0dyniselasrtime);
	dcs_reg_write_all_chan(rDCS_AMPH_B0_DYN_ISEL, dcs_params.b0dynisel);
	dcs_reg_write_all_chan(rDCS_AMPH_B1_DYN_ISEL, dcs_params.b1dynisel);

}

//================================================================================
// Step 17: Enable Fast Critical Word Forwarding Feature
//================================================================================
void dcs_init_Fast_Critical_Word_Forwarding(void)
{

	dcs_reg_write_all_chan(rDCS_MCU_QBRPARAM, dcs_params.qbrparam);
	dcs_reg_write_all_chan(rDCS_MCU_QBREN, dcs_params.qbren);
	dcs_reg_write(rAMCC_MCCGEN_ADDR, dcs_params.mccgen);


#if NUM_AMCCS == 2
	dcs_reg_write(rAMCC1_MCCGEN_ADDR, dcs_params.mccgen);

#endif // #if NUM_AMCCS == 2

}

//================================================================================
// Step 18: Enable Power- and Clock-Gating Features; Config MCC and Global Timers
//================================================================================
void dcs_init_Gating_Global_Timers(void)
{
	dcs_reg_write_all_chan(rDCS_AMP_AMPCLK(AMP_CA), dcs_params.caampclk);
	dcs_reg_write_all_chan(rDCS_AMP_AMPCLK(AMP_DQ0), dcs_params.dqampclk);
	dcs_reg_write_all_chan(rDCS_AMP_AMPCLK(AMP_DQ1), dcs_params.dqampclk);
	dcs_reg_write_all_chan(rDCS_SPLLCTRL_MDLLPWRDNCFG(0), 0x00000001);

	
	dcs_reg_write_all_chan(rDCS_MCU_PWRMNGTEN, dcs_params.pwrmngten);
	dcs_reg_write(rGLBTIMER_PMGRWAKEUPCFG, 0x000001ff);


	if(!izFPGA) {

		dcs_reg_write_all_chan(rDCS_MCU_PSQWQCTL1, 0x01e60120);
	}
	

	// Global Timer Setup
	dcs_reg_write(rGLBTIMER_PREFREQ2ALLBANKDLY(0),          0x00f000f0);
	dcs_reg_write(rGLBTIMER_PREFREQ2ALLBANKDLY(1),          0x00f000f0);
	dcs_reg_write(rGLBTIMER_PREFREQCHNG2FREQCHNGDLY(0),     0x02400240);
	dcs_reg_write(rGLBTIMER_PREFREQCHNG2FREQCHNGDLY(1),     0x02400240);
	dcs_reg_write(rGLBTIMER_MDLLTIMER,            0x000bb8);	//    3000
	dcs_reg_write(rGLBTIMER_DCCTIMER,             0x000bb8);
	dcs_reg_write(rGLBTIMER_MDLLVOLTRAMPTIMER,    0x00004b);	//      75
	dcs_reg_write(rGLBTIMER_CTRLUPDMASKTIMER,     0x00000f);	//      15
	dcs_reg_write(rGLBTIMER_IDTTIMER,             0x007530);	// 30000
	dcs_reg_write(rGLBTIMER_RDCALTIMER,           0x2dc6c0);	// 3000000
	dcs_reg_write(rGLBTIMER_WRCALTIMER,           0x2dc6c0);	// 3000000
	dcs_reg_write(rGLBTIMER_ZQCTIMER,             0x3d0900);	// 4000000
	dcs_reg_write(rGLBTIMER_PERCAL_FREQCHNGTIMER, 0x000001);	//  300000
	dcs_reg_write(rGLBTIMER_IMPCALTIMER,          0x002ee0);	//   12000
	dcs_reg_write(rGLBTIMER_PERVREFCALCFG,        0x0000000b);
	dcs_reg_write(rGLBTIMER_FREQCHNGCFG,          0x000000ff);

	// Dynamic Clk & Power Gating
	dcs_reg_write_all_chan(rDCS_MCU_AMCCLKPWRGATE, 0x050a0000);

}

//================================================================================
// Step 19: ODTS read & set interval for periodic MR4 on-die Temp sensor reading
//================================================================================
void dcs_init_ODTS(void)
{
	uint8_t temp[16*8];	// Far more Channels/Ranks than we are likely ever to have/need
	// INSTEAD OF: uint8_t temp[dcs_cfg.num_dcs * dcs_cfg.num_rank];

	// MR4 read to bring memory out of self-refresh
	dcs_mrcmd(MR_READ, dcs_cfg.num_dcs, dcs_cfg.num_rank, 0x4, (uintptr_t)temp);
	// M8 Bug radar #20346417, two writes to ensure propogation
	dcs_reg_write_all_chan(rDCS_MCU_ODTSZQC, dcs_params.odtszqc3);
	dcs_reg_write_all_chan(rDCS_MCU_ODTSZQC, dcs_params.odtszqc3);
	
}

//================================================================================
