1 /*-
2 * Copyright (C) 2009 Nathan Whitehorn
3 * Copyright (C) 2015 The FreeBSD Foundation
4 * All rights reserved.
5 *
6 * Portions of this software were developed by Andrew Turner
7 * under sponsorship from the FreeBSD Foundation.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
36 #include <sys/bus.h>
37 #include <sys/cpu.h>
38 #include <machine/bus.h>
39
40 #include <dev/ofw/openfirm.h>
41 #include <dev/ofw/ofw_bus.h>
42 #include <dev/ofw/ofw_bus_subr.h>
43 #include <dev/ofw/ofw_cpu.h>
44
45 #if defined(__arm__) || defined(__arm64__) || defined(__riscv)
46 #include <dev/clk/clk.h>
47 #define HAS_CLK
48 #endif
49
50 static int ofw_cpulist_probe(device_t);
51 static int ofw_cpulist_attach(device_t);
52 static const struct ofw_bus_devinfo *ofw_cpulist_get_devinfo(device_t dev,
53 device_t child);
54
55 static MALLOC_DEFINE(M_OFWCPU, "ofwcpu", "OFW CPU device information");
56
57 struct ofw_cpulist_softc {
58 pcell_t sc_addr_cells;
59 };
60
61 static device_method_t ofw_cpulist_methods[] = {
62 /* Device interface */
63 DEVMETHOD(device_probe, ofw_cpulist_probe),
64 DEVMETHOD(device_attach, ofw_cpulist_attach),
65
66 /* Bus interface */
67 DEVMETHOD(bus_add_child, bus_generic_add_child),
68 DEVMETHOD(bus_child_pnpinfo, ofw_bus_gen_child_pnpinfo),
69 DEVMETHOD(bus_get_device_path, ofw_bus_gen_get_device_path),
70
71 /* ofw_bus interface */
72 DEVMETHOD(ofw_bus_get_devinfo, ofw_cpulist_get_devinfo),
73 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
74 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
75 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
76 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
77 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
78
79 DEVMETHOD_END
80 };
81
82 static driver_t ofw_cpulist_driver = {
83 "cpulist",
84 ofw_cpulist_methods,
85 sizeof(struct ofw_cpulist_softc)
86 };
87
88 DRIVER_MODULE(ofw_cpulist, ofwbus, ofw_cpulist_driver, 0, 0);
89
90 static int
ofw_cpulist_probe(device_t dev)91 ofw_cpulist_probe(device_t dev)
92 {
93 const char *name;
94
95 name = ofw_bus_get_name(dev);
96
97 if (name == NULL || strcmp(name, "cpus") != 0)
98 return (ENXIO);
99
100 device_set_desc(dev, "Open Firmware CPU Group");
101
102 return (0);
103 }
104
105 static int
ofw_cpulist_attach(device_t dev)106 ofw_cpulist_attach(device_t dev)
107 {
108 struct ofw_cpulist_softc *sc;
109 phandle_t root, child;
110 device_t cdev;
111 struct ofw_bus_devinfo *dinfo;
112
113 sc = device_get_softc(dev);
114 root = ofw_bus_get_node(dev);
115
116 sc->sc_addr_cells = 1;
117 OF_getencprop(root, "#address-cells", &sc->sc_addr_cells,
118 sizeof(sc->sc_addr_cells));
119
120 for (child = OF_child(root); child != 0; child = OF_peer(child)) {
121 dinfo = malloc(sizeof(*dinfo), M_OFWCPU, M_WAITOK | M_ZERO);
122
123 if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
124 free(dinfo, M_OFWCPU);
125 continue;
126 }
127 cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
128 if (cdev == NULL) {
129 device_printf(dev, "<%s>: device_add_child failed\n",
130 dinfo->obd_name);
131 ofw_bus_gen_destroy_devinfo(dinfo);
132 free(dinfo, M_OFWCPU);
133 continue;
134 }
135 device_set_ivars(cdev, dinfo);
136 }
137
138 bus_attach_children(dev);
139 return (0);
140 }
141
142 static const struct ofw_bus_devinfo *
ofw_cpulist_get_devinfo(device_t dev,device_t child)143 ofw_cpulist_get_devinfo(device_t dev, device_t child)
144 {
145 return (device_get_ivars(child));
146 }
147
148 static int ofw_cpu_probe(device_t);
149 static int ofw_cpu_attach(device_t);
150 static int ofw_cpu_read_ivar(device_t dev, device_t child, int index,
151 uintptr_t *result);
152
153 struct ofw_cpu_softc {
154 struct pcpu *sc_cpu_pcpu;
155 uint32_t sc_nominal_mhz;
156 bool sc_reg_valid;
157 pcell_t sc_reg[2];
158 };
159
160 static device_method_t ofw_cpu_methods[] = {
161 /* Device interface */
162 DEVMETHOD(device_probe, ofw_cpu_probe),
163 DEVMETHOD(device_attach, ofw_cpu_attach),
164
165 /* Bus interface */
166 DEVMETHOD(bus_add_child, bus_generic_add_child),
167 DEVMETHOD(bus_read_ivar, ofw_cpu_read_ivar),
168 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
169 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
170 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
171 DEVMETHOD(bus_release_resource, bus_generic_release_resource),
172 DEVMETHOD(bus_activate_resource,bus_generic_activate_resource),
173
174 DEVMETHOD_END
175 };
176
177 static driver_t ofw_cpu_driver = {
178 "cpu",
179 ofw_cpu_methods,
180 sizeof(struct ofw_cpu_softc)
181 };
182
183 DRIVER_MODULE(ofw_cpu, cpulist, ofw_cpu_driver, 0, 0);
184
185 static bool
ofw_cpu_is_runnable(phandle_t node)186 ofw_cpu_is_runnable(phandle_t node)
187 {
188 /*
189 * Per the DeviceTree Specification, a cpu node (under /cpus) that
190 * has 'status = disabled' indicates that "the CPU is in a quiescent
191 * state."
192 *
193 * A quiescent CPU that specifies an "enable-method", such as
194 * "spin-table", can still be used by the kernel.
195 *
196 * Lacking this, any CPU marked "disabled" or other non-okay status
197 * should be excluded from the kernel's view.
198 */
199 return (ofw_bus_node_status_okay(node) ||
200 OF_hasprop(node, "enable-method"));
201 }
202
203 static int
ofw_cpu_probe(device_t dev)204 ofw_cpu_probe(device_t dev)
205 {
206 const char *type = ofw_bus_get_type(dev);
207
208 if (type == NULL || strcmp(type, "cpu") != 0)
209 return (ENXIO);
210
211 if (!ofw_cpu_is_runnable(ofw_bus_get_node(dev)))
212 return (ENXIO);
213
214 device_set_desc(dev, "Open Firmware CPU");
215 if (!bootverbose && device_get_unit(dev) != 0) {
216 device_quiet(dev);
217 device_quiet_children(dev);
218 }
219
220 return (0);
221 }
222
223 static int
get_freq_from_clk(device_t dev,struct ofw_cpu_softc * sc)224 get_freq_from_clk(device_t dev, struct ofw_cpu_softc *sc)
225 {
226 #ifdef HAS_CLK
227 clk_t cpuclk;
228 uint64_t freq;
229 int rv;
230
231 rv = clk_get_by_ofw_index(dev, 0, 0, &cpuclk);
232 if (rv == 0) {
233 rv = clk_get_freq(cpuclk, &freq);
234 if (rv != 0 && bootverbose)
235 device_printf(dev,
236 "Cannot get freq of property clocks\n");
237 else
238 sc->sc_nominal_mhz = freq / 1000000;
239 }
240
241 return (rv);
242 #else
243 return (ENODEV);
244 #endif
245 }
246
247 static int
ofw_cpu_attach(device_t dev)248 ofw_cpu_attach(device_t dev)
249 {
250 struct ofw_cpulist_softc *psc;
251 struct ofw_cpu_softc *sc;
252 phandle_t node;
253 pcell_t cell;
254 int rv;
255
256 sc = device_get_softc(dev);
257 psc = device_get_softc(device_get_parent(dev));
258
259 if (nitems(sc->sc_reg) < psc->sc_addr_cells) {
260 if (bootverbose)
261 device_printf(dev, "Too many address cells\n");
262 return (EINVAL);
263 }
264
265 node = ofw_bus_get_node(dev);
266
267 /* Read and validate the reg property for use later */
268 sc->sc_reg_valid = false;
269 rv = OF_getencprop(node, "reg", sc->sc_reg, sizeof(sc->sc_reg));
270 if (rv < 0)
271 device_printf(dev, "missing 'reg' property\n");
272 else if ((rv % 4) != 0) {
273 if (bootverbose)
274 device_printf(dev, "Malformed reg property\n");
275 } else if ((rv / 4) != psc->sc_addr_cells) {
276 if (bootverbose)
277 device_printf(dev, "Invalid reg size %u\n", rv);
278 } else
279 sc->sc_reg_valid = true;
280
281 #ifdef __powerpc__
282 /*
283 * On powerpc, "interrupt-servers" denotes a SMT CPU. Look for any
284 * thread on this CPU, and assign that.
285 */
286 if (OF_hasprop(node, "ibm,ppc-interrupt-server#s")) {
287 struct cpuref cpuref;
288 cell_t *servers;
289 int i, nservers, rv;
290
291 if ((nservers = OF_getencprop_alloc(node,
292 "ibm,ppc-interrupt-server#s", (void **)&servers)) < 0)
293 return (ENXIO);
294 nservers /= sizeof(cell_t);
295 for (i = 0; i < nservers; i++) {
296 for (rv = platform_smp_first_cpu(&cpuref); rv == 0;
297 rv = platform_smp_next_cpu(&cpuref)) {
298 if (cpuref.cr_hwref == servers[i]) {
299 sc->sc_cpu_pcpu =
300 pcpu_find(cpuref.cr_cpuid);
301 if (sc->sc_cpu_pcpu == NULL) {
302 OF_prop_free(servers);
303 return (ENXIO);
304 }
305 break;
306 }
307 }
308 if (rv != ENOENT)
309 break;
310 }
311 OF_prop_free(servers);
312 if (sc->sc_cpu_pcpu == NULL) {
313 device_printf(dev, "No CPU found for this device.\n");
314 return (ENXIO);
315 }
316 } else
317 #endif
318 sc->sc_cpu_pcpu = pcpu_find(device_get_unit(dev));
319
320 if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) < 0) {
321 if (get_freq_from_clk(dev, sc) != 0) {
322 if (bootverbose)
323 device_printf(dev,
324 "missing 'clock-frequency' property\n");
325 }
326 } else
327 sc->sc_nominal_mhz = cell / 1000000; /* convert to MHz */
328
329 if (sc->sc_nominal_mhz != 0 && bootverbose)
330 device_printf(dev, "Nominal frequency %dMhz\n",
331 sc->sc_nominal_mhz);
332
333 bus_identify_children(dev);
334 bus_attach_children(dev);
335 return (0);
336 }
337
338 static int
ofw_cpu_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)339 ofw_cpu_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
340 {
341 struct ofw_cpulist_softc *psc;
342 struct ofw_cpu_softc *sc;
343
344 sc = device_get_softc(dev);
345
346 switch (index) {
347 case CPU_IVAR_PCPU:
348 *result = (uintptr_t)sc->sc_cpu_pcpu;
349 return (0);
350 case CPU_IVAR_NOMINAL_MHZ:
351 if (sc->sc_nominal_mhz > 0) {
352 *result = (uintptr_t)sc->sc_nominal_mhz;
353 return (0);
354 }
355 break;
356 case CPU_IVAR_CPUID_SIZE:
357 psc = device_get_softc(device_get_parent(dev));
358 *result = psc->sc_addr_cells;
359 return (0);
360 case CPU_IVAR_CPUID:
361 if (sc->sc_reg_valid) {
362 *result = (uintptr_t)sc->sc_reg;
363 return (0);
364 }
365 break;
366 }
367
368 return (ENOENT);
369 }
370
371 int
ofw_cpu_early_foreach(ofw_cpu_foreach_cb callback,bool only_runnable)372 ofw_cpu_early_foreach(ofw_cpu_foreach_cb callback, bool only_runnable)
373 {
374 phandle_t node, child;
375 pcell_t addr_cells, reg[2];
376 char device_type[16];
377 u_int id, next_id;
378 int count, rv;
379
380 count = 0;
381 id = 0;
382 next_id = 0;
383
384 node = OF_finddevice("/cpus");
385 if (node == -1)
386 return (-1);
387
388 /* Find the number of cells in the cpu register */
389 if (OF_getencprop(node, "#address-cells", &addr_cells,
390 sizeof(addr_cells)) < 0)
391 return (-1);
392
393 for (child = OF_child(node); child != 0; child = OF_peer(child),
394 id = next_id) {
395 /* Check if child is a CPU */
396 memset(device_type, 0, sizeof(device_type));
397 rv = OF_getprop(child, "device_type", device_type,
398 sizeof(device_type) - 1);
399 if (rv < 0)
400 continue;
401 if (strcmp(device_type, "cpu") != 0)
402 continue;
403
404 /* We're processing CPU, update next_id used in the next iteration */
405 next_id++;
406
407 /*
408 * If we are filtering by runnable then limit to only
409 * those that have been enabled, or do provide a method
410 * to enable them.
411 */
412 if (only_runnable && !ofw_cpu_is_runnable(child))
413 continue;
414
415 /*
416 * Check we have a register to identify the cpu
417 */
418 rv = OF_getencprop(child, "reg", reg,
419 addr_cells * sizeof(cell_t));
420 if (rv != addr_cells * sizeof(cell_t))
421 continue;
422
423 if (callback == NULL || callback(id, child, addr_cells, reg))
424 count++;
425 }
426
427 return (only_runnable ? count : id);
428 }
429