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 32#include "cfe_api.h" 33#include "cfe_error.h" 34 35/* Max ram addressable in 32-bit segments */ 36#ifdef CONFIG_64BIT 37#define MAX_RAM_SIZE (~0ULL) 38#else 39#ifdef CONFIG_HIGHMEM 40#ifdef CONFIG_64BIT_PHYS_ADDR 41#define MAX_RAM_SIZE (~0ULL) 42#else 43#define MAX_RAM_SIZE (0xffffffffULL) 44#endif 45#else 46#define MAX_RAM_SIZE (0x1fffffffULL) 47#endif 48#endif 49 50#define SIBYTE_MAX_MEM_REGIONS 8 51phys_t board_mem_region_addrs[SIBYTE_MAX_MEM_REGIONS]; 52phys_t board_mem_region_sizes[SIBYTE_MAX_MEM_REGIONS]; 53unsigned int board_mem_region_count; 54 55int cfe_cons_handle; 56 57#ifdef CONFIG_BLK_DEV_INITRD 58extern unsigned long initrd_start, initrd_end; 59#endif 60 61#ifdef CONFIG_KGDB 62extern int kgdb_port; 63#endif 64 65static void ATTRIB_NORET cfe_linux_exit(void *arg) 66{ 67 int warm = *(int *)arg; 68 69 if (smp_processor_id()) { 70 static int reboot_smp; 71 72 /* Don't repeat the process from another CPU */ 73 if (!reboot_smp) { 74 /* Get CPU 0 to do the cfe_exit */ 75 reboot_smp = 1; 76 smp_call_function(cfe_linux_exit, arg, 1, 0); 77 } 78 } else { 79 printk("Passing control back to CFE...\n"); 80 cfe_exit(warm, 0); 81 printk("cfe_exit returned??\n"); 82 } 83 while (1); 84} 85 86static void ATTRIB_NORET cfe_linux_restart(char *command) 87{ 88 static const int zero; 89 90 cfe_linux_exit((void *)&zero); 91} 92 93static void ATTRIB_NORET cfe_linux_halt(void) 94{ 95 static const int one = 1; 96 97 cfe_linux_exit((void *)&one); 98} 99 100static __init void prom_meminit(void) 101{ 102 u64 addr, size, type; /* regardless of 64BIT_PHYS_ADDR */ 103 int mem_flags = 0; 104 unsigned int idx; 105 int rd_flag; 106#ifdef CONFIG_BLK_DEV_INITRD 107 unsigned long initrd_pstart; 108 unsigned long initrd_pend; 109 110 initrd_pstart = CPHYSADDR(initrd_start); 111 initrd_pend = CPHYSADDR(initrd_end); 112 if (initrd_start && 113 ((initrd_pstart > MAX_RAM_SIZE) 114 || (initrd_pend > MAX_RAM_SIZE))) { 115 panic("initrd out of addressable memory"); 116 } 117 118#endif /* INITRD */ 119 120 for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE; 121 idx++) { 122 rd_flag = 0; 123 if (type == CFE_MI_AVAILABLE) { 124 /* 125 * See if this block contains (any portion of) the 126 * ramdisk 127 */ 128#ifdef CONFIG_BLK_DEV_INITRD 129 if (initrd_start) { 130 if ((initrd_pstart > addr) && 131 (initrd_pstart < (addr + size))) { 132 add_memory_region(addr, 133 initrd_pstart - addr, 134 BOOT_MEM_RAM); 135 rd_flag = 1; 136 } 137 if ((initrd_pend > addr) && 138 (initrd_pend < (addr + size))) { 139 add_memory_region(initrd_pend, 140 (addr + size) - initrd_pend, 141 BOOT_MEM_RAM); 142 rd_flag = 1; 143 } 144 } 145#endif 146 if (!rd_flag) { 147 if (addr > MAX_RAM_SIZE) 148 continue; 149 if (addr+size > MAX_RAM_SIZE) 150 size = MAX_RAM_SIZE - (addr+size) + 1; 151 /* 152 * memcpy/__copy_user prefetch, which 153 * will cause a bus error for 154 * KSEG/KUSEG addrs not backed by RAM. 155 * Hence, reserve some padding for the 156 * prefetch distance. 157 */ 158 if (size > 512) 159 size -= 512; 160 add_memory_region(addr, size, BOOT_MEM_RAM); 161 } 162 board_mem_region_addrs[board_mem_region_count] = addr; 163 board_mem_region_sizes[board_mem_region_count] = size; 164 board_mem_region_count++; 165 if (board_mem_region_count == 166 SIBYTE_MAX_MEM_REGIONS) { 167 /* 168 * Too many regions. Need to configure more 169 */ 170 while(1); 171 } 172 } 173 } 174#ifdef CONFIG_BLK_DEV_INITRD 175 if (initrd_start) { 176 add_memory_region(initrd_pstart, initrd_pend - initrd_pstart, 177 BOOT_MEM_RESERVED); 178 } 179#endif 180} 181 182#ifdef CONFIG_BLK_DEV_INITRD 183static int __init initrd_setup(char *str) 184{ 185 char rdarg[64]; 186 int idx; 187 char *tmp, *endptr; 188 unsigned long initrd_size; 189 190 /* Make a copy of the initrd argument so we can smash it up here */ 191 for (idx = 0; idx < sizeof(rdarg)-1; idx++) { 192 if (!str[idx] || (str[idx] == ' ')) break; 193 rdarg[idx] = str[idx]; 194 } 195 196 rdarg[idx] = 0; 197 str = rdarg; 198 199 /* 200 *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>" 201 * e.g. initrd=3abfd@80010000. This is set up by the loader. 202 */ 203 for (tmp = str; *tmp != '@'; tmp++) { 204 if (!*tmp) { 205 goto fail; 206 } 207 } 208 *tmp = 0; 209 tmp++; 210 if (!*tmp) { 211 goto fail; 212 } 213 initrd_size = simple_strtoul(str, &endptr, 16); 214 if (*endptr) { 215 *(tmp-1) = '@'; 216 goto fail; 217 } 218 *(tmp-1) = '@'; 219 initrd_start = simple_strtoul(tmp, &endptr, 16); 220 if (*endptr) { 221 goto fail; 222 } 223 initrd_end = initrd_start + initrd_size; 224 printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start); 225 return 1; 226 fail: 227 printk("Bad initrd argument. Disabling initrd\n"); 228 initrd_start = 0; 229 initrd_end = 0; 230 return 1; 231} 232 233#endif 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#ifdef CONFIG_KGDB 246 char *arg; 247#endif 248 249 _machine_restart = cfe_linux_restart; 250 _machine_halt = cfe_linux_halt; 251 pm_power_off = cfe_linux_halt; 252 253 /* 254 * Check if a loader was used; if NOT, the 4 arguments are 255 * what CFE gives us (handle, 0, EPT and EPTSEAL) 256 */ 257 if (argc < 0) { 258 cfe_handle = (uint64_t)(long)argc; 259 cfe_ept = (long)envp; 260 cfe_eptseal = (uint32_t)(unsigned long)prom_vec; 261 } else { 262 if ((int32_t)(long)prom_vec < 0) { 263 /* 264 * Old loader; all it gives us is the handle, 265 * so use the "known" entrypoint and assume 266 * the seal. 267 */ 268 cfe_handle = (uint64_t)(long)prom_vec; 269 cfe_ept = (uint64_t)((int32_t)0x9fc00500); 270 cfe_eptseal = CFE_EPTSEAL; 271 } else { 272 /* 273 * Newer loaders bundle the handle/ept/eptseal 274 * Note: prom_vec is in the loader's useg 275 * which is still alive in the TLB. 276 */ 277 cfe_handle = (uint64_t)((int32_t *)prom_vec)[0]; 278 cfe_ept = (uint64_t)((int32_t *)prom_vec)[2]; 279 cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3]; 280 } 281 } 282 if (cfe_eptseal != CFE_EPTSEAL) { 283 /* too early for panic to do any good */ 284 printk("CFE's entrypoint seal doesn't match. Spinning."); 285 while (1) ; 286 } 287 cfe_init(cfe_handle, cfe_ept); 288 /* 289 * Get the handle for (at least) prom_putchar, possibly for 290 * boot console 291 */ 292 cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); 293 if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, CL_SIZE) < 0) { 294 if (argc < 0) { 295 /* 296 * It's OK for direct boot to not provide a 297 * command line 298 */ 299 strcpy(arcs_cmdline, "root=/dev/ram0 "); 300#ifdef CONFIG_SIBYTE_PTSWARM 301 strcat(arcs_cmdline, "console=ttyS0,115200 "); 302#endif 303 } else { 304 /* The loader should have set the command line */ 305 /* too early for panic to do any good */ 306 printk("LINUX_CMDLINE not defined in cfe."); 307 while (1) ; 308 } 309 } 310 311#ifdef CONFIG_KGDB 312 if ((arg = strstr(arcs_cmdline,"kgdb=duart")) != NULL) 313 kgdb_port = (arg[10] == '0') ? 0 : 1; 314 else 315 kgdb_port = 1; 316#endif 317 318#ifdef CONFIG_BLK_DEV_INITRD 319 { 320 char *ptr; 321 /* Need to find out early whether we've got an initrd. So scan 322 the list looking now */ 323 for (ptr = arcs_cmdline; *ptr; ptr++) { 324 while (*ptr == ' ') { 325 ptr++; 326 } 327 if (!strncmp(ptr, "initrd=", 7)) { 328 initrd_setup(ptr+7); 329 break; 330 } else { 331 while (*ptr && (*ptr != ' ')) { 332 ptr++; 333 } 334 } 335 } 336 } 337#endif /* CONFIG_BLK_DEV_INITRD */ 338 339 /* Not sure this is needed, but it's the safe way. */ 340 arcs_cmdline[CL_SIZE-1] = 0; 341 342 mips_machgroup = MACH_GROUP_SIBYTE; 343 prom_meminit(); 344} 345 346void __init prom_free_prom_memory(void) 347{ 348 /* Not sure what I'm supposed to do here. Nothing, I think */ 349} 350 351void prom_putchar(char c) 352{ 353 int ret; 354 355 while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0) 356 ; 357} 358