jcs's openbsd hax
openbsd
at jcs 175 lines 4.7 kB view raw
1/* $OpenBSD: syscon.c,v 1.8 2023/09/22 01:10:44 jsg 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#include <machine/simplebusvar.h> 30 31extern void (*cpuresetfn)(void); 32extern void (*powerdownfn)(void); 33 34struct syscon_softc { 35 struct simplebus_softc sc_sbus; 36 bus_space_tag_t sc_iot; 37 bus_space_handle_t sc_ioh; 38 uint32_t sc_regmap; 39 bus_size_t sc_offset; 40 uint32_t sc_mask; 41 uint32_t sc_value; 42}; 43 44struct syscon_softc *syscon_reboot_sc; 45struct syscon_softc *syscon_poweroff_sc; 46 47int syscon_match(struct device *, void *, void *); 48void syscon_attach(struct device *, struct device *, void *); 49 50const struct cfattach syscon_ca = { 51 sizeof(struct syscon_softc), syscon_match, syscon_attach 52}; 53 54struct cfdriver syscon_cd = { 55 NULL, "syscon", DV_DULL 56}; 57 58void syscon_reset(void); 59void syscon_powerdown(void); 60 61int 62syscon_match(struct device *parent, void *match, void *aux) 63{ 64 struct fdt_attach_args *faa = aux; 65 66 return OF_is_compatible(faa->fa_node, "syscon") || 67 OF_is_compatible(faa->fa_node, "syscon-reboot") || 68 OF_is_compatible(faa->fa_node, "syscon-poweroff"); 69} 70 71void 72syscon_attach(struct device *parent, struct device *self, void *aux) 73{ 74 struct syscon_softc *sc = (struct syscon_softc *)self; 75 struct fdt_attach_args *faa = aux; 76 char name[32]; 77 78 OF_getprop(faa->fa_node, "name", name, sizeof(name)); 79 name[sizeof(name) - 1] = 0; 80 81 if (OF_is_compatible(faa->fa_node, "syscon")) { 82 if (faa->fa_nreg < 1) { 83 printf(": no registers\n"); 84 return; 85 } 86 87 sc->sc_iot = faa->fa_iot; 88 89 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 90 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 91 printf(": can't map registers\n"); 92 return; 93 } 94 95 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh, 96 faa->fa_reg[0].size); 97 } 98 99 if (OF_is_compatible(faa->fa_node, "simple-mfd")) 100 simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa); 101 else 102 printf(": \"%s\"\n", name); 103 104 if (OF_is_compatible(faa->fa_node, "syscon-reboot") || 105 OF_is_compatible(faa->fa_node, "syscon-poweroff")) { 106 sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0); 107 if (sc->sc_regmap == 0) 108 return; 109 110 if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t)) 111 return; 112 113 /* At least one of "mask" and "value" should be provided. */ 114 if (OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t) && 115 OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t)) 116 return; 117 118 sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0); 119 sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0xffffffff); 120 sc->sc_value = OF_getpropint(faa->fa_node, "value", 0); 121 122 /* 123 * Old binding used "mask" as the value to write with 124 * an all-ones mask. This is still supported. 125 */ 126 if (OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t)) { 127 sc->sc_value = sc->sc_mask; 128 sc->sc_mask = 0xffffffff; 129 } 130 131 if (OF_is_compatible(faa->fa_node, "syscon-reboot")) { 132 syscon_reboot_sc = sc; 133 cpuresetfn = syscon_reset; 134 } else if (OF_is_compatible(faa->fa_node, "syscon-poweroff")) { 135 syscon_poweroff_sc = sc; 136 powerdownfn = syscon_powerdown; 137 } 138 } 139} 140 141void 142syscon_reset(void) 143{ 144 struct syscon_softc *sc = syscon_reboot_sc; 145 struct regmap *rm; 146 uint32_t value; 147 148 rm = regmap_byphandle(sc->sc_regmap); 149 if (rm == NULL) 150 return; 151 152 value = regmap_read_4(rm, sc->sc_offset); 153 value &= ~sc->sc_mask; 154 value |= sc->sc_value; 155 regmap_write_4(rm, sc->sc_offset, value); 156 delay(1000000); 157} 158 159void 160syscon_powerdown(void) 161{ 162 struct syscon_softc *sc = syscon_poweroff_sc; 163 struct regmap *rm; 164 uint32_t value; 165 166 rm = regmap_byphandle(sc->sc_regmap); 167 if (rm == NULL) 168 return; 169 170 value = regmap_read_4(rm, sc->sc_offset); 171 value &= ~sc->sc_mask; 172 value |= sc->sc_value; 173 regmap_write_4(rm, sc->sc_offset, value); 174 delay(1000000); 175}