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