xref: /openbsd-src/sys/dev/fdt/psci.c (revision 45584b64b56a7a79f7810b5769d32515e13929a2)
1 /*	$OpenBSD: psci.c,v 1.6 2018/02/23 19:08:56 kettenis Exp $	*/
2 
3 /*
4  * Copyright (c) 2016 Jonathan Gray <jsg@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/device.h>
21 #include <sys/systm.h>
22 
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25 
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/fdt.h>
28 
29 #include <dev/fdt/pscivar.h>
30 
31 extern void (*cpuresetfn)(void);
32 extern void (*powerdownfn)(void);
33 
34 #define PSCI_VERSION	0x84000000
35 #define SYSTEM_OFF	0x84000008
36 #define SYSTEM_RESET	0x84000009
37 #ifdef __LP64__
38 #define CPU_ON		0xc4000003
39 #else
40 #define CPU_ON		0x84000003
41 #endif
42 
43 struct psci_softc {
44 	struct device	 sc_dev;
45 	register_t	 (*sc_callfn)(register_t, register_t, register_t,
46 			     register_t);
47 	uint32_t	 sc_psci_version;
48 	uint32_t	 sc_system_off;
49 	uint32_t	 sc_system_reset;
50 	uint32_t	 sc_cpu_on;
51 };
52 
53 struct psci_softc *psci_sc;
54 
55 int	psci_match(struct device *, void *, void *);
56 void	psci_attach(struct device *, struct device *, void *);
57 void	psci_reset(void);
58 void	psci_powerdown(void);
59 
60 extern register_t hvc_call(register_t, register_t, register_t, register_t);
61 extern register_t smc_call(register_t, register_t, register_t, register_t);
62 
63 struct cfattach psci_ca = {
64 	sizeof(struct psci_softc), psci_match, psci_attach
65 };
66 
67 struct cfdriver psci_cd = {
68 	NULL, "psci", DV_DULL
69 };
70 
71 int
72 psci_match(struct device *parent, void *match, void *aux)
73 {
74 	struct fdt_attach_args *faa = aux;
75 
76 	return OF_is_compatible(faa->fa_node, "arm,psci") ||
77 	    OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
78 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0");
79 }
80 
81 void
82 psci_attach(struct device *parent, struct device *self, void *aux)
83 {
84 	struct psci_softc *sc = (struct psci_softc *)self;
85 	struct fdt_attach_args *faa = aux;
86 	char method[128];
87 	uint32_t version;
88 
89 	if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) {
90 		if (strcmp(method, "hvc") == 0)
91 			sc->sc_callfn = hvc_call;
92 		else if (strcmp(method, "smc") == 0)
93 			sc->sc_callfn = smc_call;
94 	}
95 
96 	/*
97 	 * The function IDs are only to be parsed for the old specification
98 	 * (as in version 0.1).  All newer implementations are supposed to
99 	 * use the specified values.
100 	 */
101 	if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
102 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0")) {
103 		sc->sc_psci_version = PSCI_VERSION;
104 		sc->sc_system_off = SYSTEM_OFF;
105 		sc->sc_system_reset = SYSTEM_RESET;
106 		sc->sc_cpu_on = CPU_ON;
107 	} else if (OF_is_compatible(faa->fa_node, "arm,psci")) {
108 		sc->sc_system_off = OF_getpropint(faa->fa_node,
109 		    "system_off", 0);
110 		sc->sc_system_reset = OF_getpropint(faa->fa_node,
111 		    "system_reset", 0);
112 		sc->sc_cpu_on = OF_getpropint(faa->fa_node, "cpu_on", 0);
113 	}
114 
115 	psci_sc = sc;
116 
117 	version = psci_version();
118 	printf(": PSCI %d.%d\n", version >> 16, version & 0xffff);
119 
120 	if (sc->sc_system_off != 0)
121 		powerdownfn = psci_powerdown;
122 	if (sc->sc_system_reset != 0)
123 		cpuresetfn = psci_reset;
124 }
125 
126 uint32_t
127 psci_version(void)
128 {
129 	struct psci_softc *sc = psci_sc;
130 
131 	if (sc && sc->sc_callfn && sc->sc_psci_version != 0)
132 		return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0);
133 
134 	/* No version support; return 0.0. */
135 	return 0;
136 }
137 
138 void
139 psci_reset(void)
140 {
141 	struct psci_softc *sc = psci_sc;
142 	if (sc->sc_callfn)
143 		(*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0);
144 }
145 
146 void
147 psci_powerdown(void)
148 {
149 	struct psci_softc *sc = psci_sc;
150 	if (sc->sc_callfn)
151 		(*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0);
152 }
153 
154 int32_t
155 psci_cpu_on(register_t target_cpu, register_t entry_point_address,
156     register_t context_id)
157 {
158 	struct psci_softc *sc = psci_sc;
159 
160 	if (sc && sc->sc_callfn && sc->sc_cpu_on != 0)
161 		return (*sc->sc_callfn)(sc->sc_cpu_on, target_cpu,
162 		    entry_point_address, context_id);
163 
164 	return PSCI_NOT_SUPPORTED;
165 }
166