xref: /openbsd-src/sys/dev/fdt/psci.c (revision a60283c74c8a7419a8c65808e8144a04d86a7439)
1 /*	$OpenBSD: psci.c,v 1.2 2017/02/27 08:39:25 patrick 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 extern void (*cpuresetfn)(void);
30 extern void (*powerdownfn)(void);
31 
32 #define SYSTEM_OFF	0x84000008
33 #define SYSTEM_RESET	0x84000009
34 
35 struct psci_softc {
36 	struct device		 sc_dev;
37 	void			 (*callfn)(uint32_t, uint32_t, uint32_t, uint32_t);
38 	int			 sc_system_off;
39 	int			 sc_system_reset;
40 };
41 
42 struct psci_softc *psci_sc;
43 
44 int	psci_match(struct device *, void *, void *);
45 void	psci_attach(struct device *, struct device *, void *);
46 void	psci_reset(void);
47 void	psci_powerdown(void);
48 
49 extern void hvc_call(uint32_t, uint32_t, uint32_t, uint32_t);
50 extern void smc_call(uint32_t, uint32_t, uint32_t, uint32_t);
51 
52 struct cfattach psci_ca = {
53 	sizeof(struct psci_softc), psci_match, psci_attach
54 };
55 
56 struct cfdriver psci_cd = {
57 	NULL, "psci", DV_DULL
58 };
59 
60 int
61 psci_match(struct device *parent, void *match, void *aux)
62 {
63 	struct fdt_attach_args *faa = aux;
64 
65 	return OF_is_compatible(faa->fa_node, "arm,psci") ||
66 	    OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
67 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0");
68 }
69 
70 void
71 psci_attach(struct device *parent, struct device *self, void *aux)
72 {
73 	struct psci_softc		*sc = (struct psci_softc *) self;
74 	struct fdt_attach_args		*faa = aux;
75 	char				 method[128];
76 
77 	if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) {
78 		if (strcmp(method, "hvc") == 0)
79 			sc->callfn = hvc_call;
80 		else if (strcmp(method, "smc") == 0)
81 			sc->callfn = smc_call;
82 	}
83 
84 	/*
85 	 * The function IDs are only to be parsed for the old specification
86 	 * (as in version 0.1).  All newer implementations are supposed to
87 	 * use the specified values.
88 	 */
89 	if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") ||
90 	    OF_is_compatible(faa->fa_node, "arm,psci-1.0")) {
91 		sc->sc_system_off = SYSTEM_OFF;
92 		sc->sc_system_reset = SYSTEM_RESET;
93 	} else if (OF_is_compatible(faa->fa_node, "arm,psci")) {
94 		sc->sc_system_off = OF_getpropint(faa->fa_node,
95 		    "system_off", 0);
96 		sc->sc_system_reset = OF_getpropint(faa->fa_node,
97 		    "system_reset", 0);
98 	}
99 
100 	printf("\n");
101 
102 	psci_sc = sc;
103 	if (sc->sc_system_off != 0)
104 		powerdownfn = psci_powerdown;
105 	if (sc->sc_system_reset != 0)
106 		cpuresetfn = psci_reset;
107 }
108 
109 void
110 psci_reset(void)
111 {
112 	struct psci_softc *sc = psci_sc;
113 	if (sc->callfn)
114 		(*sc->callfn)(sc->sc_system_reset, 0, 0, 0);
115 }
116 
117 void
118 psci_powerdown(void)
119 {
120 	struct psci_softc *sc = psci_sc;
121 	if (sc->callfn)
122 		(*sc->callfn)(sc->sc_system_off, 0, 0, 0);
123 }
124