xref: /netbsd-src/sys/arch/riscv/fdt/cpu_fdt.c (revision f801e0a3ea5e07f9ffe215c2f37620b6448d0ca0)
1*f801e0a3Sskrll /* $NetBSD: cpu_fdt.c,v 1.4 2024/01/01 13:51:56 skrll Exp $ */
275b842b8Sskrll 
375b842b8Sskrll /*-
475b842b8Sskrll  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
575b842b8Sskrll  * All rights reserved.
675b842b8Sskrll  *
775b842b8Sskrll  * Redistribution and use in source and binary forms, with or without
875b842b8Sskrll  * modification, are permitted provided that the following conditions
975b842b8Sskrll  * are met:
1075b842b8Sskrll  * 1. Redistributions of source code must retain the above copyright
1175b842b8Sskrll  *    notice, this list of conditions and the following disclaimer.
1275b842b8Sskrll  * 2. Redistributions in binary form must reproduce the above copyright
1375b842b8Sskrll  *    notice, this list of conditions and the following disclaimer in the
1475b842b8Sskrll  *    documentation and/or other materials provided with the distribution.
1575b842b8Sskrll  *
1675b842b8Sskrll  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1775b842b8Sskrll  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1875b842b8Sskrll  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1975b842b8Sskrll  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2075b842b8Sskrll  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2175b842b8Sskrll  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2275b842b8Sskrll  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2375b842b8Sskrll  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2475b842b8Sskrll  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2575b842b8Sskrll  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2675b842b8Sskrll  * SUCH DAMAGE.
2775b842b8Sskrll  */
2875b842b8Sskrll 
2975b842b8Sskrll #include "opt_multiprocessor.h"
3075b842b8Sskrll 
3175b842b8Sskrll #include <sys/cdefs.h>
32*f801e0a3Sskrll __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.4 2024/01/01 13:51:56 skrll Exp $");
3375b842b8Sskrll 
3475b842b8Sskrll #include <sys/param.h>
3508c3a075Sskrll #include <sys/cpu.h>
3675b842b8Sskrll 
3775b842b8Sskrll #include <dev/fdt/fdtvar.h>
3875b842b8Sskrll 
3908c3a075Sskrll #include <riscv/cpufunc.h>
4075b842b8Sskrll #include <riscv/cpuvar.h>
4108c3a075Sskrll #include <riscv/machdep.h>
4208c3a075Sskrll #include <riscv/sbi.h>
4308c3a075Sskrll 
4408c3a075Sskrll #include <riscv/fdt/riscv_fdtvar.h>
4508c3a075Sskrll 
46*f801e0a3Sskrll bool
riscv_fdt_cpu_okay(const int node)47*f801e0a3Sskrll riscv_fdt_cpu_okay(const int node)
4808c3a075Sskrll {
4908c3a075Sskrll 	const char *s;
5008c3a075Sskrll 
51*f801e0a3Sskrll 	s = fdtbus_get_string(node, "device_type");
5208c3a075Sskrll 	if (!s || strcmp(s, "cpu") != 0)
5308c3a075Sskrll 		return false;
5408c3a075Sskrll 
55*f801e0a3Sskrll 	s = fdtbus_get_string(node, "status");
5608c3a075Sskrll 	if (s) {
5708c3a075Sskrll 		if (strcmp(s, "okay") == 0)
5808c3a075Sskrll 			return true;
5908c3a075Sskrll 		if (strcmp(s, "disabled") == 0)
6008c3a075Sskrll 			return false;
6108c3a075Sskrll 		return false;
6208c3a075Sskrll 	} else {
6308c3a075Sskrll 		return true;
6408c3a075Sskrll 	}
6508c3a075Sskrll }
6608c3a075Sskrll 
6708c3a075Sskrll void
riscv_fdt_cpu_bootstrap(void)6808c3a075Sskrll riscv_fdt_cpu_bootstrap(void)
6908c3a075Sskrll {
7008c3a075Sskrll 	const int cpus = OF_finddevice("/cpus");
7108c3a075Sskrll 	if (cpus == -1) {
7208c3a075Sskrll 		aprint_error("%s: no /cpus node found\n", __func__);
7308c3a075Sskrll 		return;
7408c3a075Sskrll 	}
7508c3a075Sskrll 
766345bad4Sskrll 	/* Count harts and add hart index numbers to the cpu_hartindex array */
776345bad4Sskrll 	u_int cpuindex = 1;
7808c3a075Sskrll 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
7908c3a075Sskrll 		if (!riscv_fdt_cpu_okay(child))
8008c3a075Sskrll 			continue;
8108c3a075Sskrll 
8208c3a075Sskrll 		uint64_t reg;
8308c3a075Sskrll 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
8408c3a075Sskrll 			continue;
8508c3a075Sskrll 
8608c3a075Sskrll 		const cpuid_t hartid = reg;
876345bad4Sskrll 		if (hartid > MAXCPUS) {
886345bad4Sskrll 			aprint_error("hart id too big %lu (%u)", hartid,
896345bad4Sskrll 			    MAXCPUS);
906345bad4Sskrll 			continue;
916345bad4Sskrll 		}
9208c3a075Sskrll 
9308c3a075Sskrll 		struct sbiret sbiret = sbi_hart_get_status(hartid);
9408c3a075Sskrll 		switch (sbiret.error) {
9508c3a075Sskrll 		case SBI_ERR_INVALID_PARAM:
9608c3a075Sskrll 			aprint_error("Unknown hart id %lx", hartid);
9708c3a075Sskrll 			continue;
9808c3a075Sskrll 		case SBI_SUCCESS:
9908c3a075Sskrll 			break;
10008c3a075Sskrll 		default:
10108c3a075Sskrll 			aprint_error("Unexpected error (%ld) from get_status",
10208c3a075Sskrll 			    sbiret.error);
10308c3a075Sskrll 		}
10408c3a075Sskrll 
10508c3a075Sskrll 		/* Assume the BP is the only one started. */
10608c3a075Sskrll 		if (sbiret.value == SBI_HART_STARTED) {
1076345bad4Sskrll 			if (cpu_bphartid != ~0UL) {
10808c3a075Sskrll 				panic("more than 1 hart started");
10908c3a075Sskrll 			}
1106345bad4Sskrll 			cpu_bphartid = hartid;
1116345bad4Sskrll 			cpu_hartindex[hartid] = 0;
11208c3a075Sskrll 			continue;
11308c3a075Sskrll 		}
11408c3a075Sskrll 
11508c3a075Sskrll 		KASSERT(cpuindex < MAXCPUS);
1166345bad4Sskrll 		cpu_hartindex[hartid] = cpuindex++;
1176345bad4Sskrll 	}
1186345bad4Sskrll }
11908c3a075Sskrll 
12008c3a075Sskrll int
riscv_fdt_cpu_mpstart(void)12108c3a075Sskrll riscv_fdt_cpu_mpstart(void)
12208c3a075Sskrll {
12308c3a075Sskrll 	int ret = 0;
12408c3a075Sskrll #ifdef MULTIPROCESSOR
12508c3a075Sskrll 	const int cpus = OF_finddevice("/cpus");
12608c3a075Sskrll 	if (cpus == -1) {
12708c3a075Sskrll 		aprint_error("%s: no /cpus node found\n", __func__);
12808c3a075Sskrll 		return 0;
12908c3a075Sskrll 	}
13008c3a075Sskrll 
13108c3a075Sskrll 	/* BootAPs */
1326345bad4Sskrll 	u_int cpuindex = 1;
13308c3a075Sskrll 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
13408c3a075Sskrll 		if (!riscv_fdt_cpu_okay(child))
13508c3a075Sskrll 			continue;
13608c3a075Sskrll 
13708c3a075Sskrll 		uint64_t reg;
13808c3a075Sskrll 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
13908c3a075Sskrll 			continue;
14008c3a075Sskrll 
1416345bad4Sskrll 		const cpuid_t hartid = reg;
1426345bad4Sskrll 		if (hartid == cpu_bphartid)
14308c3a075Sskrll 			continue;		/* BP already started */
14408c3a075Sskrll 
14508c3a075Sskrll 		const paddr_t entry = KERN_VTOPHYS(cpu_mpstart);
1466345bad4Sskrll 		struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex);
14708c3a075Sskrll 		switch (sbiret.error) {
14808c3a075Sskrll 		case SBI_SUCCESS:
14908c3a075Sskrll 			break;
15008c3a075Sskrll 		case SBI_ERR_INVALID_ADDRESS:
15108c3a075Sskrll 			break;
15208c3a075Sskrll 		case SBI_ERR_INVALID_PARAM:
15308c3a075Sskrll 			break;
15408c3a075Sskrll 		case SBI_ERR_ALREADY_AVAILABLE:
15508c3a075Sskrll 			break;
15608c3a075Sskrll 		case SBI_ERR_FAILED:
15708c3a075Sskrll 			break;
15808c3a075Sskrll 		default:
15908c3a075Sskrll 			aprint_error("%s: failed to enable CPU %#lx\n",
16008c3a075Sskrll 			    __func__, hartid);
16108c3a075Sskrll 		}
16208c3a075Sskrll 
16308c3a075Sskrll 		size_t i;
16408c3a075Sskrll 		/* Wait for AP to start */
16508c3a075Sskrll 		for (i = 0x10000000; i > 0; i--) {
16608c3a075Sskrll 			if (cpu_hatched_p(cpuindex))
16708c3a075Sskrll 				break;
16808c3a075Sskrll 		}
16908c3a075Sskrll 
17008c3a075Sskrll 		if (i == 0) {
17108c3a075Sskrll 			ret++;
1726345bad4Sskrll 			aprint_error("hart%ld: WARNING: AP %u failed to start\n",
1736345bad4Sskrll 			    hartid, cpuindex);
17408c3a075Sskrll 		}
17508c3a075Sskrll 
17608c3a075Sskrll 		cpuindex++;
17708c3a075Sskrll 	}
17808c3a075Sskrll #else
17908c3a075Sskrll 	aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__);
18008c3a075Sskrll #endif /* MULTIPROCESSOR */
18108c3a075Sskrll 	return ret;
18208c3a075Sskrll }
18375b842b8Sskrll 
18475b842b8Sskrll static int
cpu_fdt_match(device_t parent,cfdata_t cf,void * aux)18575b842b8Sskrll cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
18675b842b8Sskrll {
18775b842b8Sskrll 	struct fdt_attach_args * const faa = aux;
18875b842b8Sskrll 	const int phandle = faa->faa_phandle;
18975b842b8Sskrll 	const char *device_type;
19075b842b8Sskrll 
19175b842b8Sskrll 	device_type = fdtbus_get_string(phandle, "device_type");
19275b842b8Sskrll 	return device_type != NULL && strcmp(device_type, "cpu") == 0;
19375b842b8Sskrll }
19475b842b8Sskrll 
19575b842b8Sskrll static void
cpu_fdt_attach(device_t parent,device_t self,void * aux)19675b842b8Sskrll cpu_fdt_attach(device_t parent, device_t self, void *aux)
19775b842b8Sskrll {
19875b842b8Sskrll 	struct fdt_attach_args * const faa = aux;
19975b842b8Sskrll 	const int phandle = faa->faa_phandle;
2006345bad4Sskrll 	bus_addr_t hartid;
20175b842b8Sskrll 
2026345bad4Sskrll 
2036345bad4Sskrll 	if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0)
2046345bad4Sskrll 		hartid = 0;
20575b842b8Sskrll 
20675b842b8Sskrll 	/* Attach the CPU */
2076345bad4Sskrll 	cpu_attach(self, hartid);
20875b842b8Sskrll 
20975b842b8Sskrll 	fdt_add_bus(self, phandle, faa);
21075b842b8Sskrll }
21175b842b8Sskrll 
21275b842b8Sskrll CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
213