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