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