/*
 * 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
 */

/* Call-backs */
#include <delay.h>

static u16 mctGet_NVbits(u8 index)
{
	u16 val = 0;

	switch (index) {
	case NV_PACK_TYPE:
#if CONFIG_CPU_SOCKET_TYPE == 0x10	/* Socket F */
		val = 0;
#elif CONFIG_CPU_SOCKET_TYPE == 0x11	/* AM3 */
		val = 1;
#elif CONFIG_CPU_SOCKET_TYPE == 0x13	/* ASB2 */
		val = 4;
//#elif SYSTEM_TYPE == MOBILE
//		val = 2;
#endif
		break;
	case NV_MAX_NODES:
		val = MAX_NODES_SUPPORTED;
		break;
	case NV_MAX_DIMMS:
		val = MAX_DIMMS_SUPPORTED;
		//val = 8;
		break;
	case NV_MAX_MEMCLK:
		/* Maximum platform supported memclk */
		//val =  200;	/* 200MHz(DDR400) */
		//val =  266;	/* 266MHz(DDR533) */
		//val =  333;	/* 333MHz(DDR667) */
		val =  MEM_MAX_LOAD_FREQ;	/* 400MHz(DDR800) */
		break;
	case NV_ECC_CAP:
#if SYSTEM_TYPE == SERVER
		val = 1;	/* memory bus ECC capable */
#else
		val = 0;	/* memory bus ECC not capable */
#endif
		break;
	case NV_4RANKType:
		/* Quad Rank DIMM slot type */
		val = 0;	/* normal */
		//val = 1;	/* R4 (registered DIMMs in AMD server configuration) */
		//val = 2;	/* S4 (Unbuffered SO-DIMMS) */
		break;
	case NV_BYPMAX:
#if !CONFIG_GFXUMA
		val = 4;
#elif  CONFIG_GFXUMA
		val = 7;
#endif
		break;
	case NV_RDWRQBYP:
#if !CONFIG_GFXUMA
		val = 2;
#elif CONFIG_GFXUMA
		val = 3;
#endif
		break;
	case NV_MCTUSRTMGMODE:
		val = 0;	/* Automatic (recommended) */
		//val = 1;	/* Limited */
		//val = 2;	/* Manual */
		break;
	case NV_MemCkVal:
		//val = 0;	/* 200MHz */
		//val = 1;	/* 266MHz */
		val = 2;	/* 333MHz */
		break;
	case NV_BankIntlv:
		/* Bank (chip select) interleaving */
		//val = 0;	/* disabled */
		val = 1;	/* enabled (recommended) */
		break;
	case NV_MemHole:
		//val = 0;	/* Disabled */
		val = 1;	/* Enabled (recommended) */
		break;
	case NV_AllMemClks:
		val = 0;	/* Normal (only to slots that have enabled DIMMs) */
		//val = 1;	/* Enable all memclocks */
		break;
	case NV_SPDCHK_RESTRT:
		val = 0;	/* Exit current node initialization if any DIMM has SPD checksum error */
		//val = 1;	/* Ignore faulty SPD checksum (DIMM will still be disabled), continue current node intialization */
		break;
	case NV_DQSTrainCTL:
		//val = 0;	/*Skip dqs training */
		val = 1;	/* Perform dqs training */
		break;
	case NV_NodeIntlv:
		val = 0;	/* Disabled (recommended) */
		//val = 1;	/* Enable */
		break;
	case NV_BurstLen32:
#if !CONFIG_GFXUMA
		val = 0;	/* 64 byte mode */
#elif CONFIG_GFXUMA
		val = 1;	/* 32 byte mode */
#endif
		break;
	case NV_CKE_PDEN:
		//val = 0;	/* Disable */
		val = 1;	/* Enable */
		break;
	case NV_CKE_CTL:
		val = 0;	/* per channel control */
		//val = 1;	/* per chip select control */
		break;
	case NV_CLKHZAltVidC3:
		val = 0;	/* disable */
		//val = 1;	/* enable */
		break;
	case NV_BottomIO:
#if !CONFIG_GFXUMA
		val = 0xE0;	/* address bits [31:24] */
#elif CONFIG_GFXUMA
		val = 0xC0;	/* address bits [31:24] */
#endif
		break;
	case NV_BottomUMA:
#if !CONFIG_GFXUMA
		val = 0xE0;	/* address bits [31:24] */
#elif CONFIG_GFXUMA
		val = 0xC0;	/* address bits [31:24] */
#endif
		break;
	case NV_ECC:
#if (SYSTEM_TYPE == SERVER)
		val = 1;	/* Enable */
#else
		val = 0;	/* Disable */
#endif
		break;
	case NV_NBECC:
#if (SYSTEM_TYPE == SERVER)
		val = 1;	/* Enable */
#else
		val = 0;	/* Disable */
#endif
		break;
	case NV_ChipKill:
#if (SYSTEM_TYPE == SERVER)
		val = 1;	/* Enable */
#else
		val = 0;	/* Disable */
#endif
		break;
	case NV_ECCRedir:
		val = 0;	/* Disable */
		//val = 1;	/* Enable */
		break;
	case NV_DramBKScrub:
		val = 0x00;	/* Disabled */
		//val = 0x01;	/* 40ns */
		//val = 0x02;	/* 80ns */
		//val = 0x03;	/* 160ns */
		//val = 0x04;	/* 320ns */
		//val = 0x05;	/* 640ns */
		//val = 0x06;	/* 1.28us */
		//val = 0x07;	/* 2.56us */
		//val = 0x08;	/* 5.12us */
		//val = 0x09;	/* 10.2us */
		//val = 0x0a;	/* 20.5us */
		//val = 0x0b;	/* 41us */
		//val = 0x0c;	/* 81.9us */
		//val = 0x0d;	/* 163.8us */
		//val = 0x0e;	/* 327.7us */
		//val = 0x0f;	/* 655.4us */
		//val = 0x10;	/* 1.31ms */
		//val = 0x11;	/* 2.62ms */
		//val = 0x12;	/* 5.24ms */
		//val = 0x13;	/* 10.49ms */
		//val = 0x14;	/* 20.97sms */
		//val = 0x15;	/* 42ms */
		//val = 0x16;	/* 84ms */
		break;
	case NV_L2BKScrub:
		val = 0;	/* Disabled - See L2Scrub in BKDG */
		break;
	case NV_DCBKScrub:
		val = 0;	/* Disabled - See DcacheScrub in BKDG */
		break;
	case NV_CS_SpareCTL:
		val = 0;	/* Disabled */
		//val = 1;	/* Enabled */
		break;
	case NV_SyncOnUnEccEn:
		val = 0;	/* Disabled */
		//val = 1;	/* Enabled */
		break;
	case NV_Unganged:
		/* channel interleave is better performance than ganged mode at this time */
		val = 1;		/* Enabled */
		//val = 0;	/* Disabled */
		break;
	case NV_ChannelIntlv:
		val = 5;	/* Not currently checked in mctchi_d.c */
	/* Bit 0 =     0 - Disable
	 *             1 - Enable
	 * Bits[2:1] = 00b - Address bits 6
	 *             01b - Address bits 1
	 *             10b - Hash*, XOR of address bits [20:16, 6]
	 *             11b - Hash*, XOR of address bits [20:16, 9]
	 */
		break;
	}

	return val;
}


static void mctHookAfterDIMMpre(void)
{
}


static void mctGet_MaxLoadFreq(struct DCTStatStruc *pDCTstat)
{
	pDCTstat->PresetmaxFreq = MEM_MAX_LOAD_FREQ;
}

#ifdef UNUSED_CODE
static void mctAdjustAutoCycTmg(void)
{
}
#endif


static void mctAdjustAutoCycTmg_D(void)
{
}


static void mctHookAfterAutoCycTmg(void)
{
}


static void mctGetCS_ExcludeMap(void)
{
}


static void mctHookAfterAutoCfg(void)
{
}


static void mctHookAfterPSCfg(void)
{
}


static void mctHookAfterHTMap(void)
{
}


static void mctHookAfterCPU(void)
{
}


static void mctSaveDQSSigTmg_D(void)
{
}


static void mctGetDQSSigTmg_D(void)
{
}


static void mctHookBeforeECC(void)
{
}


static void mctHookAfterECC(void)
{
}

#ifdef UNUSED_CODE
static void mctInitMemGPIOs_A(void)
{
}
#endif


static void mctInitMemGPIOs_A_D(void)
{
}


static void mctNodeIDDebugPort_D(void)
{
}


#ifdef UNUSED_CODE
static void mctWarmReset(void)
{
}
#endif


static void mctWarmReset_D(void)
{
}


static void mctHookBeforeDramInit(void)
{
}


static void mctHookAfterDramInit(void)
{
}

#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
static void coreDelay(u32 microseconds)
{
	msr_t now;
	msr_t end;
	u32 cycles;

	/* delay ~40us
	   This seems like a hack to me...
	   It would be nice to have a central delay function. */

	cycles = (microseconds * 100) << 3;  /* x8 (number of 1.25ns ticks) */

        if (!(rdmsr(HWCR).lo & TSC_FREQ_SEL_MASK)) {
            msr_t pstate_msr = rdmsr(CUR_PSTATE_MSR);
            if (!(rdmsr(0xC0010064+pstate_msr.lo).lo & NB_DID_M_ON)) {
	      cycles = cycles <<1; // half freq, double cycles
	    }
	} // else should we keep p0 freq at the time of setting TSC_FREQ_SEL_MASK somewhere and check it here ?

	now = rdmsr(TSC_MSR);
        // avoid overflow when called near 2^32 ticks ~ 5.3 s boundaries
	if (0xffffffff - cycles >= now.lo ) {
	  end.hi =  now.hi;
          end.lo = now.lo + cycles;
	} else {
          end.hi = now.hi +1; //
          end.lo = cycles - (1+(0xffffffff - now.lo));
	}
	do {
          now = rdmsr(TSC_MSR);
        } while ((now.hi < end.hi) || ((now.hi == end.hi) && (now.lo < end.lo)));
}

/* Erratum 350 */
static void vErrata350(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstat)
{
	u8 u8Channel;
	u8 u8Receiver;
	u32 u32Addr;
	u8 u8Valid;
	u32 u32DctDev;

	// 1. dummy read for each installed DIMM */
	for (u8Channel = 0; u8Channel < 2; u8Channel++) {
		// This will be 0 for vaild DIMMS, eles 8
		u8Receiver = mct_InitReceiver_D(pDCTstat, u8Channel);

		for (; u8Receiver < 8; u8Receiver += 2) {
			u32Addr = mct_GetRcvrSysAddr_D(pMCTstat, pDCTstat, u8Channel, u8Receiver, &u8Valid);

			if(!u8Valid) {	/* Address not supported on current CS */
				print_t("vErrata350: Address not supported on current CS\n");
				continue;
			}
			print_t("vErrata350: dummy read \n");
			read32_fs(u32Addr);
		}
	}

	print_t("vErrata350: step 2a\n");

	/* 2. Write 0000_8000h to register F2x[1, 0]9C_xD080F0C. */
	u32DctDev = pDCTstat->dev_dct;
	Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00008000);
	/*                                                ^--- value
	                                        ^---F2x[1, 0]9C_x0D080F0C, No description in BKDG.
	                                 ^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */

	if(!pDCTstat->GangedMode) {
		print_t("vErrata350: step 2b\n");
		Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00008000);
		/*                                                ^--- value
		                                        ^---F2x[1, 0]9C_x0D080F0C, No description in BKDG
		                                ^----F2x[1, 0]98 DRAM Controller Additional Data Offset Register */
	}

	print_t("vErrata350: step 3\n");
	/* 3. Wait at least 300 nanoseconds. */
	coreDelay(1);

	print_t("vErrata350: step 4\n");
	/* 4. Write 0000_0000h to register F2x[1, 0]9C_xD080F0C. */
	Set_NB32_index_wait(u32DctDev, 0x098, 0xD080F0C, 0x00000000);

	if(!pDCTstat->GangedMode) {
		print_t("vErrata350: step 4b\n");
		Set_NB32_index_wait(u32DctDev, 0x198, 0xD080F0C, 0x00000000);
	}

	print_t("vErrata350: step 5\n");
	/* 5. Wait at least 2 microseconds. */
	coreDelay(2);

}

static void vErratum372(struct DCTStatStruc *pDCTstat)
{
        msr_t msr = rdmsr(NB_CFG_MSR);

	int nbPstate1supported = !(msr.hi & (1 << (NB_GfxNbPstateDis -32)));

        // is this the right way to check for NB pstate 1 or DDR3-1333 ?
        if (((pDCTstat->PresetmaxFreq==1333)||(nbPstate1supported))
            &&(!pDCTstat->GangedMode)) {
           	/* DisableCf8ExtCfg */
        	msr.hi &= ~(3 << (51 - 32));
        	wrmsr(NB_CFG_MSR, msr);
        }
}

static void vErratum414(struct DCTStatStruc *pDCTstat)
{
     int dct=0;
    for(; dct < 2 ; dct++)
    {
        int dRAMConfigHi = Get_NB32(pDCTstat->dev_dct,0x94 + (0x100 * dct));
		int powerDown =  dRAMConfigHi & (1 << PowerDownEn );
		int ddr3 = dRAMConfigHi & (1 << Ddr3Mode );
        int dRAMMRS = Get_NB32(pDCTstat->dev_dct,0x84 + (0x100 * dct));
		int pchgPDModeSel = dRAMMRS & (1 << PchgPDModeSel);
	if (powerDown && ddr3 && pchgPDModeSel )
	{
	  Set_NB32(pDCTstat->dev_dct,0x84 + (0x100 * dct), dRAMMRS & ~(1 << PchgPDModeSel) );
	}
    }
}
#endif


static void mctHookBeforeAnyTraining(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA)
{
#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
  /* FIXME :  as of 25.6.2010 errata 350 and 372 should apply to  ((RB|BL|DA)-C[23])|(HY-D[01])|(PH-E0) but I don't find constants for all of them */
	if (pDCTstatA->LogicalCPUID & AMD_DRBH_Cx) {
		vErrata350(pMCTstat, pDCTstatA);
		vErratum372(pDCTstatA);
		vErratum414(pDCTstatA);
	}
#endif
}

#if (CONFIG_DIMM_SUPPORT & 0x000F)==0x0005 /* AMD_FAM10_DDR3 */
static u32 mct_AdjustSPDTimings(struct MCTStatStruc *pMCTstat, struct DCTStatStruc *pDCTstatA, u32 val)
{
	if (pDCTstatA->LogicalCPUID & AMD_DR_Bx) {
		if (pDCTstatA->Status & (1 << SB_Registered)) {
			val ++;
		}
	}
	return val;
}
#endif

static void mctHookAfterAnyTraining(void)
{
}

static u32 mctGetLogicalCPUID_D(u8 node)
{
	return mctGetLogicalCPUID(node);
}

#if (CONFIG_DIMM_SUPPORT & 0x000F)!=0x0005 /* not needed for AMD_FAM10_DDR3 */
static u8 mctSetNodeBoundary_D(void)
{
	return 0;
}
#endif
