xref: /netbsd-src/sys/arch/riscv/fdt/cpu_fdt.c (revision 5971e316fdea024efff6be8f03536623db06833e)
1 /* $NetBSD: cpu_fdt.c,v 1.3 2023/09/03 08:48:19 skrll Exp $ */
2 
3 /*-
4  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include "opt_multiprocessor.h"
30 
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: cpu_fdt.c,v 1.3 2023/09/03 08:48:19 skrll Exp $");
33 
34 #include <sys/param.h>
35 #include <sys/cpu.h>
36 
37 #include <dev/fdt/fdtvar.h>
38 
39 #include <riscv/cpufunc.h>
40 #include <riscv/cpuvar.h>
41 #include <riscv/machdep.h>
42 #include <riscv/sbi.h>
43 
44 #include <riscv/fdt/riscv_fdtvar.h>
45 
46 
47 static bool
48 riscv_fdt_cpu_okay(const int child)
49 {
50 	const char *s;
51 
52 	s = fdtbus_get_string(child, "device_type");
53 	if (!s || strcmp(s, "cpu") != 0)
54 		return false;
55 
56 	s = fdtbus_get_string(child, "status");
57 	if (s) {
58 		if (strcmp(s, "okay") == 0)
59 			return true;
60 		if (strcmp(s, "disabled") == 0)
61 			return false;
62 		return false;
63 	} else {
64 		return true;
65 	}
66 }
67 
68 void
69 riscv_fdt_cpu_bootstrap(void)
70 {
71 	const int cpus = OF_finddevice("/cpus");
72 	if (cpus == -1) {
73 		aprint_error("%s: no /cpus node found\n", __func__);
74 		return;
75 	}
76 
77 	/* Count harts and add hart index numbers to the cpu_hartindex array */
78 	u_int cpuindex = 1;
79 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
80 		if (!riscv_fdt_cpu_okay(child))
81 			continue;
82 
83 		uint64_t reg;
84 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
85 			continue;
86 
87 		const cpuid_t hartid = reg;
88 		if (hartid > MAXCPUS) {
89 			aprint_error("hart id too big %lu (%u)", hartid,
90 			    MAXCPUS);
91 			continue;
92 		}
93 
94 		struct sbiret sbiret = sbi_hart_get_status(hartid);
95 		switch (sbiret.error) {
96 		case SBI_ERR_INVALID_PARAM:
97 			aprint_error("Unknown hart id %lx", hartid);
98 			continue;
99 		case SBI_SUCCESS:
100 			break;
101 		default:
102 			aprint_error("Unexpected error (%ld) from get_status",
103 			    sbiret.error);
104 		}
105 
106 		/* Assume the BP is the only one started. */
107 		if (sbiret.value == SBI_HART_STARTED) {
108 			if (cpu_bphartid != ~0UL) {
109 				panic("more than 1 hart started");
110 			}
111 			cpu_bphartid = hartid;
112 			cpu_hartindex[hartid] = 0;
113 			continue;
114 		}
115 
116 		KASSERT(cpuindex < MAXCPUS);
117 		cpu_hartindex[hartid] = cpuindex++;
118 	}
119 }
120 
121 int
122 riscv_fdt_cpu_mpstart(void)
123 {
124 	int ret = 0;
125 #ifdef MULTIPROCESSOR
126 	const int cpus = OF_finddevice("/cpus");
127 	if (cpus == -1) {
128 		aprint_error("%s: no /cpus node found\n", __func__);
129 		return 0;
130 	}
131 
132 	/* BootAPs */
133 	u_int cpuindex = 1;
134 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
135 		if (!riscv_fdt_cpu_okay(child))
136 			continue;
137 
138 		uint64_t reg;
139 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
140 			continue;
141 
142 		const cpuid_t hartid = reg;
143 		if (hartid == cpu_bphartid)
144 			continue;		/* BP already started */
145 
146 		const paddr_t entry = KERN_VTOPHYS(cpu_mpstart);
147 		struct sbiret sbiret = sbi_hart_start(hartid, entry, cpuindex);
148 		switch (sbiret.error) {
149 		case SBI_SUCCESS:
150 			break;
151 		case SBI_ERR_INVALID_ADDRESS:
152 			break;
153 		case SBI_ERR_INVALID_PARAM:
154 			break;
155 		case SBI_ERR_ALREADY_AVAILABLE:
156 			break;
157 		case SBI_ERR_FAILED:
158 			break;
159 		default:
160 			aprint_error("%s: failed to enable CPU %#lx\n",
161 			    __func__, hartid);
162 		}
163 
164 		size_t i;
165 		/* Wait for AP to start */
166 		for (i = 0x10000000; i > 0; i--) {
167 			if (cpu_hatched_p(cpuindex))
168 				break;
169 		}
170 
171 		if (i == 0) {
172 			ret++;
173 			aprint_error("hart%ld: WARNING: AP %u failed to start\n",
174 			    hartid, cpuindex);
175 		}
176 
177 		cpuindex++;
178 	}
179 #else
180 	aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__);
181 #endif /* MULTIPROCESSOR */
182 	return ret;
183 }
184 
185 static int
186 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
187 {
188 	struct fdt_attach_args * const faa = aux;
189 	const int phandle = faa->faa_phandle;
190 	const char *device_type;
191 
192 	device_type = fdtbus_get_string(phandle, "device_type");
193 	return device_type != NULL && strcmp(device_type, "cpu") == 0;
194 }
195 
196 static void
197 cpu_fdt_attach(device_t parent, device_t self, void *aux)
198 {
199 	struct fdt_attach_args * const faa = aux;
200 	const int phandle = faa->faa_phandle;
201 	bus_addr_t hartid;
202 
203 
204 	if (fdtbus_get_reg(phandle, 0, &hartid, NULL) != 0)
205 		hartid = 0;
206 
207 	/* Attach the CPU */
208 	cpu_attach(self, hartid);
209 
210 	fdt_add_bus(self, phandle, faa);
211 }
212 
213 CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
214