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 devclass_t dwwdt_devclass; 88 89 static struct ofw_compat_data compat_data[] = { 90 { "snps,dw-wdt", 1 }, 91 { NULL, 0 } 92 }; 93 94 SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 95 "Synopsys Designware watchdog timer"); 96 97 /* Setting this to true disables full restart mode. */ 98 static bool dwwdt_prevent_restart = false; 99 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, prevent_restart, CTLFLAG_RW | CTLFLAG_MPSAFE, 100 &dwwdt_prevent_restart, 0, "Disable system reset on timeout"); 101 102 static bool dwwdt_debug_enabled = false; 103 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_MPSAFE, 104 &dwwdt_debug_enabled, 0, "Enable debug mode"); 105 106 static bool dwwdt_panic_first = true; 107 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, panic_first, CTLFLAG_RW | CTLFLAG_MPSAFE, 108 &dwwdt_panic_first, 0, 109 "Try to panic on timeout, reset on another timeout"); 110 111 static int dwwdt_probe(device_t); 112 static int dwwdt_attach(device_t); 113 static int dwwdt_detach(device_t); 114 static int dwwdt_shutdown(device_t); 115 116 static void dwwdt_intr(void *); 117 static void dwwdt_event(void *, unsigned int, int *); 118 119 /* Helpers */ 120 static inline void dwwdt_start(struct dwwdt_softc *sc); 121 static inline bool dwwdt_started(const struct dwwdt_softc *sc); 122 static inline void dwwdt_stop(struct dwwdt_softc *sc); 123 static inline void dwwdt_set_timeout(const struct dwwdt_softc *sc, int val); 124 125 static void dwwdt_debug(device_t); 126 127 static void 128 dwwdt_debug(device_t dev) 129 { 130 /* 131 * Reading from EOI may clear interrupt flag. 132 */ 133 const struct dwwdt_softc *sc = device_get_softc(dev); 134 135 device_printf(dev, "Registers dump: \n"); 136 device_printf(dev, " CR: %08x\n", DWWDT_READ4(sc, DWWDT_CR)); 137 device_printf(dev, " CCVR: %08x\n", DWWDT_READ4(sc, DWWDT_CCVR)); 138 device_printf(dev, " CRR: %08x\n", DWWDT_READ4(sc, DWWDT_CRR)); 139 device_printf(dev, " STAT: %08x\n", DWWDT_READ4(sc, DWWDT_STAT)); 140 141 device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk)); 142 device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq); 143 } 144 145 static inline bool 146 dwwdt_started(const struct dwwdt_softc *sc) 147 { 148 149 /* CR_WDT_E bit can be clear only by full CPU reset. */ 150 return ((DWWDT_READ4(sc, DWWDT_CR) & DWWDT_CR_WDT_EN) != 0); 151 } 152 153 static void inline 154 dwwdt_start(struct dwwdt_softc *sc) 155 { 156 uint32_t val; 157 158 /* Enable watchdog */ 159 val = DWWDT_READ4(sc, DWWDT_CR); 160 val |= DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE; 161 DWWDT_WRITE4(sc, DWWDT_CR, val); 162 sc->sc_status = DWWDT_RUNNING; 163 } 164 165 static void inline 166 dwwdt_stop(struct dwwdt_softc *sc) 167 { 168 169 sc->sc_status = DWWDT_STOPPED; 170 dwwdt_set_timeout(sc, 0x0f); 171 } 172 173 static void inline 174 dwwdt_set_timeout(const struct dwwdt_softc *sc, int val) 175 { 176 177 DWWDT_WRITE4(sc, DWWDT_TORR, val); 178 DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK); 179 } 180 181 static void 182 dwwdt_intr(void *arg) 183 { 184 struct dwwdt_softc *sc = arg; 185 186 KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0, 187 ("Missing interrupt status bit?")); 188 189 if (dwwdt_prevent_restart || sc->sc_status == DWWDT_STOPPED) { 190 /* 191 * Confirm interrupt reception. Restart counter. 192 * This also emulates stopping watchdog. 193 */ 194 (void)DWWDT_READ4(sc, DWWDT_EOI); 195 return; 196 } 197 198 if (dwwdt_panic_first) 199 panic("dwwdt pre-timeout interrupt"); 200 } 201 202 static void 203 dwwdt_event(void *arg, unsigned int cmd, int *error) 204 { 205 struct dwwdt_softc *sc = arg; 206 const int exponent = flsl(sc->sc_clk_freq); 207 int timeout; 208 int val; 209 210 timeout = cmd & WD_INTERVAL; 211 val = MAX(0, timeout + exponent - DWWDT_EXP_OFFSET + 1); 212 213 dwwdt_stop(sc); 214 if (cmd == 0 || val > 0x0f) { 215 /* 216 * Set maximum time between interrupts and Leave watchdog 217 * disabled. 218 */ 219 return; 220 } 221 222 dwwdt_set_timeout(sc, val); 223 dwwdt_start(sc); 224 *error = 0; 225 226 if (dwwdt_debug_enabled) 227 dwwdt_debug(sc->sc_dev); 228 } 229 230 static int 231 dwwdt_probe(device_t dev) 232 { 233 if (!ofw_bus_status_okay(dev)) 234 return (ENXIO); 235 236 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 237 return (ENXIO); 238 239 device_set_desc(dev, "Synopsys Designware watchdog timer"); 240 return (BUS_PROBE_DEFAULT); 241 } 242 243 static int 244 dwwdt_attach(device_t dev) 245 { 246 struct dwwdt_softc *sc; 247 248 sc = device_get_softc(dev); 249 sc->sc_dev = dev; 250 251 sc->sc_mem_rid = 0; 252 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 253 &sc->sc_mem_rid, RF_ACTIVE); 254 if (sc->sc_mem_res == NULL) { 255 device_printf(dev, "cannot allocate memory resource\n"); 256 goto err_no_mem; 257 } 258 259 sc->sc_irq_rid = 0; 260 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 261 &sc->sc_irq_rid, RF_ACTIVE); 262 if (sc->sc_irq_res == NULL) { 263 device_printf(dev, "cannot allocate ireq resource\n"); 264 goto err_no_irq; 265 } 266 267 sc->sc_intr_cookie = NULL; 268 if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, 269 NULL, dwwdt_intr, sc, &sc->sc_intr_cookie) != 0) { 270 device_printf(dev, "cannot setup interrupt routine\n"); 271 goto err_no_intr; 272 } 273 274 if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) { 275 device_printf(dev, "cannot find clock\n"); 276 goto err_no_clock; 277 } 278 279 if (clk_enable(sc->sc_clk) != 0) { 280 device_printf(dev, "cannot enable clock\n"); 281 goto err_no_freq; 282 } 283 284 if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) { 285 device_printf(dev, "cannot get clock frequency\n"); 286 goto err_no_freq; 287 } 288 289 if (sc->sc_clk_freq == 0UL) 290 goto err_no_freq; 291 292 sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0); 293 sc->sc_status = DWWDT_STOPPED; 294 295 return (bus_generic_attach(dev)); 296 297 err_no_freq: 298 clk_release(sc->sc_clk); 299 err_no_clock: 300 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie); 301 err_no_intr: 302 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res); 303 err_no_irq: 304 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, 305 sc->sc_mem_res); 306 err_no_mem: 307 return (ENXIO); 308 } 309 310 static int 311 dwwdt_detach(device_t dev) 312 { 313 struct dwwdt_softc *sc = device_get_softc(dev); 314 315 if (dwwdt_started(sc)) { 316 /* 317 * Once started it cannot be stopped. Prevent module unload 318 * instead. 319 */ 320 return (EBUSY); 321 } 322 323 EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag); 324 sc->sc_evtag = NULL; 325 326 if (sc->sc_clk != NULL) 327 clk_release(sc->sc_clk); 328 329 if (sc->sc_intr_cookie != NULL) 330 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie); 331 332 if (sc->sc_irq_res) { 333 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, 334 sc->sc_irq_res); 335 } 336 337 if (sc->sc_mem_res) { 338 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, 339 sc->sc_mem_res); 340 } 341 342 return (bus_generic_detach(dev)); 343 } 344 345 static int 346 dwwdt_shutdown(device_t dev) 347 { 348 struct dwwdt_softc *sc; 349 350 sc = device_get_softc(dev); 351 352 /* Prevent restarts during shutdown. */ 353 dwwdt_prevent_restart = true; 354 dwwdt_stop(sc); 355 return (bus_generic_shutdown(dev)); 356 } 357 358 static device_method_t dwwdt_methods[] = { 359 DEVMETHOD(device_probe, dwwdt_probe), 360 DEVMETHOD(device_attach, dwwdt_attach), 361 DEVMETHOD(device_detach, dwwdt_detach), 362 DEVMETHOD(device_shutdown, dwwdt_shutdown), 363 364 {0, 0} 365 }; 366 367 static driver_t dwwdt_driver = { 368 "dwwdt", 369 dwwdt_methods, 370 sizeof(struct dwwdt_softc), 371 }; 372 373 DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, dwwdt_devclass, NULL, NULL); 374 MODULE_VERSION(dwwdt, 1); 375 OFWBUS_PNP_INFO(compat_data); 376