/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2009 One Laptop per Child, Association, Inc.
 *
 * 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 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

void DRAMSetVRNum(DRAM_SYS_ATTR *DramAttr, u8 PhyRank /* physical rank */,
		  u8 VirRank /* virtual rank */, BOOLEAN Enable);
void SetEndingAddr(DRAM_SYS_ATTR *DramAttr, u8 VirRank /* Ending address
                   register number indicator (INDEX */, INT8 Value /* (value)
                   add or subtract value to this and after banks. */);
void InitDDR2CHA(DRAM_SYS_ATTR *DramAttr);
void InitDDR2CHB(DRAM_SYS_ATTR *DramAttr);
void InitDDR2CHC(DRAM_SYS_ATTR *DramAttr);

CB_STATUS VerifyChc(void);

/*===================================================================
Function   : DRAMRegInitValue()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
Output     : Void
Purpose   : Set necessary register before DRAM initialize
===================================================================*/

static const u8 DramRegTbl[][3] = {
	/* Reg AND   OR */
	{0x50, 0x11, 0xEE},	// DDR default MA7 for DRAM init
	{0x51, 0x11, 0x60},	// DDR default MA3 for CHB init
	{0x52, 0x00, 0x33},	// DDR use BA0 = M17, BA1 = M18,
	{0x53, 0x00, 0x3F},	// DDR     BA2 = M19

	{0x54, 0x00, 0x00},	// default PR0 = VR0; PR1 = VR1
	{0x55, 0x00, 0x00},	// default PR2 = VR2; PR3 = VR3
	{0x56, 0x00, 0x00},	// default PR4 = VR4; PR5 = VR5
	{0x57, 0x00, 0x00},	// default PR4 = VR4; PR5 = VR5

	{0x60, 0x00, 0x00},	// disable fast turn-around
	{0x65, 0x00, 0xD9},	// AGP timer = 0XD; Host timer = 8;
	{0x66, 0x00, 0x88},	// DRAMC Queue Size = 4; park at the last bus
				// owner,Priority promotion timer = 8
	{0x68, 0x00, 0x0C},
	{0x69, 0xF0, 0x04},	// set RX69[3:0]=0000b
	{0x6A, 0x00, 0x00},	// refresh counter
	{0x6E, 0xF8, 0x80},	// must set 6E[7], or else DDR2 probe test
				// will fail
	/*
	 * In here, we not set RX70~RX74, because we just init DRAM but no
	 * need R/W DRAM, when we check DQS input/output delay, then we need
	 * R/W DRAM.
	 */

	{0x85, 0x00, 0x00},
	{0x40, 0x00, 0x00},

	{0, 0, 0}
};

void DRAMRegInitValue(DRAM_SYS_ATTR *DramAttr)
{
	u8 Idx, CL, Data;

	for (Idx = 0; DramRegTbl[Idx][0] != 0; Idx++) {
		Data = pci_read_config8(MEMCTRL, DramRegTbl[Idx][0]);
		Data &= DramRegTbl[Idx][1];
		Data |= DramRegTbl[Idx][2];
		pci_write_config8(MEMCTRL, DramRegTbl[Idx][0], Data);
	}

	Data = 0x80;
	pci_write_config8(PCI_DEV(0, 0, 4), 0xa3, Data);

	// Set DRAM controller mode. */
	Data = pci_read_config8(MEMCTRL, 0x6c);
	Data &= 0xFB;
	if (ENABLE_CHC == 0) {
		Data |= 0x4;	/* Only CHA 64 bit mode */
		pci_write_config8(MEMCTRL, 0x6c, Data);
	} else {
		Data |= 0x0;	/* CHA + CHC */
		pci_write_config8(MEMCTRL, 0x6c, Data);

		// set CHB DQSB input delay, or else will meet error which
		// is some byte is right but another bit is error.
		Data = pci_read_config8(MEMCTRL, 0xff);
		Data = (Data & 0x03) | 0x3D;
		pci_write_config8(MEMCTRL, 0xff, Data);

		// enable CHC  RXDB[7]

		// rx62[2:0], CHA and CHB CL
		Data = pci_read_config8(MEMCTRL, 0x62);
		CL = Data & 0x07;

		// If CL = 6, so I set CHB CL = 5 default.
		if (CL >= 4)
			CL = 3;

		/* Set CHC Read CL rxDC[6:7]. */
		Data = pci_read_config8(MEMCTRL, 0xdc);
		Data = (Data & 0x3F) | (CL << 6);
		pci_write_config8(MEMCTRL, 0xdc, Data);

		/* Set CHC write CL rxDF[6:7]. */
		Data = pci_read_config8(MEMCTRL, 0xdf);
		Data = (Data & 0x3F) | (CL << 6);
		pci_write_config8(MEMCTRL, 0xdf, Data);

		/* Set CHC ODT RxDC[5:0] */
		Data = pci_read_config8(MEMCTRL, 0xdc);
		Data = (Data & 0xC0) | 0x03;
		pci_write_config8(MEMCTRL, 0xdc, Data);

		/* Set column type RXDD[6] and enable ODT PAD RXDD[7]. */
		Data = pci_read_config8(MEMCTRL, 0xdd);
		Data |= 0x80;
		Idx = DramAttr->DimmInfo[2].SPDDataBuf[SPD_SDRAM_COL_ADDR];
		if ((Idx & 0x0F) == 10)
			Data |= 0x40;	/* MA9~MA0 */
		else
			Data &= 0xBF;	/* MA8~MA0 */
		pci_write_config8(MEMCTRL, 0xdd, Data);
	}

	// Disable Read DRAM fast ready ;Rx51[7]
	// Disable Read Around Write    ;Rx51[6]

	// Disable Consecutive Read     ;RX52[1:0]
	// Disable Speculative Read
}

/*===================================================================
Function   : DRAMInitializeProc()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
Output     : Void
Purpose   : DRAM initialize according to the bios porting guid
===================================================================*/

#define EXIST_TEST_PATTERN		0x55555555
#define NOT_EXIST_TEST_PATTERN		0xAAAAAAAA

static BOOLEAN ChkForExistLowBank(void)
{
	u32 *Address, data32;

	/* Check pattern */
	Address = (u32 *) 8;
	*Address = EXIST_TEST_PATTERN;
	Address = (u32 *) 4;
	*Address = EXIST_TEST_PATTERN;

	// _asm {WBINVD}
	WaitMicroSec(100);
	Address = (u32 *) 8;
	data32 = *Address;
	if (data32 != EXIST_TEST_PATTERN)
		return FALSE;
	Address = (u32 *) 4;
	data32 = *Address;
	if (data32 != EXIST_TEST_PATTERN)
		return FALSE;

	/* Check not Pattern */
	Address = (u32 *) 8;
	*Address = NOT_EXIST_TEST_PATTERN;
	Address = (u32 *) 4;
	*Address = NOT_EXIST_TEST_PATTERN;
	// _asm {WBINVD}
	WaitMicroSec(100);

	Address = (u32 *) 8;
	data32 = *Address;
	if (data32 != (u32) (NOT_EXIST_TEST_PATTERN))
		return FALSE;
	Address = (u32 *) 4;
	data32 = *Address;
	if (data32 != (u32) (NOT_EXIST_TEST_PATTERN))
		return FALSE;

	return TRUE;
}

void DRAMInitializeProc(DRAM_SYS_ATTR *DramAttr)
{
	u8 shift, idx;
	BOOLEAN Status;

	shift = 1;
	for (idx = 0; idx < MAX_RANKS; idx++) {
		if ((DramAttr->RankPresentMap & shift) != 0) {
			/*
			 * Set VR# to physical rank indicated = PR + physical
			 * rank enable bit.
			 */
			DRAMSetVRNum(DramAttr, idx, idx, TRUE);
			SetEndingAddr(DramAttr, idx, 0x10); /* Assume 1G size */
			if (idx < 4) /* CHA init */
				InitDDR2CHA(DramAttr);	// temp wjb 2007/1 only for compiling
			// in the function InitDDR2,the parameter is no need
			Status = ChkForExistLowBank();
			if (Status == TRUE) {
				PRINT_DEBUG_MEM(" S\r");
			} else {
				PRINT_DEBUG_MEM(" F\r");
			}

			/*
			 * Set VR# to physical rank indicated = 00h + physical
			 * rank enable bit.
			 */
			DRAMSetVRNum(DramAttr, idx, 0, FALSE);
			SetEndingAddr(DramAttr, idx, -16);
		}
		shift <<= 1;
	}
	if (ENABLE_CHC)
		InitDDR2CHC(DramAttr);

}

/*===================================================================
Function   : DRAMSetVRNUM()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
		  PhyRank:   Physical Rank number
		  VirRank:    Virtual Rank number
		  Enable:      Enable/Disable Physical Rank
Output     : Void
Purpose   : Set virtual rank number for physical rank
                 Program the specific physical rank with specific virtual rank number
                 Program when necessary, otherwise don't touch the pr-vr-mapping registers
===================================================================*/

void DRAMSetVRNum(DRAM_SYS_ATTR *DramAttr, u8 PhyRank /* physical rank */,
		  u8 VirRank /* virtual rank */, BOOLEAN Enable)
{
	u8 Data, AndData, OrData;

	Data = pci_read_config8(MEMCTRL, (0x54 + (PhyRank >> 1)));

	OrData = 0;
	if (Enable)
		OrData |= 0x08;
	OrData |= VirRank;
	if ((PhyRank & 0x01) == 0x00) {
		AndData = 0x0F;	// keep the value of odd rank on PR # is even(keep 1,3,5,7)
		OrData <<= 4;	// VR #, value to be set
	} else {
		AndData = 0xF0;	// keep the value of even rank on PR # is odd(keep 0,2,4,6)
	}

	Data &= AndData;
	Data |= OrData;
	pci_write_config8(MEMCTRL, (0x54 + (PhyRank >> 1)), Data);
}

/*===================================================================
Function   : SetEndingAddr()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
		  VirRank:    Virtual Rank number
		  Value:       (value) add or subtract value to this and after banks
Output     : Void
Purpose   : Set ending address of virtual rank specified by VirRank
===================================================================*/

void SetEndingAddr(DRAM_SYS_ATTR *DramAttr, u8 VirRank /* ending address
		   register number indicator (INDEX */, INT8 Value /* (value)
		   add or subtract value to this and after banks */) {
	u8 Data;

	/* Read register,Rx40-Rx47(0,1,2,3,4,5,6,7) and set ending address. */
	Data = pci_read_config8(MEMCTRL, 0x40 + VirRank);
	Data = (u8) (Data + Value);
	pci_write_config8(MEMCTRL, 0x40 + VirRank, Data);

	/* Program the virank's begining address to zero. */
	Data = 0x00;
	pci_write_config8(MEMCTRL, 0x48 + VirRank, Data);
}

/*===================================================================
Function   : InitDDR2()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
Output     : Void
Purpose   : Initialize DDR2 by standard sequence
===================================================================*/

//                               DLL:         Enable                              Reset
static const u32 CHA_MRS_DLL_150[2] = { 0x00020200, 0x00000800 };	// with 150 ohm (A17 = 1, A9 = 1), (A11 = 1)(cpu address)
static const u32 CHA_MRS_DLL_75[2] = { 0x00020020, 0x00000800 };	// with 75 ohm  (A17 = 1, A5 = 1), (A11 = 1)(cpu address)

//               CPU(DRAM)
// { DLL: Enable. A17(BA0)=1 and A3(MA0)=0 }
// { DLL: reset.  A11(MA8)=1 }
//
//                      DDR2                    CL = 2    CL = 3    CL = 4    CL = 5     CL = 6(Burst type = interleave)(WR fine tune in code)
static const u16 CHA_DDR2_MRS_table[5] = { 0x0150, 0x01D0, 0x0250, 0x02D0, 0x350 };	// BL = 4; Use 1X-bandwidth MA table to init DRAM

//                                                       MA11        MA10(AP)      MA9
#define CHA_MRS_DDR2_TWR2		(0 << 13) + (0 << 20) + (1 << 12)	// Value = 001000h
#define CHA_MRS_DDR2_TWR3		(0 << 13) + (1 << 20) + (0 << 12)	// Value = 100000h
#define CHA_MRS_DDR2_TWR4		(0 << 13) + (1 << 20) + (1 << 12)	// Value = 101000h
#define CHA_MRS_DDR2_TWR5		(1 << 13) + (0 << 20) + (0 << 12)	// Value = 002000h
#define CHA_MRS_DDR2_TWR6		(1 << 13) + (0 << 20) + (1 << 12)	// Value = 003000h

//                              DDR2             Twr = 2                  Twr = 3              Twr = 4                  Twr = 5
static const u32 CHA_DDR2_Twr_table[5] = {
	CHA_MRS_DDR2_TWR2, CHA_MRS_DDR2_TWR3, CHA_MRS_DDR2_TWR4,
	CHA_MRS_DDR2_TWR5, CHA_MRS_DDR2_TWR6
};

#define CHA_OCD_Exit_150ohm		0x20200	// EMRS(1), BA0 = 1, MA9 = MA8 = MA7 = 0,MA6 = 1,MA2 = 0 (DRAM bus address)
//                A17 = 1, A12 = A11 = A10 = 0,A9 = 1 ,A5 = 0  (CPU address)
#define CHA_OCD_Default_150ohm		0x21E00	// EMRS(1), BA0 = 1, MA9 = MA8 = MA7 = 1,MA6 = 1,MA2 = 0 (DRAM bus address)
//               A17 = 1, A12 = A11 = A10 = 1,A9 = 1 ,A5 = 0  (CPU address)
#define CHA_OCD_Exit_75ohm		0x20020	// EMRS(1), BA0 = 1, MA9 = MA8 = MA7 = 0,MA6 = 0,MA2 = 1 (DRAM bus address)
//              A17 = 1, A12 = A11 = A10 = 0,A9 = 0 ,A5 = 1  (CPU address)
#define CHA_OCD_Default_75ohm		0x21C20	// EMRS(1), BA0 = 1, MA9 = MA8 = MA7 = 1,MA6 = 0,MA2 = 1 (DRAM bus address)
//              A17 = 1, A12 = A11 = A10 = 1,A9 = 0 ,A5 = 1  (CPU address)

void InitDDR2CHA(DRAM_SYS_ATTR *DramAttr)
{
	u8 Data, Reg6BVal, Idx, CL, BL, Twr, DimmNum;
	u32 AccessAddr;

	/* Step 2 */
	/* Disable bank paging and multi page. */
	Data = pci_read_config8(MEMCTRL, 0x69);
	Data &= ~0x03;
	pci_write_config8(MEMCTRL, 0x69, Data);

	Reg6BVal = pci_read_config8(MEMCTRL, 0x6b);
	Reg6BVal &= ~0x07;

	/* Step 3 */
	/* At least one NOP cycle will be issued after the 1m sec device
	 * deselect.
	 */
	Data = Reg6BVal | 0x01;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	/* Step 4 */
	/* Read a double word from any address of the DIMM. */
	DimmRead(0x0);

	/* Step 5 */
	/*
	 * A minimum pause of 200u sec will be provided after the NOP.
	 * - <<<    reduce BOOT UP time >>> -
	 * Loop 200us
	 */
	for (Idx = 0; Idx < 0x10; Idx++)
		WaitMicroSec(100);

	// Step 6.
	// Precharge all (PALL) will be issued to the DDR.
	Data = Reg6BVal | 0x02;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	// Step7.
	// Read a double word from any address of the DIMM
	DimmRead(0x0);

	// Step 8.
	// MSR Eable will be issued to the DDR
	Data = Reg6BVal | 0x03;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	/* Step 9, 10.
	 *
	 * Check ODT value for EMRS(1) command according to ODTLookUp_TBL
	 * in driving_setting.c if there is one DIMM in MB's one channel,
	 * the DDR2's ODT is 150ohm if there is two DIMM in MB's one channel,
	 * the DDR2's ODT is 75 ohm.
	 */
	DimmNum = DramAttr->DimmNumChA;

	if (DimmNum == 1) { /* DDR's ODT is 150ohm */
		AccessAddr = (u32) CHA_MRS_DLL_150[0];
		DimmRead(AccessAddr); /* Issue EMRS DLL Enable. */
		PRINT_DEBUG_MEM("Step 9 Address ");
		PRINT_DEBUG_MEM_HEX32(AccessAddr);
		PRINT_DEBUG_MEM("\r");

		AccessAddr = (u32) CHA_MRS_DLL_150[1];
		DimmRead(AccessAddr); /* Issue MRS DLL Reset. */
		PRINT_DEBUG_MEM("Step 10 Address ");
		PRINT_DEBUG_MEM_HEX32(AccessAddr);
		PRINT_DEBUG_MEM("\r");
	} else if (DimmNum == 2) { /* DDR's ODT is 75ohm */
		AccessAddr = (u32) CHA_MRS_DLL_75[0];
		DimmRead(AccessAddr); /* Issue EMRS DLL Enable. */
		AccessAddr = (u32) CHA_MRS_DLL_75[1];
		DimmRead(AccessAddr); /* Issue MRS DLL Reset. */
	} else {
		PRINT_DEBUG_MEM("Dimm NUM ERROR:");
		PRINT_DEBUG_MEM_HEX8(DimmNum);
		PRINT_DEBUG_MEM("\r");
	}

	/* Step 11. Precharge all (PALL) will be issued to the DDR. */
	Data = Reg6BVal | 0x02;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	/* Step 12. Read a double word from any address of the DIMM. */
	DimmRead(0x0);

	/* Step 13. Execute 8 CBR refresh. */
	Data = Reg6BVal | 0x04;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	// issue 14,15 , 16
	//reads and wait 100us between each read
	for (Idx = 0; Idx < 8; Idx++) {
		DimmRead(0x0);
		WaitMicroSec(100);
	}

	/* Step 17. Enable MRS for MAA. */
	Data = Reg6BVal | 0x03;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	/*
	 * Step 18. The SDRAM parameters (Burst Length, CAS# Latency,
	 * Write recovery etc.)
	 */

	/* Burst Length: really offset Rx6c[3] */
	Data = pci_read_config8(MEMCTRL, 0x6c);
	BL = (Data & 0x08) >> 3;

	/* CL: really offset RX62[2:0] */
	Data = pci_read_config8(MEMCTRL, 0x62);
	CL = Data & 0x03;

	AccessAddr = (u32) (CHA_DDR2_MRS_table[CL]);
	if (BL)
		AccessAddr += 8;

	/* Write recovery: really offset Rx63[7-5] */
	Data = pci_read_config8(MEMCTRL, 0x63);
	Twr = (Data & 0xE0) >> 5;

	AccessAddr += CHA_DDR2_Twr_table[Twr];

	DimmRead(AccessAddr); /* Set MRS command. */
	PRINT_DEBUG_MEM("Step 18 Address");
	PRINT_DEBUG_MEM_HEX32(AccessAddr);
	PRINT_DEBUG_MEM("\r");

	/* Step 19, 20 */
	if (DimmNum == 1) { /* DDR's ODT is 150ohm */
		AccessAddr = (u32) CHA_OCD_Default_150ohm;
		DimmRead(AccessAddr);	/* Issue EMRS OCD Default. */
		PRINT_DEBUG_MEM("Step 19 Address ");
		PRINT_DEBUG_MEM_HEX32(AccessAddr);
		PRINT_DEBUG_MEM("\r");

		AccessAddr = (u32) CHA_OCD_Exit_150ohm;
		DimmRead(AccessAddr); /* Issue EMRS OCD Calibration Mode Exit. */
		PRINT_DEBUG_MEM("Step 20 Address ");
		PRINT_DEBUG_MEM_HEX32(AccessAddr);
		PRINT_DEBUG_MEM("\r");
	} else if (DimmNum == 2) { /* DDR's ODT is 75ohm */
		AccessAddr = (u32) CHA_OCD_Default_75ohm;
		DimmRead(AccessAddr); /* Issue EMRS OCD Default. */
		AccessAddr = (u32) CHA_OCD_Exit_75ohm;
		DimmRead(AccessAddr); /* Issue EMRS OCD Calibration Mode Exit. */
	} else {
		PRINT_DEBUG_MEM("Dimm NUM ERROR: ");
		PRINT_DEBUG_MEM_HEX8(DimmNum);
		PRINT_DEBUG_MEM("\r");
	}

	/*
	 * Step 21. After MRS the device should be ready for full
	 * functionality within 3 clocks after Tmrd is met.
	 */
	Data = Reg6BVal;
	pci_write_config8(MEMCTRL, 0x6b, Data);

	/* Enable bank paging and multi page. */
	Data = pci_read_config8(MEMCTRL, 0x69);
	Data |= 0x03;
	pci_write_config8(MEMCTRL, 0x69, Data);
}

/*===================================================================
Function   : InitDDR2CHC()
Precondition :
Input      :
		   DramAttr:  pointer point to  DRAM_SYS_ATTR  which consist the DDR and Dimm information
		                    in MotherBoard
Output     : Void
Purpose   : Initialize DDR2 of CHC by standard sequence
Reference  :
===================================================================*/
//                      DDR2                 CL = 2          CL = 3 CL = 4   CL = 5     (Burst type = interleave)(WR fine tune in code)
static const u16 CHC_MRS_table[4] = { 0x22B, 0x23B, 0x24B, 0x25B };	// Use 1X-bandwidth MA table to init DRAM

void InitDDR2CHC(DRAM_SYS_ATTR *DramAttr)
{
	u8 Data, Idx, CL, Twr;
	u32 AccessAddr;
	CB_STATUS Status;

	/* Step 3. Clear RxDF[2] to disable Tri-state output. */
	Data = pci_read_config8(MEMCTRL, 0xdf);
	Data &= 0xFB;
	pci_write_config8(MEMCTRL, 0xdf, Data);

	/*
	 * Step 4. Enable the initialization mode of DRAM Controller C with
	 * NB's PLL clock.
	 */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x60;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 5. NOP command enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x00;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 6. Issue a nop cycle, RegDB[1] 0 -> 1. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/*
	 * Step 7.
	 * A minimum pause of 200u sec will be provided after the NOP.
	 * - <<<    reduce BOOT UP time >>> -
	 * Loop 200us
	 */
	for (Idx = 0; Idx < 0x10; Idx++)
		WaitMicroSec(100);

	/* Step 8. Signal bank precharge command enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x14;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Set MA10 = 1, precharge all bank. */
	Data = 0x00;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x04;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	/* step 9. Issue a precharge all cycle, RegD3[7] 0 -> 1. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 10. MRS enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x1C;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 11. EMRS DLL enable and Disable DQS. */
	Data = 0x40;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x24;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	/* Step 12. Issue EMRS cycle. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 13. MSR enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x1C;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 14. MSR DLL Reset. */
	Data = 0x00;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x01;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	/* Step 15. Issue MRS cycle. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 16. Signal banks precharge command enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x14;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Set MA10 = 1, precharge all bank. */
	Data = 0x00;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x04;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	/* Step 17. Issue precharge all cycle. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	/* Step 18. CBR cycle enable. */
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x18;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	Data = 0x00;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x00;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	//step 19.20.21
	//repeat issue 8 CBR cycle, between each cycle stop 100us
	for (Idx = 0; Idx < 8; Idx++) {
		// issue CBR cycle
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		WaitMicroSec(100);
	}

	//the SDRAM parameters.(, CAS# Latency , Write recovery etc.)
	//------------------------------------------------------------

	// CL = really offset RXDC[7:6]
	Data = pci_read_config8(MEMCTRL, 0xdc);
	CL = (Data & 0xC0) >> 6;

	AccessAddr = (u32) (CHC_MRS_table[CL]);

	//Write recovery  : really offset Rx63[7:5]
	Data = pci_read_config8(MEMCTRL, 0x63);
	Twr = (Data & 0xE0) >> 5;

	AccessAddr += Twr * 0x200;

	//step22. MSR enable
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x1C;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	//step 23. MSR command
	Data = (u8) (AccessAddr & 0xFF);
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = (u8) ((AccessAddr & 0xFF00) >> 8);
	pci_write_config8(MEMCTRL, 0xf9, Data);

	//step 24.  issue MRS cycle
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	//step 25. EMRS enable
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0xE3;
	Data |= 0x1C;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	//step 26. OCD default
	Data = 0xC0;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x27;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	//step 27.  issue EMRS cycle
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	//step 28. OCD Exit
	Data = 0x40;
	pci_write_config8(MEMCTRL, 0xf8, Data);

	Data = 0x24;
	pci_write_config8(MEMCTRL, 0xf9, Data);

	//step 29. issue EMRS cycle
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data |= 0x2;
	pci_write_config8(MEMCTRL, 0xdb, Data);
	Data &= 0xFD;
	pci_write_config8(MEMCTRL, 0xdb, Data);

	Status = VerifyChc();
	if (Status != CB_SUCCESS)
		PRINT_DEBUG_MEM("Error!!!!CHC init error!\r");
	//step 31.  exit the initialization mode
	Data = pci_read_config8(MEMCTRL, 0xdb);
	Data &= 0x9F;
	pci_write_config8(MEMCTRL, 0xdb, Data);
}

CB_STATUS VerifyChc(void)
{
	u8 Data, ByteVal, Index, pad;
	u16 row;

	//first write the pad to all the address

	//the  row bits is 13 and  rank bit is 2, so the  address bits is 15 and the value is 0x7fff
	//verify each MA[0:12],BA[0:1]
	pad = 1;
	for (row = 0; row < 0x8000; row++) {
		/* Set the write value, Verify each MD[15:0]. */
		for (Data = pad, Index = 0; Index < 16; Index++) {
			Data <<= 1;
			if (Data == 0)
				Data = 1;
			pci_write_config8(PCI_DEV(0, 0, 7), 0xC0 + Index, Data);
		}

		/* Issue the bank active command. */
		// bank active command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x10;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = (u8) (row && 0xFF);
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = (u8) ((row && 0xFF) >> 8);
		pci_write_config8(MEMCTRL, 0xf9, Data);

		/* Issue active cycle. */
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		/* Issue ready/completion for read/write. */
		// read/completion command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x04;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf9, Data);

		/* Issue read/completion cycle. */
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		/* Issue write command. */
		// write command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x0C;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = (u8) ((row & 0x60) << 5);
		pci_write_config8(MEMCTRL, 0xf9, Data);

		/* Issue write cycle. */
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		////issue ready/completion for read/write
		// read/completion command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x04;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf9, Data);

		/* Issue read/completion cycle. */
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		/* Issue the bank active command. */
		// bank active command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x10;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = (u8) (row && 0xFF);
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = (u8) ((row && 0xFF) >> 8);
		pci_write_config8(MEMCTRL, 0xf9, Data);

		//  issue active cycle
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		////issue ready/completion for read/write
		// read/completion command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x04;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf9, Data);

		//  issue read/completion cycle
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		////issue read command
		// read/completion command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x08;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = (u8) ((row & 0x60) << 5);
		pci_write_config8(MEMCTRL, 0xf9, Data);

		//  issue read cycle
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		////issue ready/completion for read/write
		// read/completion command enable
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data &= 0xE3;
		Data |= 0x04;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf8, Data);

		Data = 0x00;
		pci_write_config8(MEMCTRL, 0xf9, Data);

		/* Issue read/completion cycle. */
		Data = pci_read_config8(MEMCTRL, 0xdb);
		Data |= 0x2;
		pci_write_config8(MEMCTRL, 0xdb, Data);
		Data &= 0xFD;
		pci_write_config8(MEMCTRL, 0xdb, Data);

		/* Verify the value. */
		for (ByteVal = pad, Index = 0; Index < 16; Index++) {
			Data = pci_read_config8(PCI_DEV(0, 0, 7), 0xD0 + Index);
			if (ByteVal != Data) {
				PRINT_DEBUG_MEM("Error! row = %x, index =%x, "
						"data = %x, byteval=%x\r");
			}
			ByteVal <<= 1;
			if (ByteVal == 0)
				ByteVal = 1;
		}
		pad <<= 1;
		if (pad == 0)
			pad = 1;
	}

	return CB_SUCCESS;
}