/*
 * spd_sdram.c: read SPD on SDRAM for mpc824x
 *
 * This code is mostly pinched from AlliedWare resdimm1.c
 *
 * Author: Matthew Millman
 *
 */


#include <common.h>
#include <mpc824x.h>
#include <asm/processor.h>
#include <i2c.h>
#include <spd.h>
#include <asm/mmu.h>

#ifdef CONFIG_SPD_EEPROM

#ifdef CONFIG_133MHZ_SYSTEM_BUS
#define BUS_FREQ		133		/* Mhz */
#else
#define BUS_FREQ		100		/* Mhz */
#endif


#define EQUAL_TO 		0
#define GREATER_THAN 		1
#define LESS_THAN 		2

#define CL_MODE_1_BIT		0x01
#define CL_MODE_2_BIT		0x02
#define CL_MODE_3_BIT		0x04
#define CL_MODES_SUPPORTED	0x07

#define CL_MODE_1		0x01
#define CL_MODE_2		0x02
#define CL_MODE_3		0x03

#define ASRISE			0xf		/* XPort !AS rise time */
#define ASFALL			0x3		/* XPort !AS fall time */

#define INLINE_PAR_NOT_ECC	0x0		/* Use ECC */
#define INLINE_WR_EN		0x0		/* In-line write error checks */
#define INLINE_RD_EN		0x1		/* In-line read error checks */
#define RSV_PG			0x1		/* Reserve 1 SDRAM page */
#define RMW_PAR			0x1		/* Enable RMW parity */

#define ACTORW			0x3		/* Activate to R/W !!! */
#define ACTORW_MASK		0xf0	/* bits 4-7 */
#define ACTORW_MINIMUM		0x3		/* enforce minimum of 3 */
#define ACTORW_MAXIMUM		0xf		/* set up to 15, do not use 0 */

#define BSTOPRE			0x00c	/* Burst to precharge !!! */
#define BUF_TYPE		0x2	

#define BUF_TYPE0		((BUF_TYPE >> 1) & 0x1)
#define BUF_TYPE1		((BUF_TYPE >> 0) & 0x1)

#define BSTOPRE0_1		((BSTOPRE >> 8) & 0x3)
#define BSTOPRE2_5		((BSTOPRE >> 4) & 0xf)
#define BSTOPRE6_9		((BSTOPRE >> 0) & 0xf)

#define BURST			0x0		/* No ROM bursting */
#define SREN			0x0		/* No self-refresh */
#define SDRAM_EN		0x0		/* Enable SDRAM */
#define PCKEN			0x0		/* No parity checking */

#define WMODE			0x0		/* 4 beats per burst */

#ifdef CONFIG_COMPACTFLASH_PORTX
#define EXTROM			0x1		/* Enable extended ROM */
#else
#define EXTROM			0x0		/* Disable extended ROM */
#endif

#define REGDIMM			0x0		/* Normal DIMMS */
#define SDMODE_WRAP_TYPE	0x0		/* SDRAM sequential */
#define SDMODE_BURST_LENGTH	0x2		/* SDRAM burst length = 4 */

#define ROMNAL			0x0
#define ROMFAL 			(NS2CYCLES_RND_UP(240) - 2)
#define TS_WAIT_TIMER		0x2 /* Transaction start wait */
#define TWACC 			(((ROMFAL + 2) * 8 + 3) * 4 + (2 + (TS_WAIT_TIMER - 1)) + 2)
#define T_RAS_MAX		120000UL

#define NS2CYCLES_RND_UP(ns)	(((ns) * BUS_FREQ + 999) / 1000)
#define NS2CYCLES_RND_DOWN(ns)	(((ns) * BUS_FREQ) / 1000)

#define PGMAX			((NS2CYCLES_RND_DOWN(T_RAS_MAX) - TWACC - 2) / 64)

#define REFREC			NS2CYCLES_RND_UP(75) /* Refresh to activate 75ns */

#define MCCR1_VALUE		((ROMNAL << 28)|  (ROMFAL << 23) | (BURST << 20) | (SREN << 18) \
				| (SDRAM_EN << 17) | (PCKEN << 16))
#define MCCR2_VALUE		((TS_WAIT_TIMER << 29) | (ASRISE << 25) | (ASFALL << 21) \
				| (INLINE_PAR_NOT_ECC << 20) | (INLINE_WR_EN << 19) \
				| (INLINE_RD_EN << 18) | (RSV_PG << 1) | (RMW_PAR << 0))
#define MCCR3_VALUE		((BSTOPRE2_5 << 28) | (REFREC << 24))
#define MCCR4_VALUE		((WMODE << 23) | (BUF_TYPE0 << 22) | (EXTROM << 21) \
				| (BUF_TYPE1 << 20) | (BSTOPRE0_1 << 18) | (REGDIMM << 15) \
				| (SDMODE_WRAP_TYPE << 11) | (SDMODE_BURST_LENGTH << 8) \
				| (ACTORW << 4) | (BSTOPRE6_9))

typedef struct
{
	u8 address;
	u8 mask;
	u32 matchType;
	u8 value;
	
} check_parameter;

const check_parameter parametersToCheck[] =
{
	{ 2,	0xff, EQUAL_TO,		4    },/*MemoryType = 4 (sdram)*/
	{ 5,	0xff, LESS_THAN,	4    }, /*NUMBANKS < 4*/
	{ 3,	0xff, LESS_THAN,	14   }, /*ROWADDRESS < 14*/
	{ 3,	0xff, GREATER_THAN,	10   }, /*ROWADDRESS > 10*/
	{ 21,	0x03, EQUAL_TO,		0    }, /*Not buffered and Not registered*/
	{ 18,	0x06, GREATER_THAN,	0    }, /*CAS Latency of 1-3*/
	{ 16,	0x04, EQUAL_TO,		4    }, /*Burst length of 4 supported*/
	{ 12,	0x7f, LESS_THAN,	6    }, /*Refresh rate is less than current define*/
	{ 0xff,	0xff, 0xff, 		0xff } /*end of table marker*/
};	

const u32 refreshRateLookup[] =
{
	/*0x80	0x81	0x82	0x83	0x84	0x85*/
	15625,	3900,	7800,	31300,	62500,	125000
};

/* Completely configure the memory controller from the SPD
 */
 
long int spd_sdram(void)
{
	int 		i;
	spd_eeprom_t	spd;
	u8 		data;
	u32		memstart1;
	u32		memstartext1;
	u32		memend1;
	u32		memendext1;
	u32		memstart2;
	u32		memstartext2;
	u32		memend2;
	u32		memendext2;
	u32		temp;
	u32		bankNum;
	u32		bankRow;
	u32		membank_enable;
	u32		mccr1;
	u32		mccr2;
	u32		mccr3;
	u32		mccr4;
	u32		rowsPerBank;
	
	memstart1 = mpc824x_mpc107_getreg(MSAR1);
	memend1 = mpc824x_mpc107_getreg(MEAR1);
	memstartext1 = mpc824x_mpc107_getreg(EMSAR1);
	memendext1 = mpc824x_mpc107_getreg(EMEAR1);
			
	memstart2 = mpc824x_mpc107_getreg(MSAR2);
	memend2 = mpc824x_mpc107_getreg(MEAR2);
	memstartext2 = mpc824x_mpc107_getreg(EMSAR2);
	memendext2 = mpc824x_mpc107_getreg(EMEAR2);
	
	i2c_read(SPD_EEPROM_ADDRESS, 0, 1, (uchar *) &spd, sizeof(spd));
	
	i = 0;
	while (parametersToCheck[i].address != 0xff)
	{
		data = *(u8 *)((u32)&spd + parametersToCheck[i].address);
	
		data &= parametersToCheck[i].mask;
		if (parametersToCheck[i].matchType == EQUAL_TO)
		{	
			if (data != parametersToCheck[i].value)
			{
				printf("Incompatible DIMM: SPD Parameter %u bad\n", parametersToCheck[i].address);
				while(1);			   		
			}
		
		}
		else if (parametersToCheck[i].matchType == LESS_THAN)
		{
			if (data >= parametersToCheck[i].value)
			{
				printf("Incompatible DIMM: SPD Parameter %u bad\n", parametersToCheck[i].address);
				while(1);			   		
			}
		
		}
		else /*Greater Than*/
		{
			if (data <= parametersToCheck[i].value)
			{
				printf("Incompatible DIMM: SPD Parameter %u bad\n", parametersToCheck[i].address);
				while(1);			   		
			}
		}
		i++;
	}
	
	for (bankNum = 0; bankNum < spd.nrows; bankNum++)
	{
		memstart1 &= ~(0xff << ( bankNum * 8));
		memstartext1 &= ~(0x07 << (bankNum * 8));
		memend1 &= ~(0xff << (bankNum * 8));
		memendext1 &= ~(0x07 << (bankNum * 8));

		temp = ((spd.row_dens << 2 ) * bankNum);
		memstart1 |= (temp & 0x00FF) << (bankNum * 8);
		memstartext1 |= ((temp & 0x0700 ) >> 8) << (bankNum * 8);

		temp = (((spd.row_dens << 2) * (bankNum + 1)) - 1) ;
		memend1 |= ((temp) & 0x00FF) << (bankNum * 8);
		memendext1 |= (((temp) & 0x0700 ) >> 8) << (bankNum * 8);
	}
	
	
	/*calc MEMBANKENABLE*/
	i = 0xffffffff;
	i = ~(i << spd.nrows);
	
	membank_enable = i & 0xff;

	
	/*set PGMAX via the high byte of PCI word*/
	membank_enable |= (PGMAX & 0xff) <<24;

	
	/*calc MCCR1 bank row address bit counts*/
	if (spd.nbanks == 2 && spd.nrow_addr == 11)
		bankRow=0x03;
	else if (spd.nbanks == 4 && spd.nrow_addr == 13)
		bankRow=0x02;
	else if (spd.nbanks == 4 && (spd.nrow_addr == 11 || spd.nrow_addr == 12))
		bankRow=0x00;
	else
	{
		printf("Incompatible DIMM (1)\n");			
		while(1);			
	}
	
	mccr1 = 0;
	for (i = 0; i < 8; i++)	/*set all 8 bank row address bit counts*/
	{
		mccr1 = (mccr1 << 2) | bankRow;
	}
	
	/* set the rest of MCCR1 */
	mccr1 |= MCCR1_VALUE;

	
	/*calc MCCR2 REFINT (MPC8245 Sec 6.2.12)*/
	rowsPerBank = 0x1 << spd.nrow_addr;

	/* REFINT < (RP/((n+1)16)) - (16(ROH)/16) - (TWACC/16) */
	
	/* (RP/((n+1)*16)) */
	/* RP = refPeriodPerBank(us) * numBanks * memFreq(MHz) */
	i = ((rowsPerBank * refreshRateLookup[spd.refresh & 0x7f]) / 1000)
			* spd.nbanks
			* BUS_FREQ;
	/* ((n+1)*16)  NOTE: n=(rowsPerBank*banksPerDevice/16) => calc simple */
	i /= (rowsPerBank * spd.nbanks + 16);
	
	/* - (16(ROH)/16)  NOTE: the 16/16 cancel out */
	/* ROH = preChargeCycles + PRETOACTcycles + 4commandCycle + 1deadCycle */
	i -= ((NS2CYCLES_RND_UP(spd.tras) * spd.nbanks)
			+ NS2CYCLES_RND_UP(spd.trp)
			+ 4
			+ 1);
	
	/* - (TWACC/16) */
	i -= (TWACC / 16);

	mccr2 = (i & 0x3fff) << 2;

	/*set the rest of MCCR2*/
	mccr2 |= MCCR2_VALUE;


	/*set MCCR3*/
	mccr3 = MCCR3_VALUE;
	
/*	convert to number of clock cycles rounded up
 */
	temp = NS2CYCLES_RND_UP(spd.trcd);

/* 	get the supported cas latency modes : only support mode 1, 2, or 3
 */
	data = spd.cas_lat & CL_MODES_SUPPORTED;
	if (0 == data)
	{
/* 		did not find any supported cas latency modes
 */
		printf("Incompatible DIMM: No supported CAS latency modes\n");			
		while(1);			
	}

/* 	Check that the required CASLATENCY mode is supported
	*	If the required mode is not supported, check if a slower mode is, in which
	*	case use the closest mode found
 */
	if (1 == temp)
	{
/* 		we want to set SDMODE_CAS_LATENCY = 1, so check we support it
 */
		if (data & CL_MODE_1_BIT)
			data = CL_MODE_1;
		else if (data & CL_MODE_2_BIT)
			data = CL_MODE_2;
		else if (data & CL_MODE_3_BIT)
			data = CL_MODE_3;
		else
		{
			printf("Incompatible DIMM: CAS latency mode not supported (1)\n");				
			while(1);			
		}
	}
	else if (2 == temp)
	{
/*		 we want to set SDMODE_CAS_LATENCY = 2, so check we support it
 */
		if (data & CL_MODE_2_BIT)
			data = CL_MODE_2;
		else if ( data & CL_MODE_3_BIT )
			data = CL_MODE_3;
		else
		{
			printf("Incompatible DIMM: CAS latency mode not supported (2)\n");			
			while(1);			
		}
	}
	else if (3 == temp)
	{
/*		we want to set SDMODE_CAS_LATENCY = 3, so check we support it
 */
		if (data & CL_MODE_3_BIT)
			data = CL_MODE_3;
		else
		{
			printf("Incompatible DIMM: CAS latency mode not supported (3)\n");
			while(1);			
		}
	}
	else
	{
/* 		the required SDMODE_CAS_LATENCY is not supported
 */
		printf("Incompatible DIMM: No supported CAS latency modes (2)\n");		
		while(1);			
	}
/*	fill in the SDMODE CAS LATENCY data 
 */
	mccr4 = data << 12;

	/*calc MCCR4 PRETOACT*/
	mccr4 |= (NS2CYCLES_RND_UP(spd.trp) & 0xf) << 28;
	
	/*calc MCCR4 ACTOPRE*/
	mccr4 |= (NS2CYCLES_RND_UP(spd.trp) & 0xf) << 24;
	
	/*set the rest of MCCR4*/
	mccr4 |= MCCR4_VALUE;

/*	ACTORW modification - This code not tested yet
 */
/*
	MCCR4 &= ~ACTORW_MASK;
	data = NS2CYCLES_RND_UP(EXTRACT_TRRD_ACTORW());
	if ( data < ACTORW_MINIMUM )
	{
		data = ACTORW_MINIMUM;
	}
	else if ( data > ACTORW_MAXIMUM )
	{
		data = ACTORW_MAXIMUM;
	}
	MCCR4 |= (data << 4);
*/	
	mpc824x_mpc107_setreg(MSAR1,  memstart1);
	mpc824x_mpc107_setreg(MSAR2,  memstart2);
	
	mpc824x_mpc107_setreg(MEAR1,  memend1);
	mpc824x_mpc107_setreg(MEAR2,  memend2);
	
	mpc824x_mpc107_setreg(EMSAR1, memstartext1);
	mpc824x_mpc107_setreg(EMSAR2, memstartext2);
	
	mpc824x_mpc107_setreg(EMEAR1, memendext1);
	mpc824x_mpc107_setreg(EMEAR2, memendext2);
	
	mpc824x_mpc107_setreg(MCCR1, mccr1);
	mpc824x_mpc107_setreg(MCCR2, mccr2);
	mpc824x_mpc107_setreg(MCCR3, mccr3);
	mpc824x_mpc107_setreg(MCCR4, mccr4);
	
	mpc824x_mpc107_setreg(MBER, membank_enable);
	
	mccr1 |= MCCR1_MEMGO;
	
	mpc824x_mpc107_setreg(MCCR1, mccr1);
	
#if 0
	printf("MSAR1: 0x%08X\n", mpc824x_mpc107_getreg(MSAR1));
	printf("MEAR1: 0x%08X\n", mpc824x_mpc107_getreg(MEAR1));
	printf("EMSAR1: 0x%08X\n", mpc824x_mpc107_getreg(EMSAR1));
	printf("EMEAR1: 0x%08X\n", mpc824x_mpc107_getreg(EMEAR1));
			
	printf("MSAR2: 0x%08X\n", mpc824x_mpc107_getreg(MSAR2));
	printf("MEAR2: 0x%08X\n", mpc824x_mpc107_getreg(MEAR2));
	printf("EMSAR2: 0x%08X\n", mpc824x_mpc107_getreg(EMSAR2));
	printf("EMEAR2: 0x%08X\n", mpc824x_mpc107_getreg(EMEAR2));
	
	printf("MCCR1: 0x%08X\n", mpc824x_mpc107_getreg(MCCR1));
	printf("MCCR2: 0x%08X\n", mpc824x_mpc107_getreg(MCCR2));
	printf("MCCR3: 0x%08X\n", mpc824x_mpc107_getreg(MCCR3));
	printf("MCCR4: 0x%08X\n", mpc824x_mpc107_getreg(MCCR4));
	
	printf("MBEN: 0x%08X\n", mpc824x_mpc107_getreg(MBER));
#endif	
	/* Is this function even supposed to return the size of the memory? - works for me */
	return (spd.nrows * (spd.row_dens << 2)) * 1024 * 1024;
}

#endif
