summaryrefslogtreecommitdiff
path: root/arch/arm64/lib/system.c
blob: 25af2bf8dced6fdffc50e984096ed0c799304e72 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * system.c - system power control, the PSCI SYSTEM_OFF and
 * SYSTEM_RESET backends. off parks the core in WFI forever, the
 * manual's low power entry (D1-2255). reset drives the PE reset
 * domain: RMR_EL2 reset request with the system reset bit, RR bit 1,
 * followed by a barrier pair so the request retires before anything
 * else observes the core.
 *
 * On real hardware a SoC also needs a watchdog or PMIC write for a
 * full board reset, that is board territory, the arch part is this.
 *
 * Copyright (C) 2026 Bradley Morgan <brads@mainlining.org>
 */

#include <debug.h>
#include <stdint.h>

void tb_system_off(void)
{
	dprintf(ALWAYS, "system off\n");

	for (;;) {
		asm volatile("wfi");
	}
}

void tb_system_reset(void)
{
	uint64_t rmr;
	uint64_t el;
	register uint64_t r0 asm("x0") = 0x84000008;
	register uint64_t r1 asm("x1") = 0;
	register uint64_t r2 asm("x2") = 0;
	register uint64_t r3 asm("x3") = 0;

	dprintf(ALWAYS, "system reset\n");

	/*
	 * the firmware conduit first, PSCI SYSTEM_RESET through
	 * the machine's own monitor. this is the only legal way
	 * up from EL1: RMR_EL1 is undefined on hardware that
	 * implements a higher exception level, the access traps
	 * and the machine never resets.
	 */
	asm volatile("smc #0"
		     : "+r"(r0), "+r"(r1), "+r"(r2), "+r"(r3));

	/*
	 * no monitor answered, or it refused. ask the reset
	 * domain directly from a level that may write it.
	 */
	asm volatile("mrs %0, CurrentEL" : "=r" (el));
	el >>= 2;

	if (el >= 2) {
		asm volatile("mrs %0, rmr_el2" : "=r" (rmr));
		rmr |= (1 << 1);	/* RR, request reset */
		asm volatile(
			"msr	rmr_el2, %0\n"
			"dsb	sy\n"
			"isb\n"
			:: "r" (rmr));
	}

	/* nothing worked, park */
	for (;;) {
		asm volatile("wfi");
	}
}