2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2020 BusyTech
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/param.h>
29 #include <sys/eventhandler.h>
30 #include <sys/kernel.h>
31 #include <sys/types.h>
33 #include <sys/module.h>
34 #include <sys/systm.h>
35 #include <sys/sysctl.h>
37 #include <sys/resource.h>
38 #include <sys/watchdog.h>
40 #include <machine/bus.h>
41 #include <machine/resource.h>
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>
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
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))
66 * 47 = 16 (timeout shift of dwwdt) + 30 (1s ~= 2 ** 30ns) + 1
69 #define DWWDT_EXP_OFFSET 47
73 struct resource *sc_mem_res;
74 struct resource *sc_irq_res;
78 eventhandler_tag sc_evtag;
87 static devclass_t dwwdt_devclass;
89 static struct ofw_compat_data compat_data[] = {
94 SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
95 "Synopsys Designware watchdog timer");
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");
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");
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");
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);
116 static void dwwdt_intr(void *);
117 static void dwwdt_event(void *, unsigned int, int *);
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);
125 static void dwwdt_debug(device_t);
128 dwwdt_debug(device_t dev)
131 * Reading from EOI may clear interrupt flag.
133 const struct dwwdt_softc *sc = device_get_softc(dev);
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));
141 device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk));
142 device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq);
146 dwwdt_started(const struct dwwdt_softc *sc)
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);
154 dwwdt_start(struct dwwdt_softc *sc)
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;
166 dwwdt_stop(struct dwwdt_softc *sc)
169 sc->sc_status = DWWDT_STOPPED;
170 dwwdt_set_timeout(sc, 0x0f);
174 dwwdt_set_timeout(const struct dwwdt_softc *sc, int val)
177 DWWDT_WRITE4(sc, DWWDT_TORR, val);
178 DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK);
182 dwwdt_intr(void *arg)
184 struct dwwdt_softc *sc = arg;
186 KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0,
187 ("Missing interrupt status bit?"));
189 if (dwwdt_prevent_restart || sc->sc_status == DWWDT_STOPPED) {
191 * Confirm interrupt reception. Restart counter.
192 * This also emulates stopping watchdog.
194 (void)DWWDT_READ4(sc, DWWDT_EOI);
198 if (dwwdt_panic_first)
199 panic("dwwdt pre-timeout interrupt");
203 dwwdt_event(void *arg, unsigned int cmd, int *error)
205 struct dwwdt_softc *sc = arg;
206 const int exponent = flsl(sc->sc_clk_freq);
210 timeout = cmd & WD_INTERVAL;
211 val = MAX(0, timeout + exponent - DWWDT_EXP_OFFSET + 1);
214 if (cmd == 0 || val > 0x0f) {
216 * Set maximum time between interrupts and Leave watchdog
222 dwwdt_set_timeout(sc, val);
226 if (dwwdt_debug_enabled)
227 dwwdt_debug(sc->sc_dev);
231 dwwdt_probe(device_t dev)
233 if (!ofw_bus_status_okay(dev))
236 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
239 device_set_desc(dev, "Synopsys Designware watchdog timer");
240 return (BUS_PROBE_DEFAULT);
244 dwwdt_attach(device_t dev)
246 struct dwwdt_softc *sc;
248 sc = device_get_softc(dev);
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");
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");
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");
274 if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) {
275 device_printf(dev, "cannot find clock\n");
279 if (clk_enable(sc->sc_clk) != 0) {
280 device_printf(dev, "cannot enable clock\n");
284 if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) {
285 device_printf(dev, "cannot get clock frequency\n");
289 if (sc->sc_clk_freq == 0UL)
292 sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0);
293 sc->sc_status = DWWDT_STOPPED;
295 return (bus_generic_attach(dev));
298 clk_release(sc->sc_clk);
300 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
302 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
304 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
311 dwwdt_detach(device_t dev)
313 struct dwwdt_softc *sc = device_get_softc(dev);
315 if (dwwdt_started(sc)) {
317 * Once started it cannot be stopped. Prevent module unload
323 EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag);
326 if (sc->sc_clk != NULL)
327 clk_release(sc->sc_clk);
329 if (sc->sc_intr_cookie != NULL)
330 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
332 if (sc->sc_irq_res) {
333 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
337 if (sc->sc_mem_res) {
338 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
342 return (bus_generic_detach(dev));
346 dwwdt_shutdown(device_t dev)
348 struct dwwdt_softc *sc;
350 sc = device_get_softc(dev);
352 /* Prevent restarts during shutdown. */
353 dwwdt_prevent_restart = true;
355 return (bus_generic_shutdown(dev));
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),
367 static driver_t dwwdt_driver = {
370 sizeof(struct dwwdt_softc),
373 DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, dwwdt_devclass, NULL, NULL);
374 MODULE_VERSION(dwwdt, 1);
375 OFWBUS_PNP_INFO(compat_data);