1 /* $OpenBSD: syscon.c,v 1.4 2018/03/17 18:04:15 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2017 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/bus.h> 23 #include <machine/fdt.h> 24 25 #include <dev/ofw/openfirm.h> 26 #include <dev/ofw/ofw_misc.h> 27 #include <dev/ofw/fdt.h> 28 29 #ifdef __armv7__ 30 #include <arm/simplebus/simplebusvar.h> 31 #else 32 #include <arm64/dev/simplebusvar.h> 33 #endif 34 35 extern void (*cpuresetfn)(void); 36 extern void (*powerdownfn)(void); 37 38 struct syscon_softc { 39 struct simplebus_softc sc_sbus; 40 bus_space_tag_t sc_iot; 41 bus_space_handle_t sc_ioh; 42 uint32_t sc_regmap; 43 bus_size_t sc_offset; 44 uint32_t sc_mask; 45 }; 46 47 struct syscon_softc *syscon_reboot_sc; 48 struct syscon_softc *syscon_poweroff_sc; 49 50 int syscon_match(struct device *, void *, void *); 51 void syscon_attach(struct device *, struct device *, void *); 52 53 struct cfattach syscon_ca = { 54 sizeof(struct syscon_softc), syscon_match, syscon_attach 55 }; 56 57 struct cfdriver syscon_cd = { 58 NULL, "syscon", DV_DULL 59 }; 60 61 void syscon_reset(void); 62 void syscon_powerdown(void); 63 64 int 65 syscon_match(struct device *parent, void *match, void *aux) 66 { 67 struct fdt_attach_args *faa = aux; 68 69 return OF_is_compatible(faa->fa_node, "syscon") || 70 OF_is_compatible(faa->fa_node, "syscon-reboot") || 71 OF_is_compatible(faa->fa_node, "syscon-poweroff"); 72 } 73 74 void 75 syscon_attach(struct device *parent, struct device *self, void *aux) 76 { 77 struct syscon_softc *sc = (struct syscon_softc *)self; 78 struct fdt_attach_args *faa = aux; 79 char name[32]; 80 81 OF_getprop(faa->fa_node, "name", name, sizeof(name)); 82 name[sizeof(name) - 1] = 0; 83 84 if (OF_is_compatible(faa->fa_node, "syscon")) { 85 if (faa->fa_nreg < 1) { 86 printf(": no registers\n"); 87 return; 88 } 89 90 sc->sc_iot = faa->fa_iot; 91 92 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 93 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 94 printf(": can't map registers\n"); 95 return; 96 } 97 98 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh, 99 faa->fa_reg[0].size); 100 } 101 102 if (OF_is_compatible(faa->fa_node, "simple-mfd")) 103 simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); 104 else 105 printf(": \"%s\"\n", name); 106 107 if (OF_is_compatible(faa->fa_node, "syscon-reboot") || 108 OF_is_compatible(faa->fa_node, "syscon-poweroff")) { 109 sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0); 110 if (sc->sc_regmap == 0) 111 return; 112 113 if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t) || 114 OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t)) 115 return; 116 117 sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0); 118 sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0); 119 120 if (OF_is_compatible(faa->fa_node, "syscon-reboot")) { 121 syscon_reboot_sc = sc; 122 cpuresetfn = syscon_reset; 123 } else if (OF_is_compatible(faa->fa_node, "syscon-poweroff")) { 124 syscon_poweroff_sc = sc; 125 powerdownfn = syscon_powerdown; 126 } 127 } 128 } 129 130 void 131 syscon_reset(void) 132 { 133 struct syscon_softc *sc = syscon_reboot_sc; 134 struct regmap *rm; 135 136 rm = regmap_byphandle(sc->sc_regmap); 137 if (rm == NULL) 138 return; 139 140 regmap_write_4(rm, sc->sc_offset, sc->sc_mask); 141 delay(1000000); 142 } 143 144 void 145 syscon_powerdown(void) 146 { 147 struct syscon_softc *sc = syscon_poweroff_sc; 148 struct regmap *rm; 149 150 rm = regmap_byphandle(sc->sc_regmap); 151 if (rm == NULL) 152 return; 153 154 regmap_write_4(rm, sc->sc_offset, sc->sc_mask); 155 delay(1000000); 156 } 157