From: Gabor Juhos <juhosg@openwrt.org>
Date: Sun, 1 Jul 2007 09:30:21 +0000 (+0000)
Subject: rewrite of memory detection code, should be fix #1909
X-Git-Tag: reboot~28779
X-Git-Url: http://git.cdn.openwrt.org/?a=commitdiff_plain;h=0f6020d1716b70b4915c1678ef7c99293b51996c;p=openwrt%2Fstaging%2Fpepe2k.git

rewrite of memory detection code, should be fix #1909

SVN-Revision: 7819
---

diff --git a/target/linux/adm5120-2.6/files/arch/mips/adm5120/adm5120_info.c b/target/linux/adm5120-2.6/files/arch/mips/adm5120/adm5120_info.c
index 6d512f1a75..c34dbd0095 100644
--- a/target/linux/adm5120-2.6/files/arch/mips/adm5120/adm5120_info.c
+++ b/target/linux/adm5120-2.6/files/arch/mips/adm5120/adm5120_info.c
@@ -15,6 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/string.h>
+#include <linux/module.h>
 
 #include <asm/bootinfo.h>
 #include <asm/addrspace.h>
@@ -22,6 +23,7 @@
 
 #include <asm/mach-adm5120/adm5120_defs.h>
 #include <asm/mach-adm5120/adm5120_switch.h>
+#include <asm/mach-adm5120/adm5120_mpmc.h>
 #include <asm/mach-adm5120/adm5120_info.h>
 #include <asm/mach-adm5120/myloader.h>
 #include <asm/mach-adm5120/routerboot.h>
@@ -32,6 +34,8 @@ extern char *prom_getenv(char *envname);
  * Globals
  */
 struct adm5120_board adm5120_board;
+EXPORT_SYMBOL_GPL(adm5120_board);
+
 unsigned int adm5120_boot_loader;
 
 unsigned int adm5120_product_code;
@@ -81,6 +85,7 @@ static struct adm5120_board __initdata adm5120_boards[] = {
 		.mach_type	= MACH_ADM5120_CAS771,
 		.has_usb	= 0,
 		.iface_num	= 5,
+		.mem_size	= (32 << 20),
 		.flash0_size	= 4*1024*1024,
 	},
 	{
@@ -137,6 +142,7 @@ static struct adm5120_board __initdata adm5120_boards[] = {
 		.mach_type	= MACH_ADM5120_WP54AG,
 		.has_usb	= 0,
 		.iface_num	= 2,
+		.mem_size	= (16 << 20),
 		.flash0_size	= 4*1024*1024,
 	},
 	{
@@ -179,6 +185,7 @@ static struct adm5120_board __initdata adm5120_boards[] = {
 		.mach_type	= MACH_ADM5120_BR6104K,
 		.has_usb	= 0,
 		.iface_num	= 5,
+		.mem_size	= (16 << 20),
 		.flash0_size	= 2*1024*1024,
 	},
 	{
@@ -926,19 +933,76 @@ static void __init adm5120_detect_cpuinfo(void)
 		adm5120_speed += 50000000;
 }
 
-#if 1
-#  define mem_dbg(f, ...)	prom_printf("mem_detect: " f, ## __VA_ARGS__)
+static void adm5120_ndelay(u32 ns)
+{
+	u32	t;
+
+	SWITCH_WRITE(SWITCH_REG_TIMER, TIMER_PERIOD_DEFAULT);
+	SWITCH_WRITE(SWITCH_REG_TIMER_INT, (TIMER_INT_TOS | TIMER_INT_TOM));
+
+	t = (ns+640) / 640;
+	t &= TIMER_PERIOD_MASK;
+	SWITCH_WRITE(SWITCH_REG_TIMER, t | TIMER_TE);
+
+	/* wait until the timer expires */
+	do {
+		t = SWITCH_READ(SWITCH_REG_TIMER_INT);
+	} while ((t & TIMER_INT_TOS) == 0);
+
+	/* leave the timer disabled */
+	SWITCH_WRITE(SWITCH_REG_TIMER, TIMER_PERIOD_DEFAULT);
+	SWITCH_WRITE(SWITCH_REG_TIMER_INT, (TIMER_INT_TOS | TIMER_INT_TOM));
+}
+
+#define MPMC_READ(r) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))
+#define MPMC_WRITE(r,v) *(u32 *)(KSEG1ADDR(ADM5120_SWITCH_BASE)+(r))=(v)
+
 extern void prom_printf(char *, ...);
+#if 1
+#  define mem_dbg(f, a...)	prom_printf("mem_detect: " f, ## a)
 #else
-#  define mem_dbg(f, ...)
+#  define mem_dbg(f, a...)
 #endif
 
+#define MEM_WR_DELAY	10000 /* 0.01 usec */
+
+static int mem_check_pattern(u8 *addr, unsigned long offs)
+{
+	volatile u32 *p1 = (volatile u32 *)addr;
+	volatile u32 *p2 = (volatile u32 *)(addr+offs);
+	u32 t,u,v;
+
+	/* save original value */
+	t = *p1;
+	u = *p2;
+
+	if (t != u)
+		return 0;
+
+	v = 0x55555555;
+	if (u == v)
+		v = 0xAAAAAAAA;
+
+	mem_dbg("write 0x%08lX to 0x%08lX\n", v, (unsigned long)p1);
+
+	*p1 = v;
+	mem_dbg("delay %d ns\n", MEM_WR_DELAY);
+	adm5120_ndelay(MEM_WR_DELAY);
+	u = *p2;
+
+	mem_dbg("pattern at 0x%08lX is 0x%08lX\n", (unsigned long)p2, u);
+
+	/* restore original value */
+	*p1 = t;
+
+	return (v == u);
+}
+
 static void __init adm5120_detect_memsize(void)
 {
 	u32	memctrl;
 	u32	size, maxsize;
-	volatile u8	*p,*r;
-	u8	t;
+	u8	*p;
 
 	memctrl = SWITCH_READ(SWITCH_REG_MEMCTRL);
 	switch (memctrl & MEMCTRL_SDRS_MASK) {
@@ -956,71 +1020,45 @@ static void __init adm5120_detect_memsize(void)
 		break;
 	}
 
-	/* FIXME: need to disable buffers for both SDRAM banks? */
+	/* disable buffers for both SDRAM banks */
+	mem_dbg("disable buffers for both banks\n");
+	MPMC_WRITE(MPMC_REG_DC0, MPMC_READ(MPMC_REG_DC0) & ~DC_BE);
+	MPMC_WRITE(MPMC_REG_DC1, MPMC_READ(MPMC_REG_DC1) & ~DC_BE);
 
-	mem_dbg("checking for %ldMB chip\n",maxsize >> 20);
+	mem_dbg("checking for %ldMB chip in 1st bank\n", maxsize >> 20);
 
 	/* detect size of the 1st SDRAM bank */
-	p = (volatile u8 *)KSEG1ADDR(0);
-	t = *p;
+	p = (u8 *)KSEG1ADDR(0);
 	for (size = 2<<20; size <= (maxsize >> 1); size <<= 1) {
-#if 1
-		r = (p+size);
-		*p = 0x55;
-		mem_dbg("1st pattern at 0x%lx is 0x%02x\n", size, *r);
-		if (*r == 0x55) {
-			*p = 0xAA;
-			mem_dbg("2nd pattern at 0x%lx is 0x%02x\n", size, *r);
-			if (*r == 0xAA) {
-				/* mirrored address */
-				mem_dbg("mirrored data found at 0x%lx\n", size);
-				break;
-			}
+		if (mem_check_pattern(p, size)) {
+			/* mirrored address */
+			mem_dbg("mirrored data found at offset 0x%lX\n", size);
+			break;
 		}
-#else
-		p[0] = 0x55;
-		mem_dbg("1st pattern at 0x%lx is 0x%02x\n", size, p[size]);
-		if (p[size] != 0x55)
-			continue;
-
-		p[0] = 0xAA;
-		mem_dbg("2nd pattern at 0x%lx is 0x%02x\n", size, p[size]);
-		if (p[size] != 0xAA)
-			continue;
-
-		/* mirrored address */
-		mem_dbg("mirrored data found at 0x%lx\n", size);
-		break;
-#endif
 	}
-	*p = t;
 
-	mem_dbg("%ldMB chip found\n", size >> 20);
+	mem_dbg("chip size in 1st bank is %ldMB\n", size >> 20);
+	adm5120_memsize = size;
 
-	if (size == (32 << 20))
-		/* if bank size is 32MB, 2nd bank is not supported */
+	if (size != maxsize)
+		/* 2nd bank is not supported */
 		goto out;
 
 	if ((memctrl & MEMCTRL_SDR1_ENABLE) == 0)
-		/* if 2nd bank is not enabled, we are done */
+		/* 2nd bank is disabled */
 		goto out;
 
 	/*
 	 * some bootloaders enable 2nd bank, even if the 2nd SDRAM chip
 	 * are missing.
 	 */
-	mem_dbg("checking second bank\n");
-	p += (maxsize+size)-1;
-	t = *p;
-	*p = 0x55;
-	if (*p != 0x55)
-		goto out;
+	mem_dbg("check presence of 2nd bank\n");
 
-	*p = 0xAA;
-	if (*p != 0xAA)
-		goto out;
+	p = (u8 *)KSEG1ADDR(maxsize+size-4);
+	if (mem_check_pattern(p, 0)) {
+		adm5120_memsize += size;
+	}
 
-	*p = t;
 	if (maxsize != size) {
 		/* adjusting MECTRL register */
 		memctrl &= ~(MEMCTRL_SDRS_MASK);
@@ -1040,11 +1078,21 @@ static void __init adm5120_detect_memsize(void)
 		}
 		SWITCH_WRITE(SWITCH_REG_MEMCTRL, memctrl);
 	}
-	size <<= 1;
 
 out:
-	adm5120_memsize = size;
-	mem_dbg("%ldMB memory found\n",size>>20);
+	/* reenable buffer for both SDRAM banks */
+	mem_dbg("enable buffers for both banks\n");
+	MPMC_WRITE(MPMC_REG_DC0, MPMC_READ(MPMC_REG_DC0) | DC_BE);
+	MPMC_WRITE(MPMC_REG_DC1, MPMC_READ(MPMC_REG_DC1) | DC_BE);
+
+	mem_dbg("%dx%ldMB memory found\n", (adm5120_memsize == size) ? 1 : 2 ,
+		size >>20);
+
+	size = adm5120_board_memsize();
+	if (size > 0 && size != adm5120_memsize) {
+		mem_dbg("wrong memory size detected, board settings will be used\n");
+		adm5120_memsize = size;
+	}
 }
 
 void __init adm5120_info_show(void)
@@ -1063,10 +1111,9 @@ void __init adm5120_info_show(void)
 
 void __init adm5120_info_init(void)
 {
-
 	adm5120_detect_cpuinfo();
-	adm5120_detect_memsize();
 	adm5120_detect_board();
+	adm5120_detect_memsize();
 
 	adm5120_info_show();
 }
diff --git a/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h
index b4730dc0f0..5c6424418a 100644
--- a/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h
+++ b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_info.h
@@ -22,6 +22,7 @@ struct adm5120_board {
 	unsigned long	mach_type;
 	unsigned int	iface_num;	/* Number of Ethernet interfaces */
 	unsigned int	has_usb;	/* USB controller presence flag */
+	u32		mem_size;	/* onboard memory size */
 	u32		flash0_size;	/* Flash 0 size */
 };
 
@@ -79,4 +80,9 @@ static inline char *adm5120_board_name(void)
 	return adm5120_board.name;
 }
 
+static inline u32 adm5120_board_memsize(void)
+{
+	return adm5120_board.mem_size;
+}
+
 #endif /* _ADM5120_INFO_H */
diff --git a/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h
new file mode 100644
index 0000000000..df536520cc
--- /dev/null
+++ b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_mpmc.h
@@ -0,0 +1,87 @@
+/*
+ *  $Id$
+ *
+ *  ADM5120 MPMC (Multiport Memory Controller) register definitions
+ *
+ *  Copyright (C) 2007 OpenWrt.org
+ *  Copyright (C) 2007 Gabor Juhos <juhosg@freemail.hu>
+ *
+ *  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., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _ADM5120_MPMC_H_
+#define _ADM5120_MPMC_H_
+
+#define MPMC_REG_CTRL	0x0000
+#define MPMC_REG_STATUS	0x0004
+#define MPMC_REG_CONF	0x0008
+#define MPMC_REG_DC	0x0020
+#define MPMC_REG_DR	0x0024
+#define MPMC_REG_DRP	0x0030
+
+#define MPMC_REG_DC0	0x0100
+#define MPMC_REG_DRC0	0x0104
+#define MPMC_REG_DC1	0x0120
+#define MPMC_REG_DRC1	0x0124
+#define MPMC_REG_DC2	0x0140
+#define MPMC_REG_DRC2	0x0144
+#define MPMC_REG_DC3	0x0160
+#define MPMC_REG_DRC3	0x0164
+#define MPMC_REG_SC0	0x0200  /* for F_CS1_N */
+#define MPMC_REG_SC1	0x0220  /* for F_CS0_N */
+#define MPMC_REG_SC2    0x0240
+#define MPMC_REG_SC3    0x0260
+
+#define MPMC_CTRL_AM		( 1 << 1 )
+
+/* Dynamic Control register bits */
+#define MPMC_DC_CE		( 1 << 0 )
+#define MPMC_DC_DMC		( 1 << 1 )
+#define MPMC_DC_SRR		( 1 << 2 )
+#define MPMC_DC_SI_SHIFT	7
+#define MPMC_DC_SI_MASK		( 3 << 7 )
+#define MPMC_DC_SI_NORMAL	( 0 << 7 )
+#define MPMC_DC_SI_MODE		( 1 << 7 )
+#define MPMC_DC_SI_PALL		( 2 << 7 )
+#define MPMC_DC_SI_NOP		( 3 << 7 )
+
+#define SRAM_REG_CONF	0x00
+#define SRAM_REG_WWE	0x04
+#define SRAM_REG_WOE	0x08
+#define SRAM_REG_WRD    0x0C
+#define SRAM_REG_WPG    0x10
+#define SRAM_REG_WWR    0x14
+#define SRAM_REG_WTR    0x18
+
+/* Dynamic Configuration register bits */
+#define DC_BE		(1 << 19) /* buffer enable */
+#define DC_RW_SHIFT	28	/* shift for number of rows */
+#define DC_RW_MASK	0x03
+#define DC_NB_SHIFT	26	/* shift for number of banks */
+#define DC_NB_MASK	0x01
+#define DC_CW_SHIFT	22	/* shift for number of columns */
+#define DC_CW_MASK	0x07
+#define DC_DW_SHIFT	7	/* shift for device width */
+#define DC_DW_MASK	0x03
+
+/* Static Configuration register bits */
+#define SC_MW_MASK	0x03	/* memory width mask */
+#define SC_MW_8		0x00	/* 8 bit memory width */
+#define SC_MW_16	0x01	/* 16 bit memory width */
+#define SC_MW_32	0x02	/* 32 bit memory width */
+
+#endif /* _ADM5120_MPMC_H_ */
diff --git a/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h
index f7664587d7..c796475c4c 100644
--- a/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h
+++ b/target/linux/adm5120-2.6/files/include/asm-mips/mach-adm5120/adm5120_switch.h
@@ -93,10 +93,15 @@
 #define MEMCTRL_SDRS_64M	0x04
 #define MEMCTRL_SDRS_128M	0x05
 #define MEMCTRL_SDR1_ENABLE	ONEBIT(5)	/* enable SDRAM bank 1 */
-#define MEMCTRL_SR0S_MASK	BITMASK(3)	/* SRAM0 size */
-#define MEMCTRL_SR0S_SHIFT	8		
-#define MEMCTRL_SR1S_MASK	BITMAKS(3)	/* SRAM1 size */
-#define MEMCTRL_SR1S_SHIFT	16
+
+#define MEMCTRL_SRS0_SHIFT	8		/* shift for SRAM0 size */
+#define MEMCTRL_SRS1_SHIFT	16		/* shift for SRAM1 size */
+#define MEMCTRL_SRS_MASK	BITMASK(3)	/* SRAM size mask */
+#define MEMCTRL_SRS_DISABLED	0x00		/* Disabled */
+#define MEMCTRL_SRS_512K	0x01		/* 512KB*/
+#define MEMCTRL_SRS_1M		0x02		/* 1MB */
+#define MEMCTRL_SRS_2M		0x03		/* 2MB */
+#define MEMCTRL_SRS_4M		0x04		/* 4MB */
 
 /* GPIO_CONF0 register bits */
 #define GPIO_CONF0_MASK		BITMASK(8)
@@ -109,6 +114,15 @@
 #define GPIO_CONF0_OE_MASK	(0xFF << GPIO_CONF0_OE_SHIFT)
 #define GPIO_CONF0_OV_MASK	(0xFF << GPIO_CONF0_OV_SHIFT)
 
+/* TIMER_INT register bits */
+#define TIMER_INT_TOS		ONEBIT(1)	/* time-out status */
+#define TIMER_INT_TOM		ONEBIT(16)	/* mask time-out interrupt */
+
+/* TIMER register bits */
+#define TIMER_PERIOD_MASK	BITMASK(16)	/* mask for timer period */
+#define TIMER_PERIOD_DEFAULT	0xFFFF		/* default timer period */
+#define TIMER_TE		ONEBIT(16)	/* timer enable bit */
+
 /* PORTx_LED register bits */
 #define LED_MODE_MASK		BITMASK(4)
 #define LED_MODE_INPUT		0
diff --git a/target/linux/adm5120eb-2.6/config/default b/target/linux/adm5120eb-2.6/config/default
index 9c5a35b3b8..c9ef9b1c2c 100644
--- a/target/linux/adm5120eb-2.6/config/default
+++ b/target/linux/adm5120eb-2.6/config/default
@@ -1,7 +1,7 @@
 CONFIG_32BIT=y
 # CONFIG_64BIT is not set
 # CONFIG_64BIT_PHYS_ADDR is not set
-CONFIG_ADM5120_HARDWARE_SWAB=y
+# CONFIG_ADM5120_HARDWARE_SWAB is not set
 CONFIG_ADM5120_NR_UARTS=2
 # CONFIG_ARCH_HAS_ILOG2_U32 is not set
 # CONFIG_ARCH_HAS_ILOG2_U64 is not set