1 /* $NetBSD: bcm2835_pmwdog.c,v 1.3 2024/06/10 06:03:48 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 2012, 2016 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: bcm2835_pmwdog.c,v 1.3 2024/06/10 06:03:48 mlelstv Exp $"); 34 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/kernel.h> 40 #include <sys/timetc.h> 41 #include <sys/wdog.h> 42 #include <sys/bus.h> 43 44 #include <dev/sysmon/sysmonvar.h> 45 46 #include <arm/broadcom/bcm2835reg.h> 47 #include <arm/broadcom/bcm2835_intr.h> 48 #include <arm/broadcom/bcm2835_pmwdogvar.h> 49 50 #include <dev/fdt/fdtvar.h> 51 52 #ifndef BCM2835_PM_DEFAULT_PERIOD 53 #define BCM2835_PM_DEFAULT_PERIOD 15 /* seconds */ 54 #endif 55 56 #define BCM2835_PM_PASSWORD 0x5a000000 57 #define BCM2835_PM_PASSWORD_MASK 0xff000000 58 59 #define BCM2835_PM_RSTC 0x1c 60 #define BCM2835_PM_RSTC_CONFIGMASK 0x00000030 61 #define BCM2835_PM_RSTC_FULL_RESET 0x00000020 62 #define BCM2835_PM_RSTC_RESET 0x00000102 63 64 #define BCM2835_PM_RSTS 0x20 65 #define BCM2835_PM_RSTS_PART(x) \ 66 ((uint16_t)((x) & 0x01) << 0 | (uint16_t)((x) & 0x02) << 1 | \ 67 (uint16_t)((x) & 0x04) << 2 | (uint16_t)((x) & 0x08) << 3 | \ 68 (uint16_t)((x) & 0x10) << 4 | (uint16_t)((x) & 0x20) << 5) 69 70 #define BCM2835_PM_WDOG 0x24 71 #define BCM2835_PM_WDOG_TIMEMASK 0x000fffff 72 73 struct bcm2835pmwdog_softc { 74 device_t sc_dev; 75 76 bus_space_tag_t sc_iot; 77 bus_space_handle_t sc_ioh; 78 79 struct sysmon_wdog sc_smw; 80 }; 81 82 static struct bcm2835pmwdog_softc *bcm2835pmwdog_sc; 83 84 static int bcmpmwdog_match(device_t, cfdata_t, void *); 85 static void bcmpmwdog_attach(device_t, device_t, void *); 86 87 static void bcmpmwdog_set_timeout(struct bcm2835pmwdog_softc *, uint32_t); 88 89 static int bcmpmwdog_setmode(struct sysmon_wdog *); 90 static int bcmpmwdog_tickle(struct sysmon_wdog *); 91 92 /* fdt power controller */ 93 static void bcm2835_power_reset(device_t); 94 static void bcm2835_power_poweroff(device_t); 95 96 CFATTACH_DECL_NEW(bcmpmwdog_fdt, sizeof(struct bcm2835pmwdog_softc), 97 bcmpmwdog_match, bcmpmwdog_attach, NULL, NULL); 98 99 static const struct device_compatible_entry compat_data[] = { 100 { .compat = "brcm,bcm2835-pm-wdt" }, 101 DEVICE_COMPAT_EOL 102 }; 103 104 static struct fdtbus_power_controller_func bcmpmwdog_power_funcs = { 105 .reset = bcm2835_power_reset, 106 .poweroff = bcm2835_power_poweroff 107 }; 108 109 /* ARGSUSED */ 110 static int 111 bcmpmwdog_match(device_t parent, cfdata_t match, void *aux) 112 { 113 struct fdt_attach_args * const faa = aux; 114 115 return of_compatible_match(faa->faa_phandle, compat_data); 116 } 117 118 static void 119 bcmpmwdog_attach(device_t parent, device_t self, void *aux) 120 { 121 struct bcm2835pmwdog_softc *sc = device_private(self); 122 struct fdt_attach_args * const faa = aux; 123 const int phandle = faa->faa_phandle; 124 125 aprint_naive("\n"); 126 aprint_normal(": Power management, Reset and Watchdog controller\n"); 127 128 if (bcm2835pmwdog_sc == NULL) 129 bcm2835pmwdog_sc = sc; 130 131 sc->sc_dev = self; 132 sc->sc_iot = faa->faa_bst; 133 134 bus_addr_t addr; 135 bus_size_t size; 136 137 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 138 aprint_error(": couldn't get register address\n"); 139 return; 140 } 141 142 if (bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_ioh) != 0) { 143 aprint_error_dev(sc->sc_dev, "unable to map device\n"); 144 return; 145 } 146 147 /* watchdog */ 148 sc->sc_smw.smw_name = device_xname(sc->sc_dev); 149 sc->sc_smw.smw_cookie = sc; 150 sc->sc_smw.smw_setmode = bcmpmwdog_setmode; 151 sc->sc_smw.smw_tickle = bcmpmwdog_tickle; 152 sc->sc_smw.smw_period = BCM2835_PM_DEFAULT_PERIOD; 153 if (sysmon_wdog_register(&sc->sc_smw) != 0) 154 aprint_error_dev(self, "couldn't register watchdog\n"); 155 156 fdtbus_register_power_controller(self, phandle, 157 &bcmpmwdog_power_funcs); 158 } 159 160 static void 161 bcmpmwdog_set_timeout(struct bcm2835pmwdog_softc *sc, uint32_t ticks) 162 { 163 uint32_t tmp, rstc, wdog; 164 165 tmp = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_RSTC); 166 167 rstc = wdog = BCM2835_PM_PASSWORD; 168 169 rstc |= tmp & ~BCM2835_PM_RSTC_CONFIGMASK; 170 rstc |= BCM2835_PM_RSTC_FULL_RESET; 171 172 wdog |= ticks & BCM2835_PM_WDOG_TIMEMASK; 173 174 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_WDOG, wdog); 175 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_RSTC, rstc); 176 } 177 178 static int 179 bcmpmwdog_setmode(struct sysmon_wdog *smw) 180 { 181 struct bcm2835pmwdog_softc *sc = smw->smw_cookie; 182 int error = 0; 183 184 if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 185 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_RSTC, 186 BCM2835_PM_PASSWORD | BCM2835_PM_RSTC_RESET); 187 } else { 188 if (smw->smw_period == WDOG_PERIOD_DEFAULT) 189 smw->smw_period = BCM2835_PM_DEFAULT_PERIOD; 190 if (smw->smw_period > (BCM2835_PM_WDOG_TIMEMASK >> 16)) 191 return EINVAL; 192 error = bcmpmwdog_tickle(smw); 193 } 194 195 return error; 196 } 197 198 static void 199 bcmpmwdog_set_partition(struct bcm2835pmwdog_softc *sc, uint8_t part) 200 { 201 uint32_t tmp; 202 203 tmp = bus_space_read_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_RSTS); 204 tmp &= ~(BCM2835_PM_PASSWORD_MASK | BCM2835_PM_RSTS_PART(~0)); 205 tmp |= BCM2835_PM_PASSWORD | BCM2835_PM_RSTS_PART(part); 206 bus_space_write_4(sc->sc_iot, sc->sc_ioh, BCM2835_PM_RSTS, tmp); 207 } 208 209 static int 210 bcmpmwdog_tickle(struct sysmon_wdog *smw) 211 { 212 struct bcm2835pmwdog_softc *sc = smw->smw_cookie; 213 uint32_t timeout = smw->smw_period << 16; 214 215 bcmpmwdog_set_timeout(sc, timeout); 216 217 return 0; 218 } 219 220 static void 221 bcm2835_restart(struct bcm2835pmwdog_softc *sc, int partition) 222 { 223 uint32_t timeout = 10; 224 225 bcmpmwdog_set_partition(sc, partition); 226 bcmpmwdog_set_timeout(sc, timeout); 227 } 228 229 void 230 bcm2835_system_reset(void) 231 { 232 struct bcm2835pmwdog_softc *sc = bcm2835pmwdog_sc; 233 234 bcm2835_restart(sc, 0); 235 } 236 237 static void 238 bcm2835_power_reset(device_t self) 239 { 240 struct bcm2835pmwdog_softc *sc = device_private(self); 241 242 bcm2835_restart(sc, 0); 243 244 for (;;) continue; 245 /* NOTREACHED */ 246 } 247 248 static void 249 bcm2835_power_poweroff(device_t self) 250 { 251 struct bcm2835pmwdog_softc *sc = device_private(self); 252 253 /* Boot from partition 63 is magic to halt boot process */ 254 bcm2835_restart(sc, 63); 255 256 for (;;) continue; 257 /* NOTREACHED */ 258 } 259 260