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