Updates to common PPC4xx onboard (DDR)SDRAM init code (405 and 440)

405 SDRAM: - The SDRAM parameters can now be defined in the board
             config file and the 405 SDRAM controller values will
             be calculated upon bootup (see PPChameleonEVB).
             When those settings are not defined in the board
             config file, the register setup will be as it is now,
             so this implementation should not break any current
             design using this code.

             Thanks to Andrea Marson from DAVE for this patch.

440 DDR:   - Added function sdram_tr1_set to auto calculate the
             TR1 value for the DDR.
           - Added ECC support (see p3p440).

Patch by Stefan Roese, 17 Mar 2006
diff --git a/cpu/ppc4xx/sdram.c b/cpu/ppc4xx/sdram.c
index e9548cd..c5ab017 100644
--- a/cpu/ppc4xx/sdram.c
+++ b/cpu/ppc4xx/sdram.c
@@ -1,7 +1,10 @@
 /*
- * (C) Copyright 2005
+ * (C) Copyright 2005-2006
  * Stefan Roese, DENX Software Engineering, sr@denx.de.
  *
+ * (C) Copyright 2006
+ * DAVE Srl <www.dave-tech.it>
+ *
  * (C) Copyright 2002-2004
  * Stefan Roese, esd gmbh germany, stefan.roese@esd-electronics.com
  *
@@ -27,28 +30,19 @@
 #include <common.h>
 #include <ppc4xx.h>
 #include <asm/processor.h>
+#include "sdram.h"
 
 
 #ifdef CONFIG_SDRAM_BANK0
 
-
-#define mtsdram0(reg, data)  mtdcr(memcfga,reg);mtdcr(memcfgd,data)
-
-
-struct sdram_conf_s {
-	unsigned long size;
-	unsigned long reg;
-};
-
-typedef struct sdram_conf_s sdram_conf_t;
 
 #ifndef CFG_SDRAM_TABLE
 sdram_conf_t mb0cf[] = {
-	{(128 << 20), 0x000A4001},      /* (0-128MB) Address Mode 3, 13x10(4) */
-	{(64 << 20),  0x00084001},      /* (0-64MB) Address Mode 3, 13x9(4)   */
-	{(32 << 20),  0x00062001},      /* (0-32MB) Address Mode 2, 12x9(4)   */
-	{(16 << 20),  0x00046001},      /* (0-16MB) Address Mode 4, 12x8(4)   */
-	{(4 << 20),   0x00008001},      /* (0-4MB) Address Mode 5, 11x8(2)    */
+	{(128 << 20), 13, 0x000A4001},      /* (0-128MB) Address Mode 3, 13x10(4) */
+	{(64 << 20),  13, 0x00084001},      /* (0-64MB) Address Mode 3, 13x9(4)   */
+	{(32 << 20),  12, 0x00062001},      /* (0-32MB) Address Mode 2, 12x9(4)   */
+	{(16 << 20),  12, 0x00046001},      /* (0-16MB) Address Mode 4, 12x8(4)   */
+	{(4 << 20),   11, 0x00008001},      /* (0-4MB) Address Mode 5, 11x8(2)    */
 };
 #else
 sdram_conf_t mb0cf[] = CFG_SDRAM_TABLE;
@@ -59,31 +53,138 @@
 
 #ifndef CONFIG_440
 
+#ifdef CFG_SDRAM_CASL
+static ulong ns2clks(ulong ns)
+{
+	ulong bus_period_x_10 = ONE_BILLION / (get_bus_freq(0) / 10);
+
+	return ((ns * 10) + bus_period_x_10) / bus_period_x_10;
+}
+#endif /* CFG_SDRAM_CASL */
+
+static ulong compute_sdtr1(ulong speed)
+{
+#ifdef CFG_SDRAM_CASL
+        ulong tmp;
+        ulong sdtr1 = 0;
+
+        /* CASL */
+        if (CFG_SDRAM_CASL < 2)
+                sdtr1 |= (1 << SDRAM0_TR_CASL);
+        else
+                if (CFG_SDRAM_CASL > 4)
+                        sdtr1 |= (3 << SDRAM0_TR_CASL);
+                else
+                        sdtr1 |= ((CFG_SDRAM_CASL-1) << SDRAM0_TR_CASL);
+
+        /* PTA */
+        tmp = ns2clks(CFG_SDRAM_PTA);
+        if ((tmp >= 2) && (tmp <= 4))
+                sdtr1 |= ((tmp-1) << SDRAM0_TR_PTA);
+        else
+                sdtr1 |= ((4-1) << SDRAM0_TR_PTA);
+
+        /* CTP */
+        tmp = ns2clks(CFG_SDRAM_CTP);
+        if ((tmp >= 2) && (tmp <= 4))
+                sdtr1 |= ((tmp-1) << SDRAM0_TR_CTP);
+        else
+                sdtr1 |= ((4-1) << SDRAM0_TR_CTP);
+
+        /* LDF */
+        tmp = ns2clks(CFG_SDRAM_LDF);
+        if ((tmp >= 2) && (tmp <= 4))
+                sdtr1 |= ((tmp-1) << SDRAM0_TR_LDF);
+        else
+                sdtr1 |= ((2-1) << SDRAM0_TR_LDF);
+
+        /* RFTA */
+        tmp = ns2clks(CFG_SDRAM_RFTA);
+        if ((tmp >= 4) && (tmp <= 10))
+                sdtr1 |= ((tmp-4) << SDRAM0_TR_RFTA);
+        else
+                sdtr1 |= ((10-4) << SDRAM0_TR_RFTA);
+
+        /* RCD */
+        tmp = ns2clks(CFG_SDRAM_RCD);
+        if ((tmp >= 2) && (tmp <= 4))
+                sdtr1 |= ((tmp-1) << SDRAM0_TR_RCD);
+        else
+                sdtr1 |= ((4-1) << SDRAM0_TR_RCD);
+
+        return sdtr1;
+#else /* CFG_SDRAM_CASL */
+        /*
+         * If no values are configured in the board config file
+         * use the default values, which seem to be ok for most
+         * boards.
+         *
+         * REMARK:
+         * For new board ports we strongly recommend to define the
+         * correct values for the used SDRAM chips in your board
+         * config file (see PPChameleonEVB.h)
+         */
+        if (speed > 100000000) {
+                /*
+                 * 133 MHz SDRAM
+                 */
+                return 0x01074015;
+        } else {
+                /*
+                 * default: 100 MHz SDRAM
+                 */
+                return 0x0086400d;
+        }
+#endif /* CFG_SDRAM_CASL */
+}
+
+/* refresh is expressed in ms */
+static ulong compute_rtr(ulong speed, ulong rows, ulong refresh)
+{
+#ifdef CFG_SDRAM_CASL
+        ulong tmp;
+
+        tmp = ((refresh*1000*1000) / (1 << rows)) * (speed / 1000);
+        tmp /= 1000000;
+
+        return ((tmp & 0x00003FF8) << 16);
+#else /* CFG_SDRAM_CASL */
+        if (speed > 100000000) {
+                /*
+                 * 133 MHz SDRAM
+                 */
+		return 0x07f00000;
+        } else {
+                /*
+                 * default: 100 MHz SDRAM
+                 */
+		return 0x05f00000;
+        }
+#endif /* CFG_SDRAM_CASL */
+}
+
 /*
  * Autodetect onboard SDRAM on 405 platforms
  */
 void sdram_init(void)
 {
+	ulong speed;
 	ulong sdtr1;
-	ulong rtr;
 	int i;
 
-	/*
-	 * Support for 100MHz and 133MHz SDRAM
-	 */
-	if (get_bus_freq(0) > 100000000) {
-		/*
-		 * 133 MHz SDRAM
-		 */
-		sdtr1 = 0x01074015;
-		rtr = 0x07f00000;
-	} else {
-		/*
-		 * default: 100 MHz SDRAM
-		 */
-		sdtr1 = 0x0086400d;
-		rtr = 0x05f00000;
-	}
+        /*
+         * Determine SDRAM speed
+         */
+        speed = get_bus_freq(0); /* parameter not used on ppc4xx */
+
+        /*
+         * sdtr1 (register SDRAM0_TR) must take into account timings listed
+         * in SDRAM chip datasheet. rtr (register SDRAM0_RTR) must take into
+         * account actual SDRAM size. So we can set up sdtr1 according to what
+         * is specified in board configuration file while rtr dependds on SDRAM
+         * size we are assuming before detection.
+         */
+        sdtr1 = compute_sdtr1(speed);
 
 	for (i=0; i<N_MB0CF; i++) {
 		/*
@@ -96,7 +197,7 @@
 		 */
 		mtsdram0(mem_mb0cf, mb0cf[i].reg);
 		mtsdram0(mem_sdtr1, sdtr1);
-		mtsdram0(mem_rtr, rtr);
+		mtsdram0(mem_rtr, compute_rtr(speed, mb0cf[i].rows, 64));
 
 		udelay(200);
 
@@ -120,6 +221,124 @@
 
 #else /* CONFIG_440 */
 
+#define NUM_TRIES 64
+#define NUM_READS 10
+
+static void sdram_tr1_set(int ram_address, int* tr1_value)
+{
+	int i;
+	int j, k;
+	volatile unsigned int* ram_pointer = (unsigned int *)ram_address;
+	int first_good = -1, last_bad = 0x1ff;
+
+	unsigned long test[NUM_TRIES] = {
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+		0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555,
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+		0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA,
+		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A,
+		0xA5A5A5A5, 0xA5A5A5A5, 0x5A5A5A5A, 0x5A5A5A5A,
+		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5,
+		0x5A5A5A5A, 0x5A5A5A5A, 0xA5A5A5A5, 0xA5A5A5A5,
+		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA,
+		0xAA55AA55, 0xAA55AA55, 0x55AA55AA, 0x55AA55AA,
+		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55,
+		0x55AA55AA, 0x55AA55AA, 0xAA55AA55, 0xAA55AA55 };
+
+	/* go through all possible SDRAM0_TR1[RDCT] values */
+	for (i=0; i<=0x1ff; i++) {
+		/* set the current value for TR1 */
+		mtsdram(mem_tr1, (0x80800800 | i));
+
+		/* write values */
+		for (j=0; j<NUM_TRIES; j++) {
+			ram_pointer[j] = test[j];
+
+			/* clear any cache at ram location */
+			__asm__("dcbf 0,%0": :"r" (&ram_pointer[j]));
+		}
+
+		/* read values back */
+		for (j=0; j<NUM_TRIES; j++) {
+			for (k=0; k<NUM_READS; k++) {
+				/* clear any cache at ram location */
+				__asm__("dcbf 0,%0": :"r" (&ram_pointer[j]));
+
+				if (ram_pointer[j] != test[j])
+					break;
+			}
+
+			/* read error */
+			if (k != NUM_READS)
+				break;
+		}
+
+		/* we have a SDRAM0_TR1[RDCT] that is part of the window */
+		if (j == NUM_TRIES) {
+			if (first_good == -1)
+				first_good = i;		/* found beginning of window */
+		} else { /* bad read */
+			/* if we have not had a good read then don't care */
+			if (first_good != -1) {
+				/* first failure after a good read */
+				last_bad = i-1;
+				break;
+			}
+		}
+	}
+
+	/* return the current value for TR1 */
+	*tr1_value = (first_good + last_bad) / 2;
+}
+
+
+#ifdef CONFIG_SDRAM_ECC
+static void ecc_init(ulong start, ulong size)
+{
+	ulong	current_addr;		/* current byte address */
+	ulong	end_addr;		/* end of memory region */
+	ulong	addr_inc;		/* address skip between writes */
+	ulong	cfg0_reg;		/* for restoring ECC state */
+
+	/*
+	 * TODO: Enable dcache before running this test (speedup)
+	 */
+
+	mfsdram(mem_cfg0, cfg0_reg);
+	mtsdram(mem_cfg0, (cfg0_reg & ~SDRAM_CFG0_MEMCHK) | SDRAM_CFG0_MEMCHK_GEN);
+
+	/*
+	 * look at geometry of SDRAM (data width) to determine whether we
+	 * can skip words when writing
+	 */
+	if ((cfg0_reg & SDRAM_CFG0_DRAMWDTH) == SDRAM_CFG0_DRAMWDTH_32)
+		addr_inc = 4;
+	else
+		addr_inc = 8;
+
+	current_addr = start;
+	end_addr = start + size;
+
+	while (current_addr < end_addr) {
+		*((ulong *)current_addr) = 0x00000000;
+		current_addr += addr_inc;
+	}
+
+	/*
+	 * TODO: Flush dcache and disable it again
+	 */
+
+	/*
+	 * Enable ecc checking and parity errors
+	 */
+	mtsdram(mem_cfg0, (cfg0_reg & ~SDRAM_CFG0_MEMCHK) | SDRAM_CFG0_MEMCHK_CHK);
+}
+#endif
+
 /*
  * Autodetect onboard DDR SDRAM on 440 platforms
  *
@@ -130,6 +349,7 @@
 long int initdram(int board_type)
 {
 	int i;
+	int tr1_bank1;
 
 	for (i=0; i<N_MB0CF; i++) {
 		/*
@@ -164,6 +384,16 @@
 
 		if (get_ram_size(0, mb0cf[i].size) == mb0cf[i].size) {
 			/*
+			 * Optimize TR1 to current hardware environment
+			 */
+			sdram_tr1_set(0x00000000, &tr1_bank1);
+			mtsdram(mem_tr1, (tr1_bank1 | 0x80800800));
+
+#ifdef CONFIG_SDRAM_ECC
+			ecc_init(0, mb0cf[i].size);
+#endif
+
+			/*
 			 * OK, size detected -> all done
 			 */
 			return mb0cf[i].size;