162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/init.h> 762306a36Sopenharmony_ci#include <linux/kernel.h> 862306a36Sopenharmony_ci#include <linux/linkage.h> 962306a36Sopenharmony_ci#include <linux/mm.h> 1062306a36Sopenharmony_ci#include <linux/memblock.h> 1162306a36Sopenharmony_ci#include <linux/pm.h> 1262306a36Sopenharmony_ci#include <linux/smp.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <asm/bootinfo.h> 1562306a36Sopenharmony_ci#include <asm/reboot.h> 1662306a36Sopenharmony_ci#include <asm/setup.h> 1762306a36Sopenharmony_ci#include <asm/sibyte/board.h> 1862306a36Sopenharmony_ci#include <asm/smp-ops.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <asm/fw/cfe/cfe_api.h> 2162306a36Sopenharmony_ci#include <asm/fw/cfe/cfe_error.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Max ram addressable in 32-bit segments */ 2462306a36Sopenharmony_ci#ifdef CONFIG_64BIT 2562306a36Sopenharmony_ci#define MAX_RAM_SIZE (~0ULL) 2662306a36Sopenharmony_ci#else 2762306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM 2862306a36Sopenharmony_ci#ifdef CONFIG_PHYS_ADDR_T_64BIT 2962306a36Sopenharmony_ci#define MAX_RAM_SIZE (~0ULL) 3062306a36Sopenharmony_ci#else 3162306a36Sopenharmony_ci#define MAX_RAM_SIZE (0xffffffffULL) 3262306a36Sopenharmony_ci#endif 3362306a36Sopenharmony_ci#else 3462306a36Sopenharmony_ci#define MAX_RAM_SIZE (0x1fffffffULL) 3562306a36Sopenharmony_ci#endif 3662306a36Sopenharmony_ci#endif 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciint cfe_cons_handle; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 4162306a36Sopenharmony_ciextern unsigned long initrd_start, initrd_end; 4262306a36Sopenharmony_ci#endif 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic void __noreturn cfe_linux_exit(void *arg) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci int warm = *(int *)arg; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (smp_processor_id()) { 4962306a36Sopenharmony_ci static int reboot_smp; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci /* Don't repeat the process from another CPU */ 5262306a36Sopenharmony_ci if (!reboot_smp) { 5362306a36Sopenharmony_ci /* Get CPU 0 to do the cfe_exit */ 5462306a36Sopenharmony_ci reboot_smp = 1; 5562306a36Sopenharmony_ci smp_call_function(cfe_linux_exit, arg, 0); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci } else { 5862306a36Sopenharmony_ci printk("Passing control back to CFE...\n"); 5962306a36Sopenharmony_ci cfe_exit(warm, 0); 6062306a36Sopenharmony_ci printk("cfe_exit returned??\n"); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci while (1); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic void __noreturn cfe_linux_restart(char *command) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci static const int zero; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci cfe_linux_exit((void *)&zero); 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic void __noreturn cfe_linux_halt(void) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci static const int one = 1; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci cfe_linux_exit((void *)&one); 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic __init void prom_meminit(void) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci u64 addr, size, type; /* regardless of PHYS_ADDR_T_64BIT */ 8262306a36Sopenharmony_ci int mem_flags = 0; 8362306a36Sopenharmony_ci unsigned int idx; 8462306a36Sopenharmony_ci int rd_flag; 8562306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 8662306a36Sopenharmony_ci unsigned long initrd_pstart; 8762306a36Sopenharmony_ci unsigned long initrd_pend; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci initrd_pstart = CPHYSADDR(initrd_start); 9062306a36Sopenharmony_ci initrd_pend = CPHYSADDR(initrd_end); 9162306a36Sopenharmony_ci if (initrd_start && 9262306a36Sopenharmony_ci ((initrd_pstart > MAX_RAM_SIZE) 9362306a36Sopenharmony_ci || (initrd_pend > MAX_RAM_SIZE))) { 9462306a36Sopenharmony_ci panic("initrd out of addressable memory"); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#endif /* INITRD */ 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE; 10062306a36Sopenharmony_ci idx++) { 10162306a36Sopenharmony_ci rd_flag = 0; 10262306a36Sopenharmony_ci if (type == CFE_MI_AVAILABLE) { 10362306a36Sopenharmony_ci /* 10462306a36Sopenharmony_ci * See if this block contains (any portion of) the 10562306a36Sopenharmony_ci * ramdisk 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 10862306a36Sopenharmony_ci if (initrd_start) { 10962306a36Sopenharmony_ci if ((initrd_pstart > addr) && 11062306a36Sopenharmony_ci (initrd_pstart < (addr + size))) { 11162306a36Sopenharmony_ci memblock_add(addr, 11262306a36Sopenharmony_ci initrd_pstart - addr); 11362306a36Sopenharmony_ci rd_flag = 1; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci if ((initrd_pend > addr) && 11662306a36Sopenharmony_ci (initrd_pend < (addr + size))) { 11762306a36Sopenharmony_ci memblock_add(initrd_pend, 11862306a36Sopenharmony_ci (addr + size) - initrd_pend); 11962306a36Sopenharmony_ci rd_flag = 1; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci#endif 12362306a36Sopenharmony_ci if (!rd_flag) { 12462306a36Sopenharmony_ci if (addr > MAX_RAM_SIZE) 12562306a36Sopenharmony_ci continue; 12662306a36Sopenharmony_ci if (addr+size > MAX_RAM_SIZE) 12762306a36Sopenharmony_ci size = MAX_RAM_SIZE - (addr+size) + 1; 12862306a36Sopenharmony_ci /* 12962306a36Sopenharmony_ci * memcpy/__copy_user prefetch, which 13062306a36Sopenharmony_ci * will cause a bus error for 13162306a36Sopenharmony_ci * KSEG/KUSEG addrs not backed by RAM. 13262306a36Sopenharmony_ci * Hence, reserve some padding for the 13362306a36Sopenharmony_ci * prefetch distance. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_ci if (size > 512) 13662306a36Sopenharmony_ci size -= 512; 13762306a36Sopenharmony_ci memblock_add(addr, size); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 14262306a36Sopenharmony_ci if (initrd_start) { 14362306a36Sopenharmony_ci memblock_add(initrd_pstart, initrd_pend - initrd_pstart); 14462306a36Sopenharmony_ci memblock_reserve(initrd_pstart, initrd_pend - initrd_pstart); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci#endif 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 15062306a36Sopenharmony_cistatic int __init initrd_setup(char *str) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci char rdarg[64]; 15362306a36Sopenharmony_ci int idx; 15462306a36Sopenharmony_ci char *tmp, *endptr; 15562306a36Sopenharmony_ci unsigned long initrd_size; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* Make a copy of the initrd argument so we can smash it up here */ 15862306a36Sopenharmony_ci for (idx = 0; idx < sizeof(rdarg)-1; idx++) { 15962306a36Sopenharmony_ci if (!str[idx] || (str[idx] == ' ')) break; 16062306a36Sopenharmony_ci rdarg[idx] = str[idx]; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci rdarg[idx] = 0; 16462306a36Sopenharmony_ci str = rdarg; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* 16762306a36Sopenharmony_ci *Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>" 16862306a36Sopenharmony_ci * e.g. initrd=3abfd@80010000. This is set up by the loader. 16962306a36Sopenharmony_ci */ 17062306a36Sopenharmony_ci for (tmp = str; *tmp != '@'; tmp++) { 17162306a36Sopenharmony_ci if (!*tmp) { 17262306a36Sopenharmony_ci goto fail; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci *tmp = 0; 17662306a36Sopenharmony_ci tmp++; 17762306a36Sopenharmony_ci if (!*tmp) { 17862306a36Sopenharmony_ci goto fail; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci initrd_size = simple_strtoul(str, &endptr, 16); 18162306a36Sopenharmony_ci if (*endptr) { 18262306a36Sopenharmony_ci *(tmp-1) = '@'; 18362306a36Sopenharmony_ci goto fail; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci *(tmp-1) = '@'; 18662306a36Sopenharmony_ci initrd_start = simple_strtoul(tmp, &endptr, 16); 18762306a36Sopenharmony_ci if (*endptr) { 18862306a36Sopenharmony_ci goto fail; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci initrd_end = initrd_start + initrd_size; 19162306a36Sopenharmony_ci printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start); 19262306a36Sopenharmony_ci return 1; 19362306a36Sopenharmony_ci fail: 19462306a36Sopenharmony_ci printk("Bad initrd argument. Disabling initrd\n"); 19562306a36Sopenharmony_ci initrd_start = 0; 19662306a36Sopenharmony_ci initrd_end = 0; 19762306a36Sopenharmony_ci return 1; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci#endif 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ciextern const struct plat_smp_ops sb_smp_ops; 20362306a36Sopenharmony_ciextern const struct plat_smp_ops bcm1480_smp_ops; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/* 20662306a36Sopenharmony_ci * prom_init is called just after the cpu type is determined, from setup_arch() 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_civoid __init prom_init(void) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci uint64_t cfe_ept, cfe_handle; 21162306a36Sopenharmony_ci unsigned int cfe_eptseal; 21262306a36Sopenharmony_ci int argc = fw_arg0; 21362306a36Sopenharmony_ci char **envp = (char **) fw_arg2; 21462306a36Sopenharmony_ci int *prom_vec = (int *) fw_arg3; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci _machine_restart = cfe_linux_restart; 21762306a36Sopenharmony_ci _machine_halt = cfe_linux_halt; 21862306a36Sopenharmony_ci pm_power_off = cfe_linux_halt; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* 22162306a36Sopenharmony_ci * Check if a loader was used; if NOT, the 4 arguments are 22262306a36Sopenharmony_ci * what CFE gives us (handle, 0, EPT and EPTSEAL) 22362306a36Sopenharmony_ci */ 22462306a36Sopenharmony_ci if (argc < 0) { 22562306a36Sopenharmony_ci cfe_handle = (uint64_t)(long)argc; 22662306a36Sopenharmony_ci cfe_ept = (long)envp; 22762306a36Sopenharmony_ci cfe_eptseal = (uint32_t)(unsigned long)prom_vec; 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci if ((int32_t)(long)prom_vec < 0) { 23062306a36Sopenharmony_ci /* 23162306a36Sopenharmony_ci * Old loader; all it gives us is the handle, 23262306a36Sopenharmony_ci * so use the "known" entrypoint and assume 23362306a36Sopenharmony_ci * the seal. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci cfe_handle = (uint64_t)(long)prom_vec; 23662306a36Sopenharmony_ci cfe_ept = (uint64_t)((int32_t)0x9fc00500); 23762306a36Sopenharmony_ci cfe_eptseal = CFE_EPTSEAL; 23862306a36Sopenharmony_ci } else { 23962306a36Sopenharmony_ci /* 24062306a36Sopenharmony_ci * Newer loaders bundle the handle/ept/eptseal 24162306a36Sopenharmony_ci * Note: prom_vec is in the loader's useg 24262306a36Sopenharmony_ci * which is still alive in the TLB. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci cfe_handle = (uint64_t)((int32_t *)prom_vec)[0]; 24562306a36Sopenharmony_ci cfe_ept = (uint64_t)((int32_t *)prom_vec)[2]; 24662306a36Sopenharmony_ci cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3]; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci if (cfe_eptseal != CFE_EPTSEAL) { 25062306a36Sopenharmony_ci /* too early for panic to do any good */ 25162306a36Sopenharmony_ci printk("CFE's entrypoint seal doesn't match. Spinning."); 25262306a36Sopenharmony_ci while (1) ; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci cfe_init(cfe_handle, cfe_ept); 25562306a36Sopenharmony_ci /* 25662306a36Sopenharmony_ci * Get the handle for (at least) prom_putchar, possibly for 25762306a36Sopenharmony_ci * boot console 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); 26062306a36Sopenharmony_ci if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, COMMAND_LINE_SIZE) < 0) { 26162306a36Sopenharmony_ci if (argc >= 0) { 26262306a36Sopenharmony_ci /* The loader should have set the command line */ 26362306a36Sopenharmony_ci /* too early for panic to do any good */ 26462306a36Sopenharmony_ci printk("LINUX_CMDLINE not defined in cfe."); 26562306a36Sopenharmony_ci while (1) ; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#ifdef CONFIG_BLK_DEV_INITRD 27062306a36Sopenharmony_ci { 27162306a36Sopenharmony_ci char *ptr; 27262306a36Sopenharmony_ci /* Need to find out early whether we've got an initrd. So scan 27362306a36Sopenharmony_ci the list looking now */ 27462306a36Sopenharmony_ci for (ptr = arcs_cmdline; *ptr; ptr++) { 27562306a36Sopenharmony_ci while (*ptr == ' ') { 27662306a36Sopenharmony_ci ptr++; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci if (!strncmp(ptr, "initrd=", 7)) { 27962306a36Sopenharmony_ci initrd_setup(ptr+7); 28062306a36Sopenharmony_ci break; 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci while (*ptr && (*ptr != ' ')) { 28362306a36Sopenharmony_ci ptr++; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci#endif /* CONFIG_BLK_DEV_INITRD */ 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* Not sure this is needed, but it's the safe way. */ 29162306a36Sopenharmony_ci arcs_cmdline[COMMAND_LINE_SIZE-1] = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci prom_meminit(); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250) 29662306a36Sopenharmony_ci register_smp_ops(&sb_smp_ops); 29762306a36Sopenharmony_ci#endif 29862306a36Sopenharmony_ci#ifdef CONFIG_SIBYTE_BCM1x80 29962306a36Sopenharmony_ci register_smp_ops(&bcm1480_smp_ops); 30062306a36Sopenharmony_ci#endif 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_civoid prom_putchar(char c) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci int ret; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0) 30862306a36Sopenharmony_ci ; 30962306a36Sopenharmony_ci} 310