11b1a53cfSOleksandr Tymoshenko /*- 2af3dc4a7SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3af3dc4a7SPedro F. Giffuni * 41b1a53cfSOleksandr Tymoshenko * Copyright (c) 2012 Alexander Rybalko <ray@freebsd.org> 51b1a53cfSOleksandr Tymoshenko * All rights reserved. 61b1a53cfSOleksandr Tymoshenko * 71b1a53cfSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 81b1a53cfSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 91b1a53cfSOleksandr Tymoshenko * are met: 101b1a53cfSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 111b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 121b1a53cfSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 131b1a53cfSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 141b1a53cfSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 151b1a53cfSOleksandr Tymoshenko * 161b1a53cfSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 171b1a53cfSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 181b1a53cfSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 191b1a53cfSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 201b1a53cfSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 211b1a53cfSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 221b1a53cfSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 231b1a53cfSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 241b1a53cfSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251b1a53cfSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261b1a53cfSOleksandr Tymoshenko * SUCH DAMAGE. 271b1a53cfSOleksandr Tymoshenko */ 281b1a53cfSOleksandr Tymoshenko #include <sys/cdefs.h> 291b1a53cfSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 301b1a53cfSOleksandr Tymoshenko 311b1a53cfSOleksandr Tymoshenko #include <sys/param.h> 32e2e050c8SConrad Meyer #include <sys/bus.h> 33e2e050c8SConrad Meyer #include <sys/eventhandler.h> 34e2e050c8SConrad Meyer #include <sys/kernel.h> 35e2e050c8SConrad Meyer #include <sys/lock.h> 36e2e050c8SConrad Meyer #include <sys/module.h> 37e2e050c8SConrad Meyer #include <sys/mutex.h> 38*fc0804f1SAndrew Turner #include <sys/reboot.h> 39e2e050c8SConrad Meyer #include <sys/rman.h> 401b1a53cfSOleksandr Tymoshenko #include <sys/systm.h> 411b1a53cfSOleksandr Tymoshenko #include <sys/watchdog.h> 421b1a53cfSOleksandr Tymoshenko 431b1a53cfSOleksandr Tymoshenko #include <dev/ofw/openfirm.h> 441b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 451b1a53cfSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 461b1a53cfSOleksandr Tymoshenko 471b1a53cfSOleksandr Tymoshenko #include <machine/bus.h> 481b1a53cfSOleksandr Tymoshenko #include <machine/machdep.h> 491b1a53cfSOleksandr Tymoshenko 501b1a53cfSOleksandr Tymoshenko #include <arm/broadcom/bcm2835/bcm2835_wdog.h> 511b1a53cfSOleksandr Tymoshenko 525613f784SGavin Atkinson #define BCM2835_PASSWORD 0x5a 531b1a53cfSOleksandr Tymoshenko 541b1a53cfSOleksandr Tymoshenko #define BCM2835_WDOG_RESET 0 551b1a53cfSOleksandr Tymoshenko #define BCM2835_PASSWORD_MASK 0xff000000 561b1a53cfSOleksandr Tymoshenko #define BCM2835_PASSWORD_SHIFT 24 571b1a53cfSOleksandr Tymoshenko #define BCM2835_WDOG_TIME_MASK 0x000fffff 581b1a53cfSOleksandr Tymoshenko #define BCM2835_WDOG_TIME_SHIFT 0 591b1a53cfSOleksandr Tymoshenko 6058a3e50dSOleksandr Tymoshenko #define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset) 6158a3e50dSOleksandr Tymoshenko #define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r) + (_sc)->regs_offset, (_v)) 621b1a53cfSOleksandr Tymoshenko 631b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTC_WRCFG_CLR 0xffffffcf 641b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTC_WRCFG_SET 0x00000030 651b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTC_WRCFG_FULL_RESET 0x00000020 661b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTC_RESET 0x00000102 671b1a53cfSOleksandr Tymoshenko 681b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTC_REG 0x00 691b1a53cfSOleksandr Tymoshenko #define BCM2835_RSTS_REG 0x04 701b1a53cfSOleksandr Tymoshenko #define BCM2835_WDOG_REG 0x08 711b1a53cfSOleksandr Tymoshenko 721b1a53cfSOleksandr Tymoshenko static struct bcmwd_softc *bcmwd_lsc = NULL; 731b1a53cfSOleksandr Tymoshenko 741b1a53cfSOleksandr Tymoshenko struct bcmwd_softc { 751b1a53cfSOleksandr Tymoshenko device_t dev; 761b1a53cfSOleksandr Tymoshenko struct resource * res; 771b1a53cfSOleksandr Tymoshenko bus_space_tag_t bst; 781b1a53cfSOleksandr Tymoshenko bus_space_handle_t bsh; 791b1a53cfSOleksandr Tymoshenko int wdog_armed; 801b1a53cfSOleksandr Tymoshenko int wdog_period; 811b1a53cfSOleksandr Tymoshenko char wdog_passwd; 8200b1705bSOleksandr Tymoshenko struct mtx mtx; 8358a3e50dSOleksandr Tymoshenko int regs_offset; 8458a3e50dSOleksandr Tymoshenko }; 8558a3e50dSOleksandr Tymoshenko 8658a3e50dSOleksandr Tymoshenko #define BSD_DTB 1 8758a3e50dSOleksandr Tymoshenko #define UPSTREAM_DTB 2 8858a3e50dSOleksandr Tymoshenko #define UPSTREAM_DTB_REGS_OFFSET 0x1c 8958a3e50dSOleksandr Tymoshenko 9058a3e50dSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = { 9158a3e50dSOleksandr Tymoshenko {"broadcom,bcm2835-wdt", BSD_DTB}, 9258a3e50dSOleksandr Tymoshenko {"brcm,bcm2835-pm-wdt", UPSTREAM_DTB}, 93*fc0804f1SAndrew Turner {"brcm,bcm2835-pm", UPSTREAM_DTB}, 9458a3e50dSOleksandr Tymoshenko {NULL, 0} 951b1a53cfSOleksandr Tymoshenko }; 961b1a53cfSOleksandr Tymoshenko 971b1a53cfSOleksandr Tymoshenko static void bcmwd_watchdog_fn(void *private, u_int cmd, int *error); 98*fc0804f1SAndrew Turner static void bcmwd_reboot_system(void *, int); 991b1a53cfSOleksandr Tymoshenko 1001b1a53cfSOleksandr Tymoshenko static int 1011b1a53cfSOleksandr Tymoshenko bcmwd_probe(device_t dev) 1021b1a53cfSOleksandr Tymoshenko { 1031b1a53cfSOleksandr Tymoshenko 104add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 105add35ed5SIan Lepore return (ENXIO); 106add35ed5SIan Lepore 10758a3e50dSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1081b1a53cfSOleksandr Tymoshenko return (ENXIO); 10958a3e50dSOleksandr Tymoshenko 11058a3e50dSOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 Watchdog"); 11158a3e50dSOleksandr Tymoshenko 11258a3e50dSOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 1131b1a53cfSOleksandr Tymoshenko } 1141b1a53cfSOleksandr Tymoshenko 1151b1a53cfSOleksandr Tymoshenko static int 1161b1a53cfSOleksandr Tymoshenko bcmwd_attach(device_t dev) 1171b1a53cfSOleksandr Tymoshenko { 1181b1a53cfSOleksandr Tymoshenko struct bcmwd_softc *sc; 1191b1a53cfSOleksandr Tymoshenko int rid; 1201b1a53cfSOleksandr Tymoshenko 1211b1a53cfSOleksandr Tymoshenko if (bcmwd_lsc != NULL) 1221b1a53cfSOleksandr Tymoshenko return (ENXIO); 1231b1a53cfSOleksandr Tymoshenko 1241b1a53cfSOleksandr Tymoshenko sc = device_get_softc(dev); 1251b1a53cfSOleksandr Tymoshenko sc->wdog_period = 7; 1265613f784SGavin Atkinson sc->wdog_passwd = BCM2835_PASSWORD; 1271b1a53cfSOleksandr Tymoshenko sc->wdog_armed = 0; 1281b1a53cfSOleksandr Tymoshenko sc->dev = dev; 1291b1a53cfSOleksandr Tymoshenko 1301b1a53cfSOleksandr Tymoshenko rid = 0; 1311b1a53cfSOleksandr Tymoshenko sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 1321b1a53cfSOleksandr Tymoshenko if (sc->res == NULL) { 1331b1a53cfSOleksandr Tymoshenko device_printf(dev, "could not allocate memory resource\n"); 1341b1a53cfSOleksandr Tymoshenko return (ENXIO); 1351b1a53cfSOleksandr Tymoshenko } 1361b1a53cfSOleksandr Tymoshenko 1371b1a53cfSOleksandr Tymoshenko sc->bst = rman_get_bustag(sc->res); 1381b1a53cfSOleksandr Tymoshenko sc->bsh = rman_get_bushandle(sc->res); 1391b1a53cfSOleksandr Tymoshenko 14058a3e50dSOleksandr Tymoshenko /* compensate base address difference */ 14158a3e50dSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data 14258a3e50dSOleksandr Tymoshenko == UPSTREAM_DTB) 14358a3e50dSOleksandr Tymoshenko sc->regs_offset = UPSTREAM_DTB_REGS_OFFSET; 14458a3e50dSOleksandr Tymoshenko 1451b1a53cfSOleksandr Tymoshenko bcmwd_lsc = sc; 14600b1705bSOleksandr Tymoshenko mtx_init(&sc->mtx, "BCM2835 Watchdog", "bcmwd", MTX_DEF); 1471b1a53cfSOleksandr Tymoshenko EVENTHANDLER_REGISTER(watchdog_list, bcmwd_watchdog_fn, sc, 0); 14800b1705bSOleksandr Tymoshenko 149*fc0804f1SAndrew Turner /* 150*fc0804f1SAndrew Turner * Handle reboot events. This needs to happen with slightly greater 151*fc0804f1SAndrew Turner * priority than the PSCI handler, since PSCI reset is not properly 152*fc0804f1SAndrew Turner * implemented on the Pi and it just puts the Pi into a halt 153*fc0804f1SAndrew Turner * state. 154*fc0804f1SAndrew Turner */ 155*fc0804f1SAndrew Turner EVENTHANDLER_REGISTER(shutdown_final, bcmwd_reboot_system, sc, 156*fc0804f1SAndrew Turner SHUTDOWN_PRI_LAST-1); 157*fc0804f1SAndrew Turner 1581b1a53cfSOleksandr Tymoshenko return (0); 1591b1a53cfSOleksandr Tymoshenko } 1601b1a53cfSOleksandr Tymoshenko 1611b1a53cfSOleksandr Tymoshenko static void 1621b1a53cfSOleksandr Tymoshenko bcmwd_watchdog_fn(void *private, u_int cmd, int *error) 1631b1a53cfSOleksandr Tymoshenko { 16400b1705bSOleksandr Tymoshenko struct bcmwd_softc *sc; 16500b1705bSOleksandr Tymoshenko uint64_t sec; 16600b1705bSOleksandr Tymoshenko uint32_t ticks, reg; 16700b1705bSOleksandr Tymoshenko 16800b1705bSOleksandr Tymoshenko sc = private; 16900b1705bSOleksandr Tymoshenko mtx_lock(&sc->mtx); 17000b1705bSOleksandr Tymoshenko 17100b1705bSOleksandr Tymoshenko cmd &= WD_INTERVAL; 17200b1705bSOleksandr Tymoshenko 17300b1705bSOleksandr Tymoshenko if (cmd > 0) { 17400b1705bSOleksandr Tymoshenko sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000; 1757f353dddSIan Lepore if (sec == 0 || sec > 15) { 17600b1705bSOleksandr Tymoshenko /* 17700b1705bSOleksandr Tymoshenko * Can't arm 17800b1705bSOleksandr Tymoshenko * disable watchdog as watchdog(9) requires 17900b1705bSOleksandr Tymoshenko */ 18000b1705bSOleksandr Tymoshenko device_printf(sc->dev, 1817f353dddSIan Lepore "Can't arm, timeout must be between 1-15 seconds\n"); 18200b1705bSOleksandr Tymoshenko WRITE(sc, BCM2835_RSTC_REG, 1835613f784SGavin Atkinson (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 18400b1705bSOleksandr Tymoshenko BCM2835_RSTC_RESET); 18500b1705bSOleksandr Tymoshenko mtx_unlock(&sc->mtx); 186*fc0804f1SAndrew Turner *error = EINVAL; 18700b1705bSOleksandr Tymoshenko return; 1881b1a53cfSOleksandr Tymoshenko } 18900b1705bSOleksandr Tymoshenko 1907f353dddSIan Lepore ticks = (sec << 16) & BCM2835_WDOG_TIME_MASK; 1915613f784SGavin Atkinson reg = (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | ticks; 19200b1705bSOleksandr Tymoshenko WRITE(sc, BCM2835_WDOG_REG, reg); 19300b1705bSOleksandr Tymoshenko 19400b1705bSOleksandr Tymoshenko reg = READ(sc, BCM2835_RSTC_REG); 19500b1705bSOleksandr Tymoshenko reg &= BCM2835_RSTC_WRCFG_CLR; 19600b1705bSOleksandr Tymoshenko reg |= BCM2835_RSTC_WRCFG_FULL_RESET; 1975613f784SGavin Atkinson reg |= (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT); 19800b1705bSOleksandr Tymoshenko WRITE(sc, BCM2835_RSTC_REG, reg); 19900b1705bSOleksandr Tymoshenko 20000b1705bSOleksandr Tymoshenko *error = 0; 20100b1705bSOleksandr Tymoshenko } 20200b1705bSOleksandr Tymoshenko else 20300b1705bSOleksandr Tymoshenko WRITE(sc, BCM2835_RSTC_REG, 2045613f784SGavin Atkinson (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 20500b1705bSOleksandr Tymoshenko BCM2835_RSTC_RESET); 20600b1705bSOleksandr Tymoshenko 20700b1705bSOleksandr Tymoshenko mtx_unlock(&sc->mtx); 20800b1705bSOleksandr Tymoshenko } 2091b1a53cfSOleksandr Tymoshenko 2101b1a53cfSOleksandr Tymoshenko void 21159249a51SAndrew Turner bcmwd_watchdog_reset(void) 2121b1a53cfSOleksandr Tymoshenko { 2131b1a53cfSOleksandr Tymoshenko 2141b1a53cfSOleksandr Tymoshenko if (bcmwd_lsc == NULL) 2151b1a53cfSOleksandr Tymoshenko return; 2161b1a53cfSOleksandr Tymoshenko 2171b1a53cfSOleksandr Tymoshenko WRITE(bcmwd_lsc, BCM2835_WDOG_REG, 2185613f784SGavin Atkinson (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 10); 2191b1a53cfSOleksandr Tymoshenko 2201b1a53cfSOleksandr Tymoshenko WRITE(bcmwd_lsc, BCM2835_RSTC_REG, 2211b1a53cfSOleksandr Tymoshenko (READ(bcmwd_lsc, BCM2835_RSTC_REG) & BCM2835_RSTC_WRCFG_CLR) | 2225613f784SGavin Atkinson (BCM2835_PASSWORD << BCM2835_PASSWORD_SHIFT) | 2231b1a53cfSOleksandr Tymoshenko BCM2835_RSTC_WRCFG_FULL_RESET); 2241b1a53cfSOleksandr Tymoshenko } 2251b1a53cfSOleksandr Tymoshenko 226*fc0804f1SAndrew Turner static void 227*fc0804f1SAndrew Turner bcmwd_reboot_system(void *sc, int howto) 228*fc0804f1SAndrew Turner { 229*fc0804f1SAndrew Turner int cmd, error = 0; 230*fc0804f1SAndrew Turner 231*fc0804f1SAndrew Turner /* Only handle reset. */ 232*fc0804f1SAndrew Turner if (howto & RB_HALT || howto & RB_POWEROFF) 233*fc0804f1SAndrew Turner return; 234*fc0804f1SAndrew Turner 235*fc0804f1SAndrew Turner printf("Resetting system ... "); 236*fc0804f1SAndrew Turner 237*fc0804f1SAndrew Turner cmd = WD_TO_1SEC; 238*fc0804f1SAndrew Turner bcmwd_watchdog_fn(sc, cmd, &error); 239*fc0804f1SAndrew Turner 240*fc0804f1SAndrew Turner /* Wait for watchdog timeout. */ 241*fc0804f1SAndrew Turner DELAY(2000000); 242*fc0804f1SAndrew Turner 243*fc0804f1SAndrew Turner /* Not reached ... one hopes. */ 244*fc0804f1SAndrew Turner printf("failed to reset (errno %d).\n", error); 245*fc0804f1SAndrew Turner } 246*fc0804f1SAndrew Turner 2471b1a53cfSOleksandr Tymoshenko static device_method_t bcmwd_methods[] = { 2481b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_probe, bcmwd_probe), 2491b1a53cfSOleksandr Tymoshenko DEVMETHOD(device_attach, bcmwd_attach), 2501b1a53cfSOleksandr Tymoshenko 2511b1a53cfSOleksandr Tymoshenko DEVMETHOD_END 2521b1a53cfSOleksandr Tymoshenko }; 2531b1a53cfSOleksandr Tymoshenko 2541b1a53cfSOleksandr Tymoshenko static driver_t bcmwd_driver = { 2551b1a53cfSOleksandr Tymoshenko "bcmwd", 2561b1a53cfSOleksandr Tymoshenko bcmwd_methods, 2571b1a53cfSOleksandr Tymoshenko sizeof(struct bcmwd_softc), 2581b1a53cfSOleksandr Tymoshenko }; 2591b1a53cfSOleksandr Tymoshenko static devclass_t bcmwd_devclass; 2601b1a53cfSOleksandr Tymoshenko 2611b1a53cfSOleksandr Tymoshenko DRIVER_MODULE(bcmwd, simplebus, bcmwd_driver, bcmwd_devclass, 0, 0); 262