syscon.c revision 1.4
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 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}; 46 47struct syscon_softc *syscon_reboot_sc; 48struct syscon_softc *syscon_poweroff_sc; 49 50int syscon_match(struct device *, void *, void *); 51void syscon_attach(struct device *, struct device *, void *); 52 53struct cfattach syscon_ca = { 54 sizeof(struct syscon_softc), syscon_match, syscon_attach 55}; 56 57struct cfdriver syscon_cd = { 58 NULL, "syscon", DV_DULL 59}; 60 61void syscon_reset(void); 62void syscon_powerdown(void); 63 64int 65syscon_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 74void 75syscon_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 130void 131syscon_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 144void 145syscon_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