1/* 2 * Early initialization code for BCM94710 boards 3 * 4 * Copyright (C) 2011, Broadcom Corporation. All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 * 18 * $Id: prom.c,v 1.8 2010-07-09 06:00:16 $ 19 */ 20 21#include <linux/version.h> 22#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) 23#include <linux/config.h> 24#endif 25#include <linux/init.h> 26#include <linux/kernel.h> 27#include <linux/types.h> 28#include <asm/bootinfo.h> 29#include <asm/cpu.h> 30#include <typedefs.h> 31#include <osl.h> 32#include <bcmutils.h> 33#include <bcmnvram.h> 34#include <bcmendian.h> 35#include <hndsoc.h> 36#include <siutils.h> 37#include <hndcpu.h> 38#include <mipsinc.h> 39#include <mips74k_core.h> 40#ifdef CONFIG_CFE 41#include <asm/fw/cfe/cfe_api.h> 42#endif 43 44#include "bcm947xx.h" 45 46/* Global SB handle */ 47extern si_t *bcm947xx_sih; 48 49/* Convenience */ 50#define sih bcm947xx_sih 51 52#define MB << 20 53 54#ifdef CONFIG_HIGHMEM 55 56#define EXTVBASE 0xc0000000 57#define ENTRYLO(x) ((pte_val(pfn_pte((x) >> PAGE_SHIFT, PAGE_KERNEL_UNCACHED)) >> 6) | 1) 58#define UNIQUE_ENTRYHI(idx) (CKSEG0 + ((idx) << (PAGE_SHIFT + 1))) 59 60static unsigned long tmp_tlb_ent __initdata; 61#ifdef CONFIG_CFE 62static int cfe_cons_handle; 63#endif 64 65#if defined(CONFIG_CFE) && defined(CONFIG_EARLY_PRINTK) 66void prom_putchar(char c) 67{ 68 while (cfe_write(cfe_cons_handle, &c, 1) == 0) 69 ; 70} 71#endif 72 73#ifdef CONFIG_CFE 74 75static __init void prom_init_cfe(void) 76{ 77 uint32_t cfe_ept; 78 uint32_t cfe_handle; 79 uint32_t cfe_eptseal; 80 int argc = fw_arg0; 81 char **envp = (char **) fw_arg2; 82 int *prom_vec = (int *) fw_arg3; 83 84 /* 85 * Check if a loader was used; if NOT, the 4 arguments are 86 * what CFE gives us (handle, 0, EPT and EPTSEAL) 87 */ 88 if (argc < 0) { 89 cfe_handle = (uint32_t)argc; 90 cfe_ept = (uint32_t)envp; 91 cfe_eptseal = (uint32_t)prom_vec; 92 } else { 93 if ((int)prom_vec < 0) { 94 /* 95 * Old loader; all it gives us is the handle, 96 * so use the "known" entrypoint and assume 97 * the seal. 98 */ 99 cfe_handle = (uint32_t)prom_vec; 100 cfe_ept = 0xBFC00500; 101 cfe_eptseal = CFE_EPTSEAL; 102 } else { 103 /* 104 * Newer loaders bundle the handle/ept/eptseal 105 * Note: prom_vec is in the loader's useg 106 * which is still alive in the TLB. 107 */ 108 cfe_handle = prom_vec[0]; 109 cfe_ept = prom_vec[2]; 110 cfe_eptseal = prom_vec[3]; 111 } 112 } 113 114 if (cfe_eptseal != CFE_EPTSEAL) { 115 /* too early for panic to do any good */ 116 printk(KERN_ERR "CFE's entrypoint seal doesn't match."); 117 while (1) ; 118 } 119 120 cfe_init(cfe_handle, cfe_ept); 121} 122 123static __init void prom_init_console(void) 124{ 125 /* Initialize CFE console */ 126 cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); 127} 128 129static __init void prom_init_cmdline(void) 130{ 131 static char buf[COMMAND_LINE_SIZE] __initdata; 132 133 /* Get the kernel command line from CFE */ 134 if (cfe_getenv("LINUX_CMDLINE", buf, COMMAND_LINE_SIZE) >= 0) { 135 buf[COMMAND_LINE_SIZE - 1] = 0; 136 strcpy(arcs_cmdline, buf); 137 } 138 139 /* Force a console handover by adding a console= argument if needed, 140 * as CFE is not available anymore later in the boot process. */ 141 if ((strstr(arcs_cmdline, "console=")) == NULL) { 142 /* Try to read the default serial port used by CFE */ 143 if ((cfe_getenv("BOOT_CONSOLE", buf, COMMAND_LINE_SIZE) < 0) 144 || (strncmp("uart", buf, 4))) 145 /* Default to uart0 */ 146 strcpy(buf, "uart0"); 147 148 /* Compute the new command line */ 149 snprintf(arcs_cmdline, COMMAND_LINE_SIZE, "%s console=ttyS%c,115200", 150 arcs_cmdline, buf[4]); 151 } 152} 153#endif /* CONFIG_CFE */ 154 155/* Initialize the wired register and all tlb entries to 156 * known good state. 157 */ 158void __init 159early_tlb_init(void) 160{ 161 unsigned long index; 162 struct cpuinfo_mips *c = ¤t_cpu_data; 163 164 tmp_tlb_ent = c->tlbsize; 165 166 /* printk(KERN_ALERT "%s: tlb size %ld\n", __FUNCTION__, c->tlbsize); */ 167 168 /* 169 * initialize entire TLB to uniqe virtual addresses 170 * but with the PAGE_VALID bit not set 171 */ 172 write_c0_wired(0); 173 write_c0_pagemask(PM_DEFAULT_MASK); 174 175 write_c0_entrylo0(0); /* not _PAGE_VALID */ 176 write_c0_entrylo1(0); 177 178 for (index = 0; index < c->tlbsize; index++) { 179 /* Make sure all entries differ. */ 180 write_c0_entryhi(UNIQUE_ENTRYHI(index+32)); 181 write_c0_index(index); 182 mtc0_tlbw_hazard(); 183 tlb_write_indexed(); 184 } 185 186 tlbw_use_hazard(); 187 188} 189 190void __init 191add_tmptlb_entry(unsigned long entrylo0, unsigned long entrylo1, 192 unsigned long entryhi, unsigned long pagemask) 193{ 194/* write one tlb entry */ 195 --tmp_tlb_ent; 196 write_c0_index(tmp_tlb_ent); 197 write_c0_pagemask(pagemask); 198 write_c0_entryhi(entryhi); 199 write_c0_entrylo0(entrylo0); 200 write_c0_entrylo1(entrylo1); 201 mtc0_tlbw_hazard(); 202 tlb_write_indexed(); 203 tlbw_use_hazard(); 204} 205#endif /* CONFIG_HIGHMEM */ 206 207extern char ram_nvram_buf[]; 208 209void __init 210prom_init(void) 211{ 212 unsigned long mem, extmem = 0, off, data; 213 unsigned long off1, data1; 214 struct nvram_header *header; 215 216#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) 217 /* These are not really being used anywhere - LR */ 218 mips_machgroup = MACH_GROUP_BRCM; 219 mips_machtype = MACH_BCM947XX; 220#endif 221 222#ifdef CONFIG_CFE 223 prom_init_cfe(); 224 prom_init_console(); 225 prom_init_cmdline(); 226#endif 227 228 off = (unsigned long)prom_init; 229 data = *(unsigned long *)prom_init; 230 off1 = off + 4; 231 data1 = *(unsigned long *)off1; 232 233 /* Figure out memory size by finding aliases */ 234 for (mem = (1 MB); mem < (128 MB); mem <<= 1) { 235 if ((*(unsigned long *)(off + mem) == data) && 236 (*(unsigned long *)(off1 + mem) == data1)) 237 break; 238 } 239 240#if CONFIG_RAM_SIZE 241 { 242 unsigned long config_mem; 243 config_mem = CONFIG_RAM_SIZE * 0x100000; 244 if (config_mem < mem) 245 mem = config_mem; 246 } 247#endif 248#ifdef CONFIG_HIGHMEM 249 if (mem == 128 MB) { 250 bool highmem_region = FALSE; 251 int idx; 252 253 sih = si_kattach(SI_OSH); 254 /* save current core index */ 255 idx = si_coreidx(sih); 256 if ((si_setcore(sih, DMEMC_CORE_ID, 0) != NULL) || 257 (si_setcore(sih, DMEMS_CORE_ID, 0) != NULL)) { 258 uint32 addr, size; 259 uint asidx = 0; 260 261 do { 262 si_coreaddrspaceX(sih, asidx, &addr, &size); 263 if (size == 0) 264 break; 265 if (addr == SI_SDRAM_R2) { 266 highmem_region = TRUE; 267 break; 268 } 269 asidx++; 270 } while (1); 271 } 272 /* switch back to previous core */ 273 si_setcoreidx(sih, idx); 274 275 if (highmem_region) { 276 early_tlb_init(); 277 /* Add one temporary TLB entries to map SDRAM Region 2. 278 * Physical Virtual 279 * 0x80000000 0xc0000000 (1st: 256MB) 280 * 0x90000000 0xd0000000 (2nd: 256MB) 281 */ 282 add_tmptlb_entry(ENTRYLO(SI_SDRAM_R2), 283 ENTRYLO(SI_SDRAM_R2 + (256 MB)), 284 EXTVBASE, PM_256M); 285 286 off = EXTVBASE + __pa(off); 287 for (extmem = (128 MB); extmem < (512 MB); extmem <<= 1) { 288 if (*(unsigned long *)(off + extmem) == data) 289 break; 290 } 291 292 extmem -= mem; 293 /* Keep tlb entries back in consistent state */ 294 early_tlb_init(); 295 } 296 } 297#endif /* CONFIG_HIGHMEM */ 298 /* Ignoring the last page when ddr size is 128M. Cached 299 * accesses to last page is causing the processor to prefetch 300 * using address above 128M stepping out of the ddr address 301 * space. 302 */ 303 if (MIPS74K(current_cpu_data.processor_id) && (mem == (128 MB))) 304 mem -= 0x1000; 305 306 /* CFE could have loaded nvram during netboot 307 * to top 32KB of RAM, Just check for nvram signature 308 * and copy it to nvram space embedded in linux 309 * image for later use by nvram driver. 310 */ 311 header = (struct nvram_header *)(KSEG0ADDR(mem - NVRAM_SPACE)); 312 if (ltoh32(header->magic) == NVRAM_MAGIC) { 313 uint32 *src = (uint32 *)header; 314 uint32 *dst = (uint32 *)ram_nvram_buf; 315 uint32 i; 316 317 printk("Copying NVRAM bytes: %d from: 0x%p To: 0x%p\n", ltoh32(header->len), 318 src, dst); 319 for (i = 0; i < ltoh32(header->len) && i < NVRAM_SPACE; i += 4) 320 *dst++ = ltoh32(*src++); 321 } 322 323#ifdef CONFIG_BLK_DEV_RAM 324 init_ramdisk(mem); 325#endif 326 add_memory_region(SI_SDRAM_BASE, mem, BOOT_MEM_RAM); 327 328#ifdef CONFIG_HIGHMEM 329 if (extmem) { 330 /* We should deduct 0x1000 from the second memory 331 * region, because of the fact that processor does prefetch. 332 * Now that we are deducting a page from second memory 333 * region, we could add the earlier deducted 4KB (from first bank) 334 * to the second region (the fact that 0x80000000 -> 0x88000000 335 * shadows 0x0 -> 0x8000000) 336 */ 337 if (MIPS74K(current_cpu_data.processor_id) && (mem == (128 MB))) 338 extmem -= 0x1000; 339 add_memory_region(SI_SDRAM_R2 + (128 MB) - 0x1000, extmem, BOOT_MEM_RAM); 340 } 341#endif /* CONFIG_HIGHMEM */ 342} 343 344void __init 345prom_free_prom_memory(void) 346{ 347} 348