1 /* $OpenBSD: psci.c,v 1.4 2018/01/17 10:17:33 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 extern int (*cpu_on_fn)(register_t, register_t); 34 35 #define PSCI_VERSION 0x84000000 36 #define SYSTEM_OFF 0x84000008 37 #define SYSTEM_RESET 0x84000009 38 #ifdef __LP64__ 39 #define CPU_ON 0xc4000003 40 #else 41 #define CPU_ON 0x84000003 42 #endif 43 44 struct psci_softc { 45 struct device sc_dev; 46 register_t (*sc_callfn)(register_t, register_t, register_t, 47 register_t); 48 int sc_psci_version; 49 int sc_system_off; 50 int sc_system_reset; 51 int sc_cpu_on; 52 }; 53 54 struct psci_softc *psci_sc; 55 56 int psci_match(struct device *, void *, void *); 57 void psci_attach(struct device *, struct device *, void *); 58 void psci_reset(void); 59 void psci_powerdown(void); 60 int psci_cpu_on(register_t, register_t); 61 62 extern register_t hvc_call(register_t, register_t, register_t, register_t); 63 extern register_t smc_call(register_t, register_t, register_t, register_t); 64 65 struct cfattach psci_ca = { 66 sizeof(struct psci_softc), psci_match, psci_attach 67 }; 68 69 struct cfdriver psci_cd = { 70 NULL, "psci", DV_DULL 71 }; 72 73 int 74 psci_match(struct device *parent, void *match, void *aux) 75 { 76 struct fdt_attach_args *faa = aux; 77 78 return OF_is_compatible(faa->fa_node, "arm,psci") || 79 OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 80 OF_is_compatible(faa->fa_node, "arm,psci-1.0"); 81 } 82 83 void 84 psci_attach(struct device *parent, struct device *self, void *aux) 85 { 86 struct psci_softc *sc = (struct psci_softc *)self; 87 struct fdt_attach_args *faa = aux; 88 char method[128]; 89 uint32_t version; 90 91 if (OF_getprop(faa->fa_node, "method", method, sizeof(method))) { 92 if (strcmp(method, "hvc") == 0) 93 sc->sc_callfn = hvc_call; 94 else if (strcmp(method, "smc") == 0) 95 sc->sc_callfn = smc_call; 96 } 97 98 /* 99 * The function IDs are only to be parsed for the old specification 100 * (as in version 0.1). All newer implementations are supposed to 101 * use the specified values. 102 */ 103 if (OF_is_compatible(faa->fa_node, "arm,psci-0.2") || 104 OF_is_compatible(faa->fa_node, "arm,psci-1.0")) { 105 sc->sc_psci_version = PSCI_VERSION; 106 sc->sc_system_off = SYSTEM_OFF; 107 sc->sc_system_reset = SYSTEM_RESET; 108 sc->sc_cpu_on = CPU_ON; 109 } else if (OF_is_compatible(faa->fa_node, "arm,psci")) { 110 sc->sc_system_off = OF_getpropint(faa->fa_node, 111 "system_off", 0); 112 sc->sc_system_reset = OF_getpropint(faa->fa_node, 113 "system_reset", 0); 114 sc->sc_cpu_on = OF_getpropint(faa->fa_node, "cpu_on", 0); 115 } 116 117 psci_sc = sc; 118 119 version = psci_version(); 120 printf(": PSCI %d.%d\n", version >> 16, version & 0xffff); 121 122 if (sc->sc_system_off != 0) 123 powerdownfn = psci_powerdown; 124 if (sc->sc_system_reset != 0) 125 cpuresetfn = psci_reset; 126 if (sc->sc_cpu_on != 0) 127 cpu_on_fn = psci_cpu_on; 128 } 129 130 uint32_t 131 psci_version(void) 132 { 133 struct psci_softc *sc = psci_sc; 134 135 if (sc && sc->sc_callfn && sc->sc_psci_version != 0) 136 return (*sc->sc_callfn)(sc->sc_psci_version, 0, 0, 0); 137 138 /* No version support; return 0.0. */ 139 return 0; 140 } 141 142 void 143 psci_reset(void) 144 { 145 struct psci_softc *sc = psci_sc; 146 if (sc->sc_callfn) 147 (*sc->sc_callfn)(sc->sc_system_reset, 0, 0, 0); 148 } 149 150 void 151 psci_powerdown(void) 152 { 153 struct psci_softc *sc = psci_sc; 154 if (sc->sc_callfn) 155 (*sc->sc_callfn)(sc->sc_system_off, 0, 0, 0); 156 } 157 158 int 159 psci_cpu_on(register_t mpidr, register_t pc) 160 { 161 struct psci_softc *sc = psci_sc; 162 if (sc->sc_callfn) 163 return (*sc->sc_callfn)(sc->sc_cpu_on, mpidr, pc, 0); 164 return -1; 165 } 166