xref: /freebsd/sys/dev/psci/psci.c (revision 5add83935a135f802f0a047281757ad13eef0be5)
1 /*-
2  * Copyright (c) 2014 Robin Randhawa
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 /*
29  * This implements support for ARM's Power State Co-ordination Interface
30  * [PSCI]. The implementation adheres to version 0.2 of the PSCI specification
31  * but also supports v0.1. PSCI standardizes operations such as system reset, CPU
32  * on/off/suspend. PSCI requires a compliant firmware implementation.
33  *
34  * The PSCI specification used for this implementation is available at:
35  *
36  * <http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0022b/index.html>.
37  *
38  * TODO:
39  * - Add support for remaining PSCI calls [this implementation only
40  *   supports get_version, system_reset and cpu_on].
41  */
42 
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45 
46 #include "opt_acpi.h"
47 #include "opt_platform.h"
48 
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/bus.h>
52 #include <sys/eventhandler.h>
53 #include <sys/kernel.h>
54 #include <sys/module.h>
55 #include <sys/reboot.h>
56 
57 #include <machine/bus.h>
58 #include <machine/machdep.h>
59 
60 #ifdef DEV_ACPI
61 #include <contrib/dev/acpica/include/acpi.h>
62 #include <dev/acpica/acpivar.h>
63 #endif
64 
65 #ifdef FDT
66 #include <dev/fdt/fdt_common.h>
67 #include <dev/ofw/openfirm.h>
68 #include <dev/ofw/ofw_bus.h>
69 #include <dev/ofw/ofw_bus_subr.h>
70 #endif
71 
72 #include <dev/psci/psci.h>
73 
74 struct psci_softc {
75 	device_t        dev;
76 
77 	uint32_t	psci_version;
78 	uint32_t	psci_fnids[PSCI_FN_MAX];
79 };
80 
81 #ifdef FDT
82 static int psci_v0_1_init(device_t dev);
83 #endif
84 static int psci_v0_2_init(device_t dev);
85 
86 struct psci_softc *psci_softc = NULL;
87 
88 #ifdef __arm__
89 #define	USE_ACPI	0
90 #define	USE_FDT		1
91 #elif defined(__aarch64__)
92 #define	USE_ACPI	(arm64_bus_method == ARM64_BUS_ACPI)
93 #define	USE_FDT		(arm64_bus_method == ARM64_BUS_FDT)
94 #else
95 #error Unknown architecture
96 #endif
97 
98 #ifdef FDT
99 static struct ofw_compat_data compat_data[] = {
100 	{"arm,psci-1.0",        (uintptr_t)psci_v0_2_init},
101 	{"arm,psci-0.2",        (uintptr_t)psci_v0_2_init},
102 	{"arm,psci",            (uintptr_t)psci_v0_1_init},
103 	{NULL,                  0}
104 };
105 #endif
106 
107 static int psci_attach(device_t, psci_initfn_t);
108 static void psci_shutdown(void *, int);
109 
110 static int psci_find_callfn(psci_callfn_t *);
111 static int psci_def_callfn(register_t, register_t, register_t, register_t);
112 
113 static psci_callfn_t psci_callfn = psci_def_callfn;
114 
115 static inline int
116 psci_call(register_t a, register_t b, register_t c, register_t d)
117 {
118 
119 	return (psci_callfn(a, b, c, d));
120 }
121 
122 static void
123 psci_init(void *dummy)
124 {
125 	psci_callfn_t new_callfn;
126 
127 	if (psci_find_callfn(&new_callfn) != PSCI_RETVAL_SUCCESS) {
128 		printf("No PSCI/SMCCC call function found");
129 		return;
130 	}
131 
132 	psci_callfn = new_callfn;
133 }
134 /* This needs to be before cpu_mp at SI_SUB_CPU, SI_ORDER_THIRD */
135 SYSINIT(psci_start, SI_SUB_CPU, SI_ORDER_FIRST, psci_init, NULL);
136 
137 static int
138 psci_def_callfn(register_t a __unused, register_t b __unused,
139     register_t c __unused, register_t d __unused)
140 {
141 
142 	panic("No PSCI/SMCCC call function set");
143 }
144 
145 #ifdef FDT
146 static int psci_fdt_probe(device_t dev);
147 static int psci_fdt_attach(device_t dev);
148 
149 static device_method_t psci_fdt_methods[] = {
150 	DEVMETHOD(device_probe,     psci_fdt_probe),
151 	DEVMETHOD(device_attach,    psci_fdt_attach),
152 
153 	DEVMETHOD_END
154 };
155 
156 static driver_t psci_fdt_driver = {
157 	"psci",
158 	psci_fdt_methods,
159 	sizeof(struct psci_softc),
160 };
161 
162 static devclass_t psci_fdt_devclass;
163 
164 EARLY_DRIVER_MODULE(psci, simplebus, psci_fdt_driver, psci_fdt_devclass, 0, 0,
165     BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
166 EARLY_DRIVER_MODULE(psci, ofwbus, psci_fdt_driver, psci_fdt_devclass, 0, 0,
167     BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
168 
169 static psci_callfn_t
170 psci_fdt_get_callfn(phandle_t node)
171 {
172 	char method[16];
173 
174 	if ((OF_getprop(node, "method", method, sizeof(method))) > 0) {
175 		if (strcmp(method, "hvc") == 0)
176 			return (psci_hvc_despatch);
177 		else if (strcmp(method, "smc") == 0)
178 			return (psci_smc_despatch);
179 		else
180 			printf("psci: PSCI conduit \"%s\" invalid\n", method);
181 	} else
182 		printf("psci: PSCI conduit not supplied in the device tree\n");
183 
184 	return (NULL);
185 }
186 
187 static int
188 psci_fdt_probe(device_t dev)
189 {
190 	const struct ofw_compat_data *ocd;
191 
192 	if (!ofw_bus_status_okay(dev))
193 		return (ENXIO);
194 
195 	ocd = ofw_bus_search_compatible(dev, compat_data);
196 	if (ocd->ocd_str == NULL)
197 		return (ENXIO);
198 
199 	device_set_desc(dev, "ARM Power State Co-ordination Interface Driver");
200 
201 	return (BUS_PROBE_SPECIFIC);
202 }
203 
204 static int
205 psci_fdt_attach(device_t dev)
206 {
207 	const struct ofw_compat_data *ocd;
208 	psci_initfn_t psci_init;
209 
210 	ocd = ofw_bus_search_compatible(dev, compat_data);
211 	psci_init = (psci_initfn_t)ocd->ocd_data;
212 
213 	return (psci_attach(dev, psci_init));
214 }
215 #endif
216 
217 #ifdef DEV_ACPI
218 static void psci_acpi_identify(driver_t *, device_t);
219 static int psci_acpi_probe(device_t);
220 static int psci_acpi_attach(device_t);
221 
222 static device_method_t psci_acpi_methods[] = {
223 	/* Device interface */
224 	DEVMETHOD(device_identify,	psci_acpi_identify),
225 	DEVMETHOD(device_probe,		psci_acpi_probe),
226 	DEVMETHOD(device_attach,	psci_acpi_attach),
227 
228 	DEVMETHOD_END
229 };
230 
231 static driver_t psci_acpi_driver = {
232 	"psci",
233 	psci_acpi_methods,
234 	sizeof(struct psci_softc),
235 };
236 
237 static devclass_t psci_acpi_devclass;
238 
239 EARLY_DRIVER_MODULE(psci, acpi, psci_acpi_driver, psci_acpi_devclass, 0, 0,
240     BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
241 
242 static int
243 psci_acpi_bootflags(void)
244 {
245 	ACPI_TABLE_FADT *fadt;
246 	vm_paddr_t physaddr;
247 	int flags;
248 
249 	physaddr = acpi_find_table(ACPI_SIG_FADT);
250 	if (physaddr == 0)
251 		return (0);
252 
253 	fadt = acpi_map_table(physaddr, ACPI_SIG_FADT);
254 	if (fadt == NULL) {
255 		printf("psci: Unable to map the FADT\n");
256 		return (0);
257 	}
258 
259 	flags = fadt->ArmBootFlags;
260 
261 	acpi_unmap_table(fadt);
262 	return (flags);
263 }
264 
265 static psci_callfn_t
266 psci_acpi_get_callfn(int flags)
267 {
268 
269 	if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) {
270 		if ((flags & ACPI_FADT_PSCI_USE_HVC) != 0)
271 			return (psci_hvc_despatch);
272 		else
273 			return (psci_smc_despatch);
274 	} else {
275 		printf("psci: PSCI conduit not supplied in the device tree\n");
276 	}
277 
278 	return (NULL);
279 }
280 
281 static void
282 psci_acpi_identify(driver_t *driver, device_t parent)
283 {
284 	device_t dev;
285 	int flags;
286 
287 	flags = psci_acpi_bootflags();
288 	if ((flags & ACPI_FADT_PSCI_COMPLIANT) != 0) {
289 		dev = BUS_ADD_CHILD(parent,
290 		    BUS_PASS_CPU + BUS_PASS_ORDER_FIRST, "psci", -1);
291 
292 		if (dev != NULL)
293 			acpi_set_private(dev, (void *)(uintptr_t)flags);
294 	}
295 }
296 
297 static int
298 psci_acpi_probe(device_t dev)
299 {
300 	uintptr_t flags;
301 
302 	flags = (uintptr_t)acpi_get_private(dev);
303 	if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0)
304 		return (ENXIO);
305 
306 	device_set_desc(dev, "ARM Power State Co-ordination Interface Driver");
307 	return (BUS_PROBE_SPECIFIC);
308 }
309 
310 static int
311 psci_acpi_attach(device_t dev)
312 {
313 
314 	return (psci_attach(dev, psci_v0_2_init));
315 }
316 #endif
317 
318 static int
319 psci_attach(device_t dev, psci_initfn_t psci_init)
320 {
321 	struct psci_softc *sc = device_get_softc(dev);
322 
323 	if (psci_softc != NULL)
324 		return (ENXIO);
325 
326 	KASSERT(psci_init != NULL, ("PSCI init function cannot be NULL"));
327 	if (psci_init(dev))
328 		return (ENXIO);
329 
330 	psci_softc = sc;
331 
332 	return (0);
333 }
334 
335 static int
336 _psci_get_version(struct psci_softc *sc)
337 {
338 	uint32_t fnid;
339 
340 	/* PSCI version wasn't supported in v0.1. */
341 	fnid = sc->psci_fnids[PSCI_FN_VERSION];
342 	if (fnid)
343 		return (psci_call(fnid, 0, 0, 0));
344 
345 	return (PSCI_RETVAL_NOT_SUPPORTED);
346 }
347 
348 int
349 psci_get_version(void)
350 {
351 
352 	if (psci_softc == NULL)
353 		return (PSCI_RETVAL_NOT_SUPPORTED);
354 	return (_psci_get_version(psci_softc));
355 }
356 
357 #ifdef FDT
358 static int
359 psci_fdt_callfn(psci_callfn_t *callfn)
360 {
361 	phandle_t node;
362 
363 	node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-0.2");
364 	if (node == 0) {
365 		node = ofw_bus_find_compatible(OF_peer(0), "arm,psci-1.0");
366 		if (node == 0)
367 			return (PSCI_MISSING);
368 	}
369 
370 	*callfn = psci_fdt_get_callfn(node);
371 	return (0);
372 }
373 #endif
374 
375 #ifdef DEV_ACPI
376 static int
377 psci_acpi_callfn(psci_callfn_t *callfn)
378 {
379 	int flags;
380 
381 	flags = psci_acpi_bootflags();
382 	if ((flags & ACPI_FADT_PSCI_COMPLIANT) == 0)
383 		return (PSCI_MISSING);
384 
385 	*callfn = psci_acpi_get_callfn(flags);
386 	return (0);
387 }
388 #endif
389 
390 static int
391 psci_find_callfn(psci_callfn_t *callfn)
392 {
393 	int error;
394 
395 	*callfn = NULL;
396 #ifdef FDT
397 	if (USE_FDT) {
398 		error = psci_fdt_callfn(callfn);
399 		if (error != 0)
400 			return (error);
401 	}
402 #endif
403 #ifdef DEV_ACPI
404 	if (*callfn == NULL && USE_ACPI) {
405 		error = psci_acpi_callfn(callfn);
406 		if (error != 0)
407 			return (error);
408 	}
409 #endif
410 
411 	if (*callfn == NULL)
412 		return (PSCI_MISSING);
413 
414 	return (PSCI_RETVAL_SUCCESS);
415 }
416 
417 int32_t
418 psci_features(uint32_t psci_func_id)
419 {
420 
421 	if (psci_softc == NULL)
422 		return (PSCI_RETVAL_NOT_SUPPORTED);
423 
424 	/* The feature flags were added to PSCI 1.0 */
425 	if (PSCI_VER_MAJOR(psci_softc->psci_version) < 1)
426 		return (PSCI_RETVAL_NOT_SUPPORTED);
427 
428 	return (psci_call(PSCI_FNID_FEATURES, psci_func_id, 0, 0));
429 }
430 
431 int
432 psci_cpu_on(unsigned long cpu, unsigned long entry, unsigned long context_id)
433 {
434 	uint32_t fnid;
435 
436 	fnid = PSCI_FNID_CPU_ON;
437 	if (psci_softc != NULL)
438 		fnid = psci_softc->psci_fnids[PSCI_FN_CPU_ON];
439 
440 	/* PSCI v0.1 and v0.2 both support cpu_on. */
441 	return (psci_call(fnid, cpu, entry, context_id));
442 }
443 
444 static void
445 psci_shutdown(void *xsc, int howto)
446 {
447 	uint32_t fn = 0;
448 
449 	if (psci_softc == NULL)
450 		return;
451 
452 	/* PSCI system_off and system_reset werent't supported in v0.1. */
453 	if ((howto & RB_POWEROFF) != 0)
454 		fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_OFF];
455 	else if ((howto & RB_HALT) == 0)
456 		fn = psci_softc->psci_fnids[PSCI_FN_SYSTEM_RESET];
457 
458 	if (fn)
459 		psci_call(fn, 0, 0, 0);
460 
461 	/* System reset and off do not return. */
462 }
463 
464 void
465 psci_reset(void)
466 {
467 
468 	psci_shutdown(NULL, 0);
469 }
470 
471 #ifdef FDT
472 /* Only support PSCI 0.1 on FDT */
473 static int
474 psci_v0_1_init(device_t dev)
475 {
476 	struct psci_softc *sc = device_get_softc(dev);
477 	int psci_fn;
478 	uint32_t psci_fnid;
479 	phandle_t node;
480 	int len;
481 
482 
483 	/* Zero out the function ID table - Is this needed ? */
484 	for (psci_fn = PSCI_FN_VERSION, psci_fnid = PSCI_FNID_VERSION;
485 	    psci_fn < PSCI_FN_MAX; psci_fn++, psci_fnid++)
486 		sc->psci_fnids[psci_fn] = 0;
487 
488 	/* PSCI v0.1 doesn't specify function IDs. Get them from DT */
489 	node = ofw_bus_get_node(dev);
490 
491 	if ((len = OF_getproplen(node, "cpu_suspend")) > 0) {
492 		OF_getencprop(node, "cpu_suspend", &psci_fnid, len);
493 		sc->psci_fnids[PSCI_FN_CPU_SUSPEND] = psci_fnid;
494 	}
495 
496 	if ((len = OF_getproplen(node, "cpu_on")) > 0) {
497 		OF_getencprop(node, "cpu_on", &psci_fnid, len);
498 		sc->psci_fnids[PSCI_FN_CPU_ON] = psci_fnid;
499 	}
500 
501 	if ((len = OF_getproplen(node, "cpu_off")) > 0) {
502 		OF_getencprop(node, "cpu_off", &psci_fnid, len);
503 		sc->psci_fnids[PSCI_FN_CPU_OFF] = psci_fnid;
504 	}
505 
506 	if ((len = OF_getproplen(node, "migrate")) > 0) {
507 		OF_getencprop(node, "migrate", &psci_fnid, len);
508 		sc->psci_fnids[PSCI_FN_MIGRATE] = psci_fnid;
509 	}
510 
511 	sc->psci_version = (0 << 16) | 1;
512 	if (bootverbose)
513 		device_printf(dev, "PSCI version 0.1 available\n");
514 
515 	return(0);
516 }
517 #endif
518 
519 static int
520 psci_v0_2_init(device_t dev)
521 {
522 	struct psci_softc *sc = device_get_softc(dev);
523 	int version;
524 
525 	/* PSCI v0.2 specifies explicit function IDs. */
526 	sc->psci_fnids[PSCI_FN_VERSION]		    = PSCI_FNID_VERSION;
527 	sc->psci_fnids[PSCI_FN_CPU_SUSPEND]	    = PSCI_FNID_CPU_SUSPEND;
528 	sc->psci_fnids[PSCI_FN_CPU_OFF]		    = PSCI_FNID_CPU_OFF;
529 	sc->psci_fnids[PSCI_FN_CPU_ON]		    = PSCI_FNID_CPU_ON;
530 	sc->psci_fnids[PSCI_FN_AFFINITY_INFO]	    = PSCI_FNID_AFFINITY_INFO;
531 	sc->psci_fnids[PSCI_FN_MIGRATE]		    = PSCI_FNID_MIGRATE;
532 	sc->psci_fnids[PSCI_FN_MIGRATE_INFO_TYPE]   = PSCI_FNID_MIGRATE_INFO_TYPE;
533 	sc->psci_fnids[PSCI_FN_MIGRATE_INFO_UP_CPU] = PSCI_FNID_MIGRATE_INFO_UP_CPU;
534 	sc->psci_fnids[PSCI_FN_SYSTEM_OFF]	    = PSCI_FNID_SYSTEM_OFF;
535 	sc->psci_fnids[PSCI_FN_SYSTEM_RESET]	    = PSCI_FNID_SYSTEM_RESET;
536 
537 	version = _psci_get_version(sc);
538 
539 	if (version == PSCI_RETVAL_NOT_SUPPORTED)
540 		return (1);
541 
542 	sc->psci_version = version;
543 	if ((PSCI_VER_MAJOR(version) == 0 && PSCI_VER_MINOR(version) == 2) ||
544 	    PSCI_VER_MAJOR(version) == 1) {
545 		if (bootverbose)
546 			device_printf(dev, "PSCI version 0.2 compatible\n");
547 
548 		/*
549 		 * We only register this for v0.2 since v0.1 doesn't support
550 		 * system_reset.
551 		 */
552 		EVENTHANDLER_REGISTER(shutdown_final, psci_shutdown, sc,
553 		    SHUTDOWN_PRI_LAST);
554 
555 		return (0);
556 	}
557 
558 	device_printf(dev, "PSCI version number mismatched with DT\n");
559 	return (1);
560 }
561