xref: /netbsd-src/sys/arch/riscv/fdt/cpu_fdt.c (revision 2dd295436a0082eb4f8d294f4aa73c223413d0f2)
1 /* $NetBSD: cpu_fdt.c,v 1.2 2023/06/12 19:04:13 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.2 2023/06/12 19:04:13 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 #ifdef MULTIPROCESSOR
48 static bool
49 riscv_fdt_cpu_okay(const int child)
50 {
51 	const char *s;
52 
53 	s = fdtbus_get_string(child, "device_type");
54 	if (!s || strcmp(s, "cpu") != 0)
55 		return false;
56 
57 	s = fdtbus_get_string(child, "status");
58 	if (s) {
59 		if (strcmp(s, "okay") == 0)
60 			return true;
61 		if (strcmp(s, "disabled") == 0)
62 			return false;
63 		return false;
64 	} else {
65 		return true;
66 	}
67 }
68 #endif /* MULTIPROCESSOR */
69 
70 void
71 riscv_fdt_cpu_bootstrap(void)
72 {
73 #ifdef MULTIPROCESSOR
74 
75 	const int cpus = OF_finddevice("/cpus");
76 	if (cpus == -1) {
77 		aprint_error("%s: no /cpus node found\n", __func__);
78 		riscv_cpu_max = 1;
79 		return;
80 	}
81 
82 	/* Count harts and add hart IDs to to cpu_hartid array */
83 	size_t cpuindex = 1;
84 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
85 		if (!riscv_fdt_cpu_okay(child))
86 			continue;
87 
88 		riscv_cpu_max++;
89 
90 		uint64_t reg;
91 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
92 			continue;
93 
94 		const cpuid_t hartid = reg;
95 
96 		struct sbiret sbiret = sbi_hart_get_status(hartid);
97 		switch (sbiret.error) {
98 		case SBI_ERR_INVALID_PARAM:
99 			aprint_error("Unknown hart id %lx", hartid);
100 			continue;
101 		case SBI_SUCCESS:
102 			break;
103 		default:
104 			aprint_error("Unexpected error (%ld) from get_status",
105 			    sbiret.error);
106 		}
107 
108 		/* Assume the BP is the only one started. */
109 		if (sbiret.value == SBI_HART_STARTED) {
110 			if (cpu_hartid[0] != -1) {
111 				panic("more than 1 hart started");
112 			}
113 			cpu_hartid[0] = hartid;
114 			continue;
115 		}
116 
117 		KASSERT(cpuindex < MAXCPUS);
118 		cpu_hartid[cpuindex] = hartid;
119 		cpu_dcache_wb_range((vaddr_t)&cpu_hartid[cpuindex],
120 		    sizeof(cpu_hartid[cpuindex]));
121 
122 		cpuindex++;
123 	}
124 #endif
125 }
126 int
127 riscv_fdt_cpu_mpstart(void)
128 {
129 	int ret = 0;
130 #ifdef MULTIPROCESSOR
131 	const int cpus = OF_finddevice("/cpus");
132 	if (cpus == -1) {
133 		aprint_error("%s: no /cpus node found\n", __func__);
134 		return 0;
135 	}
136 
137 	// riscv_fdt_cpu_bootstrap put the boot hart id in cpu_hartid[0]
138 	const cpuid_t bp_hartid = cpu_hartid[0];
139 
140 	/* BootAPs */
141 	size_t cpuindex = 1;
142 	for (int child = OF_child(cpus); child; child = OF_peer(child)) {
143 		if (!riscv_fdt_cpu_okay(child))
144 			continue;
145 
146 		uint64_t reg;
147 		if (fdtbus_get_reg64(child, 0, &reg, NULL) != 0)
148 			continue;
149 
150 		cpuid_t hartid = reg;
151 
152 		if (hartid == bp_hartid)
153 			continue;		/* BP already started */
154 
155 		const paddr_t entry = KERN_VTOPHYS(cpu_mpstart);
156 		struct sbiret sbiret = sbi_hart_start(hartid, entry, 0);
157 		switch (sbiret.error) {
158 		case SBI_SUCCESS:
159 			break;
160 		case SBI_ERR_INVALID_ADDRESS:
161 			break;
162 		case SBI_ERR_INVALID_PARAM:
163 			break;
164 		case SBI_ERR_ALREADY_AVAILABLE:
165 			break;
166 		case SBI_ERR_FAILED:
167 			break;
168 		default:
169 			aprint_error("%s: failed to enable CPU %#lx\n",
170 			    __func__, hartid);
171 		}
172 
173 		size_t i;
174 		/* Wait for AP to start */
175 		for (i = 0x10000000; i > 0; i--) {
176 			if (cpu_hatched_p(cpuindex))
177 				break;
178 		}
179 
180 		if (i == 0) {
181 			ret++;
182 			aprint_error("cpu%zu: WARNING: AP failed to start\n",
183 			    cpuindex);
184 		}
185 
186 		cpuindex++;
187 	}
188 #else
189 	aprint_normal("%s: kernel compiled without MULTIPROCESSOR\n", __func__);
190 #endif /* MULTIPROCESSOR */
191 	return ret;
192 }
193 
194 static int
195 cpu_fdt_match(device_t parent, cfdata_t cf, void *aux)
196 {
197 	struct fdt_attach_args * const faa = aux;
198 	const int phandle = faa->faa_phandle;
199 	const char *device_type;
200 
201 	device_type = fdtbus_get_string(phandle, "device_type");
202 	return device_type != NULL && strcmp(device_type, "cpu") == 0;
203 }
204 
205 static void
206 cpu_fdt_attach(device_t parent, device_t self, void *aux)
207 {
208 	struct fdt_attach_args * const faa = aux;
209 	const int phandle = faa->faa_phandle;
210 	bus_addr_t cpuid;
211 
212 	if (fdtbus_get_reg(phandle, 0, &cpuid, NULL) != 0)
213 		cpuid = 0;
214 
215 	/* Attach the CPU */
216 	cpu_attach(self, cpuid);
217 
218 	fdt_add_bus(self, phandle, faa);
219 }
220 
221 CFATTACH_DECL_NEW(cpu_fdt, 0, cpu_fdt_match, cpu_fdt_attach, NULL, NULL);
222