/*
 * Copyright (c) 2004 Picture Elements, Inc.
 *    Stephen Williams (steve@icarus.com)
 *
 *    This source code is free software; you can redistribute it
 *    and/or modify it in source code form under the terms of the GNU
 *    General Public License as published by the Free Software
 *    Foundation; either version 2 of the License, or (at your option)
 *    any later version.
 *
 *    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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#include <common.h>
#include <asm/ppc4xx.h>
#include <asm/processor.h>

# define SDRAM_LEN 0x08000000

/*
 * this is even after checkboard. It returns the size of the SDRAM
 * that we have installed. This function is called by board_init_f
 * in arch/powerpc/lib/board.c to initialize the memory and return what I
 * found.
 */
phys_size_t initdram (int board_type)
{
	/* Configure the SDRAMS */

	/* disable memory controller */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_CFG);
	mtdcr (SDRAM0_CFGDATA, 0x00000000);

	udelay (500);

	/* Clear SDRAM0_BESR0 (Bus Error Syndrome Register) */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_BESR0);
	mtdcr (SDRAM0_CFGDATA, 0xffffffff);

	/* Clear SDRAM0_BESR1 (Bus Error Syndrome Register) */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_BESR1);
	mtdcr (SDRAM0_CFGDATA, 0xffffffff);

	/* Clear SDRAM0_ECCCFG (disable ECC) */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_ECCCFG);
	mtdcr (SDRAM0_CFGDATA, 0x00000000);

	/* Clear SDRAM0_ECCESR (ECC Error Syndrome Register) */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_ECCESR);
	mtdcr (SDRAM0_CFGDATA, 0xffffffff);

	/* Timing register: CASL=2, PTA=2, CTP=2, LDF=1, RFTA=5, RCD=2 */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_TR);
	mtdcr (SDRAM0_CFGDATA, 0x010a4016);

	/* Memory Bank 0 Config == BA=0x00000000, SZ=64M, AM=3, BE=1 */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_B0CR);
	mtdcr (SDRAM0_CFGDATA, 0x00084001);

	/* Memory Bank 1 Config == BA=0x04000000, SZ=64M, AM=3, BE=1 */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_B1CR);
	mtdcr (SDRAM0_CFGDATA, 0x04084001);

	/* Memory Bank 2 Config ==  BE=0 */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_B2CR);
	mtdcr (SDRAM0_CFGDATA, 0x00000000);

	/* Memory Bank 3 Config ==  BE=0 */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_B3CR);
	mtdcr (SDRAM0_CFGDATA, 0x00000000);

	/* refresh timer = 0x400  */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_RTR);
	mtdcr (SDRAM0_CFGDATA, 0x04000000);

	/* Power management idle timer set to the default. */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_PMIT);
	mtdcr (SDRAM0_CFGDATA, 0x07c00000);

	udelay (500);

	/* Enable banks (DCE=1, BPRF=1, ECCDD=1, EMDUL=1) */
	mtdcr (SDRAM0_CFGADDR, SDRAM0_CFG);
	mtdcr (SDRAM0_CFGDATA, 0x80e00000);

	return SDRAM_LEN;
}

/*
 * The U-Boot core, as part of the initialization to prepare for
 * loading the monitor into SDRAM, requests of this function that the
 * memory be tested. Return 0 if the memory tests OK.
 */
int testdram (void)
{
	unsigned long idx;
	unsigned val;
	unsigned errors;
	volatile unsigned long *sdram;

#ifdef DEBUG
	printf ("SDRAM Controller Registers --\n");

	mtdcr (SDRAM0_CFGADDR, SDRAM0_CFG);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_CFG   : 0x%08x\n", val);

	mtdcr (SDRAM0_CFGADDR, 0x24);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_STATUS: 0x%08x\n", val);

	mtdcr (SDRAM0_CFGADDR, SDRAM0_B0CR);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_B0CR  : 0x%08x\n", val);

	mtdcr (SDRAM0_CFGADDR, SDRAM0_B1CR);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_B1CR  : 0x%08x\n", val);

	mtdcr (SDRAM0_CFGADDR, SDRAM0_TR);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_TR    : 0x%08x\n", val);

	mtdcr (SDRAM0_CFGADDR, SDRAM0_RTR);
	val = mfdcr (SDRAM0_CFGDATA);
	printf ("    SDRAM0_RTR   : 0x%08x\n", val);
#endif

	/* Wait for memory to be ready by testing MRSCMPbit
	   bit. Really, there should already have been plenty of time,
	   given it was started long ago. But, best to check. */
	for (idx = 0; idx < 1000000; idx += 1) {
		mtdcr (SDRAM0_CFGADDR, 0x24);
		val = mfdcr (SDRAM0_CFGDATA);
		if (val & 0x80000000)
			break;
	}

	if (!(val & 0x80000000)) {
		printf ("SDRAM ERROR: SDRAM0_STATUS never set!\n");
		return 1;
	}

	/* Start memory test. */
	printf ("test: %u MB - ", SDRAM_LEN / 1048576);

	sdram = (unsigned long *) CONFIG_SYS_SDRAM_BASE;

	printf ("write - ");
	for (idx = 2; idx < SDRAM_LEN / 4; idx += 2) {
		sdram[idx + 0] = idx;
		sdram[idx + 1] = ~idx;
	}

	printf ("read - ");
	errors = 0;
	for (idx = 2; idx < SDRAM_LEN / 4; idx += 2) {
		if (sdram[idx + 0] != idx)
			errors += 1;
		if (sdram[idx + 1] != ~idx)
			errors += 1;
		if (errors > 0)
			break;
	}

	if (errors > 0) {
		printf ("NOT OK\n");
		printf ("FIRST ERROR at %p: 0x%08lx:0x%08lx != 0x%08lx:0x%08lx\n",
			sdram + idx, sdram[idx + 0], sdram[idx + 1], idx, ~idx);
		return 1;
	}

	printf ("ok\n");
	return 0;
}
