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