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