cleanup memory detection code, fix #2244
[librecmc/librecmc.git] / target / linux / adm5120 / files / arch / mips / adm5120 / memory.c
1 /*
2  *  $Id$
3  *
4  *  Copyright (C) 2007 OpenWrt.org
5  *  Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org>
6  *
7  *  This program is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU General Public License
9  *  as published by the Free Software Foundation; either version 2
10  *  of the License, or (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the
19  *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  *  Boston, MA  02110-1301, USA.
21  *
22  */
23
24 #include <linux/init.h>
25 #include <linux/types.h>
26 #include <linux/kernel.h>
27
28 #include <asm/io.h>
29 #include <asm/bootinfo.h>
30 #include <asm/addrspace.h>
31
32 #include <adm5120_info.h>
33 #include <adm5120_defs.h>
34 #include <adm5120_switch.h>
35 #include <adm5120_mpmc.h>
36
37 #if 1
38 #  define mem_dbg(f, a...)      printk("mem_detect: " f, ## a)
39 #else
40 #  define mem_dbg(f, a...)
41 #endif
42
43 unsigned long adm5120_memsize;
44
45 #define MEM_READL(a)            __raw_readl((void __iomem *)(a))
46 #define MEM_WRITEL(a, v)        __raw_writel((v), (void __iomem *)(a))
47
48 static int __init mem_check_pattern(u8 *addr, unsigned long offs)
49 {
50         u32 *p1 = (u32 *)addr;
51         u32 *p2 = (u32 *)(addr+offs);
52         u32 t,u,v;
53         /* save original value */
54         t = MEM_READL(p1);
55         u = MEM_READL(p2);
56
57         if (t != u)
58                 return 0;
59
60         v = 0x55555555;
61         if (u == v)
62                 v = 0xAAAAAAAA;
63
64         mem_dbg("write 0x%08X to 0x%08lX\n", v, (unsigned long)p1);
65
66         MEM_WRITEL(p1, v);
67
68         /* flush write buffers */
69         MPMC_WRITE_REG(CTRL, MPMC_READ_REG(CTRL) | MPMC_CTRL_DWB);
70
71         u = MEM_READL(p2);
72
73         mem_dbg("pattern at 0x%08lX is 0x%08X\n", (unsigned long)p2, u);
74
75         /* restore original value */
76         MEM_WRITEL(p1, t);
77
78         return (v == u);
79 }
80
81 static void __init adm5120_detect_memsize(void)
82 {
83         u32     memctrl;
84         u32     size, maxsize;
85         u8      *p;
86
87         memctrl = SW_READ_REG(MEMCTRL);
88         switch (memctrl & MEMCTRL_SDRS_MASK) {
89         case MEMCTRL_SDRS_4M:
90                 maxsize = 4 << 20;
91                 break;
92         case MEMCTRL_SDRS_8M:
93                 maxsize = 8 << 20;
94                 break;
95         case MEMCTRL_SDRS_16M:
96                 maxsize = 16 << 20;
97                 break;
98         default:
99                 maxsize = 64 << 20;
100                 break;
101         }
102
103         mem_dbg("checking for %uMB chip in 1st bank\n", maxsize >> 20);
104
105         /* detect size of the 1st SDRAM bank */
106         p = (u8 *)KSEG1ADDR(0);
107         for (size = 2<<20; size <= (maxsize >> 1); size <<= 1) {
108                 if (mem_check_pattern(p, size)) {
109                         /* mirrored address */
110                         mem_dbg("mirrored data found at offset 0x%08X\n", size);
111                         break;
112                 }
113         }
114
115         mem_dbg("chip size in 1st bank is %uMB\n", size >> 20);
116         adm5120_memsize = size;
117
118         if (size != maxsize)
119                 /* 2nd bank is not supported */
120                 goto out;
121
122         if ((memctrl & MEMCTRL_SDR1_ENABLE) == 0)
123                 /* 2nd bank is disabled */
124                 goto out;
125
126         /*
127          * some bootloaders enable 2nd bank, even if the 2nd SDRAM chip
128          * are missing.
129          */
130         mem_dbg("check presence of 2nd bank\n");
131
132         p = (u8 *)KSEG1ADDR(maxsize+size-4);
133         if (mem_check_pattern(p, 0)) {
134                 adm5120_memsize += size;
135         }
136
137         if (maxsize != size) {
138                 /* adjusting MECTRL register */
139                 memctrl &= ~(MEMCTRL_SDRS_MASK);
140                 switch (size>>20) {
141                 case 4:
142                         memctrl |= MEMCTRL_SDRS_4M;
143                         break;
144                 case 8:
145                         memctrl |= MEMCTRL_SDRS_8M;
146                         break;
147                 case 16:
148                         memctrl |= MEMCTRL_SDRS_16M;
149                         break;
150                 default:
151                         memctrl |= MEMCTRL_SDRS_64M;
152                         break;
153                 }
154                 SW_WRITE_REG(MEMCTRL, memctrl);
155         }
156
157 out:
158         mem_dbg("%dx%uMB memory found\n", (adm5120_memsize == size) ? 1 : 2 ,
159                 size >>20);
160 }
161
162 void __init adm5120_mem_init(void)
163 {
164         adm5120_detect_memsize();
165         add_memory_region(0, adm5120_memsize, BOOT_MEM_RAM);
166 }