1/* 2 * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License 6 * as published by the Free Software Foundation; either version 2 7 * of the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 */ 18 19#include <linux/init.h> 20#include <linux/kernel.h> 21#include <linux/linkage.h> 22#include <linux/mm.h> 23#include <linux/blkdev.h> 24#include <linux/bootmem.h> 25#include <linux/pm.h> 26#include <linux/smp.h> 27 28#include <asm/bootinfo.h> 29#include <asm/reboot.h> 30#include <asm/sibyte/board.h> 31#include <asm/smp-ops.h> 32 33#include <asm/fw/cfe/cfe_api.h> 34#include <asm/fw/cfe/cfe_error.h> 35 36/* Max ram addressable in 32-bit segments */ 37#ifdef CONFIG_64BIT 38#define MAX_RAM_SIZE (~0ULL) 39#else 40#ifdef CONFIG_HIGHMEM 41#ifdef CONFIG_PHYS_ADDR_T_64BIT 42#define MAX_RAM_SIZE (~0ULL) 43#else 44#define MAX_RAM_SIZE (0xffffffffULL) 45#endif 46#else 47#define MAX_RAM_SIZE (0x1fffffffULL) 48#endif 49#endif 50 51#define SIBYTE_MAX_MEM_REGIONS 8 52phys_addr_t board_mem_region_addrs[SIBYTE_MAX_MEM_REGIONS]; 53phys_addr_t board_mem_region_sizes[SIBYTE_MAX_MEM_REGIONS]; 54unsigned int board_mem_region_count; 55 56int cfe_cons_handle; 57 58#ifdef CONFIG_BLK_DEV_INITRD 59extern unsigned long initrd_start, initrd_end; 60#endif 61 62static void __noreturn cfe_linux_exit(void *arg) 63{ 64 int warm = *(int *)arg; 65 66 if (smp_processor_id()) { 67 static int reboot_smp; 68 69 /* Don't repeat the process from another CPU */ 70 if (!reboot_smp) { 71 /* Get CPU 0 to do the cfe_exit */ 72 reboot_smp = 1; 73 smp_call_function(cfe_linux_exit, arg, 0); 74 } 75 } else { 76 printk("Passing control back to CFE...\n"); 77 cfe_exit(warm, 0); 78 printk("cfe_exit returned??\n"); 79 } 80 while (1); 81} 82 83static void __noreturn cfe_linux_restart(char *command) 84{ 85 static const int zero; 86 87 cfe_linux_exit((void *)&zero); 88} 89 90static void __noreturn cfe_linux_halt(void) 91{ 92 static const int one = 1; 93 94 cfe_linux_exit((void *)&one); 95} 96 97static __init void prom_meminit(void) 98{ 99 u64 addr, size, type; /* regardless of PHYS_ADDR_T_64BIT */ 100 int mem_flags = 0; 101 unsigned int idx; 102 int rd_flag; 103#ifdef CONFIG_BLK_DEV_INITRD 104 unsigned long initrd_pstart; 105 unsigned long initrd_pend; 106 107 initrd_pstart = CPHYSADDR(initrd_start); 108 initrd_pend = CPHYSADDR(initrd_end); 109 if (initrd_start && 110 ((initrd_pstart > MAX_RAM_SIZE) 111 || (initrd_pend > MAX_RAM_SIZE))) { 112 panic("initrd out of addressable memory"); 113 } 114 115#endif /* INITRD */ 116 117 for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE; 118 idx++) { 119 rd_flag = 0; 120 if (type == CFE_MI_AVAILABLE) { 121 /* 122 * See if this block contains (any portion of) the 123 * ramdisk 124 */ 125#ifdef CONFIG_BLK_DEV_INITRD 126 if (initrd_start) { 127 if ((initrd_pstart > addr) && 128 (initrd_pstart < (addr + size))) { 129 add_memory_region(addr, 130 initrd_pstart - addr, 131 BOOT_MEM_RAM); 132 rd_flag = 1; 133 } 134 if ((initrd_pend > addr) && 135 (initrd_pend < (addr + size))) { 136 add_memory_region(initrd_pend, 137 (addr + size) - initrd_pend, 138 BOOT_MEM_RAM); 139 rd_flag = 1; 140 } 141 } 142#endif 143 if (!rd_flag) { 144 if (addr > MAX_RAM_SIZE) 145 continue; 146 if (addr+size > MAX_RAM_SIZE) 147 size = MAX_RAM_SIZE - (addr+size) + 1; 148 /* 149 * memcpy/__copy_user prefetch, which 150 * will cause a bus error for 151 * KSEG/KUSEG addrs not backed by RAM. 152 * Hence, reserve some padding for the 153 * prefetch distance. 154 */ 155 if (size > 512) 156 size -= 512; 157 add_memory_region(addr, size, BOOT_MEM_RAM); 158 } 159 board_mem_region_addrs[board_mem_region_count] = addr; 160 board_mem_region_sizes[board_mem_region_count] = size; 161 board_mem_region_count++; 162 if (board_mem_region_count == 163 SIBYTE_MAX_MEM_REGIONS) { 164 /* 165 * Too many regions. Need to configure more 166 */ 167 while(1); 168 } 169 } 170 } 171#ifdef CONFIG_BLK_DEV_INITRD 172 if (initrd_start) { 173 add_memory_region(initrd_pstart, initrd_pend - initrd_pstart, 174 BOOT_MEM_RESERVED); 175 } 176#endif 177} 178 179#ifdef CONFIG_BLK_DEV_INITRD 180static int __init initrd_setup(char *str) 181{ 182 char rdarg[64]; 183 int idx; 184 char *tmp, *endptr; 185 unsigned long initrd_size; 186 187 /* Make a copy of the initrd argument so we can smash it up here */ 188 for (idx = 0; idx < sizeof(rdarg)-1; idx++) { 189 if (!str[idx] || (str[idx] == ' ')) break; 190 rdarg[idx] = str[idx]; 191 } 192 193 rdarg[idx] = 0; 194 str = rdarg; 195 196 /* 197 *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>" 198 * e.g. initrd=3abfd@80010000. This is set up by the loader. 199 */ 200 for (tmp = str; *tmp != '@'; tmp++) { 201 if (!*tmp) { 202 goto fail; 203 } 204 } 205 *tmp = 0; 206 tmp++; 207 if (!*tmp) { 208 goto fail; 209 } 210 initrd_size = simple_strtoul(str, &endptr, 16); 211 if (*endptr) { 212 *(tmp-1) = '@'; 213 goto fail; 214 } 215 *(tmp-1) = '@'; 216 initrd_start = simple_strtoul(tmp, &endptr, 16); 217 if (*endptr) { 218 goto fail; 219 } 220 initrd_end = initrd_start + initrd_size; 221 printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start); 222 return 1; 223 fail: 224 printk("Bad initrd argument. Disabling initrd\n"); 225 initrd_start = 0; 226 initrd_end = 0; 227 return 1; 228} 229 230#endif 231 232extern struct plat_smp_ops sb_smp_ops; 233extern struct plat_smp_ops bcm1480_smp_ops; 234 235/* 236 * prom_init is called just after the cpu type is determined, from setup_arch() 237 */ 238void __init prom_init(void) 239{ 240 uint64_t cfe_ept, cfe_handle; 241 unsigned int cfe_eptseal; 242 int argc = fw_arg0; 243 char **envp = (char **) fw_arg2; 244 int *prom_vec = (int *) fw_arg3; 245 246 _machine_restart = cfe_linux_restart; 247 _machine_halt = cfe_linux_halt; 248 pm_power_off = cfe_linux_halt; 249 250 /* 251 * Check if a loader was used; if NOT, the 4 arguments are 252 * what CFE gives us (handle, 0, EPT and EPTSEAL) 253 */ 254 if (argc < 0) { 255 cfe_handle = (uint64_t)(long)argc; 256 cfe_ept = (long)envp; 257 cfe_eptseal = (uint32_t)(unsigned long)prom_vec; 258 } else { 259 if ((int32_t)(long)prom_vec < 0) { 260 /* 261 * Old loader; all it gives us is the handle, 262 * so use the "known" entrypoint and assume 263 * the seal. 264 */ 265 cfe_handle = (uint64_t)(long)prom_vec; 266 cfe_ept = (uint64_t)((int32_t)0x9fc00500); 267 cfe_eptseal = CFE_EPTSEAL; 268 } else { 269 /* 270 * Newer loaders bundle the handle/ept/eptseal 271 * Note: prom_vec is in the loader's useg 272 * which is still alive in the TLB. 273 */ 274 cfe_handle = (uint64_t)((int32_t *)prom_vec)[0]; 275 cfe_ept = (uint64_t)((int32_t *)prom_vec)[2]; 276 cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3]; 277 } 278 } 279 if (cfe_eptseal != CFE_EPTSEAL) { 280 /* too early for panic to do any good */ 281 printk("CFE's entrypoint seal doesn't match. Spinning."); 282 while (1) ; 283 } 284 cfe_init(cfe_handle, cfe_ept); 285 /* 286 * Get the handle for (at least) prom_putchar, possibly for 287 * boot console 288 */ 289 cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); 290 if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, COMMAND_LINE_SIZE) < 0) { 291 if (argc >= 0) { 292 /* The loader should have set the command line */ 293 /* too early for panic to do any good */ 294 printk("LINUX_CMDLINE not defined in cfe."); 295 while (1) ; 296 } 297 } 298 299#ifdef CONFIG_BLK_DEV_INITRD 300 { 301 char *ptr; 302 /* Need to find out early whether we've got an initrd. So scan 303 the list looking now */ 304 for (ptr = arcs_cmdline; *ptr; ptr++) { 305 while (*ptr == ' ') { 306 ptr++; 307 } 308 if (!strncmp(ptr, "initrd=", 7)) { 309 initrd_setup(ptr+7); 310 break; 311 } else { 312 while (*ptr && (*ptr != ' ')) { 313 ptr++; 314 } 315 } 316 } 317 } 318#endif /* CONFIG_BLK_DEV_INITRD */ 319 320 /* Not sure this is needed, but it's the safe way. */ 321 arcs_cmdline[COMMAND_LINE_SIZE-1] = 0; 322 323 prom_meminit(); 324 325#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250) 326 register_smp_ops(&sb_smp_ops); 327#endif 328#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) 329 register_smp_ops(&bcm1480_smp_ops); 330#endif 331} 332 333void __init prom_free_prom_memory(void) 334{ 335 /* Not sure what I'm supposed to do here. Nothing, I think */ 336} 337 338void prom_putchar(char c) 339{ 340 int ret; 341 342 while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0) 343 ; 344} 345