1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2020 BusyTech 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/eventhandler.h> 30 #include <sys/kernel.h> 31 #include <sys/types.h> 32 #include <sys/bus.h> 33 #include <sys/module.h> 34 #include <sys/systm.h> 35 #include <sys/sysctl.h> 36 #include <sys/rman.h> 37 #include <sys/resource.h> 38 #include <sys/watchdog.h> 39 40 #include <machine/bus.h> 41 #include <machine/resource.h> 42 43 #include <dev/extres/clk/clk.h> 44 #include <dev/fdt/fdt_common.h> 45 #include <dev/ofw/openfirm.h> 46 #include <dev/ofw/ofw_bus.h> 47 #include <dev/ofw/ofw_bus_subr.h> 48 49 /* Registers */ 50 #define DWWDT_CR 0x00 51 #define DWWDT_CR_WDT_EN (1 << 0) 52 #define DWWDT_CR_RESP_MODE (1 << 1) 53 #define DWWDT_TORR 0x04 54 #define DWWDT_CCVR 0x08 55 #define DWWDT_CRR 0x0C 56 #define DWWDT_CRR_KICK 0x76 57 #define DWWDT_STAT 0x10 58 #define DWWDT_STAT_STATUS 0x01 59 #define DWWDT_EOI 0x14 60 61 #define DWWDT_READ4(sc, reg) bus_read_4((sc)->sc_mem_res, (reg)) 62 #define DWWDT_WRITE4(sc, reg, val) \ 63 bus_write_4((sc)->sc_mem_res, (reg), (val)) 64 65 /* 66 * 47 = 16 (timeout shift of dwwdt) + 30 (1s ~= 2 ** 30ns) + 1 67 * (pre-restart delay) 68 */ 69 #define DWWDT_EXP_OFFSET 47 70 71 struct dwwdt_softc { 72 device_t sc_dev; 73 struct resource *sc_mem_res; 74 struct resource *sc_irq_res; 75 void *sc_intr_cookie; 76 clk_t sc_clk; 77 uint64_t sc_clk_freq; 78 eventhandler_tag sc_evtag; 79 int sc_mem_rid; 80 int sc_irq_rid; 81 enum { 82 DWWDT_STOPPED, 83 DWWDT_RUNNING, 84 } sc_status; 85 }; 86 87 static struct ofw_compat_data compat_data[] = { 88 { "snps,dw-wdt", 1 }, 89 { NULL, 0 } 90 }; 91 92 SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 93 "Synopsys Designware watchdog timer"); 94 95 /* Setting this to true disables full restart mode. */ 96 static bool dwwdt_prevent_restart = false; 97 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, prevent_restart, CTLFLAG_RW | CTLFLAG_MPSAFE, 98 &dwwdt_prevent_restart, 0, "Disable system reset on timeout"); 99 100 static bool dwwdt_debug_enabled = false; 101 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_MPSAFE, 102 &dwwdt_debug_enabled, 0, "Enable debug mode"); 103 104 static bool dwwdt_panic_first = true; 105 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, panic_first, CTLFLAG_RW | CTLFLAG_MPSAFE, 106 &dwwdt_panic_first, 0, 107 "Try to panic on timeout, reset on another timeout"); 108 109 static int dwwdt_probe(device_t); 110 static int dwwdt_attach(device_t); 111 static int dwwdt_detach(device_t); 112 static int dwwdt_shutdown(device_t); 113 114 static void dwwdt_intr(void *); 115 static void dwwdt_event(void *, unsigned int, int *); 116 117 /* Helpers */ 118 static inline void dwwdt_start(struct dwwdt_softc *sc); 119 static inline bool dwwdt_started(const struct dwwdt_softc *sc); 120 static inline void dwwdt_stop(struct dwwdt_softc *sc); 121 static inline void dwwdt_set_timeout(const struct dwwdt_softc *sc, int val); 122 123 static void dwwdt_debug(device_t); 124 125 static void 126 dwwdt_debug(device_t dev) 127 { 128 /* 129 * Reading from EOI may clear interrupt flag. 130 */ 131 const struct dwwdt_softc *sc = device_get_softc(dev); 132 133 device_printf(dev, "Registers dump: \n"); 134 device_printf(dev, " CR: %08x\n", DWWDT_READ4(sc, DWWDT_CR)); 135 device_printf(dev, " CCVR: %08x\n", DWWDT_READ4(sc, DWWDT_CCVR)); 136 device_printf(dev, " CRR: %08x\n", DWWDT_READ4(sc, DWWDT_CRR)); 137 device_printf(dev, " STAT: %08x\n", DWWDT_READ4(sc, DWWDT_STAT)); 138 139 device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk)); 140 device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq); 141 } 142 143 static inline bool 144 dwwdt_started(const struct dwwdt_softc *sc) 145 { 146 147 /* CR_WDT_E bit can be clear only by full CPU reset. */ 148 return ((DWWDT_READ4(sc, DWWDT_CR) & DWWDT_CR_WDT_EN) != 0); 149 } 150 151 static void inline 152 dwwdt_start(struct dwwdt_softc *sc) 153 { 154 uint32_t val; 155 156 /* Enable watchdog */ 157 val = DWWDT_READ4(sc, DWWDT_CR); 158 val |= DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE; 159 DWWDT_WRITE4(sc, DWWDT_CR, val); 160 sc->sc_status = DWWDT_RUNNING; 161 } 162 163 static void inline 164 dwwdt_stop(struct dwwdt_softc *sc) 165 { 166 167 sc->sc_status = DWWDT_STOPPED; 168 dwwdt_set_timeout(sc, 0x0f); 169 } 170 171 static void inline 172 dwwdt_set_timeout(const struct dwwdt_softc *sc, int val) 173 { 174 175 DWWDT_WRITE4(sc, DWWDT_TORR, val); 176 DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK); 177 } 178 179 static void 180 dwwdt_intr(void *arg) 181 { 182 struct dwwdt_softc *sc = arg; 183 184 KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0, 185 ("Missing interrupt status bit?")); 186 187 if (dwwdt_prevent_restart || sc->sc_status == DWWDT_STOPPED) { 188 /* 189 * Confirm interrupt reception. Restart counter. 190 * This also emulates stopping watchdog. 191 */ 192 (void)DWWDT_READ4(sc, DWWDT_EOI); 193 return; 194 } 195 196 if (dwwdt_panic_first) 197 panic("dwwdt pre-timeout interrupt"); 198 } 199 200 static void 201 dwwdt_event(void *arg, unsigned int cmd, int *error) 202 { 203 struct dwwdt_softc *sc = arg; 204 const int exponent = flsl(sc->sc_clk_freq); 205 int timeout; 206 int val; 207 208 timeout = cmd & WD_INTERVAL; 209 val = MAX(0, timeout + exponent - DWWDT_EXP_OFFSET + 1); 210 211 dwwdt_stop(sc); 212 if (cmd == 0 || val > 0x0f) { 213 /* 214 * Set maximum time between interrupts and Leave watchdog 215 * disabled. 216 */ 217 return; 218 } 219 220 dwwdt_set_timeout(sc, val); 221 dwwdt_start(sc); 222 *error = 0; 223 224 if (dwwdt_debug_enabled) 225 dwwdt_debug(sc->sc_dev); 226 } 227 228 static int 229 dwwdt_probe(device_t dev) 230 { 231 if (!ofw_bus_status_okay(dev)) 232 return (ENXIO); 233 234 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 235 return (ENXIO); 236 237 device_set_desc(dev, "Synopsys Designware watchdog timer"); 238 return (BUS_PROBE_DEFAULT); 239 } 240 241 static int 242 dwwdt_attach(device_t dev) 243 { 244 struct dwwdt_softc *sc; 245 246 sc = device_get_softc(dev); 247 sc->sc_dev = dev; 248 249 sc->sc_mem_rid = 0; 250 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 251 &sc->sc_mem_rid, RF_ACTIVE); 252 if (sc->sc_mem_res == NULL) { 253 device_printf(dev, "cannot allocate memory resource\n"); 254 goto err_no_mem; 255 } 256 257 sc->sc_irq_rid = 0; 258 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 259 &sc->sc_irq_rid, RF_ACTIVE); 260 if (sc->sc_irq_res == NULL) { 261 device_printf(dev, "cannot allocate ireq resource\n"); 262 goto err_no_irq; 263 } 264 265 sc->sc_intr_cookie = NULL; 266 if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 267 NULL, dwwdt_intr, sc, &sc->sc_intr_cookie) != 0) { 268 device_printf(dev, "cannot setup interrupt routine\n"); 269 goto err_no_intr; 270 } 271 272 if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) { 273 device_printf(dev, "cannot find clock\n"); 274 goto err_no_clock; 275 } 276 277 if (clk_enable(sc->sc_clk) != 0) { 278 device_printf(dev, "cannot enable clock\n"); 279 goto err_no_freq; 280 } 281 282 if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) { 283 device_printf(dev, "cannot get clock frequency\n"); 284 goto err_no_freq; 285 } 286 287 if (sc->sc_clk_freq == 0UL) 288 goto err_no_freq; 289 290 sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0); 291 sc->sc_status = DWWDT_STOPPED; 292 293 return (bus_generic_attach(dev)); 294 295 err_no_freq: 296 clk_release(sc->sc_clk); 297 err_no_clock: 298 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie); 299 err_no_intr: 300 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res); 301 err_no_irq: 302 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, 303 sc->sc_mem_res); 304 err_no_mem: 305 return (ENXIO); 306 } 307 308 static int 309 dwwdt_detach(device_t dev) 310 { 311 struct dwwdt_softc *sc = device_get_softc(dev); 312 313 if (dwwdt_started(sc)) { 314 /* 315 * Once started it cannot be stopped. Prevent module unload 316 * instead. 317 */ 318 return (EBUSY); 319 } 320 321 EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag); 322 sc->sc_evtag = NULL; 323 324 if (sc->sc_clk != NULL) 325 clk_release(sc->sc_clk); 326 327 if (sc->sc_intr_cookie != NULL) 328 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie); 329 330 if (sc->sc_irq_res) { 331 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, 332 sc->sc_irq_res); 333 } 334 335 if (sc->sc_mem_res) { 336 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, 337 sc->sc_mem_res); 338 } 339 340 return (bus_generic_detach(dev)); 341 } 342 343 static int 344 dwwdt_shutdown(device_t dev) 345 { 346 struct dwwdt_softc *sc; 347 348 sc = device_get_softc(dev); 349 350 /* Prevent restarts during shutdown. */ 351 dwwdt_prevent_restart = true; 352 dwwdt_stop(sc); 353 return (bus_generic_shutdown(dev)); 354 } 355 356 static device_method_t dwwdt_methods[] = { 357 DEVMETHOD(device_probe, dwwdt_probe), 358 DEVMETHOD(device_attach, dwwdt_attach), 359 DEVMETHOD(device_detach, dwwdt_detach), 360 DEVMETHOD(device_shutdown, dwwdt_shutdown), 361 362 {0, 0} 363 }; 364 365 static driver_t dwwdt_driver = { 366 "dwwdt", 367 dwwdt_methods, 368 sizeof(struct dwwdt_softc), 369 }; 370 371 DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, NULL, NULL); 372 MODULE_VERSION(dwwdt, 1); 373 OFWBUS_PNP_INFO(compat_data); 374