1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2022 Michael J. Karels <karels@freebsd.org> 5 * Copyright (c) 2012 Alexander Rybalko <ray@freebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/bus.h> 32 #include <sys/eventhandler.h> 33 #include <sys/kernel.h> 34 #include <sys/limits.h> 35 #include <sys/lock.h> 36 #include <sys/module.h> 37 #include <sys/mutex.h> 38 #include <sys/reboot.h> 39 #include <sys/rman.h> 40 #include <sys/systm.h> 41 #include <sys/watchdog.h> 42 43 #include <dev/ofw/openfirm.h> 44 #include <dev/ofw/ofw_bus.h> 45 #include <dev/ofw/ofw_bus_subr.h> 46 47 #include <dev/clk/clk.h> 48 49 #include <machine/bus.h> 50 #include <machine/machdep.h> 51 52 #define APPLE_WDOG_WD0_TIMER 0x0000 53 #define APPLE_WDOG_WD0_RESET 0x0004 54 #define APPLE_WDOG_WD0_INTR 0x0008 55 #define APPLE_WDOG_WD0_CNTL 0x000c 56 57 #define APPLE_WDOG_WD1_TIMER 0x0010 58 #define APPLE_WDOG_WD1_RESET 0x0014 59 #define APPLE_WDOG_WD1_CNTL 0x001c 60 61 #define APPLE_WDOG_WD2_TIMER 0x0020 62 #define APPLE_WDOG_WD2_RESET 0x0024 63 #define APPLE_WDOG_WD2_CNTL 0x002c 64 65 #define APPLE_WDOG_CNTL_INTENABLE 0x0001 66 #define APPLE_WDOG_CNTL_INTSTAT 0x0002 67 #define APPLE_WDOG_CNTL_RSTENABLE 0x0004 68 69 #define READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r)) 70 #define WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r), (_v)) 71 72 struct apple_wdog_softc { 73 device_t dev; 74 struct resource * res; 75 bus_space_tag_t bst; 76 bus_space_handle_t bsh; 77 clk_t clk; 78 uint64_t clk_freq; 79 struct mtx mtx; 80 }; 81 82 static struct ofw_compat_data compat_data[] = { 83 {"apple,wdt", 1}, 84 {NULL, 0} 85 }; 86 87 static void apple_wdog_watchdog_fn(void *private, u_int cmd, int *error); 88 static void apple_wdog_reboot_system(void *, int); 89 90 static int 91 apple_wdog_probe(device_t dev) 92 { 93 94 if (!ofw_bus_status_okay(dev)) 95 return (ENXIO); 96 97 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 98 return (ENXIO); 99 100 device_set_desc(dev, "Apple Watchdog"); 101 102 return (BUS_PROBE_DEFAULT); 103 } 104 105 static int 106 apple_wdog_attach(device_t dev) 107 { 108 struct apple_wdog_softc *sc; 109 int error, rid; 110 111 sc = device_get_softc(dev); 112 sc->dev = dev; 113 114 rid = 0; 115 sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 116 if (sc->res == NULL) { 117 device_printf(dev, "could not allocate memory resource\n"); 118 return (ENXIO); 119 } 120 121 sc->bst = rman_get_bustag(sc->res); 122 sc->bsh = rman_get_bushandle(sc->res); 123 124 error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk); 125 if (error != 0) { 126 device_printf(dev, "cannot get clock\n"); 127 goto fail; 128 } 129 error = clk_enable(sc->clk); 130 if (error != 0) { 131 device_printf(dev, "cannot enable clock\n"); 132 goto fail; 133 } 134 error = clk_get_freq(sc->clk, &sc->clk_freq); 135 if (error != 0) { 136 device_printf(dev, "cannot get base frequency\n"); 137 goto fail_clk; 138 } 139 140 mtx_init(&sc->mtx, "Apple Watchdog", "apple_wdog", MTX_DEF); 141 EVENTHANDLER_REGISTER(watchdog_list, apple_wdog_watchdog_fn, sc, 0); 142 EVENTHANDLER_REGISTER(shutdown_final, apple_wdog_reboot_system, sc, 143 SHUTDOWN_PRI_LAST); 144 145 /* Reset the watchdog timers. */ 146 WRITE(sc, APPLE_WDOG_WD0_CNTL, 0); 147 WRITE(sc, APPLE_WDOG_WD1_CNTL, 0); 148 149 return (0); 150 151 fail_clk: 152 clk_disable(sc->clk); 153 fail: 154 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res); 155 return (error); 156 } 157 158 static void 159 apple_wdog_watchdog_fn(void *private, u_int cmd, int *error) 160 { 161 struct apple_wdog_softc *sc; 162 uint64_t sec; 163 uint32_t ticks, sec_max; 164 165 sc = private; 166 mtx_lock(&sc->mtx); 167 168 cmd &= WD_INTERVAL; 169 170 if (cmd > 0) { 171 sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000; 172 sec_max = UINT_MAX / sc->clk_freq; 173 if (sec == 0 || sec > sec_max) { 174 /* 175 * Can't arm 176 * disable watchdog as watchdog(9) requires 177 */ 178 device_printf(sc->dev, 179 "Can't arm, timeout must be between 1-%d seconds\n", 180 sec_max); 181 WRITE(sc, APPLE_WDOG_WD1_CNTL, 0); 182 mtx_unlock(&sc->mtx); 183 *error = EINVAL; 184 return; 185 } 186 187 ticks = sec * sc->clk_freq; 188 WRITE(sc, APPLE_WDOG_WD1_TIMER, 0); 189 WRITE(sc, APPLE_WDOG_WD1_RESET, ticks); 190 WRITE(sc, APPLE_WDOG_WD1_CNTL, APPLE_WDOG_CNTL_RSTENABLE); 191 192 *error = 0; 193 } else 194 WRITE(sc, APPLE_WDOG_WD1_CNTL, 0); 195 196 mtx_unlock(&sc->mtx); 197 } 198 199 static void 200 apple_wdog_reboot_system(void *private, int howto) 201 { 202 struct apple_wdog_softc *sc = private; 203 204 /* Only handle reset. */ 205 if ((howto & (RB_HALT | RB_POWEROFF)) != 0) 206 return; 207 208 printf("Resetting system ... "); 209 210 WRITE(sc, APPLE_WDOG_WD1_CNTL, APPLE_WDOG_CNTL_RSTENABLE); 211 WRITE(sc, APPLE_WDOG_WD1_RESET, 1); 212 WRITE(sc, APPLE_WDOG_WD1_TIMER, 0); 213 214 /* Wait for watchdog timeout; should take milliseconds. */ 215 DELAY(2000000); 216 217 /* Not reached ... one hopes. */ 218 printf("failed to reset.\n"); 219 } 220 221 static device_method_t apple_wdog_methods[] = { 222 DEVMETHOD(device_probe, apple_wdog_probe), 223 DEVMETHOD(device_attach, apple_wdog_attach), 224 225 DEVMETHOD_END 226 }; 227 228 static driver_t apple_wdog_driver = { 229 "apple_wdog", 230 apple_wdog_methods, 231 sizeof(struct apple_wdog_softc), 232 }; 233 234 DRIVER_MODULE(apple_wdog, simplebus, apple_wdog_driver, 0, 0); 235