/*
* Copyright (C) 2015 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*/

#include <delay.h>
#include <console/console.h>
#include <soc/config.h>
#include <soc/reg_utils.h>

#define DDR_CTL_TYPE_1 1
#define DDR_DRAM_TYPE_DDR3L 31

extern unsigned int ddr_init_tab[];
#ifdef DDR2_SUPPORT
extern unsigned int ddr2_init_tab[];
extern unsigned int ddr2_init_tab_400[];
extern unsigned int ddr2_init_tab_667[];
extern unsigned int ddr2_init_tab_800[];
extern unsigned int ddr2_init_tab_1066[];
extern unsigned int ddr2_mode_reg_tab[];
#endif

#ifdef CONFIG_DDR333
#define CONFIG_DRAM_FREQ 333
extern unsigned int ddr3_init_tab_667[];
#endif
#ifdef CONFIG_DDR400
#define CONFIG_DRAM_FREQ 400
extern unsigned int ddr3_init_tab_800[];
#endif
#ifdef CONFIG_DDR533
#define CONFIG_DRAM_FREQ 533
extern unsigned int ddr3_init_tab_1066[];
#endif
#ifdef CONFIG_DDR667
#define CONFIG_DRAM_FREQ 667
extern unsigned int ddr3_init_tab_1333[];
#endif
#if IS_ENABLED(CONFIG_CYGNUS_DDR800)
#define CONFIG_DRAM_FREQ 800
extern unsigned int ddr3_init_tab_1600[];
#endif

#define __udelay udelay

/* Local function prototype */
uint32_t change_ddr_clock(uint32_t clk);
void dump_phy_regs(void);
void ddr_init_regs(unsigned int * tblptr);
void ddr_phy_ctl_regs_ovrd(unsigned int * tblptr);
void ddr_phy_wl_regs_ovrd(unsigned int * tblptr);
int is_ddr_32bit(void);
uint32_t iproc_get_ddr3_clock_mhz(uint32_t unit);
int cygnus_phy_powerup(void);
void ddr_init2(void);
void PRE_SRX(void);

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
void PRE_SRX(void)
{
	uint32_t readvalue = 0;

	// Disable low power receivers:  bit 0 of the byte lane STATIC_PAD_CTL register
	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_CONTROL_REGS_STATIC_PAD_CTL);
	reg32_write ((volatile uint32_t *)DDR_PHY_CONTROL_REGS_STATIC_PAD_CTL, ( readvalue & ~(1 << DDR_PHY_CONTROL_REGS_STATIC_PAD_CTL__RX_MODE_R)));

	// Turn off ZQ_CAL drivers: bits 0,1, and 17 of the ZQ_CAL register (other bits 0 & 1 are set to 1)
	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_CONTROL_REGS_ZQ_CAL);
	reg32_write ((volatile uint32_t *)DDR_PHY_CONTROL_REGS_ZQ_CAL, ( readvalue & ~(1 << DDR_PHY_CONTROL_REGS_ZQ_CAL__ZQ_IDDQ)));

	// Byte lane 0 power up
	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL, ( readvalue & ~(1 << DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL__IDLE)));

	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL, ( readvalue & 0xffff800f));

	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL, ( readvalue & ~(1 << DDR_PHY_BYTE_LANE_0_IDLE_PAD_CONTROL__IDDQ)));

	// Byte lane 1 power up
	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL, ( readvalue & ~(1 << DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL__IDLE)));

	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL, ( readvalue & 0xffff800f));

	readvalue = reg32_read ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL);
	reg32_write ((volatile uint32_t *)DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL, ( readvalue & ~(1 << DDR_PHY_BYTE_LANE_1_IDLE_PAD_CONTROL__IDDQ)));

	// Turn on PHY_CONTROL AUTO_OEB ¨C not required
	// Enable byte lane AUTO_DQ_RXENB_MODE: bits 18 and 19 of the byte lane IDLE_PAD_CONTROL ¨C already set 180114c8: 000f000a

	printk(BIOS_INFO, "\n....PLL power up.\n");
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG) & ~(1<<DDR_PHY_CONTROL_REGS_PLL_CONFIG__PWRDN)));

	// PLL out of reset
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG) & ~(1<<DDR_PHY_CONTROL_REGS_PLL_CONFIG__RESET)));
	printk(BIOS_INFO, "\n....poll lock..\n");
	// Poll lock
	readvalue = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_STATUS);
	while ( ( readvalue & 0x1) == 0x0 )
	{
		printk(BIOS_INFO, "\n....DDR_PHY_CONTROL_REGS_PLL_STATUS = %8x..\n",readvalue);
		readvalue = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_STATUS);
	}
	printk(BIOS_INFO, "\n....after while..\n");

	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG) & ~(1<<DDR_PHY_CONTROL_REGS_PLL_CONFIG__RESET_POST_DIV)));

	printk(BIOS_INFO, "\n....remove hold..\n");
	// Remove hold
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_CONFIG) & ~(1<<DDR_PHY_CONTROL_REGS_PLL_CONFIG__HOLD)));
	printk(BIOS_INFO, "\n....restore dac..\n");

	// Restore DAC
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_VREF_DAC_CONTROL, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_VREF_DAC_CONTROL) & 0xffff0fff));
	printk(BIOS_INFO, "\n....set iddq bit..\n");

	// Set the iddq bit in the idle control register and select all outputs except cke and rst in the idee select registers.
	//	Do NOT assert any other bits in the idle control register.	(This step can be done during init on power up.)
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL) & ~(1 << DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL__IDDQ)));
	printk(BIOS_INFO, "\n....idle pad enable 0..\n");
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_ENABLE0, 0x0);
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_ENABLE1, 0x0);
	printk(BIOS_INFO, "\n....DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL..\n");
	reg32_write((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL, (reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL) & ~(1 << DDR_PHY_CONTROL_REGS_IDLE_PAD_CONTROL__IDLE)));
}

#endif

#if defined(CONFIG_IPROC_DDR_ECC) && !defined(CONFIG_IPROC_P7)
void iproc_ddr_ovrd_ecc_lane(void)
{
	uint32_t val;

#define SET_OVR_STEP(v) ( 0x30000 | ( (v) & 0x3F ) )    /* OVR_FORCE = OVR_EN = 1, OVR_STEP = v */

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE_RD_EN);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_RD_EN, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_R_P);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_R_P, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_R_N);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_R_N, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT0_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT1_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT2_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT3_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_DM_W);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_DM_W, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT0_R_P);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_R_P, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT0_R_N);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_R_N, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT1_R_P);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_R_P, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT1_R_N);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_R_N, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT2_R_P);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_R_P, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT2_R_N);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_R_N, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT3_R_P);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_R_P, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT3_R_N);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_R_N, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);


	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE0_BIT_RD_EN);
	val = SET_OVR_STEP(val & 0xff);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT_RD_EN, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_READ_DATA_DLY);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_READ_DATA_DLY, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_READ_CONTROL);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_READ_CONTROL, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_IDLE_PAD_CONTROL);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_IDLE_PAD_CONTROL, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_DRIVE_PAD_CTL);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_DRIVE_PAD_CTL, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	val = reg32_read((volatile uint32_t *)DDR_PHY_WORD_LANE_0_WR_PREAMBLE_MODE);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_WR_PREAMBLE_MODE, val);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);

	__udelay(200);
	reg32_write((volatile uint32_t *)DDR_PHY_ECC_LANE_READ_FIFO_CLEAR, 0x1);
	val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);
	__udelay(200);
}

uint32_t iproc_read_ecc_syndrome(void)
{
	volatile uint32_t syndrome = 0;
	/* Place uncorrectible as bits 7:0, and correctible as 15:8 */
	syndrome = ((reg32_read((volatile uint32_t *)DDR_DENALI_CTL_89) >> 3) & 0x1) |
				(((reg32_read((volatile uint32_t *)DDR_DENALI_CTL_89) >> 5) & 0x1));
	return(syndrome);
}

void iproc_clear_ecc_syndrome(void)
{
	uint32_t val;

	/* Clear the interrupts, bits 6:3 */
	reg32_write((volatile uint32_t *)DDR_DENALI_CTL_213, (1 << 5) | (1<< 3));
	__udelay(1000);
}
#endif

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
uint32_t iproc_get_ddr3_clock_mhz(uint32_t unit)
{
	uint32_t ndiv, mdiv, pdiv, ddrclk, data;

	data = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_PLL_DIVIDERS);

	ndiv = data >> DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__NDIV_INT_R;
	ndiv &= (2^DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__NDIV_INT_WIDTH) - 1;

	pdiv = data >> DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__PDIV_R;
	pdiv &= (2^DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__PDIV_WIDTH) - 1;

	mdiv = data >> DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__MDIV_R;
	mdiv &= (2^DDR_PHY_CONTROL_REGS_PLL_DIVIDERS__MDIV_WIDTH) - 1;

	/* read ndiv pdiv and mdiv */
	ddrclk = (25 * ndiv * 2 * pdiv) / mdiv;
	printk(BIOS_INFO, "%s DDR PHY PLL divisor: ndiv(0x%x) mdiv(0x%x) ddrclk(0x%x)\n", __FUNCTION__, ndiv, mdiv, ddrclk);

	return(ddrclk);
}

#endif

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)

int cygnus_phy_powerup(void)
{
	int data;
	int count = 15;

	data = reg32_read((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL);

	if(reg32_read((volatile uint32_t *)CRMU_IHOST_POR_WAKEUP_FLAG)==0)
	{
		/* Step 1: POWRON */
		data = reg32_read((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL);
		data |= 0x8;// assert power ON
		reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);

		__udelay(2);

		/* Step 2: POWROK */
		data |= 0x10;// assert power OK
		reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);

		while(count--)
			__udelay(2);

	}
	else
	{
		printk(BIOS_INFO, "DeepSleep wakeup: ddr phy init bypassed 1\n");
	}

	/* Step 3: DFI normal mode */
	data &= ~(0x04);// remove DFI isolation
	reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);


	/* Step 4: Enable register access */
	data &= ~(0x02);// remove PHY register isolation
	reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);

	data &= ~(0x01);// remove PLL isolation
	reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);

	count = 20;
	while(count--)
		__udelay(2);

	if(reg32_read((volatile uint32_t *)CRMU_IHOST_POR_WAKEUP_FLAG)==0)
	{
		/* Step 5: release reset */
		data |= 0x20;// de-assert reset
		reg32_write((volatile uint32_t *)CRMU_DDR_PHY_AON_CTRL, data);
	}
	else
	{
		printk(BIOS_INFO, "DeepSleep wakeup: ddr phy init bypassed 2\n");
	}
	while((reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS) & 0x08) != 0x08) {
		//poll DDR_S1_IDM_IO_STATUS__o_phy_pwrup_rsb
	}

	return 0;
}

#endif

uint32_t change_ddr_clock(uint32_t clk)
{
	return(0);
}

void dump_phy_regs(void)
{
	int i;
	printk(BIOS_DEBUG, "\n PHY register dump: Control registers\n");
	for(i = 0; i <= 0x94; i+=4)
	{
		printk(BIOS_DEBUG, "0x%03x,\t0x%08x,\n", i,
			*(volatile uint32_t *)(DDR_PHY_CONTROL_REGS_REVISION + i));
	}

	printk(BIOS_DEBUG, "\n PHY register dump: Wordlane0 registers\n");
	for(i = 0; i <= 0xc5; i+=4)
	{
		printk(BIOS_DEBUG, "0x%03x,\t0x%08x,\n", i,
			*(volatile uint32_t *)(DDR_PHY_BYTE_LANE_0_VDL_CONTROL_WR_DQS_P + i));
	}

	return;
}

void ddr_init_regs(unsigned int * tblptr)
{
	unsigned int offset = *tblptr;
	unsigned int *addr = (unsigned int *)DDR_DENALI_CTL_00;

	while(offset != 0xffffffff) {
		++tblptr;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
		addr[offset] = *tblptr;
#else
		addr[offset] = swap_u32(*tblptr);
#endif
		++tblptr;
		offset = *tblptr;
	}
}

void ddr_phy_ctl_regs_ovrd(unsigned int * tblptr)
{
	unsigned int offset = *tblptr;
	unsigned int *addr = (unsigned int *)DDR_PHY_CONTROL_REGS_REVISION;
	unsigned int val;

	while(offset != 0xffffffff) {
		++tblptr;
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
		addr[offset/4] = *tblptr;
#else
		addr[offset/4] = swap_u32(*tblptr);
#endif
		val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);
		if (val) ;
		++tblptr;
		offset = *tblptr;
	}
}

void ddr_phy_wl_regs_ovrd(unsigned int * tblptr)
{
}

/*DDR_SHMOO_RELATED_CHANGE*/

#ifdef CONFIG_RUN_DDR_SHMOO
int ReWriteModeRegisters( void )
{
	int nRet = 0;
	int j = 100;

	reg32_clear_bits( (volatile uint32_t *)DDR_DENALI_CTL_89 , 1 << 18 );

	/* Set mode register for MR0, MR1, MR2 and MR3 write for all chip selects */
	reg32_write( (volatile uint32_t *)DDR_DENALI_CTL_43 , (1 << 17) | (1 << 24) | (1 << 25) );

	/* Trigger Mode Register Write(MRW) sequence */
	reg32_set_bits( (volatile uint32_t *)DDR_DENALI_CTL_43 , 1 << 25 );

	do {
		if ( reg32_read( (volatile uint32_t *)DDR_DENALI_CTL_89) & (1 << 18) ) {
			break;
		}
		--j;
	} while( j );

	if ( j == 0 && (reg32_read( (volatile uint32_t *)DDR_DENALI_CTL_89) & (1 << 18) ) == 0 ) {
		printk(BIOS_ERR, "Error: DRAM mode registers write failed\n");
		nRet = 1;
	};

	return nRet;
}
#endif /* CONFIG_RUN_DDR_SHMOO */


int is_ddr_32bit(void)
{
	int ddr32 = 0;

#if (CONFIG_CYGNUS_SHMOO_REUSE_DDR_32BIT)
	ddr32=1;
#endif /* (CONFIG_CYGNUS_SHMOO_REUSE_DDR_32BIT) */

	return ddr32;
}


static uint32_t get_ddr_clock(uint32_t sku_id, int ddr_type)
{
#ifdef CONFIG_DRAM_FREQ
	return  CONFIG_DRAM_FREQ;
#else
	#error Please set DDR frequency (CONFIG_DRAM_FREQ must be set)
#endif
}

#if defined(CONFIG_SHMOO_REUSE) || defined(CONFIG_SHMOO_AND28_REUSE)

#define RAND_MAGIC_1    0x0000444BUL
#define RAND_MAGIC_2    0x88740000UL
#define RAND_MAGIC_3    69069UL
#define RAND_SEED       0x5301beef
#define RAND_SEED_2     ((RAND_SEED << 21) + (RAND_SEED << 14) + (RAND_SEED << 7))
#define RAND_C_INIT     (((RAND_SEED_2 + RAND_MAGIC_1) << 1) + 1)
#define RAND_T_INIT     ((RAND_SEED_2 << (RAND_SEED_2 & 0xF)) + RAND_MAGIC_2)

static int simple_memory_test(void *start, uint32_t len)
{
	register uint32_t rand_c_value, rand_t_value, rand_value;
	register uint32_t i;
	register volatile uint32_t *paddr;

	len /= 4;
	paddr = (volatile uint32_t *)start;
	rand_c_value = RAND_C_INIT;
	rand_t_value = RAND_T_INIT;
	for(i=0; i<len; i++, paddr++) {
		rand_c_value *= RAND_MAGIC_3;
		rand_t_value ^= rand_t_value >> 15;
		rand_t_value ^= rand_t_value << 17;
		rand_value = rand_t_value ^ rand_c_value;
		*paddr = rand_value;
	}

	paddr = (volatile uint32_t *)start;
	rand_c_value = RAND_C_INIT;
	rand_t_value = RAND_T_INIT;
	for(i=0; i<len; i++, paddr++) {
		rand_c_value *= RAND_MAGIC_3;
		rand_t_value ^= rand_t_value >> 15;
		rand_t_value ^= rand_t_value << 17;
		rand_value = rand_t_value ^ rand_c_value;
		if (*paddr != rand_value) {
			return -1;
		}
	}

	return 0;
}

#endif /* CONFIG_SHMOO_REUSE || CONFIG_SHMOO_AND28_REUSE */

#if defined(CONFIG_RUN_DDR_SHMOO2) && defined(CONFIG_SHMOO_REUSE)

#define SHMOO_HEADER_MAGIC      "SHMO"
#define SHMOO_MIN_BLOCK_SIZE    0x10000

static const uint16_t ddr_phy_ctl_regs[] = {
	0x030,
	0x034,
	0x06c
};

static const uint16_t ddr_phy_wl_regs[] = {
	0x000,
	0x004,
	0x008,
	0x00c,
	0x010,
	0x014,
	0x018,
	0x01c,
	0x020,
	0x024,
	0x028,
	0x02c,
	0x030,
	0x034,
	0x038,
	0x03c,
	0x040,
	0x044,
	0x048,
	0x04c,
	0x050,
	0x054,
	0x058,
	0x05c,
	0x060,
	0x064,
	0x068,
	0x06c,
	0x070,
	0x074,
	0x0a4,
	0x0a8,
	0x0ac,
	0x0b0,
	0x0b4,
	0x0b8,
	0x0bc,
	0x0c0,
	0x0c4,
	0x0c8,
	0x0cc,
	0x0d0,
	0x0d4,
	0x0d8,
	0x0dc,
	0x0e0,
	0x0e4,
	0x0e8,
	0x0ec,
	0x0f0,
	0x0f4,
	0x0f8,
	0x0fc,
	0x100,
	0x104,
	0x108,
	0x10c,
	0x110,
	0x114,
	0x118,
	0x11c,
	0x120,
	0x124,
	0x128,
	0x12c,
	0x130,
	0x134,
	0x138,
	0x13c,
	0x140,
	0x144,
	0x148,
	0x14c,
	0x150,
	0x154,
	0x158,
	0x15c,
	0x160,
	0x164,
	0x168,
	0x16c,
	0x1a0,
	0x1a4,
	0x1a8,
	0x1ac,
	0x1b0
};
#if defined(CONFIG_IPROC_DDR_ECC) && !defined(CONFIG_IPROC_P7)
static const uint16_t ddr_phy_eccl_regs[] = {
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_RD_EN_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_R_P_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_R_N_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_DM_W_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_R_P_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT0_R_N_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_R_P_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT1_R_N_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_R_P_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT2_R_N_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_R_P_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT3_R_N_BASE,
	DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT_RD_EN_BASE,
	DDR_PHY_ECC_LANE_READ_DATA_DLY_BASE,
	DDR_PHY_ECC_LANE_READ_CONTROL_BASE,
	DDR_PHY_ECC_LANE_IDLE_PAD_CONTROL_BASE,
	DDR_PHY_ECC_LANE_DRIVE_PAD_CTL_BASE,
	DDR_PHY_ECC_LANE_WR_PREAMBLE_MODE_BASE,
};
#endif
#if defined(CONFIG_IPROC_NAND) && defined(CONFIG_ENV_IS_IN_NAND) && CONFIG_ENV_IS_IN_NAND

static int write_shmoo_to_flash(void *buf, int length)
{
	nand_info_t *nand;
	int ret = 0;
	uint32_t offset = CONFIG_SHMOO_REUSE_NAND_OFFSET;
	uint32_t end = offset + CONFIG_SHMOO_REUSE_NAND_RANGE;
	uint32_t blksize;

	/* Get flash handle */
	nand = &nand_info[0];
	if (nand->size < offset || nand->writesize == 0 || nand->erasesize == 0) {
		printk(BIOS_ERR, "Failed to initialize NAND flash for saving Shmoo values!\n");
		return -1;
	}

	/* For NAND with bad blocks, we always erase all blocks in the range */
	{
		nand_erase_options_t opts;
		memset(&opts, 0, sizeof(opts));
		opts.offset = offset / nand->erasesize * nand->erasesize;
		opts.length = (CONFIG_SHMOO_REUSE_NAND_RANGE - 1) / nand->erasesize * nand->erasesize + 1;
		opts.quiet  = 1;
		ret = nand_erase_opts(nand, &opts);
		if (ret) {
			printk(BIOS_ERR, "NAND flash erase failed, error=%d\n", ret);
			return ret;
		}
	}

	/* Write data */
	blksize = nand->erasesize > SHMOO_MIN_BLOCK_SIZE?
	nand->erasesize : SHMOO_MIN_BLOCK_SIZE;
	while (offset < end) {
		if (nand_block_isbad(nand, offset)) {
			offset += blksize;
			continue;
		}
		ret = nand_write(nand, offset, (size_t *)&length, (u_char *)buf);
		if (ret) {
			printk(BIOS_ERR, "NAND flash write failed, error=%d\n", ret);
		}
		break;
	}

	return ret;
}

#elif defined (CONFIG_SPI_FLASH) && defined(CONFIG_ENV_IS_IN_SPI_FLASH) && CONFIG_ENV_IS_IN_SPI_FLASH

static int write_shmoo_to_flash(void *buf, int length)
{
	struct spi_flash *flash;
	int erase = 0;
	volatile uint32_t *flptr;
	int i, j, ret = 0;
	uint32_t offset = CONFIG_SHMOO_REUSE_QSPI_OFFSET;

	/* Check if erasing is required */
	flptr = (volatile uint32_t *)(IPROC_QSPI_MEM_BASE + offset / 4 * 4);
	j = (length - 1) / 4 + 1;
	for(i=0; i<j; i++, flptr++) {
		if (*flptr != 0xFFFFFFFF) {
		erase = 1;
		break;
		}
	}

	/* Probe flash */
	flash = spi_flash_probe(
		CONFIG_ENV_SPI_BUS,
		CONFIG_ENV_SPI_CS,
		CONFIG_ENV_SPI_MAX_HZ,
		CONFIG_ENV_SPI_MODE
		);
	if (!flash) {
		printk(BIOS_ERR, "Failed to initialize SPI flash for saving Shmoo values!\n");
		return -1;
	}

	/* Erase if necessary */
	if (erase) {
		ret = spi_flash_erase(
			flash,
			offset / flash->sector_size * flash->sector_size,
			flash->sector_size
			);
		if (ret) {
			printk(BIOS_ERR, "SPI flash erase failed, error=%d\n", ret);
			spi_flash_free(flash);
			return ret;
		}
	}

	/* Write data */
	ret = spi_flash_write(flash, offset, length, buf);
	if (ret) {
		printk(BIOS_ERR, "SPI flash write failed, error=%d\n", ret);
	}

	/* Free flash instance */
	spi_flash_free(flash);

	return ret;
}

#elif defined (CONFIG_ENV_IS_IN_FLASH)

static int write_shmoo_to_flash(void *buf, int length)
{
	int erase = 0;
	volatile uint32_t *flptr, shmoo_start;
	int i, j, ret = 0;
	uint32_t offset = CONFIG_SHMOO_REUSE_NOR_OFFSET;
	int sect_len;

	/* Check if erasing is required */
	flptr = (volatile uint32_t *)(IPROC_NOR_MEM_BASE + offset / 4 * 4);
	shmoo_start = flptr;
	j = (length - 1) / 4 + 1;
	for(i=0; i<j; i++, flptr++) {
		if (*flptr != 0xFFFFFFFF) {
			erase = 1;
			break;
		}
	}

	sect_len = (((length / 0x20000) + 1)*0x20000 - 1);
	/* Erase if necessary */
	if (erase) {
		ret = flash_sect_erase((ulong)shmoo_start, (ulong)shmoo_start + sect_len);
		if (ret) {
			printk(BIOS_ERR, "NOR flash erase failed, error=%d, start addr: 0x%x, end addr: 0x%x\n",
		                ret, (ulong)shmoo_start, (ulong)shmoo_start + sect_len);
			return ret;
		}
	}

	/* Write data */
	ret = flash_write((char *)buf, (ulong)shmoo_start, length);

	if (ret) {
		printk(BIOS_ERR, "NOR flash write failed, error=%d\n", ret);
	}


	return ret;

}
#else
 #error Flash (SPI or NAND) must be enabled
#endif

/* Return flash pointer; or NULL if validation failed */
static volatile uint32_t *validate_flash_shmoo_values(struct shmoo_signature *psig, int *ppairs)
{
	uint32_t dev_id, sku_id, ddr_type, ddr_clk;
	volatile uint32_t *ptr;
	volatile uint32_t *flptr;
	struct shmoo_signature sig;
	uint32_t checksum, pairs, length;
	uint32_t chksum;
	int offset;
	int i;
	int numpairs = 1;

	if (is_ddr_32bit()) {
		numpairs = 2;
	}

    /* Calculate required length (register/value pair) */
	pairs =
	sizeof(ddr_phy_ctl_regs) / sizeof(ddr_phy_ctl_regs[0]) +
	sizeof(ddr_phy_wl_regs) / sizeof(ddr_phy_wl_regs[0]) * numpairs;
#ifdef CONFIG_IPROC_DDR_ECC
	pairs += sizeof(ddr_phy_eccl_regs) / sizeof(ddr_phy_eccl_regs[0]);
#endif

	if (ppairs != NULL) {
		*ppairs = pairs;
	}

#if defined(CONFIG_ENV_IS_IN_NAND) && CONFIG_ENV_IS_IN_NAND
	/* Read SHMOO data from NAND */
	flptr = (volatile uint32_t *)(IPROC_NAND_MEM_BASE + CONFIG_SHMOO_REUSE_NAND_OFFSET);
	offset = (CONFIG_SHMOO_REUSE_NAND_RANGE - 1) / SHMOO_MIN_BLOCK_SIZE * SHMOO_MIN_BLOCK_SIZE;
#elif defined (CONFIG_ENV_IS_IN_FLASH)
	/* Read SHMOO data from NOR */
	flptr = (volatile uint32_t *)(IPROC_NOR_MEM_BASE + CONFIG_SHMOO_REUSE_NOR_OFFSET);
	offset = 0;
#else
	/* Read SHMOO data from SPI */
	flptr = (volatile uint32_t *)(IPROC_QSPI_MEM_BASE + CONFIG_SHMOO_REUSE_QSPI_OFFSET);
	offset = 0;
#endif

	/* Get chip type and DDR type/clock */
	dev_id = (reg32_read((volatile uint32_t *)ChipcommonA_ChipID)) & 0x0000ffff;
	sku_id = (reg32_read((volatile uint32_t *)ROM_S0_IDM_IO_STATUS) >> 2) & 0x03;
	ddr_type = reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS) & 0x1;
	ddr_clk = get_ddr_clock(sku_id, ddr_type);

	/* Construct signature */
	memcpy(sig.magic, SHMOO_HEADER_MAGIC, 4);
	sig.dev_id = dev_id;
	sig.sku_id = sku_id;
	sig.ddr_type = ddr_type;
	sig.ddr_clock = ddr_clk;

	/* Provide signature data to caller */
	if (psig) {
		memcpy(psig, &sig, sizeof(sig));
	}

	/* Check signature (in min-blocks from bottom) */
	while (offset >= 0) {
		ptr = flptr + offset;
		if (!shmoo_sigmemcmp(&sig,(void *)ptr)) {
			break;
		}
		offset -= SHMOO_MIN_BLOCK_SIZE;
	}
	if (offset < 0) {
		printk(BIOS_ERR, " Signature mismatch ");
		return NULL;
	}
	ptr += 3;

	/* Verify checksum */
	checksum = *ptr++;
	length = *ptr++;
	if (pairs != length) {
		/* Pair count unmatched */
		printk(BIOS_ERR, " Pair count mismatch pairs %x length %x",pairs, length);
		return NULL;
	}
	chksum = 0;
	for(i=0; i<length * 2; i++, ptr++) {
		chksum += *ptr;
	}
	if (chksum != checksum) {
		printk(BIOS_ERR, " Checksum mismatch cksum: %x checksum:%x",chksum,checksum);
		return NULL;
	}

	return flptr + offset;
}

static int try_restore_shmoo(void)
{
    int invalid = 0;
    struct shmoo_signature sig;
    volatile uint32_t *flptr;
    volatile uint32_t *reg;
    uint32_t val;
    int pairs, i;

    /* Validate values in flash */
    printk(BIOS_INFO, "Validate Shmoo parameters stored in flash ..... ");
    flptr = validate_flash_shmoo_values(&sig, &pairs);
    if (flptr == NULL) {
        printk(BIOS_ERR, "failed\n");
        return 1;
    }
    printk(BIOS_INFO, "OK\n");

    /* Check if user wants to skip restoring and run Shmoo */
    if (CONFIG_SHMOO_REUSE_DELAY_MSECS > 0) {
        char c = 0;
        unsigned long start;
        printk(BIOS_INFO, "Press Ctrl-C to run Shmoo ..... ");
        start = get_timer(0);
        while(get_timer(start) <= CONFIG_SHMOO_REUSE_DELAY_MSECS) {
            if (tstc()) {
                c = getc();
                if (c == 0x03) {
                    printk(BIOS_INFO, "Pressed.\n");
                    printk(BIOS_INFO, "Do you want to run the Shmoo? [y/N] ");
                    for(;;) {
                        c = getc();
                        if (c == 'y' || c == 'Y') {
                            printk(BIOS_INFO, "Y\n");
                            invalid = 1;
                            break;
                        } else if (c == '\r' || c == 'n' || c == 'N') {
                            if (c != '\r')
                                printk(BIOS_INFO, "N\n");
                            break;
                        }
                    }
                    break;
                } else {
                    c = 0;
                }
            }
        }
        if (c == 0)
            printk(BIOS_INFO, "skipped\n");
    }

    if (invalid) {
        return 1;
    }

    /* Restore values from flash */
    printk(BIOS_INFO, "Restoring Shmoo parameters from flash ..... ");
    flptr += 5;
    for(i=0; i<pairs; i++) {
        reg = (uint32_t *)(*flptr++);
        val = (uint32_t *)(*flptr++);
	if( (((uint32_t)reg >= DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE_RD_EN) && ((uint32_t)reg <= (DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE_RD_EN + 0x114)))
#if (CONFIG_CYGNUS_SHMOO_REUSE_DDR_32BIT || defined(CONFIG_NS_PLUS))
		|| (((uint32_t)reg >= DDR_PHY_WORD_LANE_1_VDL_OVRIDE_BYTE_RD_EN) && ((uint32_t)reg <= (DDR_PHY_WORD_LANE_1_VDL_OVRIDE_BYTE_RD_EN + 0x114)))
#endif
#ifdef CONFIG_IPROC_DDR_ECC
		|| (((uint32_t)reg >= (DDR_DENALI_CTL_00 + DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_RD_EN_BASE)) && ((uint32_t)reg <= (DDR_DENALI_CTL_00 + DDR_PHY_ECC_LANE_VDL_OVRIDE_BYTE_BIT_RD_EN_BASE)))
#endif
	) {
		val |= (1 << 17); /* Force Override */
	}
        // printk(BIOS_INFO, "Writing 0x%x to 0x%x\n",val,reg);
        reg32_write(reg,val);

        reg32_read(reg); /* Dummy read back */
    }
    printk(BIOS_INFO, "done\n");

    /* Perform memory test to see if the parameters work */
    if (CONFIG_SHMOO_REUSE_MEMTEST_LENGTH > 0 ) {
        printk(BIOS_INFO, "Running simple memory test ..... ");
        i = simple_memory_test(
            (void *)CONFIG_SHMOO_REUSE_MEMTEST_START,
            CONFIG_SHMOO_REUSE_MEMTEST_LENGTH);
        if (i) {
            printk(BIOS_ERR, "failed!\n");
            return 1;
        }
        printk(BIOS_INFO, "OK\n");
    }

    return 0;
}

#define SHMOO_REG_BUFFER_SIZE 100;
static uint32_t shmoo_reg_buffer[SHMOO_REG_BUFFER_SIZE];

void iproc_save_shmoo_values(void)
{
    uint32_t *buffer, *ptr;
    volatile uint32_t *flptr;
    uint32_t reg, val;
    struct shmoo_signature sig;
    int pairs, length;
    uint32_t chksum;
    int i;

    /* Check if flash already contains valid data  */
    flptr = validate_flash_shmoo_values(&sig, &pairs);
    if (flptr != NULL) {
        /* Check if the flash data are the same as current DDR PHY values */
        flptr += 5;
	for(i=0; i<pairs; i++) {
	    reg = *flptr++;
	    val = *flptr++;
            if (val != reg32_read(reg)) {
                break;
            }

	}
	if (i == pairs) {
		/* No difference found; Saving skipped */
	    return;
	}
    }

    /* Calculate size of buffer */
    length = 12 +
        sizeof(uint32_t) * 2 +
        sizeof(uint32_t) * pairs * 2;

    /* Allocate buffer */
	if (length > size(uint32_t) * SHMOO_REG_BUFFER_SIZE) {
        printk(BIOS_INFO, "Error pre-allocated shmoo register buffer is not large enough!\n");
        return;
	}

    buffer = shmoo_reg_buffer;
    ptr = buffer;

    /* Fill signature */
    shmoo_sig2mem(&sig,ptr);
    ptr += 5;

    /* Copy registers and values to buffer */
    chksum = 0;
    for(i=0; i<sizeof(ddr_phy_ctl_regs) / sizeof(ddr_phy_ctl_regs[0]); i++) {
        reg = (uint32_t)DDR_PHY_CONTROL_REGS_REVISION + ddr_phy_ctl_regs[i];
        *ptr++ = reg;
        chksum += reg;
        // val = *(volatile uint32_t *)reg;
        val = reg32_read((volatile uint32_t *)reg);
        *ptr++ = val;
        chksum += val;
    }
    for(i=0; i<sizeof(ddr_phy_wl_regs) / sizeof(ddr_phy_wl_regs[0]); i++) {
        reg = (uint32_t)DDR_PHY_WORD_LANE_0_VDL_OVRIDE_BYTE_RD_EN + ddr_phy_wl_regs[i];
        *ptr++ = reg;
        chksum += reg;
        // val = *(volatile uint32_t *)reg;
        val = reg32_read((volatile uint32_t *)reg);
	*ptr++ = val;
        chksum += val;
    }
#if (CONFIG_CYGNUS_SHMOO_REUSE_DDR_32BIT || defined(CONFIG_NS_PLUS))
	if (is_ddr_32bit()) {
	    for(i=0; i<sizeof(ddr_phy_wl_regs) / sizeof(ddr_phy_wl_regs[0]); i++) {
	        reg = (uint32_t)DDR_PHY_WORD_LANE_1_VDL_OVRIDE_BYTE_RD_EN + ddr_phy_wl_regs[i];
	        *ptr++ = reg;
	        chksum += reg;
	        // val = *(volatile uint32_t *)reg;
		val = reg32_read((volatile uint32_t *)reg);
		*ptr++ = val;
	        chksum += val;
	    }
	}
#endif /* (CONFIG_CYGNUS_SHMOO_REUSE_DDR_32BIT || defined(CONFIG_NS_PLUS)) */
#ifdef CONFIG_IPROC_DDR_ECC
    for(i=0; i<sizeof(ddr_phy_eccl_regs) / sizeof(ddr_phy_eccl_regs[0]); i++) {
        reg = (uint32_t)DDR_DENALI_CTL_00 + ddr_phy_eccl_regs[i];
        *ptr++ = reg;
        chksum += reg;
        // val = *(volatile uint32_t *)reg;
        val = reg32_read((volatile uint32_t *)reg);
	*ptr++ = val;
        chksum += val;
    }
#endif

    /* Fill checksum and length */
    buffer[3] = chksum;
    buffer[4] = pairs;

    /* Write to flash */
    printk(BIOS_INFO, "Writing Shmoo values into flash .....\n");
    i = write_shmoo_to_flash(buffer, length);

    /* Free buffer */
//    free(buffer);
}

#endif /* CONFIG_RUN_DDR_SHMOO2 && CONFIG_SHMOO_REUSE */

#include "soc/ddr_bist.h"
#include "soc/shmoo_and28/shmoo_and28.h"

#ifdef CONFIG_IPROC_DDR_ECC
static int clear_ddr(uint32_t offset, uint32_t size)
{
	unsigned long start;
	unsigned int i, val;

    reg32_write((uint32_t *)DDR_BistConfig,reg32_read((uint32_t *)DDR_BistConfig) & ~0x1);

    for( i = 0; i < 1000; i++);

#if !defined(CONFIG_IPROC_P7)
	reg32_write((volatile uint32_t *)DDR_DENALI_CTL_213, 0x00FFFFFF);
#endif

	reg32_write((volatile uint32_t *)DDR_BistConfig, 0x00000002);
	reg32_write((volatile uint32_t *)DDR_BistConfig, 0x00000003);
	reg32_write((volatile uint32_t *)DDR_BistConfig, 0x0000C003);
	reg32_write((volatile uint32_t *)DDR_BistGeneralConfigurations, 0x00000020);

	val =  255 << DDR_BistConfigurations__WriteWeight_R |
		  0 << DDR_BistConfigurations__ReadWeight_R |
		  1 << DDR_BistConfigurations__ConsAddr8Banks;

	reg32_write((volatile uint32_t *)DDR_BistConfigurations, val);


	reg32_write((volatile uint32_t *)DDR_BistStartAddress, offset);
	reg32_write((volatile uint32_t *)DDR_BistEndAddress, (1 << DDR_BistEndAddress__BistEndAddress_WIDTH) - 1);
	reg32_write((volatile uint32_t *)DDR_BistNumberOfActions, (size + 31) / 32);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord0, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord1, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord2, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord3, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord4, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord5, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord6, 0);
	reg32_write((volatile uint32_t *)DDR_BistPatternWord7, 0);

	reg32_set_bits((volatile uint32_t *)DDR_BistConfigurations, 1 << DDR_BistConfigurations__IndWrRdAddrMode);

	reg32_set_bits((volatile uint32_t *)DDR_BistConfigurations, 1 << DDR_BistConfigurations__BistEn);

	start = get_timer(0);
	while(get_timer(start) <= 10000) {
		if(reg32_read((volatile uint32_t *)DDR_BistStatuses) & (1 << DDR_BistStatuses__BistFinished))
			break;
	}
	/* Clear BIST_EN bit */
	reg32_clear_bits((volatile uint32_t *)DDR_BistConfigurations, 1 << DDR_BistConfigurations__BistEn);

	if((get_timer(start) <= 10000)  &&
	   (!reg32_read((volatile uint32_t *)DDR_BistErrorOccurred)))
	{
		printk(BIOS_INFO, "clear_ddr: OK\n");
		return(0);
	}
	printk(BIOS_INFO, "clear_ddr: Failed: 0x%lx\n", get_timer(start));
	if(reg32_read((volatile uint32_t *)DDR_BistErrorOccurred))
		printk(BIOS_ERR, "clear_ddr: Error occurred\n");
	return(1);
}
#endif /* CONFIG_IPROC_DDR_ECC */

#if defined(CONFIG_SHMOO_AND28_REUSE)
extern void restore_shmoo_config(and28_shmoo_config_param_t *shmoo_control_para);
#endif

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
static int simple_ddr_crc32_check(void)
{
	return 0;
	register uint32_t crc_mcu = 0;
	register uint32_t crc = 0, offset = 0;
	register volatile uint32_t *buf = (uint32_t *)0x60000000;
	register uint32_t len = 0x00100000;//in word

	printk(BIOS_INFO, "Checking simple DDR CRC, word start 0x%p, len 0x%08x...\n", buf, len);

	for(offset=0; offset<len; offset++)
	{
		crc ^= *buf++;
	}

	crc_mcu = reg32_read((volatile uint32_t *)0x03012A00);

	if(crc != crc_mcu)
	{
		printk(BIOS_ERR, "DDR CRC NOT match, old=0x%08x, new=0x%08x!\n", crc_mcu, crc);
		return -1;
	}
	else
	{
		printk(BIOS_INFO, "DDR CRC 0x%08x, match!\n", crc);
		return 0;
	}
}
#endif

void ddr_init2(void)
{
	int i;
	volatile unsigned int val;
	int ddr_type;
	uint32_t status, sku_id, ddr_clk, dev_id = 0;
	uint32_t unit = 0;
	uint32_t skip_shmoo = 0;
#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	uint32_t pwrctli0 = reg32_read((volatile uint32_t *)IHOST_SCU_POWER_STATUS)  & 0x3;
	skip_shmoo = reg32_read((volatile uint32_t *)CRMU_IHOST_POR_WAKEUP_FLAG) & 0x1;

	if(pwrctli0==2)
	{
		goto wakeup;
	}
	else if(pwrctli0==3)
	{
		skip_shmoo = 1;
		reg32_write((volatile uint32_t *)IHOST_GTIM_GLOB_CTRL, reg32_read((volatile uint32_t *)IHOST_GTIM_GLOB_CTRL)| 0x1);
	}
#endif	/* IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS) */

	dev_id = dev_id;
#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	and28_shmoo_dram_info_t sdi;
	and28_shmoo_config_param_t config_param;
#endif

#if !IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	dev_id = (reg32_read((volatile uint32_t *)ChipcommonA_ChipID)) & 0x0000ffff;
#else
    dev_id = 0x5800;
    cygnus_phy_powerup();
#endif

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	sku_id = (reg32_read((volatile uint32_t *)ROM_S0_IDM_IO_STATUS) >> 8) & 0x0f;
#else
	sku_id = (reg32_read((volatile uint32_t *)ROM_S0_IDM_IO_STATUS) >> 2) & 0x03;
#endif
	/* See if it is KATANA2, KATANA2 doesn't have right chip ID in ChipcommonA_ChipID */
	if(((sku_id & 0xfff0) == 0xa450) || ((sku_id & 0xfff0) == 0xb450) || sku_id == 0xb248) {
		dev_id = 56450; /* KATANA2 */
	}

	printk(BIOS_INFO, "DEV ID = 0x%x\n", dev_id);

	printk(BIOS_INFO, "SKU ID = 0x%x\n", sku_id);

#if defined(CONFIG_IPROC_P7)
	val = reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS) & 0x3;
	if (val == 0) {
		ddr_type = 1;
	} else if (val == 1) {
		ddr_type = 2;
	} else {
		printk(BIOS_ERR, "Unsupported DDR type: %d\n", val);
		goto done;
	}
    printk(BIOS_INFO, "DDR type: DDR%d\n", (ddr_type == 1)? 3 : 4);
#elif IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
    ddr_type = 1;
#else
	ddr_type = reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS) & 0x1;
	printk(BIOS_INFO, "DDR type: DDR%d\n", (ddr_type) ? 3 : 2);
#endif /* defined(CONFIG_IPROC_P7) */

	ddr_clk = get_ddr_clock(sku_id, ddr_type);
	printk(BIOS_INFO, "MEMC 0 DDR speed = %dMHz\n", ddr_clk);

	status = change_ddr_clock(ddr_clk);
	if(status) {
		printk(BIOS_INFO, "CRU LCPLL configuratioin failed\n");
		goto done;
	}

#if defined(CONFIG_IPROC_P7)
	val = reg32_read((volatile uint32_t *)CRU_ddrphy_pwr_ctrl);

	/* assert power ON */
	val |= 1 << CRU_ddrphy_pwr_ctrl__i_pwronin_phy;
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* assert power OK */
	__udelay(10);
	val |= 1 << CRU_ddrphy_pwr_ctrl__i_pwrokin_phy;
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* remove DFI isolation */
	__udelay(150);
	val &= ~(1 << CRU_ddrphy_pwr_ctrl__i_iso_phy_dfi);
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* remove PHY register isolation */
	val &= ~(1 << CRU_ddrphy_pwr_ctrl__i_iso_phy_regs);
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* remove PLL isolation */
	val &= ~(1 << CRU_ddrphy_pwr_ctrl__i_iso_phy_pll);
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* de-assert reset */
	__udelay(200);
	val |= 1 << CRU_ddrphy_pwr_ctrl__i_hw_reset_n;
	reg32_write((volatile uint32_t *)CRU_ddrphy_pwr_ctrl, val);

	/* Wait for PHY power up */
	for(i=0; i < 0x19000; i++) {
		val = reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS);
		if((val & (1 << DDR_S1_IDM_IO_STATUS__o_phy_pwrup_rsb)))
			break;
	}
	if(i == 0x19000) {
		printk(BIOS_ERR, "DDR PHY not power up\n");
		goto done;
	}
#endif /* defined(CONFIG_IPROC_P7) */

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS) || defined(CONFIG_IPROC_P7)
	/* Get the DDR S1 and S2 out of reset */
	reg32_write((volatile uint32_t *)DDR_S1_IDM_RESET_CONTROL, 0);
	reg32_write((volatile uint32_t *)DDR_S2_IDM_RESET_CONTROL, 0);

	__udelay(1000);
	reg32_write((volatile uint32_t *)DDR_S0_IDM_RESET_CONTROL, 0);
	/* Set the ddr_ck to 400 MHz, 2x memc clock */
	reg32_write_masked((volatile uint32_t *)DDR_S1_IDM_IO_CONTROL_DIRECT, 0xfff << 16, /*ddr_clk*/ 0x190 << 16);

	if(pwrctli0==3)
	{
		printk(BIOS_INFO, "\n PRE_SRX call \n");
		PRE_SRX();
	}
#else
    reg32_write((volatile uint32_t *)DDR_S1_IDM_RESET_CONTROL, 0);
    reg32_write((volatile uint32_t *)DDR_S2_IDM_RESET_CONTROL, 0);
    /* Set the ddr_ck to 400 MHz, 2x memc clock */
    reg32_write_masked((volatile uint32_t *)DDR_S1_IDM_IO_CONTROL_DIRECT, 0xfff << 16, /*ddr_clk*/ 0x190 << 16);
#endif /* IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS) || defined(CONFIG_IPROC_P7) */

#if defined(CONFIG_IPROC_P7)
	if (is_ddr_32bit()) {
		reg32_write_masked(
			(volatile uint32_t *)DDR_S2_IDM_IO_CONTROL_DIRECT,
				1 << DDR_S2_IDM_IO_CONTROL_DIRECT__mode_32b,
				1 << DDR_S2_IDM_IO_CONTROL_DIRECT__mode_32b
				);
	}

	/* Wait for PHY ready */
	for(i=0; i < 0x19000; i++) {
		val = reg32_read((volatile uint32_t *)DDR_S1_IDM_IO_STATUS);
		if((val & (1 << DDR_S1_IDM_IO_STATUS__o_phy_ready)))
			break; /* DDR PHY is up */
	}

	if(i == 0x19000) {
		printk(BIOS_ERR, "DDR PLL not locked\n");
		goto done;
	}

	/* Get the DDR S0 out of reset */
	reg32_write((volatile uint32_t *)DDR_S0_IDM_RESET_CONTROL, 0);
#endif /* defined(CONFIG_IPROC_P7) */

	/* Wait for DDR PHY up */
	for(i=0; i < 0x19000; i++) {
		val = reg32_read((volatile uint32_t *)DDR_PHY_CONTROL_REGS_REVISION);
		if( val != 0) {
            printk(BIOS_INFO, "PHY revision version: 0x%08x\n", val);
			break; /* DDR PHY is up */
        }
	}

	if(i == 0x19000) {
		printk(BIOS_ERR, "DDR PHY is not up\n");
		return;
	}

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	if(!skip_shmoo)
	{
		printk(BIOS_INFO, "ddr_init2: Calling soc_and28_shmoo_dram_info_set\n");
		/* Cygnus clock speed:
		*
		*    clock       rate
		*    400         800
		*    533         1066
		*    667         1333
		*    800         1600
		*/
		sdi.data_rate_mbps = (ddr_clk == 667) ? 1333 : ((ddr_clk == 333) ? 667 : (ddr_clk << 1));
		sdi.ref_clk_mhz = 50;
		sdi.ctl_type = DDR_CTL_TYPE_1;
		sdi.dram_type = DDR_DRAM_TYPE_DDR3L;
		sdi.dram_bitmap = 0x00000001;
		sdi.interface_bitwidth = SDI_INTERFACE_BITWIDTH;
		sdi.num_columns = SDI_NUM_COLUMNS;
		sdi.num_rows = SDI_NUM_ROWS;
		sdi.num_banks = SDI_NUM_BANKS;
		sdi.refi = 7800;
		sdi.command_parity_latency = 0;
		sdi.sim_system_mode = 0;
		printk(BIOS_INFO, "ddr_init2: Calling soc_and28_shmoo_dram_info_set\n");
		soc_and28_shmoo_dram_info_set(unit, &sdi);
	}
	else
	{
		printk(BIOS_INFO, "DeepSleep wakeup: ddr init bypassed 1\n");
	}
#else
#error "DRAM config is not set"
#endif

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	if(!skip_shmoo)
	{
		printk(BIOS_INFO, "ddr_init2: Calling soc_and28_shmoo_phy_init\n");
		if(soc_and28_shmoo_phy_init(unit, 0) != SOC_E_NONE) {

			printk(BIOS_ERR, "DDR PHY initialization failed\n");
			goto done;
		}
	}
	else
	{
		printk(BIOS_INFO, "DeepSleep wakeup: ddr init bypassed 2\n");
	}
#endif

#ifdef CONFIG_RUN_DDR_SHMOO
	printk(BIOS_DEBUG, "PHY register dump after DDR PHY init\n");
	dump_phy_regs();
#endif

	printk(BIOS_INFO, "Programming controller register\n");
	ddr_init_regs(ddr_init_tab);

    ddr_type = 1;
	if(ddr_type) {
		/* DDR3 */
		switch(ddr_clk) {
#ifdef CONFIG_DDR333
			case 333:
				ddr_init_regs(ddr3_init_tab_667);
				break;
#endif
#ifdef CONFIG_DDR400
			case 400:
				ddr_init_regs(ddr3_init_tab_800);
				break;
#endif
#ifdef CONFIG_DDR533
			case 533:
				ddr_init_regs(ddr3_init_tab_1066);
				break;
#endif
#ifdef CONFIG_DDR667
			case 667:
				ddr_init_regs(ddr3_init_tab_1333);
				break;
#endif
#if (defined(CONFIG_DDR750) || IS_ENABLED(CONFIG_CYGNUS_DDR800))
			case 750:
			case 800:
				ddr_init_regs(ddr3_init_tab_1600);
				break;
#endif
		}
	}

#if CONFIG_CYGNUS_DDR_AUTO_SELF_REFRESH_ENABLE
#if (DDR_AUTO_SELF_REFRESH_IDLE_COUNT > 0) & (DDR_AUTO_SELF_REFRESH_IDLE_COUNT <= 0xff)
	/* Enable auto self-refresh */
	reg32_set_bits((unsigned int *)DDR_DENALI_CTL_57,
		0x2 << DDR_DENALI_CTL_57__LP_AUTO_EXIT_EN_R |
		0x2 << DDR_DENALI_CTL_57__LP_AUTO_ENTRY_EN_R );

	reg32_set_bits((unsigned int *)DDR_DENALI_CTL_58,
		DDR_AUTO_SELF_REFRESH_IDLE_COUNT << DDR_DENALI_CTL_58__LP_AUTO_SR_IDLE_R);
#else
	#error DDR_AUTO_SELF_REFRESH_IDLE_COUNT out of range
#endif
#else
	/* Disable auto-self refresh */
	reg32_clear_bits((unsigned int *)DDR_DENALI_CTL_57,
		0x2 << DDR_DENALI_CTL_57__LP_AUTO_EXIT_EN_R |
		0x2 << DDR_DENALI_CTL_57__LP_AUTO_ENTRY_EN_R );
	reg32_clear_bits((unsigned int *)DDR_DENALI_CTL_58,
		0xff << DDR_DENALI_CTL_58__LP_AUTO_SR_IDLE_R );
#endif

	/* Start the DDR */
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_00, 0x01);

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	if(!skip_shmoo)
	{
		while(!(reg32_read((volatile uint32_t *)DDR_DENALI_CTL_175) & 0x100));
		printk(BIOS_INFO, "ddr_init2: MemC initialization complete\n");

		reg32_set_bits((unsigned int *)DDR_DENALI_CTL_177, 0x00100);
		reg32_write((unsigned int *)DDR_BistConfig, 0x00000002);
		reg32_write((unsigned int *)DDR_BistConfig, 0x00000003);
		reg32_write((unsigned int *)DDR_BistConfig, 0x0000C003);
		reg32_write((unsigned int *)DDR_BistGeneralConfigurations, 0x00000020);

		printk(BIOS_INFO, "ddr_init2: Calling soc_and28_shmoo_ctl\n");
  #if defined(CONFIG_SHMOO_AND28_REUSE)
		if (is_shmoo_data_valid()) {
			restore_shmoo_config(&config_param);
			soc_and28_shmoo_ctl(unit, 0, SHMOO_AND28_SHMOO_RSVP, 0, 1, SHMOO_AND28_ACTION_RESTORE, &config_param);

    #if defined(CONFIG_SHMOO_REUSE_MEMTEST_LENGTH)
			/* Perform memory test to see if the stored SHMMO values work */
			if (CONFIG_SHMOO_REUSE_MEMTEST_LENGTH > 0) {
				/* Release DDR to AXI for memory testing */
				reg32_clear_bits((volatile uint32_t *)DDR_BistConfig, 1 << DDR_BistConfig__axi_port_sel);

				printk(BIOS_INFO, "Running simple memory test ..... ");
				i = simple_memory_test(
					(void *)CONFIG_SHMOO_REUSE_MEMTEST_START,
					CONFIG_SHMOO_REUSE_MEMTEST_LENGTH);
				if (i) {
					printk(BIOS_ERR, "failed!\n");

					/* Connect DDR controller to BIST for SHMOO */
					reg32_set_bits((volatile uint32_t *)DDR_BistConfig, 1 << DDR_BistConfig__axi_port_sel);

					/* Perform full SHMOO since stored values don't work */
					soc_and28_shmoo_ctl(unit, 0, SHMOO_AND28_SHMOO_RSVP, 0, 1, SHMOO_AND28_ACTION_RUN, &config_param);
				} else {
					printk(BIOS_INFO, "OK\n");
				}
			}
    #endif /* defined(CONFIG_SHMOO_REUSE_MEMTEST_LENGTH) */

		} else {
			soc_and28_shmoo_ctl(unit, 0, SHMOO_AND28_SHMOO_RSVP, 0, 1, SHMOO_AND28_ACTION_RUN, &config_param);
		}
  #else
		soc_and28_shmoo_ctl(unit, 0, SHMOO_AND28_SHMOO_RSVP, 0, 1, SHMOO_AND28_ACTION_RUN, &config_param);
  #endif /* CONFIG_SHMOO_AND28_REUSE */
	}
#endif
	else
	{
		printk(BIOS_INFO, "DeepSleep wakeup: ddr init bypassed 3\n");
	}

#if defined(CONFIG_IPROC_P7) && defined(CONFIG_IPROC_DDR_ECC)
	printk(BIOS_INFO, "Enabling DDR ECC correcting and reporting\n");

	/* Clear DDR ECC interrupts if any */
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_177,
		DDR_DENALI_CTL_177_ECC_MASK);

	/* Disable auto corruption */
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_148,
		1 << DDR_DENALI_CTL_148__ECC_DISABLE_W_UC_ERR);

	/* Enable ECC correction and reporting */
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_146,
		1 << DDR_DENALI_CTL_146__ECC_EN);

	/* Initialize DDR so that uninitialized reads won't report ecc error */
	clear_ddr(0, CONFIG_PHYS_SDRAM_1_SIZE);
#elif defined(CONFIG_IPROC_DDR_ECC)
	printk(BIOS_INFO, "Enabling DDR ECC reporting\n");
	/* Clear DDR interrupts if any */
	*(unsigned int *)(DDR_DENALI_CTL_213) = 0x00FFFFFF;
	__udelay(1000);
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_67, 0x01); //Disable auto correction
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_66, 0x01); //Enable ECC

	clear_ddr(0, CONFIG_PHYS_SDRAM_1_SIZE);
	printk(BIOS_INFO, "Enabling DDR ECC correction\n");
	reg32_set_bits((volatile uint32_t *)DDR_DENALI_CTL_66, 1 << 1); //Enable ECC correction
#endif /* defined(CONFIG_IPROC_P7) && defined(CONFIG_IPROC_DDR_ECC) */

	/* Release DDR slave port to AXI */
	reg32_clear_bits((volatile uint32_t *)DDR_BistConfig, 1 << DDR_BistConfig__axi_port_sel);
	printk(BIOS_INFO, "DDR Interface Ready\n");

	//dump_phy_regs();

#if IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS)
	/* SRX */
	if(skip_shmoo)
	{
		// Enter Self refresh (dummy) , to keep Denali happy
		reg32_write((unsigned int *)DDR_DENALI_CTL_56, 0x0a050505);

		__udelay(200);
		printk(BIOS_INFO, "\nDDR self refresh exit \n");

		// Assert DFI request from PHY to mask any interaction with MEMC
		reg32_write((unsigned int *)DDR_PHY_CONTROL_REGS_DFI_CNTRL, 0xe0);
		reg32_write((unsigned int *)DDR_PHY_CONTROL_REGS_DFI_CNTRL, 0);

		// Exit Self refresh
		reg32_write((unsigned int *)DDR_DENALI_CTL_56, 0x09050505);
	}

	/* Clear iHOST flag */
	reg32_write((unsigned int *)CRMU_IHOST_POR_WAKEUP_FLAG, 0x0);
	printk(BIOS_INFO, "IHOST POR WAKEUP FLAG cleared\n");

//	iproc_dump_ddr_regs();

	if(pwrctli0==0)
		goto done;

wakeup:
	printk(BIOS_INFO, "Wakeup from %s\n", pwrctli0==2 ? "SLEEP":"DEEPSLEEP");

	if(pwrctli0==3)
	{
		__udelay(10000);
		if(simple_ddr_crc32_check()<0)
		{
			printk(BIOS_INFO, "Die...\n");
			while(1);
		}
	}

	/* CRMU_IHOST_SW_PERSISTENT_REG4 = 0x03024c64 */
	asm(
		"movw	r3, #0x4c64\n"
		"movt	r3, #0x0302\n"
		"ldr 	r5, [r3]\n"
		"mov	lr, #0\n"
		"mov	pc, r5\n");
#endif /* IS_ENABLED(CONFIG_SOC_BROADCOM_CYGNUS) */

done:
	/* Reclaim everything we have previously allocated for temporary usage. */
//	free_heap();
	return;
}
