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