/*
 * This file is part of the coreboot project.
 *
 * Copyright (C) 2007-2008 Advanced Micro Devices, 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 */

/* Description: Main memory controller system configuration for DDR 2 */


/* KNOWN ISSUES - ERRATA
 *
 * Trtp is not calculated correctly when the controller is in 64-bit mode, it
 * is 1 busclock off.	No fix planned.	 The controller is not ordinarily in
 * 64-bit mode.
 *
 * 32 Byte burst not supported. No fix planned. The controller is not
 * ordinarily in 64-bit mode.
 *
 * Trc precision does not use extra Jedec defined fractional component.
 * InsteadTrc (course) is rounded up to nearest 1 ns.
 *
 * Mini and Micro DIMM not supported. Only RDIMM, UDIMM, SO-DIMM defined types
 * supported.
 */

static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA);
static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA);
static void ResetNBECCstat_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static u8 NodePresent_D(u8 Node);
static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static u8 Get_DefTrc_k_D(u8 k);
static u16 Get_40Tk_D(u8 k);
static u16 Get_Fk_D(u8 k);
static u8 Dimm_Supports_D(struct DCTStatStruc *pDCTstat, u8 i, u8 j, u8 k);
static u8 Sys_Capability_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, int j, int k);
static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i);
static void mct_initDCT(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void mct_DramInit(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct);
static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat);
static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,\
					struct DCTStatStruc *pDCTstat, u8 dct);
static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct);
static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct);
static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void Get_Twrwr(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static void Get_Twrrd(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct);
static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat);
static u8 Check_DqsRcvEn_Diff(struct DCTStatStruc *pDCTstat, u8 dct,
				u32 dev, u32 index_reg, u32 index);
static u8 Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
					u32 dev, u32 index_reg);
static u8 Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat, u8 dct,
					u32 dev, u32 index_reg);
static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
				u32 dev, u32 index_reg, u32 index);
static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat, u8 dct,
				u32 dev, u32 index_reg, u32 index);
static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void mct_init(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat);
static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat);
static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void SetCSTriState(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void SetODTTriState(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct);
static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct);
static u32 mct_NodePresent_D(void);
static void WaitRoutine_D(u32 time);
static void mct_OtherTiming(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA);
static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA);
static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat);
static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat);
void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat);
static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat);
void mct_ClrWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat);
static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA);
static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct);
static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct);


/*See mctAutoInitMCT header for index relationships to CL and T*/
static const u16 Table_F_k[]	= {00,200,266,333,400,533 };
static const u8 Table_T_k[]	= {0x00,0x50,0x3D,0x30,0x25, 0x18 };
static const u8 Table_CL2_j[]	= {0x04,0x08,0x10,0x20,0x40, 0x80 };
static const u8 Tab_defTrc_k[]	= {0x0,0x41,0x3C,0x3C,0x3A, 0x3A };
static const u16 Tab_40T_k[]	= {00,200,150,120,100,75 };
static const u8 Tab_TrefT_k[]	= {00,0,1,1,2,2,3,4,5,6,0,0};
static const u8 Tab_BankAddr[]	= {0x0,0x08,0x09,0x10,0x0C,0x0D,0x11,0x0E,0x15,0x16,0x0F,0x17};
static const u8 Tab_tCL_j[]	= {0,2,3,4,5};
static const u8 Tab_1KTfawT_k[] = {00,8,10,13,14,20};
static const u8 Tab_2KTfawT_k[] = {00,10,14,17,18,24};
static const u8 Tab_L1CLKDis[]	= {8,8,6,4,2,0,8,8};
static const u8 Tab_M2CLKDis[]	= {2,0,8,8,2,0,2,0};
static const u8 Tab_S1CLKDis[]	= {8,0,8,8,8,0,8,0};
static const u8 Table_Comp_Rise_Slew_20x[] = {7, 3, 2, 2, 0xFF};
static const u8 Table_Comp_Rise_Slew_15x[] = {7, 7, 3, 2, 0xFF};
static const u8 Table_Comp_Fall_Slew_20x[] = {7, 5, 3, 2, 0xFF};
static const u8 Table_Comp_Fall_Slew_15x[] = {7, 7, 5, 3, 0xFF};

void mctAutoInitMCT_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstatA)
{
	/*
	 * Memory may be mapped contiguously all the way up to 4GB (depending
	 * on setup options). It is the responsibility of PCI subsystem to
	 * create an uncacheable IO region below 4GB and to adjust TOP_MEM
	 * downward prior to any IO mapping or accesses. It is the same
	 * responsibility of the CPU sub-system prior toaccessing LAPIC.
	 *
	 * Slot Number is an external convention, and is determined by OEM with
	 * accompanying silk screening.  OEM may choose to use Slot number
	 * convention which is consistent with DIMM number conventions.
	 * All AMD engineering
	 * platforms do.
	 *
	 * Run-Time Requirements:
	 * 1. Complete Hypertransport Bus Configuration
	 * 2. SMBus Controller Initialized
	 * 3. Checksummed or Valid NVRAM bits
	 * 4. MCG_CTL=-1, MC4_CTL_EN=0 for all CPUs
	 * 5. MCi_STS from shutdown/warm reset recorded (if desired) prior to
	 *     entry
	 * 6. All var MTRRs reset to zero
	 * 7. State of NB_CFG.DisDatMsk set properly on all CPUs
	 * 8. All CPUs at 2Ghz Speed (unless DQS training is not installed).
	 * 9. All cHT links at max Speed/Width (unless DQS training is not
	 *     installed).
	 *
	 *
	 * Global relationship between index values and item values:
	 * j CL(j)       k   F(k)
	 * --------------------------
	 * 0 2.0         -   -
	 * 1 3.0         1   200 Mhz
	 * 2 4.0         2   266 Mhz
	 * 3 5.0         3   333 Mhz
	 * 4 6.0         4   400 Mhz
	 * 5 7.0         5   533 Mhz
	 */
	u8 Node, NodesWmem;
	u32 node_sys_base;

restartinit:
	mctInitMemGPIOs_A_D();		/* Set any required GPIOs*/
	NodesWmem = 0;
	node_sys_base = 0;
	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		struct DCTStatStruc *pDCTstat;
		pDCTstat = pDCTstatA + Node;
		pDCTstat->Node_ID = Node;
		pDCTstat->dev_host = PA_HOST(Node);
		pDCTstat->dev_map = PA_MAP(Node);
		pDCTstat->dev_dct = PA_DCT(Node);
		pDCTstat->dev_nbmisc = PA_NBMISC(Node);
		pDCTstat->NodeSysBase = node_sys_base;

		print_tx("mctAutoInitMCT_D: mct_init Node ", Node);
		mct_init(pMCTstat, pDCTstat);
		mctNodeIDDebugPort_D();
		pDCTstat->NodePresent = NodePresent_D(Node);
		if (pDCTstat->NodePresent) {		/* See if Node is there*/
			print_t("mctAutoInitMCT_D: clear_legacy_Mode\n");
			clear_legacy_Mode(pMCTstat, pDCTstat);
			pDCTstat->LogicalCPUID = mctGetLogicalCPUID_D(Node);

			print_t("mctAutoInitMCT_D: mct_InitialMCT_D\n");
			mct_InitialMCT_D(pMCTstat, pDCTstat);

			print_t("mctAutoInitMCT_D: mctSMBhub_Init\n");
			mctSMBhub_Init(Node);		/* Switch SMBUS crossbar to proper node*/

			print_t("mctAutoInitMCT_D: mct_initDCT\n");
			mct_initDCT(pMCTstat, pDCTstat);
			if (pDCTstat->ErrCode == SC_FatalErr) {
				goto fatalexit;		/* any fatal errors?*/
			} else if (pDCTstat->ErrCode < SC_StopError) {
				NodesWmem++;
			}
		}	/* if Node present */
		node_sys_base = pDCTstat->NodeSysBase;
		node_sys_base += (pDCTstat->NodeSysLimit + 2) & ~0x0F;
	}
	if (NodesWmem == 0) {
		print_debug("No Nodes?!\n");
		goto fatalexit;
	}

	print_t("mctAutoInitMCT_D: SyncDCTsReady_D\n");
	SyncDCTsReady_D(pMCTstat, pDCTstatA);	/* Make sure DCTs are ready for accesses.*/

	print_t("mctAutoInitMCT_D: HTMemMapInit_D\n");
	HTMemMapInit_D(pMCTstat, pDCTstatA);	/* Map local memory into system address space.*/
	mctHookAfterHTMap();

	print_t("mctAutoInitMCT_D: CPUMemTyping_D\n");
	CPUMemTyping_D(pMCTstat, pDCTstatA);	/* Map dram into WB/UC CPU cacheability */
	mctHookAfterCPU();			/* Setup external northbridge(s) */

	print_t("mctAutoInitMCT_D: DQSTiming_D\n");
	DQSTiming_D(pMCTstat, pDCTstatA);	/* Get Receiver Enable and DQS signal timing*/

	print_t("mctAutoInitMCT_D: UMAMemTyping_D\n");
	UMAMemTyping_D(pMCTstat, pDCTstatA);	/* Fix up for UMA sizing */

	print_t("mctAutoInitMCT_D: :OtherTiming\n");
	mct_OtherTiming(pMCTstat, pDCTstatA);

	if (ReconfigureDIMMspare_D(pMCTstat, pDCTstatA)) { /* RESET# if 1st pass of DIMM spare enabled*/
		goto restartinit;
	}

	InterleaveNodes_D(pMCTstat, pDCTstatA);
	InterleaveChannels_D(pMCTstat, pDCTstatA);

	print_t("mctAutoInitMCT_D: ECCInit_D\n");
	if (ECCInit_D(pMCTstat, pDCTstatA)) {		/* Setup ECC control and ECC check-bits*/
		print_t("mctAutoInitMCT_D: MCTMemClr_D\n");
		MCTMemClr_D(pMCTstat,pDCTstatA);
	}

	mct_FinalMCT_D(pMCTstat, (pDCTstatA + 0) );	// Node 0
	print_t("All Done\n");
	return;

fatalexit:
	die("mct_d: fatalexit");
}


static u8 ReconfigureDIMMspare_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA)
{
	u8 ret;

	if (mctGet_NVbits(NV_CS_SpareCTL)) {
		if (MCT_DIMM_SPARE_NO_WARM) {
			/* Do no warm-reset DIMM spare */
			if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
				LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA);
				ret = 0;
			} else {
				mct_ResetDataStruct_D(pMCTstat, pDCTstatA);
				pMCTstat->GStatus |= 1 << GSB_EnDIMMSpareNW;
				ret = 1;
			}
		} else {
			/* Do warm-reset DIMM spare */
			if (mctGet_NVbits(NV_DQSTrainCTL))
				mctWarmReset_D();
			ret = 0;
		}


	} else {
		ret = 0;
	}

	return ret;
}


static void DQSTiming_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	u8 nv_DQSTrainCTL;

	if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
		return;
	}
	nv_DQSTrainCTL = mctGet_NVbits(NV_DQSTrainCTL);
	/* FIXME: BOZO- DQS training every time*/
	nv_DQSTrainCTL = 1;

	print_t("DQSTiming_D: mct_BeforeDQSTrain_D:\n");
	mct_BeforeDQSTrain_D(pMCTstat, pDCTstatA);;
	phyAssistedMemFnceTraining(pMCTstat, pDCTstatA);

	if (nv_DQSTrainCTL) {
		mctHookBeforeAnyTraining();

		print_t("DQSTiming_D: TrainReceiverEn_D FirstPass:\n");
		TrainReceiverEn_D(pMCTstat, pDCTstatA, FirstPass);

		print_t("DQSTiming_D: mct_TrainDQSPos_D\n");
		mct_TrainDQSPos_D(pMCTstat, pDCTstatA);

		// Second Pass never used for Barcelona!
		//print_t("DQSTiming_D: TrainReceiverEn_D SecondPass:\n");
		//TrainReceiverEn_D(pMCTstat, pDCTstatA, SecondPass);

		print_t("DQSTiming_D: mctSetEccDQSRcvrEn_D\n");
		mctSetEccDQSRcvrEn_D(pMCTstat, pDCTstatA);

		print_t("DQSTiming_D: TrainMaxReadLatency_D\n");
//FIXME - currently uses calculated value		TrainMaxReadLatency_D(pMCTstat, pDCTstatA);
		mctHookAfterAnyTraining();
		mctSaveDQSSigTmg_D();

		print_t("DQSTiming_D: mct_EndDQSTraining_D\n");
		mct_EndDQSTraining_D(pMCTstat, pDCTstatA);

		print_t("DQSTiming_D: MCTMemClr_D\n");
		MCTMemClr_D(pMCTstat, pDCTstatA);
	} else {
		mctGetDQSSigTmg_D();	/* get values into data structure */
		LoadDQSSigTmgRegs_D(pMCTstat, pDCTstatA);	/* load values into registers.*/
		//mctDoWarmResetMemClr_D();
		MCTMemClr_D(pMCTstat, pDCTstatA);
	}
}


static void LoadDQSSigTmgRegs_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA)
{
	u8 Node, Receiver, Channel, Dir, DIMM;
	u32 dev;
	u32 index_reg;
	u32 reg;
	u32 index;
	u32 val;


	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		struct DCTStatStruc *pDCTstat;
		pDCTstat = pDCTstatA + Node;

		if (pDCTstat->DCTSysLimit) {
			dev = pDCTstat->dev_dct;
			for (Channel = 0;Channel < 2; Channel++) {
				/* there are four receiver pairs,
				   loosely associated with chipselects.*/
				index_reg = 0x98 + Channel * 0x100;
				for (Receiver = 0; Receiver < 8; Receiver += 2) {
					/* Set Receiver Enable Values */
					mct_SetRcvrEnDly_D(pDCTstat,
						0, /* RcvrEnDly */
						1, /* FinalValue, From stack */
						Channel,
						Receiver,
						dev, index_reg,
						(Receiver >> 1) * 3 + 0x10, /* Addl_Index */
						2); /* Pass Second Pass ? */

				}
			}
			for (Channel = 0; Channel<2; Channel++) {
				SetEccDQSRcvrEn_D(pDCTstat, Channel);
			}

			for (Channel = 0; Channel < 2; Channel++) {
				u8 *p;
				index_reg = 0x98 + Channel * 0x100;

				/* NOTE:
				 * when 400, 533, 667, it will support dimm0/1/2/3,
				 * and set conf for dimm0, hw will copy to dimm1/2/3
				 * set for dimm1, hw will copy to dimm3
				 * Rev A/B only support DIMM0/1 when 800Mhz and above
				 *   + 0x100 to next dimm
				 * Rev C support DIMM0/1/2/3 when 800Mhz and above
				 *   + 0x100 to next dimm
				*/
				for (DIMM = 0; DIMM < 2; DIMM++) {
					if (DIMM==0) {
						index = 0;	/* CHA Write Data Timing Low */
					} else {
						if (pDCTstat->Speed >= 4) {
							index = 0x100 * DIMM;
						} else {
							break;
						}
					}
					for (Dir=0;Dir<2;Dir++) {//RD/WR
						p = pDCTstat->CH_D_DIR_B_DQS[Channel][DIMM][Dir];
						val = stream_to_int(p); /* CHA Read Data Timing High */
						Set_NB32_index_wait(dev, index_reg, index+1, val);
						val = stream_to_int(p+4); /* CHA Write Data Timing High */
						Set_NB32_index_wait(dev, index_reg, index+2, val);
						val = *(p+8); /* CHA Write ECC Timing */
						Set_NB32_index_wait(dev, index_reg, index+3, val);
						index += 4;
					}
				}
			}

			for (Channel = 0; Channel<2; Channel++) {
				reg = 0x78 + Channel * 0x100;
				val = Get_NB32(dev, reg);
				val &= ~(0x3ff<<22);
				val |= ((u32) pDCTstat->CH_MaxRdLat[Channel] << 22);
				val &= ~(1<<DqsRcvEnTrain);
				Set_NB32(dev, reg, val);	/* program MaxRdLatency to correspond with current delay*/
			}
		}
	}
}


static void ResetNBECCstat_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	/* Clear MC4_STS for all Nodes in the system.  This is required in some
	 * circumstances to clear left over garbage from cold reset, shutdown,
	 * or normal ECC memory conditioning.
	 */

	//FIXME: this function depends on pDCTstat Array ( with Node id ) - Is this really a problem?

	u32 dev;
	u8 Node;

	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		struct DCTStatStruc *pDCTstat;
		pDCTstat = pDCTstatA + Node;

		if (pDCTstat->NodePresent) {
			dev = pDCTstat->dev_nbmisc;
			/*MCA NB Status Low (alias to MC4_STS[31:0] */
			Set_NB32(dev, 0x48, 0);
			/* MCA NB Status High (alias to MC4_STS[63:32] */
			Set_NB32(dev, 0x4C, 0);
		}
	}
}


static void HTMemMapInit_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	u8 Node;
	u32 NextBase, BottomIO;
	u8 _MemHoleRemap, DramHoleBase, DramHoleOffset;
	u32 HoleSize, DramSelBaseAddr;

	u32 val;
	u32 base;
	u32 limit;
	u32 dev, devx;
	struct DCTStatStruc *pDCTstat;

	_MemHoleRemap = mctGet_NVbits(NV_MemHole);

	if (pMCTstat->HoleBase == 0) {
		DramHoleBase = mctGet_NVbits(NV_BottomIO);
	} else {
		DramHoleBase = pMCTstat->HoleBase >> (24-8);
	}

	BottomIO = DramHoleBase << (24-8);

	NextBase = 0;
	pDCTstat = pDCTstatA + 0;
	dev = pDCTstat->dev_map;


	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		pDCTstat = pDCTstatA + Node;
		devx = pDCTstat->dev_map;
		DramSelBaseAddr = 0;
		pDCTstat = pDCTstatA + Node;
		if (!pDCTstat->GangedMode) {
			DramSelBaseAddr = pDCTstat->NodeSysLimit - pDCTstat->DCTSysLimit;
			/*In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
			val = pDCTstat->NodeSysLimit;
			if ((val & 0xFF) == 0xFE) {
				DramSelBaseAddr++;
				val++;
			}
			pDCTstat->DCTSysLimit = val;
		}

		base  = pDCTstat->DCTSysBase;
		limit = pDCTstat->DCTSysLimit;
		if (limit > base) {
			base  += NextBase;
			limit += NextBase;
			DramSelBaseAddr += NextBase;
			printk_debug(" Node: %02x  base: %02x  limit: %02x  BottomIO: %02x\n", Node, base, limit, BottomIO);

			if (_MemHoleRemap) {
				if ((base < BottomIO) && (limit >= BottomIO)) {
					/* HW Dram Remap */
					pDCTstat->Status |= 1 << SB_HWHole;
					pMCTstat->GStatus |= 1 << GSB_HWHole;
					pDCTstat->DCTSysBase = base;
					pDCTstat->DCTSysLimit = limit;
					pDCTstat->DCTHoleBase = BottomIO;
					pMCTstat->HoleBase = BottomIO;
					HoleSize = _4GB_RJ8 - BottomIO; /* HoleSize[39:8] */
					if ((DramSelBaseAddr > 0) && (DramSelBaseAddr < BottomIO))
						base = DramSelBaseAddr;
					val = ((base + HoleSize) >> (24-8)) & 0xFF;
					DramHoleOffset = val;
					val <<= 8; /* shl 16, rol 24 */
					val |= DramHoleBase << 24;
					val |= 1  << DramHoleValid;
					Set_NB32(devx, 0xF0, val); /* Dram Hole Address Reg */
					pDCTstat->DCTSysLimit += HoleSize;
					base = pDCTstat->DCTSysBase;
					limit = pDCTstat->DCTSysLimit;
				} else if (base == BottomIO) {
					/* SW Node Hoist */
					pMCTstat->GStatus |= 1<<GSB_SpIntRemapHole;
					pDCTstat->Status |= 1<<SB_SWNodeHole;
					pMCTstat->GStatus |= 1<<GSB_SoftHole;
					pMCTstat->HoleBase = base;
					limit -= base;
					base = _4GB_RJ8;
					limit += base;
					pDCTstat->DCTSysBase = base;
					pDCTstat->DCTSysLimit = limit;
				} else {
					/* No Remapping.  Normal Contiguous mapping */
					pDCTstat->DCTSysBase = base;
					pDCTstat->DCTSysLimit = limit;
				}
			} else {
				/*No Remapping.  Normal Contiguous mapping*/
				pDCTstat->DCTSysBase = base;
				pDCTstat->DCTSysLimit = limit;
			}
			base |= 3;		/* set WE,RE fields*/
			pMCTstat->SysLimit = limit;
		}
		Set_NB32(dev, 0x40 + (Node << 3), base); /* [Node] + Dram Base 0 */

		/* if Node limit > 1GB then set it to 1GB boundary for each node */
		if ((mctSetNodeBoundary_D()) && (limit > 0x00400000)) {
			limit++;
			limit &= 0xFFC00000;
			limit--;
		}
		val = limit & 0xFFFF0000;
		val |= Node;
		Set_NB32(dev, 0x44 + (Node << 3), val);	/* set DstNode */

		limit = pDCTstat->DCTSysLimit;
		if (limit) {
			NextBase = (limit & 0xFFFF0000) + 0x10000;
			if ((mctSetNodeBoundary_D()) && (NextBase > 0x00400000)) {
				NextBase++;
				NextBase &= 0xFFC00000;
				NextBase--;
			}
		}
	}

	/* Copy dram map from Node 0 to Node 1-7 */
	for (Node = 1; Node < MAX_NODES_SUPPORTED; Node++) {
		u32 reg;
		pDCTstat = pDCTstatA + Node;
		devx = pDCTstat->dev_map;

		if (pDCTstat->NodePresent) {
		printk_debug(" Copy dram map from Node 0 to Node %02x \n", Node);
			reg = 0x40;		/*Dram Base 0*/
			do {
				val = Get_NB32(dev, reg);
				Set_NB32(devx, reg, val);
				reg += 4;
			} while ( reg < 0x80);
		} else {
			break;			/* stop at first absent Node */
		}
	}

	/*Copy dram map to F1x120/124*/
	mct_HTMemMapExt(pMCTstat, pDCTstatA);
}


static void MCTMemClr_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{

	/* Initiates a memory clear operation for all node. The mem clr
	 * is done in paralel. After the memclr is complete, all processors
	 * status are checked to ensure that memclr has completed.
	 */
	u8 Node;
	struct DCTStatStruc *pDCTstat;

	if (!mctGet_NVbits(NV_DQSTrainCTL)){
		// FIXME: callback to wrapper: mctDoWarmResetMemClr_D
	} else {	// NV_DQSTrainCTL == 1
		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
			pDCTstat = pDCTstatA + Node;

			if (pDCTstat->NodePresent) {
				DCTMemClr_Init_D(pMCTstat, pDCTstat);
			}
		}
		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
			pDCTstat = pDCTstatA + Node;

			if (pDCTstat->NodePresent) {
				DCTMemClr_Sync_D(pMCTstat, pDCTstat);
			}
		}
	}
}


static void DCTMemClr_Init_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 val;
	u32 dev;
	u32 reg;

	/* Initiates a memory clear operation on one node */
	if (pDCTstat->DCTSysLimit) {
		dev = pDCTstat->dev_dct;
		reg = 0x110;

		do {
			val = Get_NB32(dev, reg);
		} while (val & (1 << MemClrBusy));

		val |= (1 << MemClrInit);
		Set_NB32(dev, reg, val);

	}
}


static void MCTMemClrSync_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	/* Ensures that memory clear has completed on all node.*/
	u8 Node;
	struct DCTStatStruc *pDCTstat;

	if (!mctGet_NVbits(NV_DQSTrainCTL)){
		// callback to wrapper: mctDoWarmResetMemClr_D
	} else {	// NV_DQSTrainCTL == 1
		for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
			pDCTstat = pDCTstatA + Node;

			if (pDCTstat->NodePresent) {
				DCTMemClr_Sync_D(pMCTstat, pDCTstat);
			}
		}
	}
}


static void DCTMemClr_Sync_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 val;
	u32 dev = pDCTstat->dev_dct;
	u32 reg;

	/* Ensure that a memory clear operation has completed on one node */
	if (pDCTstat->DCTSysLimit){
		reg = 0x110;

		do {
			val = Get_NB32(dev, reg);
		} while (val & (1 << MemClrBusy));

		do {
			val = Get_NB32(dev, reg);
		} while (!(val & (1 << Dr_MemClrStatus)));
	}

	val = 0x0FE40FC0;		// BKDG recommended
	val |= MCCH_FlushWrOnStpGnt;	// Set for S3
	Set_NB32(dev, 0x11C, val);
}


static u8 NodePresent_D(u8 Node)
{
	/*
	 * Determine if a single Hammer Node exists within the network.
	 */

	u32 dev;
	u32 val;
	u32 dword;
	u8 ret = 0;

	dev = PA_HOST(Node);		/*test device/vendor id at host bridge  */
	val = Get_NB32(dev, 0);
	dword = mct_NodePresent_D();	/* FIXME: BOZO -11001022h rev for F */
	if (val == dword) {		/* AMD Hammer Family CPU HT Configuration */
		if (oemNodePresent_D(Node, &ret))
			goto finish;
		/* Node ID register */
		val = Get_NB32(dev, 0x60);
		val &= 0x07;
		dword = Node;
		if (val  == dword)	/* current nodeID = requested nodeID ? */
			ret = 1;
finish:
	;
	}

	return ret;
}


static void DCTInit_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat, u8 dct)
{
	/*
	 * Initialize DRAM on single Athlon 64/Opteron Node.
	 */

	u8 stopDCTflag;
	u32 val;

	ClearDCT_D(pMCTstat, pDCTstat, dct);
	stopDCTflag = 1;		/*preload flag with 'disable' */
	if (mct_DIMMPresence(pMCTstat, pDCTstat, dct) < SC_StopError) {
		print_t("\t\tDCTInit_D: mct_DIMMPresence Done\n");
		if (mct_SPDCalcWidth(pMCTstat, pDCTstat, dct) < SC_StopError) {
			print_t("\t\tDCTInit_D: mct_SPDCalcWidth Done\n");
			if (AutoCycTiming_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
				print_t("\t\tDCTInit_D: AutoCycTiming_D Done\n");
				if (AutoConfig_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
					print_t("\t\tDCTInit_D: AutoConfig_D Done\n");
					if (PlatformSpec_D(pMCTstat, pDCTstat, dct) < SC_StopError) {
						print_t("\t\tDCTInit_D: PlatformSpec_D Done\n");
						stopDCTflag = 0;
						if (!(pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW))) {
							print_t("\t\tDCTInit_D: StartupDCT_D\n");
							StartupDCT_D(pMCTstat, pDCTstat, dct);   /*yeaahhh! */
						}
					}
				}
			}
		}
	}
	if (stopDCTflag) {
		u32 reg_off = dct * 0x100;
		val = 1<<DisDramInterface;
		Set_NB32(pDCTstat->dev_dct, reg_off+0x94, val);
		/*To maximize power savings when DisDramInterface=1b,
		  all of the MemClkDis bits should also be set.*/
		val = 0xFF000000;
		Set_NB32(pDCTstat->dev_dct, reg_off+0x88, val);
	}
}


static void SyncDCTsReady_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	/* Wait (and block further access to dram) for all DCTs to be ready,
	 * by polling all InitDram bits and waiting for possible memory clear
	 * operations to be complete.  Read MemClkFreqVal bit to see if
	 * the DIMMs are present in this node.
	 */

	u8 Node;

	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		struct DCTStatStruc *pDCTstat;
		pDCTstat = pDCTstatA + Node;
		mct_SyncDCTsReady(pDCTstat);
	}
}


static void StartupDCT_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Read MemClkFreqVal bit to see if the DIMMs are present in this node.
	 * If the DIMMs are present then set the DRAM Enable bit for this node.
	 *
	 * Setting dram init starts up the DCT state machine, initializes the
	 * dram devices with MRS commands, and kicks off any
	 * HW memory clear process that the chip is capable of.	The sooner
	 * that dram init is set for all nodes, the faster the memory system
	 * initialization can complete.	Thus, the init loop is unrolled into
	 * two loops so as to start the processeses for non BSP nodes sooner.
	 * This procedure will not wait for the process to finish.
	 * Synchronization is handled elsewhere.
	 */

	u32 val;
	u32 dev;
	u8 byte;
	u32 reg;
	u32 reg_off = dct * 0x100;

	dev = pDCTstat->dev_dct;
	val = Get_NB32(dev, 0x94 + reg_off);
	if (val & (1<<MemClkFreqVal)) {
		print_t("\t\t\tStartupDCT_D: MemClkFreqVal\n");
		byte = mctGet_NVbits(NV_DQSTrainCTL);
		if (byte == 1) {
			/* Enable DQSRcvEn training mode */
			print_t("\t\t\tStartupDCT_D: DqsRcvEnTrain set \n");
			reg = 0x78 + reg_off;
			val = Get_NB32(dev, reg);
			/* Setting this bit forces a 1T window with hard left
			 * pass/fail edge and a probabalistic right pass/fail
			 * edge.  LEFT edge is referenced for final
			 * receiver enable position.*/
			val |= 1 << DqsRcvEnTrain;
			Set_NB32(dev, reg, val);
		}
		mctHookBeforeDramInit();	/* generalized Hook */
		print_t("\t\t\tStartupDCT_D: DramInit \n");
		mct_DramInit(pMCTstat, pDCTstat, dct);
		AfterDramInit_D(pDCTstat, dct);
		mctHookAfterDramInit();		/* generalized Hook*/
	}
}


static void ClearDCT_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 reg_end;
	u32 dev = pDCTstat->dev_dct;
	u32 reg = 0x40 + 0x100 * dct;
	u32 val = 0;

	if (pMCTstat->GStatus & (1 << GSB_EnDIMMSpareNW)) {
		reg_end = 0x78 + 0x100 * dct;
	} else {
		reg_end = 0xA4 + 0x100 * dct;
	}

	while(reg < reg_end) {
		Set_NB32(dev, reg, val);
		reg += 4;
	}

	val = 0;
	dev = pDCTstat->dev_map;
	reg = 0xF0;
	Set_NB32(dev, reg, val);
}


static u8 AutoCycTiming_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Initialize  DCT Timing registers as per DIMM SPD.
	 * For primary timing (T, CL) use best case T value.
	 * For secondary timing params., use most aggressive settings
	 * of slowest DIMM.
	 *
	 * There are three components to determining "maximum frequency":
	 * SPD component, Bus load component, and "Preset" max frequency
	 * component.
	 *
	 * The SPD component is a function of the min cycle time specified
	 * by each DIMM, and the interaction of cycle times from all DIMMs
	 * in conjunction with CAS latency. The SPD component only applies
	 * when user timing mode is 'Auto'.
	 *
	 * The Bus load component is a limiting factor determined by electrical
	 * characteristics on the bus as a result of varying number of device
	 * loads. The Bus load component is specific to each platform but may
	 * also be a function of other factors. The bus load component only
	 * applies when user timing mode is 'Auto'.
	 *
	 * The Preset component is subdivided into three items and is the
	 * minimum of the set: Silicon revision, user limit setting when user
	 * timing mode is 'Auto' and memclock mode is 'Limit', OEM build
	 * specification of the maximum frequency. The Preset component is only
	 * applies when user timing mode is 'Auto'.
	 */

	u8 i;
	u8 Twr, Trtp;
	u8 Trp, Trrd, Trcd, Tras, Trc, Trfc[4], Rows;
	u32 DramTimingLo, DramTimingHi;
	u16 Tk10, Tk40;
	u8 Twtr;
	u8 LDIMM;
	u8 DDR2_1066;
	u8 byte;
	u32 dword;
	u32 dev;
	u32 reg;
	u32 reg_off;
	u32 val;
	u16 smbaddr;

	/* Get primary timing (CAS Latency and Cycle Time) */
	if (pDCTstat->Speed == 0) {
		mctGet_MaxLoadFreq(pDCTstat);

		/* and Factor in presets (setup options, Si cap, etc.) */
		GetPresetmaxF_D(pMCTstat, pDCTstat);

		/* Go get best T and CL as specified by DIMM mfgs. and OEM */
		SPDGetTCL_D(pMCTstat, pDCTstat, dct);
		/* skip callback mctForce800to1067_D */
		pDCTstat->Speed = pDCTstat->DIMMAutoSpeed;
		pDCTstat->CASL = pDCTstat->DIMMCASL;

		/* if "manual" memclock mode */
		if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 2)
			pDCTstat->Speed = mctGet_NVbits(NV_MemCkVal) + 1;

		mct_AfterGetCLT(pMCTstat, pDCTstat, dct);
	}

	/* Gather all DIMM mini-max values for cycle timing data */
	Rows = 0;
	Trp = 0;
	Trrd = 0;
	Trcd = 0;
	Trtp = 0;
	Tras = 0;
	Trc = 0;
	Twr = 0;
	Twtr = 0;
	for (i=0; i < 4; i++)
		Trfc[i] = 0;

	for ( i = 0; i< MAX_DIMMS_SUPPORTED; i++) {
		LDIMM = i >> 1;
		if (pDCTstat->DIMMValid & (1 << i)) {
			smbaddr = Get_DIMMAddress_D(pDCTstat, i);
			byte = mctRead_SPD(smbaddr, SPD_ROWSZ);
			if (Rows < byte)
				Rows = byte;	/* keep track of largest row sz */

			byte = mctRead_SPD(smbaddr, SPD_TRP);
			if (Trp < byte)
				Trp = byte;

			byte = mctRead_SPD(smbaddr, SPD_TRRD);
			if (Trrd < byte)
				Trrd = byte;

			byte = mctRead_SPD(smbaddr, SPD_TRCD);
			if (Trcd < byte)
				Trcd = byte;

			byte = mctRead_SPD(smbaddr, SPD_TRTP);
			if (Trtp < byte)
				Trtp = byte;

			byte = mctRead_SPD(smbaddr, SPD_TWR);
			if (Twr < byte)
				Twr = byte;

			byte = mctRead_SPD(smbaddr, SPD_TWTR);
			if (Twtr < byte)
				Twtr = byte;

			val = mctRead_SPD(smbaddr, SPD_TRC);
			if ((val == 0) || (val == 0xFF)) {
				pDCTstat->ErrStatus |= 1<<SB_NoTrcTrfc;
				pDCTstat->ErrCode = SC_VarianceErr;
				val = Get_DefTrc_k_D(pDCTstat->Speed);
			} else {
				byte = mctRead_SPD(smbaddr, SPD_TRCRFC);
				if (byte & 0xF0) {
					val++;	/* round up in case fractional extention is non-zero.*/
				}
		}
		if (Trc < val)
			Trc = val;

		/* dev density=rank size/#devs per rank */
		byte = mctRead_SPD(smbaddr, SPD_BANKSZ);

		val = ((byte >> 5) | (byte << 3)) & 0xFF;
		val <<= 2;

		byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0xFE;     /* dev density=2^(rows+columns+banks) */
		if (byte == 4) {
			val >>= 4;
		} else if (byte == 8) {
			val >>= 3;
		} else if (byte == 16) {
			val >>= 2;
		}

		byte = bsr(val);

		if (Trfc[LDIMM] < byte)
			Trfc[LDIMM] = byte;

		byte = mctRead_SPD(smbaddr, SPD_TRAS);
		if (Tras < byte)
			Tras = byte;
		}	/* Dimm Present */
	}

	/* Convert  DRAM CycleTiming values and store into DCT structure */
	DDR2_1066 = 0;
	byte = pDCTstat->Speed;
	if (byte == 5)
		DDR2_1066 = 1;
	Tk40 = Get_40Tk_D(byte);
	Tk10 = Tk40>>2;

	/* Notes:
	 1. All secondary time values given in SPDs are in binary with units of ns.
	 2. Some time values are scaled by four, in order to have least count of 0.25 ns
	    (more accuracy).  JEDEC SPD spec. shows which ones are x1 and x4.
	 3. Internally to this SW, cycle time, Tk, is scaled by 10 to affect a
	    least count of 0.1 ns (more accuracy).
	 4. SPD values not scaled are multiplied by 10 and then divided by 10T to find
	    equivalent minimum number of bus clocks (a remainder causes round-up of clocks).
	 5. SPD values that are prescaled by 4 are multiplied by 10 and then divided by 40T to find
	    equivalent minimum number of bus clocks (a remainder causes round-up of clocks).*/

	/* Tras */
	dword = Tras * 40;
	pDCTstat->DIMMTras = (u16)dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TrasT_1066)
			val = Min_TrasT_1066;
		else if (val > Max_TrasT_1066)
			val = Max_TrasT_1066;
	} else {
		if (val < Min_TrasT)
			val = Min_TrasT;
		else if (val > Max_TrasT)
			val = Max_TrasT;
	}
	pDCTstat->Tras = val;

	/* Trp */
	dword = Trp * 10;
	pDCTstat->DIMMTrp = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TrasT_1066)
			val = Min_TrpT_1066;
		else if (val > Max_TrpT_1066)
			val = Max_TrpT_1066;
	} else {
		if (val < Min_TrpT)
			val = Min_TrpT;
		else if (val > Max_TrpT)
			val = Max_TrpT;
	}
	pDCTstat->Trp = val;

	 /*Trrd*/
	dword = Trrd * 10;
	pDCTstat->DIMMTrrd = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TrrdT_1066)
			val = Min_TrrdT_1066;
		else if (val > Max_TrrdT_1066)
			val = Max_TrrdT_1066;
	} else {
		if (val < Min_TrrdT)
			val = Min_TrrdT;
		else if (val > Max_TrrdT)
			val = Max_TrrdT;
	}
	pDCTstat->Trrd = val;

	/* Trcd */
	dword = Trcd * 10;
	pDCTstat->DIMMTrcd = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TrcdT_1066)
			val = Min_TrcdT_1066;
		else if (val > Max_TrcdT_1066)
			val = Max_TrcdT_1066;
	} else {
		if (val < Min_TrcdT)
			val = Min_TrcdT;
		else if (val > Max_TrcdT)
			val = Max_TrcdT;
	}
	pDCTstat->Trcd = val;

	/* Trc */
	dword = Trc * 40;
	pDCTstat->DIMMTrc = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TrcT_1066)
			val = Min_TrcT_1066;
		else if (val > Max_TrcT_1066)
			val = Max_TrcT_1066;
	} else {
		if (val < Min_TrcT)
			val = Min_TrcT;
		else if (val > Max_TrcT)
			val = Max_TrcT;
	}
	pDCTstat->Trc = val;

	/* Trtp */
	dword = Trtp * 10;
	pDCTstat->DIMMTrtp = dword;
	val = pDCTstat->Speed;
	if (val <= 2) {		/* 7.75ns / Speed in ns to get clock # */
		val = 2;	/* for DDR400/DDR533 */
	} else {		/* Note a speed of 3 will be a Trtp of 3 */
		val = 3;	/* for DDR667/DDR800/DDR1066 */
	}
	pDCTstat->Trtp = val;

	/* Twr */
	dword = Twr * 10;
	pDCTstat->DIMMTwr = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TwrT_1066)
			val = Min_TwrT_1066;
		else if (val > Max_TwrT_1066)
			val = Max_TwrT_1066;
	} else {
		if (val < Min_TwrT)
			val = Min_TwrT;
		else if (val > Max_TwrT)
			val = Max_TwrT;
	}
	pDCTstat->Twr = val;

	/* Twtr */
	dword = Twtr * 10;
	pDCTstat->DIMMTwtr = dword;
	val = dword / Tk40;
	if (dword % Tk40) {	/* round up number of busclocks */
		val++;
	}
	if (DDR2_1066) {
		if (val < Min_TwrT_1066)
			val = Min_TwtrT_1066;
		else if (val > Max_TwtrT_1066)
			val = Max_TwtrT_1066;
	} else {
		if (val < Min_TwtrT)
			val = Min_TwtrT;
		else if (val > Max_TwtrT)
			val = Max_TwtrT;
	}
	pDCTstat->Twtr = val;


	/* Trfc0-Trfc3 */
	for (i=0; i<4; i++)
		pDCTstat->Trfc[i] = Trfc[i];

	mctAdjustAutoCycTmg_D();

	/* Program DRAM Timing values */
	DramTimingLo = 0;	/* Dram Timing Low init */
	val = pDCTstat->CASL;
	val = Tab_tCL_j[val];
	DramTimingLo |= val;

	val = pDCTstat->Trcd;
	if (DDR2_1066)
		val -= Bias_TrcdT_1066;
	else
		val -= Bias_TrcdT;

	DramTimingLo |= val<<4;

	val = pDCTstat->Trp;
	if (DDR2_1066)
		val -= Bias_TrpT_1066;
	else {
		val -= Bias_TrpT;
		val <<= 1;
	}
	DramTimingLo |= val<<7;

	val = pDCTstat->Trtp;
	val -= Bias_TrtpT;
	DramTimingLo |= val<<11;

	val = pDCTstat->Tras;
	if (DDR2_1066)
		val -= Bias_TrasT_1066;
	else
		val -= Bias_TrasT;
	DramTimingLo |= val<<12;

	val = pDCTstat->Trc;
	val -= Bias_TrcT;
	DramTimingLo |= val<<16;

	if (!DDR2_1066) {
		val = pDCTstat->Twr;
		val -= Bias_TwrT;
		DramTimingLo |= val<<20;
	}

	val = pDCTstat->Trrd;
	if (DDR2_1066)
		val -= Bias_TrrdT_1066;
	else
		val -= Bias_TrrdT;
	DramTimingLo |= val<<22;


	DramTimingHi = 0;	/* Dram Timing Low init */
	val = pDCTstat->Twtr;
	if (DDR2_1066)
		val -= Bias_TwtrT_1066;
	else
		val -= Bias_TwtrT;
	DramTimingHi |= val<<8;

	val = 2;
	DramTimingHi |= val<<16;

	val = 0;
	for (i=4;i>0;i--) {
		val <<= 3;
		val |= Trfc[i-1];
	}
	DramTimingHi |= val << 20;


	dev = pDCTstat->dev_dct;
	reg_off = 0x100 * dct;
	print_tx("AutoCycTiming: DramTimingLo ", DramTimingLo);
	print_tx("AutoCycTiming: DramTimingHi ", DramTimingHi);

	Set_NB32(dev, 0x88 + reg_off, DramTimingLo);	/*DCT Timing Low*/
	DramTimingHi |=0x0000FC77;
	Set_NB32(dev, 0x8c + reg_off, DramTimingHi);	/*DCT Timing Hi*/

	if (DDR2_1066) {
		/* Twr */
		dword = pDCTstat->Twr;
		dword -= Bias_TwrT_1066;
		dword <<= 4;
		reg = 0x84 + reg_off;
		val = Get_NB32(dev, reg);
		val &= 0x8F;
		val |= dword;
		Set_NB32(dev, reg, val);
	}
//	dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2));

	print_tx("AutoCycTiming: Status ", pDCTstat->Status);
	print_tx("AutoCycTiming: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("AutoCycTiming: ErrCode ", pDCTstat->ErrCode);
	print_t("AutoCycTiming: Done\n");

	mctHookAfterAutoCycTmg();

	return pDCTstat->ErrCode;
}


static void GetPresetmaxF_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	/* Get max frequency from OEM platform definition, from any user
	 * override (limiting) of max frequency, and from any Si Revision
	 * Specific information.  Return the least of these three in
	 * DCTStatStruc.PresetmaxFreq.
	 */

	u16 proposedFreq;
	u16 word;

	/* Get CPU Si Revision defined limit (NPT) */
	proposedFreq = 533;	 /* Rev F0 programmable max memclock is */

	/*Get User defined limit if  "limit" mode */
	if ( mctGet_NVbits(NV_MCTUSRTMGMODE) == 1) {
		word = Get_Fk_D(mctGet_NVbits(NV_MemCkVal) + 1);
		if (word < proposedFreq)
			proposedFreq = word;

		/* Get Platform defined limit */
		word = mctGet_NVbits(NV_MAX_MEMCLK);
		if (word < proposedFreq)
			proposedFreq = word;

		word = pDCTstat->PresetmaxFreq;
		if (word > proposedFreq)
			word = proposedFreq;

		pDCTstat->PresetmaxFreq = word;
	}
}



static void SPDGetTCL_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Find the best T and CL primary timing parameter pair, per Mfg.,
	 * for the given set of DIMMs, and store into DCTStatStruc
	 * (.DIMMAutoSpeed and .DIMMCASL). See "Global relationship between
	 *  index values and item values" for definition of CAS latency
	 *  index (j) and Frequency index (k).
	 */
	int i, j, k;
	u8 T1min, CL1min;

	/* i={0..7} (std. physical DIMM number)
	 * j is an integer which enumerates increasing CAS latency.
	 * k is an integer which enumerates decreasing cycle time.
	 * CL no. {0,1,2} corresponds to CL X, CL X-.5, or CL X-1 (per individual DIMM)
	 * Max timing values are per parameter, of all DIMMs, spec'd in ns like the SPD.
	 */

	CL1min = 0xFF;
	T1min = 0xFF;
	for (k=K_MAX; k >= K_MIN; k--) {
		for (j = J_MIN; j <= J_MAX; j++) {
			if (Sys_Capability_D(pMCTstat, pDCTstat, j, k) ) {
				/* 1. check to see if DIMMi is populated.
				   2. check if DIMMi supports CLj and Tjk */
				for (i = 0; i < MAX_DIMMS_SUPPORTED; i++) {
					if (pDCTstat->DIMMValid & (1 << i)) {
						if (Dimm_Supports_D(pDCTstat, i, j, k))
							break;
					}
				}	/* while ++i */
				if (i == MAX_DIMMS_SUPPORTED) {
					T1min = k;
					CL1min = j;
					goto got_TCL;
				}
			}
		}	/* while ++j */
	}	/* while --k */

got_TCL:
	if (T1min != 0xFF) {
		pDCTstat->DIMMCASL = CL1min;	/*mfg. optimized */
		pDCTstat->DIMMAutoSpeed = T1min;
		print_tx("SPDGetTCL_D: DIMMCASL ", pDCTstat->DIMMCASL);
		print_tx("SPDGetTCL_D: DIMMAutoSpeed ", pDCTstat->DIMMAutoSpeed);

	} else {
		pDCTstat->DIMMCASL = CL_DEF;	/* failsafe values (running in min. mode) */
		pDCTstat->DIMMAutoSpeed = T_DEF;
		pDCTstat->ErrStatus |= 1 << SB_DimmMismatchT;
		pDCTstat->ErrStatus |= 1 << SB_MinimumMode;
		pDCTstat->ErrCode = SC_VarianceErr;
	}
	print_tx("SPDGetTCL_D: Status ", pDCTstat->Status);
	print_tx("SPDGetTCL_D: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("SPDGetTCL_D: ErrCode ", pDCTstat->ErrCode);
	print_t("SPDGetTCL_D: Done\n");
}


static u8 PlatformSpec_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 dev;
	u32 reg;
	u32 val;

	mctGet_PS_Cfg_D(pMCTstat, pDCTstat, dct);

	if (pDCTstat->GangedMode) {
		mctGet_PS_Cfg_D(pMCTstat, pDCTstat, 1);
	}

	if ( pDCTstat->_2Tmode == 2) {
		dev = pDCTstat->dev_dct;
		reg = 0x94 + 0x100 * dct; /* Dram Configuration Hi */
		val = Get_NB32(dev, reg);
		val |= 1 << 20;		       /* 2T CMD mode */
		Set_NB32(dev, reg, val);
	}

	mct_PlatformSpec(pMCTstat, pDCTstat, dct);
	InitPhyCompensation(pMCTstat, pDCTstat, dct);
	mctHookAfterPSCfg();
	return pDCTstat->ErrCode;
}


static u8 AutoConfig_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 DramControl, DramTimingLo, Status;
	u32 DramConfigLo, DramConfigHi, DramConfigMisc, DramConfigMisc2;
	u32 val;
	u32 reg_off;
	u32 dev;
	u16 word;
	u32 dword;
	u8 byte;

	print_tx("AutoConfig_D: DCT: ", dct);

	DramConfigLo = 0;
	DramConfigHi = 0;
	DramConfigMisc = 0;
	DramConfigMisc2 = 0;

	/* set bank addessing and Masks, plus CS pops */
	SPDSetBanks_D(pMCTstat, pDCTstat, dct);
	if (pDCTstat->ErrCode == SC_StopError)
		goto AutoConfig_exit;

	/* map chip-selects into local address space */
	StitchMemory_D(pMCTstat, pDCTstat, dct);
	InterleaveBanks_D(pMCTstat, pDCTstat, dct);

	/* temp image of status (for convenience). RO usage! */
	Status = pDCTstat->Status;

	dev = pDCTstat->dev_dct;
	reg_off = 0x100 * dct;


	/* Build Dram Control Register Value */
	DramConfigMisc2 = Get_NB32 (dev, 0xA8 + reg_off);	/* Dram Control*/
	DramControl = Get_NB32 (dev, 0x78 + reg_off);		/* Dram Control*/

	if (mctGet_NVbits(NV_CLKHZAltVidC3))
		DramControl |= 1<<16;

	// FIXME: Add support(skip) for Ax and Cx versions
	DramControl |= 5;	/* RdPtrInit */


	/* Build Dram Config Lo Register Value */
	DramConfigLo |= 1 << 4;					/* 75 Ohms ODT */
	if (mctGet_NVbits(NV_MAX_DIMMS) == 8) {
		if (pDCTstat->Speed == 3) {
			if ((pDCTstat->MAdimms[dct] == 4))
				DramConfigLo |= 1 << 5;		/* 50 Ohms ODT */
		} else if (pDCTstat->Speed == 4){
			if ((pDCTstat->MAdimms[dct] != 1))
				DramConfigLo |= 1 << 5;		/* 50 Ohms ODT */
		}
	} else {
		// FIXME: Skip for Ax versions
		if ((pDCTstat->MAdimms[dct] == 4)) {
			if ( pDCTstat->DimmQRPresent != 0) {
				if ((pDCTstat->Speed == 3) || (pDCTstat->Speed == 4)) {
					DramConfigLo |= 1 << 5;	/* 50 Ohms ODT */
				}
			} else if ((pDCTstat->MAdimms[dct] == 4)) {
				if (pDCTstat->Speed == 4) {
					if ( pDCTstat->DimmQRPresent != 0) {
						DramConfigLo |= 1 << 5;	/* 50 Ohms ODT */
					}
				}
			}
		} else if ((pDCTstat->MAdimms[dct] == 2)) {
			DramConfigLo |= 1 << 5;		/* 50 Ohms ODT */
		}

	}

	// FIXME: Skip for Ax versions
	/* callback not required - if (!mctParityControl_D()) */
	if (Status & (1 << SB_PARDIMMs)) {
		DramConfigLo |= 1 << ParEn;
		DramConfigMisc2 |= 1 << ActiveCmdAtRst;
	} else {
		DramConfigLo  &= ~(1 << ParEn);
		DramConfigMisc2 &= ~(1 << ActiveCmdAtRst);
	}

	if (mctGet_NVbits(NV_BurstLen32)) {
		if (!pDCTstat->GangedMode)
			DramConfigLo |= 1 << BurstLength32;
	}

	if (Status & (1 << SB_128bitmode))
		DramConfigLo |= 1 << Width128;	/* 128-bit mode (normal) */

	word = dct;
	dword = X4Dimm;
	while (word < 8) {
		if (pDCTstat->Dimmx4Present & (1 << word))
			DramConfigLo |= 1 << dword;	/* X4Dimm[3:0] */
		word++;
		word++;
		dword++;
	}

	if (!(Status & (1 << SB_Registered)))
		DramConfigLo |= 1 << UnBuffDimm;	/* Unbufferd DIMMs */

	if (mctGet_NVbits(NV_ECC_CAP))
		if (Status & (1 << SB_ECCDIMMs))
			if ( mctGet_NVbits(NV_ECC))
				DramConfigLo |= 1 << DimmEcEn;



	/* Build Dram Config Hi Register Value */
	dword = pDCTstat->Speed;
	DramConfigHi |= dword - 1;	/* get MemClk encoding */
	DramConfigHi |= 1 << MemClkFreqVal;

	if (Status & (1 << SB_Registered))
		if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0))
			/* set only if x8 Registered DIMMs in System*/
			DramConfigHi |= 1 << RDqsEn;

	if (mctGet_NVbits(NV_CKE_PDEN)) {
		DramConfigHi |= 1 << 15;		/* PowerDownEn */
		if (mctGet_NVbits(NV_CKE_CTL))
			/*Chip Select control of CKE*/
			DramConfigHi |= 1 << 16;
	}

	/* Control Bank Swizzle */
	if (0) /* call back not needed mctBankSwizzleControl_D()) */
		DramConfigHi &= ~(1 << BankSwizzleMode);
	else
		DramConfigHi |= 1 << BankSwizzleMode; /* recommended setting (default) */

	/* Check for Quadrank DIMM presence */
	if ( pDCTstat->DimmQRPresent != 0) {
		byte = mctGet_NVbits(NV_4RANKType);
		if (byte == 2)
			DramConfigHi |= 1 << 17;	/* S4 (4-Rank SO-DIMMs) */
		else if (byte == 1)
			DramConfigHi |= 1 << 18;	/* R4 (4-Rank Registered DIMMs) */
	}

	if (0) /* call back not needed mctOverrideDcqBypMax_D ) */
		val = mctGet_NVbits(NV_BYPMAX);
	else
		val = 0x0f; // recommended setting (default)
	DramConfigHi |= val << 24;

	val = pDCTstat->DIMM2Kpage;
	if (pDCTstat->GangedMode != 0) {
		if (dct != 0) {
			val &= 0x55;
		} else {
			val &= 0xAA;
		}
	}
	if (val)
		val = Tab_2KTfawT_k[pDCTstat->Speed];
	else
		val = Tab_1KTfawT_k[pDCTstat->Speed];

	if (pDCTstat->Speed == 5)
		val >>= 1;

	val -= Bias_TfawT;
	val <<= 28;
	DramConfigHi |= val;	/* Tfaw for 1K or 2K paged drams */

	// FIXME: Skip for Ax versions
	DramConfigHi |= 1 << DcqArbBypassEn;


	/* Build MemClkDis Value from Dram Timing Lo and
	   Dram Config Misc Registers
	 1. We will assume that MemClkDis field has been preset prior to this
	    point.
	 2. We will only set MemClkDis bits if a DIMM is NOT present AND if:
	    NV_AllMemClks <>0 AND SB_DiagClks ==0 */


	/* Dram Timing Low (owns Clock Enable bits) */
	DramTimingLo = Get_NB32(dev, 0x88 + reg_off);
	if (mctGet_NVbits(NV_AllMemClks) == 0) {
		/* Special Jedec SPD diagnostic bit - "enable all clocks" */
		if (!(pDCTstat->Status & (1<<SB_DiagClks))) {
			const u8 *p;
			byte = mctGet_NVbits(NV_PACK_TYPE);
			if (byte == PT_L1)
				p = Tab_L1CLKDis;
			else if (byte == PT_M2)
				p = Tab_M2CLKDis;
			else
				p = Tab_S1CLKDis;

			dword = 0;
			while(dword < MAX_DIMMS_SUPPORTED) {
				val = p[dword];
				print_tx("DramTimingLo: val=", val);
				if (!(pDCTstat->DIMMValid & (1<<val)))
					/*disable memclk*/
					DramTimingLo |= 1<<(dword+24);
				dword++ ;
			}
		}
	}

	print_tx("AutoConfig_D: DramControl: ", DramControl);
	print_tx("AutoConfig_D: DramTimingLo: ", DramTimingLo);
	print_tx("AutoConfig_D: DramConfigMisc: ", DramConfigMisc);
	print_tx("AutoConfig_D: DramConfigMisc2: ", DramConfigMisc2);
	print_tx("AutoConfig_D: DramConfigLo: ", DramConfigLo);
	print_tx("AutoConfig_D: DramConfigHi: ", DramConfigHi);

	/* Write Values to the registers */
	Set_NB32(dev, 0x78 + reg_off, DramControl);
	Set_NB32(dev, 0x88 + reg_off, DramTimingLo);
	Set_NB32(dev, 0xA0 + reg_off, DramConfigMisc);
	Set_NB32(dev, 0xA8 + reg_off, DramConfigMisc2);
	Set_NB32(dev, 0x90 + reg_off, DramConfigLo);
	mct_SetDramConfigHi_D(pDCTstat, dct, DramConfigHi);
	mct_ForceAutoPrecharge_D(pDCTstat, dct);
	mct_EarlyArbEn_D(pMCTstat, pDCTstat);
	mctHookAfterAutoCfg();

//	dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2));

	print_tx("AutoConfig: Status ", pDCTstat->Status);
	print_tx("AutoConfig: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("AutoConfig: ErrCode ", pDCTstat->ErrCode);
	print_t("AutoConfig: Done\n");
AutoConfig_exit:
	return pDCTstat->ErrCode;
}


static void SPDSetBanks_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Set bank addressing, program Mask values and build a chip-select
	 * population map. This routine programs PCI 0:24N:2x80 config register
	 * and PCI 0:24N:2x60,64,68,6C config registers (CS Mask 0-3).
	 */

	u8 ChipSel, Rows, Cols, Ranks ,Banks, DevWidth;
	u32 BankAddrReg, csMask;

	u32 val;
	u32 reg;
	u32 dev;
	u32 reg_off;
	u8 byte;
	u16 word;
	u32 dword;
	u16 smbaddr;

	dev = pDCTstat->dev_dct;
	reg_off = 0x100 * dct;

	BankAddrReg = 0;
	for (ChipSel = 0; ChipSel < MAX_CS_SUPPORTED; ChipSel+=2) {
		byte = ChipSel;
		if ((pDCTstat->Status & (1 << SB_64MuxedMode)) && ChipSel >=4)
			byte -= 3;

		if (pDCTstat->DIMMValid & (1<<byte)) {
			smbaddr = Get_DIMMAddress_D(pDCTstat, (ChipSel + dct));

			byte = mctRead_SPD(smbaddr, SPD_ROWSZ);
			Rows = byte & 0x1f;

			byte = mctRead_SPD(smbaddr, SPD_COLSZ);
			Cols = byte & 0x1f;

			Banks = mctRead_SPD(smbaddr, SPD_LBANKS);

			byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH);
			DevWidth = byte & 0x7f; /* bits 0-6 = bank 0 width */

			byte = mctRead_SPD(smbaddr, SPD_DMBANKS);
			Ranks = (byte & 7) + 1;

			/* Configure Bank encoding
			 * Use a 6-bit key into a lookup table.
			 * Key (index) = CCCBRR, where CCC is the number of
			 * Columns minus 9,RR is the number of Rows minus 13,
			 * and B is the number of banks minus 2.
			 * See "6-bit Bank Addressing Table" at the end of
			 * this file.*/
			byte = Cols - 9;	/* 9 Cols is smallest dev size */
			byte <<= 3;		/* make room for row and bank bits*/
			if (Banks == 8)
				byte |= 4;

			/* 13 Rows is smallest dev size */
			byte |= Rows - 13;	/* CCCBRR internal encode */

			for (dword=0; dword < 12; dword++) {
				if (byte == Tab_BankAddr[dword])
					break;
			}

			if (dword < 12) {

				/* bit no. of CS field in address mapping reg.*/
				dword <<= (ChipSel<<1);
				BankAddrReg |= dword;

				/* Mask value=(2pow(rows+cols+banks+3)-1)>>8,
				   or 2pow(rows+cols+banks-5)-1*/
				csMask = 0;

				byte = Rows + Cols;		/* cl=rows+cols*/
				if (Banks == 8)
					byte -= 2;		/* 3 banks - 5 */
				else
					byte -= 3;		/* 2 banks - 5 */
								/* mask size (64-bit rank only) */

				if (pDCTstat->Status & (1 << SB_128bitmode))
					byte++;		/* double mask size if in 128-bit mode*/

				csMask |= 1 << byte;
				csMask--;

				/*set ChipSelect population indicator even bits*/
				pDCTstat->CSPresent |= (1<<ChipSel);
				if (Ranks >= 2)
					/*set ChipSelect population indicator odd bits*/
					pDCTstat->CSPresent |= 1 << (ChipSel + 1);

				reg = 0x60+(ChipSel<<1) + reg_off;	/*Dram CS Mask Register */
				val = csMask;
				val &= 0x1FF83FE0;	/* Mask out reserved bits.*/
				Set_NB32(dev, reg, val);
			}
		} else {
			if (pDCTstat->DIMMSPDCSE & (1<<ChipSel))
				pDCTstat->CSTestFail |= (1<<ChipSel);
		}	/* if DIMMValid*/
	}	/* while ChipSel*/

	SetCSTriState(pMCTstat, pDCTstat, dct);
	/* SetCKETriState */
	SetODTTriState(pMCTstat, pDCTstat, dct);

	if (pDCTstat->Status & (1 << SB_128bitmode)) {
		SetCSTriState(pMCTstat, pDCTstat, 1); /* force dct1) */
		SetODTTriState(pMCTstat, pDCTstat, 1); /* force dct1) */
	}
	word = pDCTstat->CSPresent;
	mctGetCS_ExcludeMap();		/* mask out specified chip-selects */
	word ^= pDCTstat->CSPresent;
	pDCTstat->CSTestFail |= word;	/* enable ODT to disabled DIMMs */
	if (!pDCTstat->CSPresent)
		pDCTstat->ErrCode = SC_StopError;

	reg = 0x80 + reg_off;		/* Bank Addressing Register */
	Set_NB32(dev, reg, BankAddrReg);

//	dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2));

	print_tx("SPDSetBanks: Status ", pDCTstat->Status);
	print_tx("SPDSetBanks: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("SPDSetBanks: ErrCode ", pDCTstat->ErrCode);
	print_t("SPDSetBanks: Done\n");
}


static void SPDCalcWidth_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	/* Per SPDs, check the symmetry of DIMM pairs (DIMM on Channel A
	 *  matching with DIMM on Channel B), the overall DIMM population,
	 * and determine the width mode: 64-bit, 64-bit muxed, 128-bit.
	 */

	u8 i;
	u8 smbaddr, smbaddr1;
	u8 byte, byte1;

	/* Check Symmetry of Channel A and Channel B DIMMs
	  (must be matched for 128-bit mode).*/
	for (i=0; i < MAX_DIMMS_SUPPORTED; i += 2) {
		if ((pDCTstat->DIMMValid & (1 << i)) && (pDCTstat->DIMMValid & (1<<(i+1)))) {
			smbaddr = Get_DIMMAddress_D(pDCTstat, i);
			smbaddr1 = Get_DIMMAddress_D(pDCTstat, i+1);

			byte = mctRead_SPD(smbaddr, SPD_ROWSZ) & 0x1f;
			byte1 = mctRead_SPD(smbaddr1, SPD_ROWSZ) & 0x1f;
			if (byte != byte1) {
				pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
				break;
			}

			byte =	 mctRead_SPD(smbaddr, SPD_COLSZ) & 0x1f;
			byte1 =	 mctRead_SPD(smbaddr1, SPD_COLSZ) & 0x1f;
			if (byte != byte1) {
				pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
				break;
			}

			byte = mctRead_SPD(smbaddr, SPD_BANKSZ);
			byte1 = mctRead_SPD(smbaddr1, SPD_BANKSZ);
			if (byte != byte1) {
				pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
				break;
			}

			byte = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0x7f;
			byte1 = mctRead_SPD(smbaddr1, SPD_DEVWIDTH) & 0x7f;
			if (byte != byte1) {
				pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
				break;
			}

			byte = mctRead_SPD(smbaddr, SPD_DMBANKS) & 7;	 /* #ranks-1 */
			byte1 = mctRead_SPD(smbaddr1, SPD_DMBANKS) & 7;	  /* #ranks-1 */
			if (byte != byte1) {
				pDCTstat->ErrStatus |= (1<<SB_DimmMismatchO);
				break;
			}

		}
	}

}


static void StitchMemory_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Requires that Mask values for each bank be programmed first and that
	 * the chip-select population indicator is correctly set.
	 */

	u8 b = 0;
	u32 nxtcsBase, curcsBase;
	u8 p, q;
	u32 Sizeq, BiggestBank;
	u8 _DSpareEn;

	u16 word;
	u32 dev;
	u32 reg;
	u32 reg_off;
	u32 val;


	dev = pDCTstat->dev_dct;
	reg_off = 0x100 * dct;

	_DSpareEn = 0;

	/* CS Sparing 1=enabled, 0=disabled */
	if (mctGet_NVbits(NV_CS_SpareCTL) & 1) {
		if (MCT_DIMM_SPARE_NO_WARM) {
			/* Do no warm-reset DIMM spare */
			if (pMCTstat->GStatus & 1 << GSB_EnDIMMSpareNW) {
				word = pDCTstat->CSPresent;
				val = bsf(word);
				word &= ~(1<<val);
				if (word)
					/* Make sure at least two chip-selects are available */
					_DSpareEn = 1;
				else
					pDCTstat->ErrStatus |= 1 << SB_SpareDis;
			}
		} else {
			if (!mctGet_NVbits(NV_DQSTrainCTL)) { /*DQS Training 1=enabled, 0=disabled */
				word = pDCTstat->CSPresent;
				val = bsf(word);
				word &= ~(1 << val);
				if (word)
					/* Make sure at least two chip-selects are available */
					_DSpareEn = 1;
				else
					pDCTstat->ErrStatus |= 1 << SB_SpareDis;
			}
		}
	}

	nxtcsBase = 0;		/* Next available cs base ADDR[39:8] */
	for (p=0; p < MAX_DIMMS_SUPPORTED; p++) {
		BiggestBank = 0;
		for (q = 0; q < MAX_CS_SUPPORTED; q++) { /* from DIMMS to CS */
			if (pDCTstat->CSPresent & (1 << q)) {  /* bank present? */
				reg  = 0x40 + (q << 2) + reg_off;  /* Base[q] reg.*/
				val = Get_NB32(dev, reg);
				if (!(val & 3)) {	/* (CSEnable|Spare==1)bank is enabled already? */
					reg = 0x60 + (q << 1) + reg_off; /*Mask[q] reg.*/
					val = Get_NB32(dev, reg);
					val >>= 19;
					val++;
					val <<= 19;
					Sizeq = val;  //never used
					if (val > BiggestBank) {
						/*Bingo! possibly Map this chip-select next! */
						BiggestBank = val;
						b = q;
					}
				}
			}	/*if bank present */
		}	/* while q */
		if (BiggestBank !=0) {
			curcsBase = nxtcsBase;		/* curcsBase=nxtcsBase*/
			/* DRAM CS Base b Address Register offset */
			reg = 0x40 + (b << 2) + reg_off;
			if (_DSpareEn) {
				BiggestBank = 0;
				val = 1 << Spare;	/* Spare Enable*/
			} else {
				val = curcsBase;
				val |= 1 << CSEnable;	/* Bank Enable */
			}
			Set_NB32(dev, reg, val);
			if (_DSpareEn)
				_DSpareEn = 0;
			else
				/* let nxtcsBase+=Size[b] */
				nxtcsBase += BiggestBank;
		}

		/* bank present but disabled?*/
		if ( pDCTstat->CSTestFail & (1 << p)) {
			/* DRAM CS Base b Address Register offset */
			reg = (p << 2) + 0x40 + reg_off;
			val = 1 << TestFail;
			Set_NB32(dev, reg, val);
		}
	}

	if (nxtcsBase) {
		pDCTstat->DCTSysLimit = nxtcsBase - 1;
		mct_AfterStitchMemory(pMCTstat, pDCTstat, dct);
	}

//	dump_pci_device(PCI_DEV(0, 0x18+pDCTstat->Node_ID, 2));

	print_tx("StitchMemory: Status ", pDCTstat->Status);
	print_tx("StitchMemory: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("StitchMemory: ErrCode ", pDCTstat->ErrCode);
	print_t("StitchMemory: Done\n");
}


static u8 Get_Tk_D(u8 k)
{
	return Table_T_k[k];
}


static u8 Get_CLj_D(u8 j)
{
	return Table_CL2_j[j];
}

static u8 Get_DefTrc_k_D(u8 k)
{
	return Tab_defTrc_k[k];
}


static u16 Get_40Tk_D(u8 k)
{
	return Tab_40T_k[k]; /* FIXME: k or k<<1 ?*/
}


static u16 Get_Fk_D(u8 k)
{
	return Table_F_k[k]; /* FIXME: k or k<<1 ? */
}


static u8 Dimm_Supports_D(struct DCTStatStruc *pDCTstat,
				u8 i, u8 j, u8 k)
{
	u8 Tk, CLj, CL_i;
	u8 ret = 0;

	u32 DIMMi;
	u8 byte;
	u16 word, wordx;

	DIMMi = Get_DIMMAddress_D(pDCTstat, i);

	CLj = Get_CLj_D(j);

	/* check if DIMMi supports CLj */
	CL_i = mctRead_SPD(DIMMi, SPD_CASLAT);
	byte = CL_i & CLj;
	if (byte) {
		/*find out if its CL X, CLX-1, or CLX-2 */
		word = bsr(byte);	/* bit position of CLj */
		wordx = bsr(CL_i);	/* bit position of CLX of CLi */
		wordx -= word;		/* CL number (CL no. = 0,1, 2, or 3) */
		wordx <<= 3;		/* 8 bits per SPD byte index */
		/*get T from SPD byte 9, 23, 25*/
		word = (EncodedTSPD >> wordx) & 0xFF;
		Tk = Get_Tk_D(k);
		byte = mctRead_SPD(DIMMi, word);	/* DIMMi speed */
		if (Tk < byte) {
			ret = 1;
		} else if (byte == 0){
			pDCTstat->ErrStatus |= 1<<SB_NoCycTime;
			ret = 1;
		} else {
			ret = 0;	/* DIMM is capable! */
		}
	} else {
		ret = 1;
	}
	return ret;
}


static u8 DIMMPresence_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	/* Check DIMMs present, verify checksum, flag SDRAM type,
	 * build population indicator bitmaps, and preload bus loading
	 * of DIMMs into DCTStatStruc.
	 * MAAload=number of devices on the "A" bus.
	 * MABload=number of devices on the "B" bus.
	 * MAAdimms=number of DIMMs on the "A" bus slots.
	 * MABdimms=number of DIMMs on the "B" bus slots.
	 * DATAAload=number of ranks on the "A" bus slots.
	 * DATABload=number of ranks on the "B" bus slots.
	 */

	u16 i, j;
	u8 smbaddr, Index;
	u16 Checksum;
	u8 SPDCtrl;
	u16 RegDIMMPresent, MaxDimms;
	u8 devwidth;
	u16 DimmSlots;
	u8 byte = 0, bytex;
	u16 word;

	/* preload data structure with addrs */
	mctGet_DIMMAddr(pDCTstat, pDCTstat->Node_ID);

	DimmSlots = MaxDimms = mctGet_NVbits(NV_MAX_DIMMS);

	SPDCtrl = mctGet_NVbits(NV_SPDCHK_RESTRT);

	RegDIMMPresent = 0;
	pDCTstat->DimmQRPresent = 0;

	for (i = 0;  i< MAX_DIMMS_SUPPORTED; i++) {
		if (i >= MaxDimms)
			break;

		if ((pDCTstat->DimmQRPresent & (1 << i)) || (i < DimmSlots)) {
			print_tx("\t DIMMPresence: i=", i);
			smbaddr = Get_DIMMAddress_D(pDCTstat, i);
			print_tx("\t DIMMPresence: smbaddr=", smbaddr);
			if (smbaddr) {
				Checksum = 0;
				for (Index=0; Index < 64; Index++){
					int status;
					status = mctRead_SPD(smbaddr, Index);
					if (status < 0)
						break;
					byte = status & 0xFF;
					if (Index < 63)
						Checksum += byte;
				}

				if (Index == 64) {
					pDCTstat->DIMMPresent |= 1 << i;
					if ((Checksum & 0xFF) == byte) {
						byte = mctRead_SPD(smbaddr, SPD_TYPE);
						if (byte == JED_DDR2SDRAM) {
							/*Dimm is 'Present'*/
							pDCTstat->DIMMValid |= 1 << i;
						}
					} else {
						pDCTstat->DIMMSPDCSE = 1 << i;
						if (SPDCtrl == 0) {
							pDCTstat->ErrStatus |= 1 << SB_DIMMChkSum;
							pDCTstat->ErrCode = SC_StopError;
						} else {
							/*if NV_SPDCHK_RESTRT is set to 1, ignore faulty SPD checksum*/
							pDCTstat->ErrStatus |= 1<<SB_DIMMChkSum;
							byte = mctRead_SPD(smbaddr, SPD_TYPE);
							if (byte == JED_DDR2SDRAM)
								pDCTstat->DIMMValid |= 1 << i;
						}
					}
					/* Check module type */
					byte = mctRead_SPD(smbaddr, SPD_DIMMTYPE);
					if (byte & JED_REGADCMSK)
						RegDIMMPresent |= 1 << i;
					/* Check ECC capable */
					byte = mctRead_SPD(smbaddr, SPD_EDCTYPE);
					if (byte & JED_ECC) {
						/* DIMM is ECC capable */
						pDCTstat->DimmECCPresent |= 1 << i;
					}
					if (byte & JED_ADRCPAR) {
					/* DIMM is ECC capable */
					pDCTstat->DimmPARPresent |= 1 << i;
					}
					/* Check if x4 device */
					devwidth = mctRead_SPD(smbaddr, SPD_DEVWIDTH) & 0xFE;
					if (devwidth == 4) {
						/* DIMM is made with x4 or x16 drams */
						pDCTstat->Dimmx4Present |= 1 << i;
					} else if (devwidth == 8) {
						pDCTstat->Dimmx8Present |= 1 << i;
					} else if (devwidth == 16) {
						pDCTstat->Dimmx16Present |= 1 << i;
					}
					/* check page size */
					byte = mctRead_SPD(smbaddr, SPD_COLSZ);
					byte &= 0x0F;
					word = 1 << byte;
					word >>= 3;
					word *= devwidth;	/* (((2^COLBITS) / 8) * ORG) / 2048 */
					word >>= 11;
					if (word)
						pDCTstat->DIMM2Kpage |= 1 << i;

					/*Check if SPD diag bit 'analysis probe installed' is set */
					byte = mctRead_SPD(smbaddr, SPD_ATTRIB);
					if ( byte & JED_PROBEMSK )
						pDCTstat->Status |= 1<<SB_DiagClks;

					byte = mctRead_SPD(smbaddr, SPD_DMBANKS);
					if (!(byte & (1<< SPDPLBit)))
						pDCTstat->DimmPlPresent |= 1 << i;
					byte &= 7;
					byte++;		 /* ranks */
					if (byte > 2) {
						/* if any DIMMs are QR, we have to make two passes through DIMMs*/
						if ( pDCTstat->DimmQRPresent == 0) {
							MaxDimms <<= 1;
						}
						if (i < DimmSlots) {
							pDCTstat->DimmQRPresent |= (1 << i) | (1 << (i+4));
						}
						byte = 2;	/* upper two ranks of QR DIMM will be counted on another DIMM number iteration*/
					} else if (byte == 2) {
						pDCTstat->DimmDRPresent |= 1 << i;
					}
					bytex = devwidth;
					if (devwidth == 16)
						bytex = 4;
					else if (devwidth == 4)
						bytex=16;

					if (byte == 2)
						bytex <<= 1;	/*double Addr bus load value for dual rank DIMMs*/

					j = i & (1<<0);
					pDCTstat->DATAload[j] += byte;	/*number of ranks on DATA bus*/
					pDCTstat->MAload[j] += bytex;	/*number of devices on CMD/ADDR bus*/
					pDCTstat->MAdimms[j]++;		/*number of DIMMs on A bus */
					/*check for DRAM package Year <= 06*/
					byte = mctRead_SPD(smbaddr, SPD_MANDATEYR);
					if (byte < MYEAR06) {
						/*Year < 06 and hence Week < 24 of 06 */
						pDCTstat->DimmYr06 |= 1 << i;
						pDCTstat->DimmWk2406 |= 1 << i;
					} else if (byte == MYEAR06) {
						/*Year = 06, check if Week <= 24 */
						pDCTstat->DimmYr06 |= 1 << i;
						byte = mctRead_SPD(smbaddr, SPD_MANDATEWK);
						if (byte <= MWEEK24)
							pDCTstat->DimmWk2406 |= 1 << i;
					}
				}
			}
		}
	}
	print_tx("\t DIMMPresence: DIMMValid=", pDCTstat->DIMMValid);
	print_tx("\t DIMMPresence: DIMMPresent=", pDCTstat->DIMMPresent);
	print_tx("\t DIMMPresence: RegDIMMPresent=", RegDIMMPresent);
	print_tx("\t DIMMPresence: DimmECCPresent=", pDCTstat->DimmECCPresent);
	print_tx("\t DIMMPresence: DimmPARPresent=", pDCTstat->DimmPARPresent);
	print_tx("\t DIMMPresence: Dimmx4Present=", pDCTstat->Dimmx4Present);
	print_tx("\t DIMMPresence: Dimmx8Present=", pDCTstat->Dimmx8Present);
	print_tx("\t DIMMPresence: Dimmx16Present=", pDCTstat->Dimmx16Present);
	print_tx("\t DIMMPresence: DimmPlPresent=", pDCTstat->DimmPlPresent);
	print_tx("\t DIMMPresence: DimmDRPresent=", pDCTstat->DimmDRPresent);
	print_tx("\t DIMMPresence: DimmQRPresent=", pDCTstat->DimmQRPresent);
	print_tx("\t DIMMPresence: DATAload[0]=", pDCTstat->DATAload[0]);
	print_tx("\t DIMMPresence: MAload[0]=", pDCTstat->MAload[0]);
	print_tx("\t DIMMPresence: MAdimms[0]=", pDCTstat->MAdimms[0]);
	print_tx("\t DIMMPresence: DATAload[1]=", pDCTstat->DATAload[1]);
	print_tx("\t DIMMPresence: MAload[1]=", pDCTstat->MAload[1]);
	print_tx("\t DIMMPresence: MAdimms[1]=", pDCTstat->MAdimms[1]);

	if (pDCTstat->DIMMValid != 0) {	/* If any DIMMs are present...*/
		if (RegDIMMPresent != 0) {
			if ((RegDIMMPresent ^ pDCTstat->DIMMValid) !=0) {
				/* module type DIMM mismatch (reg'ed, unbuffered) */
				pDCTstat->ErrStatus |= 1<<SB_DimmMismatchM;
				pDCTstat->ErrCode = SC_StopError;
			} else{
				/* all DIMMs are registered */
				pDCTstat->Status |= 1<<SB_Registered;
			}
		}
		if (pDCTstat->DimmECCPresent != 0) {
			if ((pDCTstat->DimmECCPresent ^ pDCTstat->DIMMValid )== 0) {
				/* all DIMMs are ECC capable */
				pDCTstat->Status |= 1<<SB_ECCDIMMs;
			}
		}
		if (pDCTstat->DimmPARPresent != 0) {
			if ((pDCTstat->DimmPARPresent ^ pDCTstat->DIMMValid) == 0) {
				/*all DIMMs are Parity capable */
				pDCTstat->Status |= 1<<SB_PARDIMMs;
			}
		}
	} else {
		/* no DIMMs present or no DIMMs that qualified. */
		pDCTstat->ErrStatus |= 1<<SB_NoDimms;
		pDCTstat->ErrCode = SC_StopError;
	}

	print_tx("\t DIMMPresence: Status ", pDCTstat->Status);
	print_tx("\t DIMMPresence: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("\t DIMMPresence: ErrCode ", pDCTstat->ErrCode);
	print_t("\t DIMMPresence: Done\n");

	mctHookAfterDIMMpre();

	return pDCTstat->ErrCode;
}


static u8 Sys_Capability_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, int j, int k)
{
	/* Determine if system is capable of operating at given input
	 * parameters for CL, and T.  There are three components to
	 * determining "maximum frequency" in AUTO mode: SPD component,
	 * Bus load component, and "Preset" max frequency component.
	 * This procedure is used to help find the SPD component and relies
	 * on pre-determination of the bus load component and the Preset
	 * components.  The generalized algorithm for finding maximum
	 * frequency is structured this way so as to optimize for CAS
	 * latency (which might get better as a result of reduced frequency).
	 * See "Global relationship between index values and item values"
	 * for definition of CAS latency index (j) and Frequency index (k).
	 */
	u8 freqOK, ClOK;
	u8 ret = 0;

	if (Get_Fk_D(k) > pDCTstat->PresetmaxFreq)
		freqOK = 0;
	else
		freqOK = 1;

	/* compare proposed CAS latency with AMD Si capabilities */
	if ((j < J_MIN) || (j > J_MAX))
		ClOK = 0;
	else
		ClOK = 1;

	if (freqOK && ClOK)
		ret = 1;

	return ret;
}


static u8 Get_DIMMAddress_D(struct DCTStatStruc *pDCTstat, u8 i)
{
	u8 *p;

	p = pDCTstat->DIMMAddr;
	//mct_BeforeGetDIMMAddress();
	return p[i];
}


static void mct_initDCT(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 val;
	u8 err_code;

	/* Config. DCT0 for Ganged or unganged mode */
	print_t("\tmct_initDCT: DCTInit_D 0\n");
	DCTInit_D(pMCTstat, pDCTstat, 0);
	if (pDCTstat->ErrCode == SC_FatalErr) {
		// Do nothing goto exitDCTInit;	/* any fatal errors? */
	} else {
		/* Configure DCT1 if unganged and enabled*/
		if (!pDCTstat->GangedMode) {
			if ( pDCTstat->DIMMValidDCT[1] > 0) {
				print_t("\tmct_initDCT: DCTInit_D 1\n");
				err_code = pDCTstat->ErrCode;		/* save DCT0 errors */
				pDCTstat->ErrCode = 0;
				DCTInit_D(pMCTstat, pDCTstat, 1);
				if (pDCTstat->ErrCode == 2)		/* DCT1 is not Running */
					pDCTstat->ErrCode = err_code;	/* Using DCT0 Error code to update pDCTstat.ErrCode */
			} else {
				val = 1 << DisDramInterface;
				Set_NB32(pDCTstat->dev_dct, 0x100 + 0x94, val);
			}
		}
	}
// exitDCTInit:
}


static void mct_DramInit(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 val;

	mct_BeforeDramInit_Prod_D(pMCTstat, pDCTstat);
	// FIXME: for rev A: mct_BeforeDramInit_D(pDCTstat, dct);

	/* Disable auto refresh before Dram init when in ganged mode (Erratum 278) */
	if (pDCTstat->LogicalCPUID & AMD_DR_LT_B2) {
		if (pDCTstat->GangedMode) {
			val = Get_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct));
			val |= 1 << DisAutoRefresh;
			Set_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct), val);
		}
	}

	mct_DramInit_Hw_D(pMCTstat, pDCTstat, dct);

	/* Re-enable auto refresh after Dram init when in ganged mode
	 * to ensure both DCTs are in sync (Erratum 278)
	 */

	if (pDCTstat->LogicalCPUID & AMD_DR_LT_B2) {
		if (pDCTstat->GangedMode) {
			do {
				val = Get_NB32(pDCTstat->dev_dct, 0x90 + (0x100 * dct));
			} while (!(val & (1 << InitDram)));

			WaitRoutine_D(50);

			val = Get_NB32(pDCTstat->dev_dct, 0x8C + (0x100 * dct));
			val &= ~(1 << DisAutoRefresh);
			val |= 1 << DisAutoRefresh;
			val &= ~(1 << DisAutoRefresh);
		}
	}
}


static u8 mct_setMode(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u8 byte;
	u8 bytex;
	u32 val;
	u32 reg;

	byte = bytex = pDCTstat->DIMMValid;
	bytex &= 0x55;		/* CHA DIMM pop */
	pDCTstat->DIMMValidDCT[0] = bytex;

	byte &= 0xAA;		/* CHB DIMM popa */
	byte >>= 1;
	pDCTstat->DIMMValidDCT[1] = byte;

	if (byte != bytex) {
		pDCTstat->ErrStatus &= ~(1 << SB_DimmMismatchO);
	} else {
	if ( mctGet_NVbits(NV_Unganged) )
		pDCTstat->ErrStatus |= (1 << SB_DimmMismatchO);

		if (!(pDCTstat->ErrStatus & (1 << SB_DimmMismatchO))) {
			pDCTstat->GangedMode = 1;
			/* valid 128-bit mode population. */
			pDCTstat->Status |= 1 << SB_128bitmode;
			reg = 0x110;
			val = Get_NB32(pDCTstat->dev_dct, reg);
			val |= 1 << DctGangEn;
			Set_NB32(pDCTstat->dev_dct, reg, val);
			print_tx("setMode: DRAM Controller Select Low Register = ", val);
		}
	}
	return pDCTstat->ErrCode;
}


u32 Get_NB32(u32 dev, u32 reg)
{
	u32 addr;

	addr = (dev>>4) | (reg & 0xFF) | ((reg & 0xf00)<<16);
	outl((1<<31) | (addr & ~3), 0xcf8);

	return inl(0xcfc);
}


void Set_NB32(u32 dev, u32 reg, u32 val)
{
	u32 addr;

	addr = (dev>>4) | (reg & 0xFF) | ((reg & 0xf00)<<16);
	outl((1<<31) | (addr & ~3), 0xcf8);
	outl(val, 0xcfc);
}


u32 Get_NB32_index(u32 dev, u32 index_reg, u32 index)
{
	u32 dword;

	Set_NB32(dev, index_reg, index);
	dword = Get_NB32(dev, index_reg+0x4);

	return dword;
}

void Set_NB32_index(u32 dev, u32 index_reg, u32 index, u32 data)
{
	Set_NB32(dev, index_reg, index);
	Set_NB32(dev, index_reg + 0x4, data);
}


u32 Get_NB32_index_wait(u32 dev, u32 index_reg, u32 index)
{

	u32 dword;


	index &= ~(1 << DctAccessWrite);
	Set_NB32(dev, index_reg, index);
	do {
		dword = Get_NB32(dev, index_reg);
	} while (!(dword & (1 << DctAccessDone)));
	dword = Get_NB32(dev, index_reg + 0x4);

	return dword;
}


void Set_NB32_index_wait(u32 dev, u32 index_reg, u32 index, u32 data)
{
	u32 dword;


	Set_NB32(dev, index_reg + 0x4, data);
	index |= (1 << DctAccessWrite);
	Set_NB32(dev, index_reg, index);
	do {
		dword = Get_NB32(dev, index_reg);
	} while (!(dword & (1 << DctAccessDone)));

}


static u8 mct_PlatformSpec(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	/* Get platform specific config/timing values from the interface layer
	 * and program them into DCT.
	 */

	u32 dev = pDCTstat->dev_dct;
	u32 index_reg;
	u8 i, i_start, i_end;

	if (pDCTstat->GangedMode) {
		SyncSetting(pDCTstat);
		i_start = 0;
		i_end = 2;
	} else {
		i_start = dct;
		i_end = dct + 1;
	}
	for (i=i_start; i<i_end; i++) {
		index_reg = 0x98 + (i * 0x100);
		Set_NB32_index_wait(dev, index_reg, 0x00, pDCTstat->CH_ODC_CTL[i]); /* Channel A Output Driver Compensation Control */
		Set_NB32_index_wait(dev, index_reg, 0x04, pDCTstat->CH_ADDR_TMG[i]); /* Channel A Output Driver Compensation Control */
	}

	return pDCTstat->ErrCode;

}


static void mct_SyncDCTsReady(struct DCTStatStruc *pDCTstat)
{
	u32 dev;
	u32 val;

	if (pDCTstat->NodePresent) {
		print_tx("mct_SyncDCTsReady: Node ", pDCTstat->Node_ID);
		dev = pDCTstat->dev_dct;

		if ((pDCTstat->DIMMValidDCT[0] ) || (pDCTstat->DIMMValidDCT[1])) {		/* This Node has dram */
			do {
				val = Get_NB32(dev, 0x110);
			} while (!(val & (1 << DramEnabled)));
			print_t("mct_SyncDCTsReady: DramEnabled\n");
		}
	}	/* Node is present */
}


static void mct_AfterGetCLT(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	if (!pDCTstat->GangedMode) {
		if (dct == 0 ) {
			pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
			if (pDCTstat->DIMMValidDCT[dct] == 0)
				pDCTstat->ErrCode = SC_StopError;
		} else {
			pDCTstat->CSPresent = 0;
			pDCTstat->CSTestFail = 0;
			pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[dct];
			if (pDCTstat->DIMMValidDCT[dct] == 0)
				pDCTstat->ErrCode = SC_StopError;
		}
	}
}

static u8 mct_SPDCalcWidth(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 ret;

	if ( dct == 0) {
		SPDCalcWidth_D(pMCTstat, pDCTstat);
		ret = mct_setMode(pMCTstat, pDCTstat);
	} else {
		ret = pDCTstat->ErrCode;
	}

	print_tx("SPDCalcWidth: Status ", pDCTstat->Status);
	print_tx("SPDCalcWidth: ErrStatus ", pDCTstat->ErrStatus);
	print_tx("SPDCalcWidth: ErrCode ", pDCTstat->ErrCode);
	print_t("SPDCalcWidth: Done\n");

	return ret;
}


static void mct_AfterStitchMemory(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 val;
	u32 dword;
	u32 dev;
	u32 reg;
	u8 _MemHoleRemap;
	u32 DramHoleBase;

	_MemHoleRemap = mctGet_NVbits(NV_MemHole);
	DramHoleBase = mctGet_NVbits(NV_BottomIO);
	DramHoleBase <<= 8;
	/* Increase hole size so;[31:24]to[31:16]
	 * it has granularity of 128MB shl eax,8
	 * Set 'effective' bottom IOmov DramHoleBase,eax
	 */
	pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;

	/* In unganged mode, we must add DCT0 and DCT1 to DCTSysLimit */
	if (!pDCTstat->GangedMode) {
		dev = pDCTstat->dev_dct;
		pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit;
		/* if DCT0 and DCT1 both exist, set DctSelBaseAddr[47:27] to the top of DCT0 */
		if (dct == 0) {
			if (pDCTstat->DIMMValidDCT[1] > 0) {
				dword = pDCTstat->DCTSysLimit + 1;
				dword += pDCTstat->NodeSysBase;
				dword >>= 8; /* scale [39:8] to [47:27],and to F2x110[31:11] */
				if ((dword >= DramHoleBase) && _MemHoleRemap) {
					pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;
					val = pMCTstat->HoleBase;
					val >>= 16;
					val = (((~val) & 0xFF) + 1);
					val <<= 8;
					dword += val;
				}
				reg = 0x110;
				val = Get_NB32(dev, reg);
				val &= 0x7F;
				val |= dword;
				val |= 3;  /* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */
				Set_NB32(dev, reg, val);
				print_tx("AfterStitch DCT0 and DCT1: DRAM Controller Select Low Register = ", val);
				print_tx("AfterStitch DCT0 and DCT1: DRAM Controller Select High Register = ", dword);

				reg = 0x114;
				val = dword;
				Set_NB32(dev, reg, val);
			}
		} else {
			/* Program the DctSelBaseAddr value to 0
			   if DCT 0 is disabled */
			if (pDCTstat->DIMMValidDCT[0] == 0) {
				dword = pDCTstat->NodeSysBase;
				dword >>= 8;
				if ((dword >= DramHoleBase) && _MemHoleRemap) {
					pMCTstat->HoleBase = (DramHoleBase & 0xFFFFF800) << 8;
					val = pMCTstat->HoleBase;
					val >>= 8;
					val &= ~(0xFFFF);
					val |= (((~val) & 0xFFFF) + 1);
					dword += val;
				}
				reg = 0x114;
				val = dword;
				Set_NB32(dev, reg, val);

				reg = 0x110;
				val |= 3;	/* Set F2x110[DctSelHiRngEn], F2x110[DctSelHi] */
				Set_NB32(dev, reg, val);
				print_tx("AfterStitch DCT1 only: DRAM Controller Select Low Register = ", val);
				print_tx("AfterStitch DCT1 only: DRAM Controller Select High Register = ", dword);
			}
		}
	} else {
		pDCTstat->NodeSysLimit += pDCTstat->DCTSysLimit;
	}
	print_tx("AfterStitch pDCTstat->NodeSysBase = ", pDCTstat->NodeSysBase);
	print_tx("mct_AfterStitchMemory: pDCTstat->NodeSysLimit ", pDCTstat->NodeSysLimit);
}


static u8 mct_DIMMPresence(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 ret;

	if ( dct == 0)
		ret = DIMMPresence_D(pMCTstat, pDCTstat);
	else
		ret = pDCTstat->ErrCode;

	return ret;
}


/* mct_BeforeGetDIMMAddress inline in C */


static void mct_OtherTiming(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	u8 Node;

	for (Node = 0; Node < MAX_NODES_SUPPORTED; Node++) {
		struct DCTStatStruc *pDCTstat;
		pDCTstat = pDCTstatA + Node;
		if (pDCTstat->NodePresent) {
			if (pDCTstat->DIMMValidDCT[0]) {
				pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[0];
				Set_OtherTiming(pMCTstat, pDCTstat, 0);
			}
			if (pDCTstat->DIMMValidDCT[1] && !pDCTstat->GangedMode ) {
				pDCTstat->DIMMValid = pDCTstat->DIMMValidDCT[1];
				Set_OtherTiming(pMCTstat, pDCTstat, 1);
			}
		}	/* Node is present*/
	}	/* while Node */
}


static void Set_OtherTiming(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 reg;
	u32 reg_off = 0x100 * dct;
	u32 val;
	u32 dword;
	u32 dev = pDCTstat->dev_dct;

	Get_Trdrd(pMCTstat, pDCTstat, dct);
	Get_Twrwr(pMCTstat, pDCTstat, dct);
	Get_Twrrd(pMCTstat, pDCTstat, dct);
	Get_TrwtTO(pMCTstat, pDCTstat, dct);
	Get_TrwtWB(pMCTstat, pDCTstat);

	reg = 0x8C + reg_off;		/* Dram Timing Hi */
	val = Get_NB32(dev, reg);
	val &= 0xffff0300;
	dword = pDCTstat->TrwtTO; //0x07
	val |= dword << 4;
	dword = pDCTstat->Twrrd; //0x03
	val |= dword << 10;
	dword = pDCTstat->Twrwr; //0x03
	val |= dword << 12;
	dword = pDCTstat->Trdrd; //0x03
	val |= dword << 14;
	dword = pDCTstat->TrwtWB; //0x07
	val |= dword;
	val = OtherTiming_A_D(pDCTstat, val);
	Set_NB32(dev, reg, val);

}


static void Get_Trdrd(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 Trdrd;
	u8 byte;
	u32 dword;
	u32 val;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;

	if ((pDCTstat->Dimmx4Present != 0) && (pDCTstat->Dimmx8Present != 0)) {
		/* mixed (x4 or x8) DIMM types
		  the largest DqsRcvEnGrossDelay of any DIMM minus the DqsRcvEnGrossDelay
		  of any other DIMM is equal to the Critical Gross Delay Difference (CGDD) for Trdrd.*/
		byte = Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg);
		if (byte == 0)
			Trdrd = 1;
		else
			Trdrd = 2;

	} else {
		/*
		 Trdrd with non-mixed DIMM types
		 RdDqsTime are the same for all DIMMs and DqsRcvEn difference between
		 any two DIMMs is less than half of a MEMCLK, BIOS should program Trdrd to 0000b,
		 else BIOS should program Trdrd to 0001b.

		 RdDqsTime are the same for all DIMMs
		 DDR400~DDR667 only use one set register
		 DDR800 have two set register for DIMM0 and DIMM1 */
		Trdrd = 1;
		if (pDCTstat->Speed > 3) {
			/* DIMM0+DIMM1 exist */ //NOTE it should be 5
			val = bsf(pDCTstat->DIMMValid);
			dword = bsr(pDCTstat->DIMMValid);
			if (dword != val && dword != 0)  {
				/* DCT Read DQS Timing Control - DIMM0 - Low */
				dword = Get_NB32_index_wait(dev, index_reg, 0x05);
				/* DCT Read DQS Timing Control - DIMM1 - Low */
				val = Get_NB32_index_wait(dev, index_reg, 0x105);
				if (val != dword)
					goto Trdrd_1;

				/* DCT Read DQS Timing Control - DIMM0 - High */
				dword = Get_NB32_index_wait(dev, index_reg, 0x06);
				/* DCT Read DQS Timing Control - DIMM1 - High */
				val = Get_NB32_index_wait(dev, index_reg, 0x106);
				if (val != dword)
					goto Trdrd_1;
			}
		}

		/* DqsRcvEn difference between any two DIMMs is
		   less than half of a MEMCLK */
		/* DqsRcvEn byte 1,0*/
		if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x10))
			goto Trdrd_1;
		/* DqsRcvEn byte 3,2*/
		if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x11))
			goto Trdrd_1;
		/* DqsRcvEn byte 5,4*/
		if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x20))
			goto Trdrd_1;
		/* DqsRcvEn byte 7,6*/
		if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x21))
			goto Trdrd_1;
		/* DqsRcvEn ECC*/
		if (Check_DqsRcvEn_Diff(pDCTstat, dct, dev, index_reg, 0x12))
			goto Trdrd_1;
		Trdrd = 0;
	Trdrd_1:
		;
	}
	pDCTstat->Trdrd = Trdrd;

}


static void Get_Twrwr(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 Twrwr = 0;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;
	u32 val;
	u32 dword;

	/* WrDatGrossDlyByte only use one set register when DDR400~DDR667
	   DDR800 have two set register for DIMM0 and DIMM1 */
	if (pDCTstat->Speed > 3) {
		val = bsf(pDCTstat->DIMMValid);
		dword = bsr(pDCTstat->DIMMValid);
		if (dword != val && dword != 0)  {
			/*the largest WrDatGrossDlyByte of any DIMM minus the
			WrDatGrossDlyByte of any other DIMM is equal to CGDD */
			val = Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg);
		}
		if (val == 0)
			Twrwr = 2;
		else
			Twrwr = 3;
	}
	pDCTstat->Twrwr = Twrwr;
}


static void Get_Twrrd(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 byte, bytex, val;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;

	/* On any given byte lane, the largest WrDatGrossDlyByte delay of
	   any DIMM minus the DqsRcvEnGrossDelay delay of any other DIMM is
	   equal to the Critical Gross Delay Difference (CGDD) for Twrrd.*/

	/* WrDatGrossDlyByte only use one set register when DDR400~DDR667
	   DDR800 have two set register for DIMM0 and DIMM1 */
	if (pDCTstat->Speed > 3) {
		val = Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg);
	} else {
		val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 1);	/* WrDatGrossDlyByte byte 0,1,2,3 for DIMM0 */
		pDCTstat->WrDatGrossH = (u8) val; /* low byte = max value */
	}

	Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg);

	bytex = pDCTstat->DqsRcvEnGrossL;
	byte = pDCTstat->WrDatGrossH;
	if (byte > bytex) {
		byte -= bytex;
		if (byte == 1)
			bytex = 1;
		else
			bytex = 2;
	} else {
		bytex = 0;
	}
	pDCTstat->Twrrd = bytex;
}


static void Get_TrwtTO(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 byte, bytex;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;

	/* On any given byte lane, the largest WrDatGrossDlyByte delay of
	   any DIMM minus the DqsRcvEnGrossDelay delay of any other DIMM is
	   equal to the Critical Gross Delay Difference (CGDD) for TrwtTO. */
	Get_DqsRcvEnGross_Diff(pDCTstat, dev, index_reg);
	Get_WrDatGross_Diff(pDCTstat, dct, dev, index_reg);
	bytex = pDCTstat->DqsRcvEnGrossL;
	byte = pDCTstat->WrDatGrossH;
	if (bytex > byte) {
		bytex -= byte;
		if ((bytex == 1) || (bytex == 2))
			bytex = 3;
		else
			bytex = 4;
	} else {
		byte -= bytex;
		if ((byte == 0) || (byte == 1))
			bytex = 2;
		else
			bytex = 1;
	}

	pDCTstat->TrwtTO = bytex;
}


static void Get_TrwtWB(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat)
{
	/* TrwtWB ensures read-to-write data-bus turnaround.
	   This value should be one more than the programmed TrwtTO.*/
	pDCTstat->TrwtWB = pDCTstat->TrwtTO + 1;
}


static u8 Check_DqsRcvEn_Diff(struct DCTStatStruc *pDCTstat,
					u8 dct, u32 dev, u32 index_reg,
					u32 index)
{
	u8 Smallest_0, Largest_0, Smallest_1, Largest_1;
	u8 i;
	u32 val;
	u8 byte;
	u8 ecc_reg = 0;

	Smallest_0 = 0xFF;
	Smallest_1 = 0xFF;
	Largest_0 = 0;
	Largest_1 = 0;

	if (index == 0x12)
		ecc_reg = 1;

	for (i=0; i < 8; i+=2) {
		if ( pDCTstat->DIMMValid & (1 << i)) {
			val = Get_NB32_index_wait(dev, index_reg, index);
			byte = val & 0xFF;
			if (byte < Smallest_0)
				Smallest_0 = byte;
			if (byte > Largest_0)
				Largest_0 = byte;
			if (!(ecc_reg)) {
				byte = (val >> 16) & 0xFF;
				if (byte < Smallest_1)
					Smallest_1 = byte;
				if (byte > Largest_1)
					Largest_1 = byte;
			}
		}
		index += 3;
	}	/* while ++i */

	/* check if total DqsRcvEn delay difference between any
	   two DIMMs is less than half of a MEMCLK */
	if ((Largest_0 - Smallest_0) > 31)
		return 1;
	if (!(ecc_reg))
		if ((Largest_1 - Smallest_1) > 31)
			return 1;
	return 0;
}


static u8 Get_DqsRcvEnGross_Diff(struct DCTStatStruc *pDCTstat,
					u32 dev, u32 index_reg)
{
	u8 Smallest, Largest;
	u32 val;
	u8 byte, bytex;

	/* The largest DqsRcvEnGrossDelay of any DIMM minus the
	   DqsRcvEnGrossDelay of any other DIMM is equal to the Critical
	   Gross Delay Difference (CGDD) */
	/* DqsRcvEn byte 1,0 */
	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x10);
	Largest = val & 0xFF;
	Smallest = (val >> 8) & 0xFF;

	/* DqsRcvEn byte 3,2 */
	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x11);
	byte = val & 0xFF;
	bytex = (val >> 8) & 0xFF;
	if (bytex < Smallest)
		Smallest = bytex;
	if (byte > Largest)
		Largest = byte;

	/* DqsRcvEn byte 5,4 */
	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x20);
	byte = val & 0xFF;
	bytex = (val >> 8) & 0xFF;
	if (bytex < Smallest)
		Smallest = bytex;
	if (byte > Largest)
		Largest = byte;

	/* DqsRcvEn byte 7,6 */
	val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x21);
	byte = val & 0xFF;
	bytex = (val >> 8) & 0xFF;
	if (bytex < Smallest)
		Smallest = bytex;
	if (byte > Largest)
		Largest = byte;

	if (pDCTstat->DimmECCPresent> 0) {
		/*DqsRcvEn Ecc */
		val = Get_DqsRcvEnGross_MaxMin(pDCTstat, dev, index_reg, 0x12);
		byte = val & 0xFF;
		bytex = (val >> 8) & 0xFF;
		if (bytex < Smallest)
			Smallest = bytex;
		if (byte > Largest)
			Largest = byte;
	}

	pDCTstat->DqsRcvEnGrossL = Largest;
	return Largest - Smallest;
}


static u8 Get_WrDatGross_Diff(struct DCTStatStruc *pDCTstat,
					u8 dct, u32 dev, u32 index_reg)
{
	u8 Smallest, Largest;
	u32 val;
	u8 byte, bytex;

	/* The largest WrDatGrossDlyByte of any DIMM minus the
	  WrDatGrossDlyByte of any other DIMM is equal to CGDD */
	val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x01);	/* WrDatGrossDlyByte byte 0,1,2,3 for DIMM0 */
	Largest = val & 0xFF;
	Smallest = (val >> 8) & 0xFF;
	val = Get_WrDatGross_MaxMin(pDCTstat, dct, dev, index_reg, 0x101);	/* WrDatGrossDlyByte byte 0,1,2,3 for DIMM1 */
	byte = val & 0xFF;
	bytex = (val >> 8) & 0xFF;
	if (bytex < Smallest)
		Smallest = bytex;
	if (byte > Largest)
		Largest = byte;

	// FIXME: Add Cx support.

	pDCTstat->WrDatGrossH = Largest;
	return Largest - Smallest;
}

static u16 Get_DqsRcvEnGross_MaxMin(struct DCTStatStruc *pDCTstat,
					u32 dev, u32 index_reg,
					u32 index)
{
	u8 Smallest, Largest;
	u8 i;
	u8 byte;
	u32 val;
	u16 word;
	u8 ecc_reg = 0;

	Smallest = 7;
	Largest = 0;

	if (index == 0x12)
		ecc_reg = 1;

	for (i=0; i < 8; i+=2) {
		if ( pDCTstat->DIMMValid & (1 << i)) {
			val = Get_NB32_index_wait(dev, index_reg, index);
			val &= 0x00E000E0;
			byte = (val >> 5) & 0xFF;
			if (byte < Smallest)
				Smallest = byte;
			if (byte > Largest)
				Largest = byte;
			if (!(ecc_reg)) {
				byte = (val >> (16 + 5)) & 0xFF;
				if (byte < Smallest)
					Smallest = byte;
				if (byte > Largest)
					Largest = byte;
			}
		}
	index += 3;
	}	/* while ++i */

	word = Smallest;
	word <<= 8;
	word |= Largest;

	return word;
}

static u16 Get_WrDatGross_MaxMin(struct DCTStatStruc *pDCTstat,
					u8 dct, u32 dev, u32 index_reg,
					u32 index)
{
	u8 Smallest, Largest;
	u8 i, j;
	u32 val;
	u8 byte;
	u16 word;

	Smallest = 3;
	Largest = 0;
	for (i=0; i < 2; i++) {
		val = Get_NB32_index_wait(dev, index_reg, index);
		val &= 0x60606060;
		val >>= 5;
		for (j=0; j < 4; j++) {
			byte = val & 0xFF;
			if (byte < Smallest)
				Smallest = byte;
			if (byte > Largest)
				Largest = byte;
			val >>= 8;
		}	/* while ++j */
		index++;
	}	/*while ++i*/

	if (pDCTstat->DimmECCPresent > 0) {
		index++;
		val = Get_NB32_index_wait(dev, index_reg, index);
		val &= 0x00000060;
		val >>= 5;
		byte = val & 0xFF;
		if (byte < Smallest)
			Smallest = byte;
		if (byte > Largest)
			Largest = byte;
	}

	word = Smallest;
	word <<= 8;
	word |= Largest;

	return word;
}



static void mct_FinalMCT_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	print_t("\tmct_FinalMCT_D: Clr Cl, Wb\n");


	mct_ClrClToNB_D(pMCTstat, pDCTstat);
	mct_ClrWbEnhWsbDis_D(pMCTstat, pDCTstat);
}


static void mct_InitialMCT_D(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
{
	print_t("\tmct_InitialMCT_D: Set Cl, Wb\n");
	mct_SetClToNB_D(pMCTstat, pDCTstat);
	mct_SetWbEnhWsbDis_D(pMCTstat, pDCTstat);
}


static u32 mct_NodePresent_D(void)
{
	u32 val;
	val = 0x12001022;
	return val;
}


static void mct_init(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat)
{
	u32 lo, hi;
	u32 addr;

	pDCTstat->GangedMode = 0;
	pDCTstat->DRPresent = 1;

	/* enable extend PCI configuration access */
	addr = 0xC001001F;
	_RDMSR(addr, &lo, &hi);
	if (hi & (1 << (46-32))) {
		pDCTstat->Status |= 1 << SB_ExtConfig;
	} else {
		hi |= 1 << (46-32);
		_WRMSR(addr, lo, hi);
	}
}


static void clear_legacy_Mode(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 reg;
	u32 val;
	u32 dev = pDCTstat->dev_dct;

	/* Clear Legacy BIOS Mode bit */
	reg = 0x94;
	val = Get_NB32(dev, reg);
	val &= ~(1<<LegacyBiosMode);
	Set_NB32(dev, reg, val);
}


static void mct_HTMemMapExt(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstatA)
{
	u8 Node;
	u32 Drambase, Dramlimit;
	u32 val;
	u32 reg;
	u32 dev;
	u32 devx;
	u32 dword;
	struct DCTStatStruc *pDCTstat;

	pDCTstat = pDCTstatA + 0;
	dev = pDCTstat->dev_map;

	/* Copy dram map from F1x40/44,F1x48/4c,
	  to F1x120/124(Node0),F1x120/124(Node1),...*/
	for (Node=0; Node < MAX_NODES_SUPPORTED; Node++) {
		pDCTstat = pDCTstatA + Node;
		devx = pDCTstat->dev_map;

		/* get base/limit from Node0 */
		reg = 0x40 + (Node << 3);		/* Node0/Dram Base 0 */
		val = Get_NB32(dev, reg);
		Drambase = val >> ( 16 + 3);

		reg = 0x44 + (Node << 3);		/* Node0/Dram Base 0 */
		val = Get_NB32(dev, reg);
		Dramlimit = val >> (16 + 3);

		/* set base/limit to F1x120/124 per Node */
		if (pDCTstat->NodePresent) {
			reg = 0x120;		/* F1x120,DramBase[47:27] */
			val = Get_NB32(devx, reg);
			val &= 0xFFE00000;
			val |= Drambase;
			Set_NB32(devx, reg, val);

			reg = 0x124;
			val = Get_NB32(devx, reg);
			val &= 0xFFE00000;
			val |= Dramlimit;
			Set_NB32(devx, reg, val);

			if ( pMCTstat->GStatus & ( 1 << GSB_HWHole)) {
				reg = 0xF0;
				val = Get_NB32(devx, reg);
				val |= (1 << DramMemHoistValid);
				val &= ~(0xFF << 24);
				dword = (pMCTstat->HoleBase >> (24 - 8)) & 0xFF;
				dword <<= 24;
				val |= dword;
				Set_NB32(devx, reg, val);
			}

		}
	}
}

static void SetCSTriState(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 val;
	u32 dev = pDCTstat->dev_dct;
	u32 index_reg = 0x98 + 0x100 * dct;
	u8 cs;
	u32 index;
	u16 word;

	/* Tri-state unused chipselects when motherboard
	   termination is available */

	// FIXME: skip for Ax

	word = pDCTstat->CSPresent;
	if (pDCTstat->Status & (1 << SB_Registered)) {
		for (cs = 0; cs < 8; cs++) {
			if (word & (1 << cs)) {
				if (!(cs & 1))
					word |= 1 << (cs + 1);
			}
		}
	}
	word = (~word) & 0xFF;
	index  = 0x0c;
	val = Get_NB32_index_wait(dev, index_reg, index);
	val |= word;
	Set_NB32_index_wait(dev, index_reg, index, val);
}



static void SetCKETriState(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 val;
	u32 dev;
	u32 index_reg = 0x98 + 0x100 * dct;
	u8 cs;
	u32 index;
	u16 word;

	/* Tri-state unused CKEs when motherboard termination is available */

	// FIXME: skip for Ax

	dev = pDCTstat->dev_dct;
	word = 0x101;
	for (cs = 0; cs < 8; cs++) {
		if (pDCTstat->CSPresent & (1 << cs)) {
			if (!(cs & 1))
				word &= 0xFF00;
			else
				word &= 0x00FF;
		}
	}

	index  = 0x0c;
	val = Get_NB32_index_wait(dev, index_reg, index);
	if ((word & 0x00FF) == 1)
		val |= 1 << 12;
	else
		val &= ~(1 << 12);

	if ((word >> 8) == 1)
		val |= 1 << 13;
	else
		val &= ~(1 << 13);

	Set_NB32_index_wait(dev, index_reg, index, val);
}


static void SetODTTriState(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat, u8 dct)
{
	u32 val;
	u32 dev;
	u32 index_reg = 0x98 + 0x100 * dct;
	u8 cs;
	u32 index;
	u8 odt;
	u8 max_dimms;

	// FIXME: skip for Ax

	/* Tri-state unused ODTs when motherboard termination is available */
	max_dimms = (u8) mctGet_NVbits(NV_MAX_DIMMS);
	odt = 0x0F;	/* tristate all the pins then clear the used ones. */

	for (cs = 0; cs < 8; cs += 2) {
		if (pDCTstat->CSPresent & (1 << cs)) {
			odt &= ~(1 << (cs / 2));

			/* if quad-rank capable platform clear adtitional pins */
			if (max_dimms != MAX_CS_SUPPORTED) {
				if (pDCTstat->CSPresent & (1 << (cs + 1)))
					odt &= ~(4 << (cs / 2));
			}
		}
	}

	index  = 0x0C;
	val = Get_NB32_index_wait(dev, index_reg, index);
	val |= (odt << 8);
	Set_NB32_index_wait(dev, index_reg, index, val);

}


static void InitPhyCompensation(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 i;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;
	u32 val;
	u32 valx = 0;
	u32 dword;
	const u8 *p;

	val = Get_NB32_index_wait(dev, index_reg, 0x00);
	dword = 0;
	for (i=0; i < 6; i++) {
		switch (i) {
			case 0:
			case 4:
				p = Table_Comp_Rise_Slew_15x;
				valx = p[(val >> 16) & 3];
				break;
			case 1:
			case 5:
				p = Table_Comp_Fall_Slew_15x;
				valx = p[(val >> 16) & 3];
				break;
			case 2:
				p = Table_Comp_Rise_Slew_20x;
				valx = p[(val >> 8) & 3];
				break;
			case 3:
				p = Table_Comp_Fall_Slew_20x;
				valx = p[(val >> 8) & 3];
				break;

		}
		dword |= valx << (5 * i);
	}

	/* Override/Exception */
	if (!pDCTstat->GangedMode) {
		i = 0; /* use i for the dct setting required */
		if (pDCTstat->MAdimms[0] < 4)
			i = 1;
		if (((pDCTstat->Speed == 2) || (pDCTstat->Speed == 3)) && (pDCTstat->MAdimms[i] == 4))
			dword &= 0xF18FFF18;
			index_reg = 0x98;	/* force dct = 0 */
	}

	Set_NB32_index_wait(dev, index_reg, 0x0a, dword);
}


static void WaitRoutine_D(u32 time)
{
	while(time) {
		_EXECFENCE;
		time--;
	}
}


static void mct_EarlyArbEn_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat)
{
	u32 reg;
	u32 val;
	u32 dev = pDCTstat->dev_dct;

	/* GhEnhancement #18429 modified by askar: For low NB CLK :
	 * Memclk ratio, the DCT may need to arbitrate early to avoid
	 * unnecessary bubbles.
	 * bit 19 of F2x[1,0]78 Dram  Control Register, set this bit only when
	 * NB CLK : Memclk ratio is between 3:1 (inclusive) to 4:5 (inclusive)
	 */

	reg = 0x78;
	val = Get_NB32(dev, reg);

	//FIXME: check for Cx
	if (CheckNBCOFEarlyArbEn(pMCTstat, pDCTstat))
		val |= (1 << EarlyArbEn);

	Set_NB32(dev, reg, val);

}


static u8 CheckNBCOFEarlyArbEn(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat)
{
	u32 reg;
	u32 val;
	u32 tmp;
	u32 rem;
	u32 dev = pDCTstat->dev_dct;
	u32  hi, lo;
	u8 NbDid = 0;

	/* Check if NB COF >= 4*Memclk, if it is not, return a fatal error
	 */

	/* 3*(Fn2xD4[NBFid]+4)/(2^NbDid)/(3+Fn2x94[MemClkFreq]) */
	_RDMSR(0xC0010071, &lo, &hi);
	if (lo & (1 << 22))
		NbDid |= 1;


	reg = 0x94;
	val = Get_NB32(dev, reg);
	if (!(val & (1 << MemClkFreqVal)))
		val = Get_NB32(dev, reg * 0x100);	/* get the DCT1 value */

	val &= 0x07;
	val += 3;
	if (NbDid)
		val <<= 1;
	tmp = val;

	dev = pDCTstat->dev_nbmisc;
	reg = 0xD4;
	val = Get_NB32(dev, reg);
	val &= 0x1F;
	val += 3;
	val *= 3;
	val = val / tmp;
	rem = val % tmp;
	tmp >>= 1;

	// Yes this could be nicer but this was how the asm was....
	if (val < 3) {				/* NClk:MemClk < 3:1 */
		return 0;
	} else if (val > 4) {			/* NClk:MemClk >= 5:1 */
		return 0;
	} else if ((val == 4) && (rem > tmp)) { /* NClk:MemClk > 4.5:1 */
		return 0;
	} else {
		return 1;			/* 3:1 <= NClk:MemClk <= 4.5:1*/
	}
}


static void mct_ResetDataStruct_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA)
{
	u8 Node;
	u32 i;
	struct DCTStatStruc *pDCTstat;
	u16 start, stop;
	u8 *p;
	u16 host_serv1, host_serv2;

	/* Initialize Data structures by clearing all entries to 0 */
	p = (u8 *) pMCTstat;
	for (i = 0; i < sizeof(struct MCTStatStruc); i++) {
		p[i] = 0;
	}

	for (Node = 0; Node < 8; Node++) {
		pDCTstat = pDCTstatA + Node;
		host_serv1 = pDCTstat->HostBiosSrvc1;
		host_serv2 = pDCTstat->HostBiosSrvc2;

		p = (u8 *) pDCTstat;
		start = 0;
		stop = ((u16) &((struct DCTStatStruc *)0)->CH_MaxRdLat[2]);
		for (i = start; i < stop ; i++) {
			p[i] = 0;
		}

		start = ((u16) &((struct DCTStatStruc *)0)->CH_D_BC_RCVRDLY[2][4]);
		stop = sizeof(struct DCTStatStruc);
		for (i = start; i < stop; i++) {
			p[i] = 0;
		}
		pDCTstat->HostBiosSrvc1 = host_serv1;
		pDCTstat->HostBiosSrvc2 = host_serv2;
	}
}


static void mct_BeforeDramInit_Prod_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat)
{
	u8 i;
	u32 reg_off;
	u32 dev = pDCTstat->dev_dct;

	// FIXME: skip for Ax
	if ((pDCTstat->Speed == 3) || ( pDCTstat->Speed == 2)) { // MemClkFreq = 667MHz or 533Mhz
		for (i=0; i < 2; i++) {
			reg_off = 0x100 * i;
			Set_NB32(dev,  0x98 + reg_off, 0x0D000030);
			Set_NB32(dev,  0x9C + reg_off, 0x00000806);
			Set_NB32(dev,  0x98 + reg_off, 0x4D040F30);
		}
	}
}


void mct_AdjustDelayRange_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat, u8 *dqs_pos)
{
	// FIXME: Skip for Ax
	if ((pDCTstat->Speed == 3) || ( pDCTstat->Speed == 2)) { // MemClkFreq = 667MHz or 533Mhz
		*dqs_pos = 32;
	}
}


void mct_SetClToNB_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat)
{
	u32 lo, hi;
	u32 msr;

	// FIXME: Maybe check the CPUID? - not for now.
	// pDCTstat->LogicalCPUID;

	msr = BU_CFG2;
	_RDMSR(msr, &lo, &hi);
	lo |= 1 << ClLinesToNbDis;
	_WRMSR(msr, lo, hi);
}


void mct_ClrClToNB_D(struct MCTStatStruc *pMCTstat,
			struct DCTStatStruc *pDCTstat)
{

	u32 lo, hi;
	u32 msr;

	// FIXME: Maybe check the CPUID? - not for now.
	// pDCTstat->LogicalCPUID;

	msr = BU_CFG2;
	_RDMSR(msr, &lo, &hi);
	if (!pDCTstat->ClToNB_flag)
		lo &= ~(1<<ClLinesToNbDis);
	_WRMSR(msr, lo, hi);

}


void mct_SetWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 lo, hi;
	u32 msr;

	// FIXME: Maybe check the CPUID? - not for now.
	// pDCTstat->LogicalCPUID;

	msr = BU_CFG;
	_RDMSR(msr, &lo, &hi);
	hi |= (1 << WbEnhWsbDis_D);
	_WRMSR(msr, lo, hi);
}


void mct_ClrWbEnhWsbDis_D(struct MCTStatStruc *pMCTstat,
				struct DCTStatStruc *pDCTstat)
{
	u32 lo, hi;
	u32 msr;

	// FIXME: Maybe check the CPUID? - not for now.
	// pDCTstat->LogicalCPUID;

	msr = BU_CFG;
	_RDMSR(msr, &lo, &hi);
	hi &= ~(1 << WbEnhWsbDis_D);
	_WRMSR(msr, lo, hi);
}


void mct_SetDramConfigHi_D(struct DCTStatStruc *pDCTstat, u32 dct,
				u32 DramConfigHi)
{
	/* Bug#15114: Comp. update interrupted by Freq. change can cause
	 * subsequent update to be invalid during any MemClk frequency change:
	 * Solution: From the bug report:
	 *  1. A software-initiated frequency change should be wrapped into the
	 *     following sequence :
	 * 	- a) Disable Compensation (F2[1, 0]9C_x08[30] )
	 * 	b) Reset the Begin Compensation bit (D3CMP->COMP_CONFIG[0]) in all the compensation engines
	 * 	c) Do frequency change
	 * 	d) Enable Compensation (F2[1, 0]9C_x08[30] )
	 *  2. A software-initiated Disable Compensation should always be
	 *     followed by step b) of the above steps.
	 * Silicon Status: Fixed In Rev B0
	 *
	 * Errata#177: DRAM Phy Automatic Compensation Updates May Be Invalid
	 * Solution: BIOS should disable the phy automatic compensation prior
	 * to initiating a memory clock frequency change as follows:
	 *  1. Disable PhyAutoComp by writing 1'b1 to F2x[1, 0]9C_x08[30]
	 *  2. Reset the Begin Compensation bits by writing 32'h0 to
	 *     F2x[1, 0]9C_x4D004F00
	 *  3. Perform frequency change
	 *  4. Enable PhyAutoComp by writing 1'b0 to F2x[1, 0]9C_08[30]
	 *  In addition, any time software disables the automatic phy
	 *   compensation it should reset the begin compensation bit per step 2.
	 *   Silicon Status: Fixed in DR-B0
	 */

	u32 dev = pDCTstat->dev_dct;
	u32 index_reg = 0x98 + 0x100 * dct;
	u32 index;

	u32 val;

	index = 0x08;
	val = Get_NB32_index_wait(dev, index_reg, index);
	Set_NB32_index_wait(dev, index_reg, index, val | (1 << DisAutoComp));

	//FIXME: check for Bx Cx CPU
	  // if Ax mct_SetDramConfigHi_Samp_D

	/* errata#177 */
	index = 0x4D014F00;	/* F2x[1, 0]9C_x[D0FFFFF:D000000] DRAM Phy Debug Registers */
	index |= 1 << DctAccessWrite;
	val = 0;
	Set_NB32_index_wait(dev, index_reg, index, val);

	Set_NB32(dev, 0x94 + 0x100 * dct, DramConfigHi);

	index = 0x08;
	val = Get_NB32_index_wait(dev, index_reg, index);
	Set_NB32_index_wait(dev, index_reg, index, val & (~(1 << DisAutoComp)));
}

static void mct_BeforeDQSTrain_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstatA)
{
	u8 Node;
	struct DCTStatStruc *pDCTstat;

	/* Errata 178
	 *
	 * Bug#15115: Uncertainty In The Sync Chain Leads To Setup Violations
	 *            In TX FIFO
	 * Solution: BIOS should program DRAM Control Register[RdPtrInit] =
	 *            5h, (F2x[1, 0]78[3:0] = 5h).
	 * Silicon Status: Fixed In Rev B0
	 *
	 * Bug#15880: Determine validity of reset settings for DDR PHY timing.
	 * Solutiuon: At least, set WrDqs fine delay to be 0 for DDR2 training.
	 */

	for (Node = 0; Node < 8; Node++) {
		pDCTstat = pDCTstatA + Node;

		if (pDCTstat->NodePresent)
			mct_BeforeDQSTrain_Samp_D(pMCTstat, pDCTstat);
			mct_ResetDLL_D(pMCTstat, pDCTstat, 0);
			mct_ResetDLL_D(pMCTstat, pDCTstat, 1);

	}
}

static void mct_ResetDLL_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat, u8 dct)
{
	u8 Receiver;
	u32 dev = pDCTstat->dev_dct;
	u32 reg_off = 0x100 * dct;
	u32 addr;
	u32 lo, hi;
	u8 wrap32dis = 0;
	u8 valid = 0;

	/* Skip reset DLL for B3 */
	if (pDCTstat->LogicalCPUID & AMD_DR_B3) {
		return;
	}

	addr = HWCR;
	_RDMSR(addr, &lo, &hi);
	if(lo & (1<<17)) {		/* save the old value */
		wrap32dis = 1;
	}
	lo |= (1<<17);			/* HWCR.wrap32dis */
	lo &= ~(1<<15);			/* SSEDIS */
	/* Setting wrap32dis allows 64-bit memory references in 32bit mode */
	_WRMSR(addr, lo, hi);


	pDCTstat->Channel = dct;
	Receiver = mct_InitReceiver_D(pDCTstat, dct);
	/* there are four receiver pairs, loosely associated with chipselects.*/
	for (; Receiver < 8; Receiver += 2) {
		if (mct_RcvrRankEnabled_D(pMCTstat, pDCTstat, dct, Receiver)) {
			addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, dct, Receiver, &valid);
			if (valid) {
				mct_Read1LTestPattern_D(pMCTstat, pDCTstat, addr);	/* cache fills */

				/* Write 0000_8000h to register F2x[1,0]9C_xD080F0C */
				Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00008000);
				mct_Wait(80); /* wait >= 300ns */

				/* Write 0000_0000h to register F2x[1,0]9C_xD080F0C */
				Set_NB32_index_wait(dev, 0x98 + reg_off, 0x4D080F0C, 0x00000000);
				mct_Wait(800); /* wait >= 2us */
				break;
			}
		}
	}
	if(!wrap32dis) {
		addr = HWCR;
		_RDMSR(addr, &lo, &hi);
		lo &= ~(1<<17);		/* restore HWCR.wrap32dis */
		_WRMSR(addr, lo, hi);
	}
}


static void mct_EnableDatIntlv_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat)
{
	u32 dev = pDCTstat->dev_dct;
	u32 val;

	/*  Enable F2x110[DctDatIntlv] */
	// Call back not required mctHookBeforeDatIntlv_D()
	// FIXME Skip for Ax
	if (!pDCTstat->GangedMode) {
		val = Get_NB32(dev, 0x110);
		val |= 1 << 5;			// DctDatIntlv
		Set_NB32(dev, 0x110, val);

		// FIXME Skip for Cx
		dev = pDCTstat->dev_nbmisc;
		val = Get_NB32(dev, 0x8C);	// NB Configuration Hi
		val |= 1 << (36-32);		// DisDatMask
		Set_NB32(dev, 0x8C, val);
	}
}


static void mct_SetupSync_D(struct MCTStatStruc *pMCTstat,
					struct DCTStatStruc *pDCTstat)
{
	/* set F2x78[ChSetupSync] when F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup,
	 * CkeSetup] setups for one DCT are all 0s and at least one of the setups,
	 * F2x[1, 0]9C_x04[AddrCmdSetup, CsOdtSetup, CkeSetup], of the other
	 * controller is 1
	 */
	u32 cha, chb;
	u32 dev = pDCTstat->dev_dct;
	u32 val;

	cha = pDCTstat->CH_ADDR_TMG[0] & 0x0202020;
	chb = pDCTstat->CH_ADDR_TMG[1] & 0x0202020;

	if ((cha != chb) && ((cha == 0) || (chb == 0))) {
		val = Get_NB32(dev, 0x78);
		val |= ChSetupSync;
		Set_NB32(dev, 0x78, val);
	}
}

static void AfterDramInit_D(struct DCTStatStruc *pDCTstat, u8 dct) {

	u32 val;
	u32 reg_off = 0x100 * dct;
	u32 dev = pDCTstat->dev_dct;

	if (pDCTstat->LogicalCPUID & (AMD_DR_B2 | AMD_DR_B3)) {
		mct_Wait(10000);	/* Wait 50 us*/
		val = Get_NB32(dev, 0x110);
		if ( val & (1 << DramEnabled)) {
			/* If 50 us expires while DramEnable =0 then do the following */
			val = Get_NB32(dev, 0x90 + reg_off);
			val &= ~(1 << Width128);		/* Program Width128 = 0 */
			Set_NB32(dev, 0x90 + reg_off, val);

			val = Get_NB32_index_wait(dev, 0x98 + reg_off, 0x05);	/* Perform dummy CSR read to F2x09C_x05 */

			if (pDCTstat->GangedMode) {
				val = Get_NB32(dev, 0x90 + reg_off);
				val |= 1 << Width128;		/* Program Width128 = 0 */
				Set_NB32(dev, 0x90 + reg_off, val);
			}
		}
	}
}


/* ==========================================================
 *  6-bit Bank Addressing Table
 *  RR=rows-13 binary
 *  B=Banks-2 binary
 *  CCC=Columns-9 binary
 * ==========================================================
 *  DCT	CCCBRR	Rows	Banks	Columns	64-bit CS Size
 *  Encoding
 *  0000	000000	13	2	9	128MB
 *  0001	001000	13	2	10	256MB
 *  0010	001001	14	2	10	512MB
 *  0011	010000	13	2	11	512MB
 *  0100	001100	13	3	10	512MB
 *  0101	001101	14	3	10	1GB
 *  0110	010001	14	2	11	1GB
 *  0111	001110	15	3	10	2GB
 *  1000	010101	14	3	11	2GB
 *  1001	010110	15	3	11	4GB
 *  1010	001111	16	3	10	4GB
 *  1011	010111	16	3	11	8GB
 */
