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/CHANGELOG b/CHANGELOG
index 34b8d20..16b50e9 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -33,6 +33,24 @@
 * Add support for Lite5200B board.
   Patch by  Patch by Jose Maria (Txema) Lopez, 16 Jan 2006
 
+* 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
+
 * Apply SoC concept to arm926ejs CPUs, i.e. move the SoC specific
   timer and cpu_reset code from cpu/$(CPU) into the new
   cpu/$(CPU)/$(SOC) directories
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;
diff --git a/cpu/ppc4xx/sdram.h b/cpu/ppc4xx/sdram.h
new file mode 100644
index 0000000..62b5442
--- /dev/null
+++ b/cpu/ppc4xx/sdram.h
@@ -0,0 +1,78 @@
+/*
+ * (C) Copyright 2006
+ * Stefan Roese, DENX Software Engineering, sr@denx.de.
+ *
+ * (C) Copyright 2006
+ * DAVE Srl <www.dave-tech.it>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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; 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
+ */
+
+#ifndef _SDRAM_H_
+#define _SDRAM_H_
+
+#include <config.h>
+
+#define mtsdram0(reg, data)  mtdcr(memcfga,reg);mtdcr(memcfgd,data)
+
+#define ONE_BILLION	1000000000
+
+struct sdram_conf_s {
+	unsigned long size;
+	int rows;
+	unsigned long reg;
+};
+
+typedef struct sdram_conf_s sdram_conf_t;
+
+/* Bitfields offsets */
+#define SDRAM0_TR_CASL		(31 - 8)
+#define SDRAM0_TR_PTA		(31 - 13)
+#define SDRAM0_TR_CTP		(31 - 15)
+#define SDRAM0_TR_LDF		(31 - 17)
+#define SDRAM0_TR_RFTA		(31 - 29)
+#define SDRAM0_TR_RCD		(31 - 31)
+
+#ifdef CFG_SDRAM_CL
+/* SDRAM timings [ns] according to AMCC/IBM names (see SDRAM_faq.doc) */
+#define CFG_SDRAM_CASL		CFG_SDRAM_CL
+#define CFG_SDRAM_PTA		CFG_SDRAM_tRP
+#define CFG_SDRAM_CTP		(CFG_SDRAM_tRC - CFG_SDRAM_tRCD - CFG_SDRAM_tRP)
+#define CFG_SDRAM_LDF		0
+#ifdef CFG_SDRAM_tRFC
+#define CFG_SDRAM_RFTA		CFG_SDRAM_tRFC
+#else
+#define CFG_SDRAM_RFTA		CFG_SDRAM_tRC
+#endif
+#define CFG_SDRAM_RCD		CFG_SDRAM_tRCD
+#endif /* #ifdef CFG_SDRAM_CL */
+
+/*
+ * Some defines for the 440 DDR controller
+ */
+#define SDRAM_CFG0_DC_EN	0x80000000	/* SDRAM Controller Enable	*/
+#define SDRAM_CFG0_MEMCHK	0x30000000	/* Memory data error checking mask*/
+#define SDRAM_CFG0_MEMCHK_NON	0x00000000	/* No ECC generation		*/
+#define SDRAM_CFG0_MEMCHK_GEN	0x20000000	/* ECC generation		*/
+#define SDRAM_CFG0_MEMCHK_CHK	0x30000000	/* ECC generation and checking	*/
+#define SDRAM_CFG0_DRAMWDTH	0x02000000	/* DRAM width mask		*/
+#define SDRAM_CFG0_DRAMWDTH_32	0x00000000	/* 32 bits			*/
+#define SDRAM_CFG0_DRAMWDTH_64	0x02000000	/* 64 bits			*/
+
+#endif
diff --git a/include/configs/PPChameleonEVB.h b/include/configs/PPChameleonEVB.h
index e1155e2..16e2cc6 100644
--- a/include/configs/PPChameleonEVB.h
+++ b/include/configs/PPChameleonEVB.h
@@ -139,8 +139,18 @@
 #define CFG_I2C_RTC_ADDR	0x68
 #define CFG_M41T11_BASE_YEAR	1900
 
+/*
+ * SDRAM configuration (please see cpu/ppc/sdram.[ch])
+ */
 #define CONFIG_SDRAM_BANK0	1	/* init onboard SDRAM bank 0	*/
 
+/* SDRAM timings used in datasheet */
+#define CFG_SDRAM_CL            2
+#define CFG_SDRAM_tRP           20
+#define CFG_SDRAM_tRC           65
+#define CFG_SDRAM_tRCD          20
+#undef  CFG_SDRAM_tRFC
+
 /*
  * Miscellaneous configurable options
  */
diff --git a/include/configs/p3p440.h b/include/configs/p3p440.h
index 831d018..0662544 100644
--- a/include/configs/p3p440.h
+++ b/include/configs/p3p440.h
@@ -1,5 +1,5 @@
 /*
- * (C) Copyright 2005
+ * (C) Copyright 2005-2006
  * Stefan Roese, DENX Software Engineering, sr@denx.de.
  *
  * (C) Copyright 2002 Scott McNutt <smcnutt@artesyncp.com>
@@ -71,9 +71,10 @@
  * DDR SDRAM
  *----------------------------------------------------------------------*/
 #define CONFIG_SDRAM_BANK0	1	/* init onboard DDR SDRAM bank 0*/
-#define CFG_SDRAM_TABLE	{	\
-		{(256 << 20), 0x000C4001}, /* 256MB mode 3, 13x10(4) */	\
-		{(64 << 20),  0x00082001}} /* 64MB mode 2, 12x9(4)   */
+#define CONFIG_SDRAM_ECC		/* enable ECC support		*/
+#define CFG_SDRAM_TABLE	{ \
+		{(256 << 20), 13, 0x000C4001}, /* 256MB mode 3, 13x10(4)*/ \
+		{(64 << 20),  12, 0x00082001}} /* 64MB mode 2, 12x9(4)	*/
 
 /*-----------------------------------------------------------------------
  * Serial Port
@@ -275,6 +276,9 @@
 #define CFG_FLASH_ERASE_TOUT	120000	/* Timeout for Flash Erase (in ms)	*/
 #define CFG_FLASH_WRITE_TOUT	500	/* Timeout for Flash Write (in ms)	*/
 
+#define CFG_FLASH_USE_BUFFER_WRITE 1	/* use buffered writes (20x faster)	*/
+#define CFG_FLASH_PROTECTION	1	/* hardware flash protection		*/
+
 #define CFG_FLASH_EMPTY_INFO		/* print 'E' for empty sector on flinfo */
 #define CFG_FLASH_QUIET_TEST	1	/* don't warn upon unknown flash	*/