1/* 2 * Copyright (C) 2007-2008 Gabor Juhos <juhosg@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 as published 6 * by the Free Software Foundation. 7 * 8 */ 9 10#include <linux/init.h> 11#include <linux/types.h> 12#include <linux/kernel.h> 13#include <linux/io.h> 14 15#include <asm/bootinfo.h> 16#include <asm/addrspace.h> 17 18#include <asm/mach-adm5120/adm5120_info.h> 19#include <asm/mach-adm5120/adm5120_defs.h> 20#include <asm/mach-adm5120/adm5120_switch.h> 21#include <asm/mach-adm5120/adm5120_mpmc.h> 22 23#ifdef DEBUG 24# define mem_dbg(f, a...) printk(KERN_INFO "mem_detect: " f, ## a) 25#else 26# define mem_dbg(f, a...) 27#endif 28 29unsigned long adm5120_memsize; 30 31#define MEM_READL(a) __raw_readl((void __iomem *)(a)) 32#define MEM_WRITEL(a, v) __raw_writel((v), (void __iomem *)(a)) 33 34static int __init mem_check_pattern(u8 *addr, unsigned long offs) 35{ 36 u32 *p1 = (u32 *)addr; 37 u32 *p2 = (u32 *)(addr+offs); 38 u32 t, u, v; 39 40 /* save original value */ 41 t = MEM_READL(p1); 42 43 u = MEM_READL(p2); 44 if (t != u) 45 return 0; 46 47 v = 0x55555555; 48 if (u == v) 49 v = 0xAAAAAAAA; 50 51 mem_dbg("write 0x%08X to 0x%08lX\n", v, (unsigned long)p1); 52 53 MEM_WRITEL(p1, v); 54 adm5120_ndelay(1000); 55 u = MEM_READL(p2); 56 57 mem_dbg("pattern at 0x%08lX is 0x%08X\n", (unsigned long)p2, u); 58 59 /* restore original value */ 60 MEM_WRITEL(p1, t); 61 62 return (v == u); 63} 64 65static void __init adm5120_detect_memsize(void) 66{ 67 u32 memctrl; 68 u32 size, maxsize; 69 u8 *p; 70 71 memctrl = SW_READ_REG(SWITCH_REG_MEMCTRL); 72 switch (memctrl & MEMCTRL_SDRS_MASK) { 73 case MEMCTRL_SDRS_4M: 74 maxsize = 4 << 20; 75 break; 76 case MEMCTRL_SDRS_8M: 77 maxsize = 8 << 20; 78 break; 79 case MEMCTRL_SDRS_16M: 80 maxsize = 16 << 20; 81 break; 82 default: 83 maxsize = 64 << 20; 84 break; 85 } 86 87 mem_dbg("checking for %uMB chip in 1st bank\n", maxsize >> 20); 88 89 /* detect size of the 1st SDRAM bank */ 90 p = (u8 *)KSEG1ADDR(0); 91 for (size = 2<<20; size <= (maxsize >> 1); size <<= 1) { 92 if (mem_check_pattern(p, size)) { 93 /* mirrored address */ 94 mem_dbg("mirrored data found at offset 0x%08X\n", size); 95 break; 96 } 97 } 98 99 mem_dbg("chip size in 1st bank is %uMB\n", size >> 20); 100 adm5120_memsize = size; 101 102 if (size != maxsize) 103 /* 2nd bank is not supported */ 104 goto out; 105 106 if ((memctrl & MEMCTRL_SDR1_ENABLE) == 0) 107 /* 2nd bank is disabled */ 108 goto out; 109 110 /* 111 * some bootloaders enable 2nd bank, even if the 2nd SDRAM chip 112 * are missing. 113 */ 114 mem_dbg("check presence of 2nd bank\n"); 115 116 p = (u8 *)KSEG1ADDR(maxsize+size-4); 117 if (mem_check_pattern(p, 0)) 118 adm5120_memsize += size; 119 120 if (maxsize != size) { 121 /* adjusting MECTRL register */ 122 memctrl &= ~(MEMCTRL_SDRS_MASK); 123 switch (size>>20) { 124 case 4: 125 memctrl |= MEMCTRL_SDRS_4M; 126 break; 127 case 8: 128 memctrl |= MEMCTRL_SDRS_8M; 129 break; 130 case 16: 131 memctrl |= MEMCTRL_SDRS_16M; 132 break; 133 default: 134 memctrl |= MEMCTRL_SDRS_64M; 135 break; 136 } 137 SW_WRITE_REG(SWITCH_REG_MEMCTRL, memctrl); 138 } 139 140out: 141 mem_dbg("%dx%uMB memory found\n", (adm5120_memsize == size) ? 1 : 2 , 142 size>>20); 143} 144 145void __init adm5120_mem_init(void) 146{ 147 adm5120_detect_memsize(); 148 add_memory_region(0, adm5120_memsize, BOOT_MEM_RAM); 149} 150