10a05676bSEmmanuel Vadot /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
30a05676bSEmmanuel Vadot *
40a05676bSEmmanuel Vadot * Copyright (c) 2020 BusyTech
50a05676bSEmmanuel Vadot *
60a05676bSEmmanuel Vadot * Redistribution and use in source and binary forms, with or without
70a05676bSEmmanuel Vadot * modification, are permitted provided that the following conditions
80a05676bSEmmanuel Vadot * are met:
90a05676bSEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright
100a05676bSEmmanuel Vadot * notice, this list of conditions and the following disclaimer.
110a05676bSEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright
120a05676bSEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the
130a05676bSEmmanuel Vadot * documentation and/or other materials provided with the distribution.
140a05676bSEmmanuel Vadot *
150a05676bSEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160a05676bSEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170a05676bSEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
180a05676bSEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
190a05676bSEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200a05676bSEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210a05676bSEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220a05676bSEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230a05676bSEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240a05676bSEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250a05676bSEmmanuel Vadot * SUCH DAMAGE.
260a05676bSEmmanuel Vadot */
270a05676bSEmmanuel Vadot
280a05676bSEmmanuel Vadot #include <sys/param.h>
290a05676bSEmmanuel Vadot #include <sys/eventhandler.h>
300a05676bSEmmanuel Vadot #include <sys/kernel.h>
310a05676bSEmmanuel Vadot #include <sys/types.h>
320a05676bSEmmanuel Vadot #include <sys/bus.h>
330a05676bSEmmanuel Vadot #include <sys/module.h>
340a05676bSEmmanuel Vadot #include <sys/systm.h>
350a05676bSEmmanuel Vadot #include <sys/sysctl.h>
360a05676bSEmmanuel Vadot #include <sys/rman.h>
370a05676bSEmmanuel Vadot #include <sys/resource.h>
380a05676bSEmmanuel Vadot #include <sys/watchdog.h>
390a05676bSEmmanuel Vadot
400a05676bSEmmanuel Vadot #include <machine/bus.h>
410a05676bSEmmanuel Vadot #include <machine/resource.h>
420a05676bSEmmanuel Vadot
43be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
440a05676bSEmmanuel Vadot #include <dev/fdt/fdt_common.h>
450a05676bSEmmanuel Vadot #include <dev/ofw/openfirm.h>
460a05676bSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
470a05676bSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
480a05676bSEmmanuel Vadot
490a05676bSEmmanuel Vadot /* Registers */
500a05676bSEmmanuel Vadot #define DWWDT_CR 0x00
510a05676bSEmmanuel Vadot #define DWWDT_CR_WDT_EN (1 << 0)
520a05676bSEmmanuel Vadot #define DWWDT_CR_RESP_MODE (1 << 1)
530a05676bSEmmanuel Vadot #define DWWDT_TORR 0x04
540a05676bSEmmanuel Vadot #define DWWDT_CCVR 0x08
550a05676bSEmmanuel Vadot #define DWWDT_CRR 0x0C
560a05676bSEmmanuel Vadot #define DWWDT_CRR_KICK 0x76
570a05676bSEmmanuel Vadot #define DWWDT_STAT 0x10
580a05676bSEmmanuel Vadot #define DWWDT_STAT_STATUS 0x01
590a05676bSEmmanuel Vadot #define DWWDT_EOI 0x14
600a05676bSEmmanuel Vadot
610a05676bSEmmanuel Vadot #define DWWDT_READ4(sc, reg) bus_read_4((sc)->sc_mem_res, (reg))
620a05676bSEmmanuel Vadot #define DWWDT_WRITE4(sc, reg, val) \
630a05676bSEmmanuel Vadot bus_write_4((sc)->sc_mem_res, (reg), (val))
640a05676bSEmmanuel Vadot
650a05676bSEmmanuel Vadot /*
660a05676bSEmmanuel Vadot * 47 = 16 (timeout shift of dwwdt) + 30 (1s ~= 2 ** 30ns) + 1
670a05676bSEmmanuel Vadot * (pre-restart delay)
680a05676bSEmmanuel Vadot */
690a05676bSEmmanuel Vadot #define DWWDT_EXP_OFFSET 47
700a05676bSEmmanuel Vadot
710a05676bSEmmanuel Vadot struct dwwdt_softc {
720a05676bSEmmanuel Vadot device_t sc_dev;
730a05676bSEmmanuel Vadot struct resource *sc_mem_res;
740a05676bSEmmanuel Vadot struct resource *sc_irq_res;
750a05676bSEmmanuel Vadot void *sc_intr_cookie;
760a05676bSEmmanuel Vadot clk_t sc_clk;
770a05676bSEmmanuel Vadot uint64_t sc_clk_freq;
780a05676bSEmmanuel Vadot eventhandler_tag sc_evtag;
790a05676bSEmmanuel Vadot int sc_mem_rid;
800a05676bSEmmanuel Vadot int sc_irq_rid;
810a05676bSEmmanuel Vadot enum {
820a05676bSEmmanuel Vadot DWWDT_STOPPED,
830a05676bSEmmanuel Vadot DWWDT_RUNNING,
840a05676bSEmmanuel Vadot } sc_status;
850a05676bSEmmanuel Vadot };
860a05676bSEmmanuel Vadot
876003bf92SEmmanuel Vadot static struct ofw_compat_data compat_data[] = {
886003bf92SEmmanuel Vadot { "snps,dw-wdt", 1 },
896003bf92SEmmanuel Vadot { NULL, 0 }
906003bf92SEmmanuel Vadot };
916003bf92SEmmanuel Vadot
920a05676bSEmmanuel Vadot SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
930a05676bSEmmanuel Vadot "Synopsys Designware watchdog timer");
940a05676bSEmmanuel Vadot
95ee900888SAndriy Gapon /* Setting this to true disables full restart mode. */
96ee900888SAndriy Gapon static bool dwwdt_prevent_restart = false;
97ee900888SAndriy Gapon SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, prevent_restart, CTLFLAG_RW | CTLFLAG_MPSAFE,
98ee900888SAndriy Gapon &dwwdt_prevent_restart, 0, "Disable system reset on timeout");
99ee900888SAndriy Gapon
100ee900888SAndriy Gapon static bool dwwdt_debug_enabled = false;
101ee900888SAndriy Gapon SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_MPSAFE,
102ee900888SAndriy Gapon &dwwdt_debug_enabled, 0, "Enable debug mode");
103ee900888SAndriy Gapon
104ee900888SAndriy Gapon static bool dwwdt_panic_first = true;
105ee900888SAndriy Gapon SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, panic_first, CTLFLAG_RW | CTLFLAG_MPSAFE,
106ee900888SAndriy Gapon &dwwdt_panic_first, 0,
107ee900888SAndriy Gapon "Try to panic on timeout, reset on another timeout");
1080a05676bSEmmanuel Vadot
1090a05676bSEmmanuel Vadot static int dwwdt_probe(device_t);
1100a05676bSEmmanuel Vadot static int dwwdt_attach(device_t);
1110a05676bSEmmanuel Vadot static int dwwdt_detach(device_t);
1120a05676bSEmmanuel Vadot static int dwwdt_shutdown(device_t);
1130a05676bSEmmanuel Vadot
1140a05676bSEmmanuel Vadot static void dwwdt_intr(void *);
1150a05676bSEmmanuel Vadot static void dwwdt_event(void *, unsigned int, int *);
1160a05676bSEmmanuel Vadot
1170a05676bSEmmanuel Vadot /* Helpers */
1180a05676bSEmmanuel Vadot static inline void dwwdt_start(struct dwwdt_softc *sc);
1190a05676bSEmmanuel Vadot static inline bool dwwdt_started(const struct dwwdt_softc *sc);
1200a05676bSEmmanuel Vadot static inline void dwwdt_stop(struct dwwdt_softc *sc);
1210a05676bSEmmanuel Vadot static inline void dwwdt_set_timeout(const struct dwwdt_softc *sc, int val);
1220a05676bSEmmanuel Vadot
1230a05676bSEmmanuel Vadot static void dwwdt_debug(device_t);
1240a05676bSEmmanuel Vadot
1250a05676bSEmmanuel Vadot static void
dwwdt_debug(device_t dev)1260a05676bSEmmanuel Vadot dwwdt_debug(device_t dev)
1270a05676bSEmmanuel Vadot {
1280a05676bSEmmanuel Vadot /*
1290a05676bSEmmanuel Vadot * Reading from EOI may clear interrupt flag.
1300a05676bSEmmanuel Vadot */
1310a05676bSEmmanuel Vadot const struct dwwdt_softc *sc = device_get_softc(dev);
1320a05676bSEmmanuel Vadot
1330a05676bSEmmanuel Vadot device_printf(dev, "Registers dump: \n");
1340a05676bSEmmanuel Vadot device_printf(dev, " CR: %08x\n", DWWDT_READ4(sc, DWWDT_CR));
1350a05676bSEmmanuel Vadot device_printf(dev, " CCVR: %08x\n", DWWDT_READ4(sc, DWWDT_CCVR));
1360a05676bSEmmanuel Vadot device_printf(dev, " CRR: %08x\n", DWWDT_READ4(sc, DWWDT_CRR));
1370a05676bSEmmanuel Vadot device_printf(dev, " STAT: %08x\n", DWWDT_READ4(sc, DWWDT_STAT));
1380a05676bSEmmanuel Vadot
1390a05676bSEmmanuel Vadot device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk));
1400a05676bSEmmanuel Vadot device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq);
1410a05676bSEmmanuel Vadot }
1420a05676bSEmmanuel Vadot
1430a05676bSEmmanuel Vadot static inline bool
dwwdt_started(const struct dwwdt_softc * sc)1440a05676bSEmmanuel Vadot dwwdt_started(const struct dwwdt_softc *sc)
1450a05676bSEmmanuel Vadot {
1460a05676bSEmmanuel Vadot
1470a05676bSEmmanuel Vadot /* CR_WDT_E bit can be clear only by full CPU reset. */
1480a05676bSEmmanuel Vadot return ((DWWDT_READ4(sc, DWWDT_CR) & DWWDT_CR_WDT_EN) != 0);
1490a05676bSEmmanuel Vadot }
1500a05676bSEmmanuel Vadot
1510a05676bSEmmanuel Vadot static void inline
dwwdt_start(struct dwwdt_softc * sc)1520a05676bSEmmanuel Vadot dwwdt_start(struct dwwdt_softc *sc)
1530a05676bSEmmanuel Vadot {
1540a05676bSEmmanuel Vadot uint32_t val;
1550a05676bSEmmanuel Vadot
1560a05676bSEmmanuel Vadot /* Enable watchdog */
1570a05676bSEmmanuel Vadot val = DWWDT_READ4(sc, DWWDT_CR);
1580a05676bSEmmanuel Vadot val |= DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE;
1590a05676bSEmmanuel Vadot DWWDT_WRITE4(sc, DWWDT_CR, val);
1600a05676bSEmmanuel Vadot sc->sc_status = DWWDT_RUNNING;
1610a05676bSEmmanuel Vadot }
1620a05676bSEmmanuel Vadot
1630a05676bSEmmanuel Vadot static void inline
dwwdt_stop(struct dwwdt_softc * sc)1640a05676bSEmmanuel Vadot dwwdt_stop(struct dwwdt_softc *sc)
1650a05676bSEmmanuel Vadot {
1660a05676bSEmmanuel Vadot
1670a05676bSEmmanuel Vadot sc->sc_status = DWWDT_STOPPED;
1680a05676bSEmmanuel Vadot dwwdt_set_timeout(sc, 0x0f);
1690a05676bSEmmanuel Vadot }
1700a05676bSEmmanuel Vadot
1710a05676bSEmmanuel Vadot static void inline
dwwdt_set_timeout(const struct dwwdt_softc * sc,int val)1720a05676bSEmmanuel Vadot dwwdt_set_timeout(const struct dwwdt_softc *sc, int val)
1730a05676bSEmmanuel Vadot {
1740a05676bSEmmanuel Vadot
1750a05676bSEmmanuel Vadot DWWDT_WRITE4(sc, DWWDT_TORR, val);
1760a05676bSEmmanuel Vadot DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK);
1770a05676bSEmmanuel Vadot }
1780a05676bSEmmanuel Vadot
1790a05676bSEmmanuel Vadot static void
dwwdt_intr(void * arg)1800a05676bSEmmanuel Vadot dwwdt_intr(void *arg)
1810a05676bSEmmanuel Vadot {
1820a05676bSEmmanuel Vadot struct dwwdt_softc *sc = arg;
1830a05676bSEmmanuel Vadot
1840a05676bSEmmanuel Vadot KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0,
1850a05676bSEmmanuel Vadot ("Missing interrupt status bit?"));
1860a05676bSEmmanuel Vadot
187ee900888SAndriy Gapon if (dwwdt_prevent_restart || sc->sc_status == DWWDT_STOPPED) {
1880a05676bSEmmanuel Vadot /*
1890a05676bSEmmanuel Vadot * Confirm interrupt reception. Restart counter.
1900a05676bSEmmanuel Vadot * This also emulates stopping watchdog.
1910a05676bSEmmanuel Vadot */
1920a05676bSEmmanuel Vadot (void)DWWDT_READ4(sc, DWWDT_EOI);
193ee900888SAndriy Gapon return;
1940a05676bSEmmanuel Vadot }
195ee900888SAndriy Gapon
196ee900888SAndriy Gapon if (dwwdt_panic_first)
197ee900888SAndriy Gapon panic("dwwdt pre-timeout interrupt");
1980a05676bSEmmanuel Vadot }
1990a05676bSEmmanuel Vadot
2000a05676bSEmmanuel Vadot static void
dwwdt_event(void * arg,unsigned int cmd,int * error)2010a05676bSEmmanuel Vadot dwwdt_event(void *arg, unsigned int cmd, int *error)
2020a05676bSEmmanuel Vadot {
2030a05676bSEmmanuel Vadot struct dwwdt_softc *sc = arg;
2040a05676bSEmmanuel Vadot const int exponent = flsl(sc->sc_clk_freq);
2050a05676bSEmmanuel Vadot int timeout;
2060a05676bSEmmanuel Vadot int val;
2070a05676bSEmmanuel Vadot
2080a05676bSEmmanuel Vadot timeout = cmd & WD_INTERVAL;
209ee900888SAndriy Gapon val = MAX(0, timeout + exponent - DWWDT_EXP_OFFSET + 1);
2100a05676bSEmmanuel Vadot
2110a05676bSEmmanuel Vadot dwwdt_stop(sc);
2120a05676bSEmmanuel Vadot if (cmd == 0 || val > 0x0f) {
2130a05676bSEmmanuel Vadot /*
2140a05676bSEmmanuel Vadot * Set maximum time between interrupts and Leave watchdog
2150a05676bSEmmanuel Vadot * disabled.
2160a05676bSEmmanuel Vadot */
2170a05676bSEmmanuel Vadot return;
2180a05676bSEmmanuel Vadot }
2190a05676bSEmmanuel Vadot
2200a05676bSEmmanuel Vadot dwwdt_set_timeout(sc, val);
2210a05676bSEmmanuel Vadot dwwdt_start(sc);
2220a05676bSEmmanuel Vadot *error = 0;
2230a05676bSEmmanuel Vadot
2240a05676bSEmmanuel Vadot if (dwwdt_debug_enabled)
2250a05676bSEmmanuel Vadot dwwdt_debug(sc->sc_dev);
2260a05676bSEmmanuel Vadot }
2270a05676bSEmmanuel Vadot
2280a05676bSEmmanuel Vadot static int
dwwdt_probe(device_t dev)2290a05676bSEmmanuel Vadot dwwdt_probe(device_t dev)
2300a05676bSEmmanuel Vadot {
2310a05676bSEmmanuel Vadot if (!ofw_bus_status_okay(dev))
2320a05676bSEmmanuel Vadot return (ENXIO);
2330a05676bSEmmanuel Vadot
2346003bf92SEmmanuel Vadot if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
2350a05676bSEmmanuel Vadot return (ENXIO);
2360a05676bSEmmanuel Vadot
2370a05676bSEmmanuel Vadot device_set_desc(dev, "Synopsys Designware watchdog timer");
2380a05676bSEmmanuel Vadot return (BUS_PROBE_DEFAULT);
2390a05676bSEmmanuel Vadot }
2400a05676bSEmmanuel Vadot
2410a05676bSEmmanuel Vadot static int
dwwdt_attach(device_t dev)2420a05676bSEmmanuel Vadot dwwdt_attach(device_t dev)
2430a05676bSEmmanuel Vadot {
2440a05676bSEmmanuel Vadot struct dwwdt_softc *sc;
2450a05676bSEmmanuel Vadot
2460a05676bSEmmanuel Vadot sc = device_get_softc(dev);
2470a05676bSEmmanuel Vadot sc->sc_dev = dev;
2480a05676bSEmmanuel Vadot
2490a05676bSEmmanuel Vadot sc->sc_mem_rid = 0;
2500a05676bSEmmanuel Vadot sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
2510a05676bSEmmanuel Vadot &sc->sc_mem_rid, RF_ACTIVE);
2520a05676bSEmmanuel Vadot if (sc->sc_mem_res == NULL) {
2530a05676bSEmmanuel Vadot device_printf(dev, "cannot allocate memory resource\n");
2540a05676bSEmmanuel Vadot goto err_no_mem;
2550a05676bSEmmanuel Vadot }
2560a05676bSEmmanuel Vadot
2570a05676bSEmmanuel Vadot sc->sc_irq_rid = 0;
2580a05676bSEmmanuel Vadot sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
2590a05676bSEmmanuel Vadot &sc->sc_irq_rid, RF_ACTIVE);
2600a05676bSEmmanuel Vadot if (sc->sc_irq_res == NULL) {
2610a05676bSEmmanuel Vadot device_printf(dev, "cannot allocate ireq resource\n");
2620a05676bSEmmanuel Vadot goto err_no_irq;
2630a05676bSEmmanuel Vadot }
2640a05676bSEmmanuel Vadot
2650a05676bSEmmanuel Vadot sc->sc_intr_cookie = NULL;
2660a05676bSEmmanuel Vadot if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
2670a05676bSEmmanuel Vadot NULL, dwwdt_intr, sc, &sc->sc_intr_cookie) != 0) {
2680a05676bSEmmanuel Vadot device_printf(dev, "cannot setup interrupt routine\n");
2690a05676bSEmmanuel Vadot goto err_no_intr;
2700a05676bSEmmanuel Vadot }
2710a05676bSEmmanuel Vadot
2720a05676bSEmmanuel Vadot if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) {
2730a05676bSEmmanuel Vadot device_printf(dev, "cannot find clock\n");
2740a05676bSEmmanuel Vadot goto err_no_clock;
2750a05676bSEmmanuel Vadot }
2760a05676bSEmmanuel Vadot
2770a05676bSEmmanuel Vadot if (clk_enable(sc->sc_clk) != 0) {
2780a05676bSEmmanuel Vadot device_printf(dev, "cannot enable clock\n");
2790a05676bSEmmanuel Vadot goto err_no_freq;
2800a05676bSEmmanuel Vadot }
2810a05676bSEmmanuel Vadot
2820a05676bSEmmanuel Vadot if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) {
2830a05676bSEmmanuel Vadot device_printf(dev, "cannot get clock frequency\n");
2840a05676bSEmmanuel Vadot goto err_no_freq;
2850a05676bSEmmanuel Vadot }
2860a05676bSEmmanuel Vadot
2870a05676bSEmmanuel Vadot if (sc->sc_clk_freq == 0UL)
2880a05676bSEmmanuel Vadot goto err_no_freq;
2890a05676bSEmmanuel Vadot
2900a05676bSEmmanuel Vadot sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0);
2910a05676bSEmmanuel Vadot sc->sc_status = DWWDT_STOPPED;
2920a05676bSEmmanuel Vadot
293*18250ec6SJohn Baldwin bus_attach_children(dev);
294*18250ec6SJohn Baldwin return (0);
2950a05676bSEmmanuel Vadot
2960a05676bSEmmanuel Vadot err_no_freq:
2970a05676bSEmmanuel Vadot clk_release(sc->sc_clk);
2980a05676bSEmmanuel Vadot err_no_clock:
2990a05676bSEmmanuel Vadot bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
3000a05676bSEmmanuel Vadot err_no_intr:
3010a05676bSEmmanuel Vadot bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
3020a05676bSEmmanuel Vadot err_no_irq:
3030a05676bSEmmanuel Vadot bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
3040a05676bSEmmanuel Vadot sc->sc_mem_res);
3050a05676bSEmmanuel Vadot err_no_mem:
3060a05676bSEmmanuel Vadot return (ENXIO);
3070a05676bSEmmanuel Vadot }
3080a05676bSEmmanuel Vadot
3090a05676bSEmmanuel Vadot static int
dwwdt_detach(device_t dev)3100a05676bSEmmanuel Vadot dwwdt_detach(device_t dev)
3110a05676bSEmmanuel Vadot {
3120a05676bSEmmanuel Vadot struct dwwdt_softc *sc = device_get_softc(dev);
313d412c076SJohn Baldwin int error;
3140a05676bSEmmanuel Vadot
3150a05676bSEmmanuel Vadot if (dwwdt_started(sc)) {
3160a05676bSEmmanuel Vadot /*
3170a05676bSEmmanuel Vadot * Once started it cannot be stopped. Prevent module unload
3180a05676bSEmmanuel Vadot * instead.
3190a05676bSEmmanuel Vadot */
3200a05676bSEmmanuel Vadot return (EBUSY);
3210a05676bSEmmanuel Vadot }
3220a05676bSEmmanuel Vadot
323d412c076SJohn Baldwin error = bus_generic_detach(dev);
324d412c076SJohn Baldwin if (error != 0)
325d412c076SJohn Baldwin return (error);
326d412c076SJohn Baldwin
3270a05676bSEmmanuel Vadot EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag);
3280a05676bSEmmanuel Vadot sc->sc_evtag = NULL;
3290a05676bSEmmanuel Vadot
3300a05676bSEmmanuel Vadot if (sc->sc_clk != NULL)
3310a05676bSEmmanuel Vadot clk_release(sc->sc_clk);
3320a05676bSEmmanuel Vadot
3330a05676bSEmmanuel Vadot if (sc->sc_intr_cookie != NULL)
3340a05676bSEmmanuel Vadot bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
3350a05676bSEmmanuel Vadot
3360a05676bSEmmanuel Vadot if (sc->sc_irq_res) {
3370a05676bSEmmanuel Vadot bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
3380a05676bSEmmanuel Vadot sc->sc_irq_res);
3390a05676bSEmmanuel Vadot }
3400a05676bSEmmanuel Vadot
3410a05676bSEmmanuel Vadot if (sc->sc_mem_res) {
3420a05676bSEmmanuel Vadot bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
3430a05676bSEmmanuel Vadot sc->sc_mem_res);
3440a05676bSEmmanuel Vadot }
3450a05676bSEmmanuel Vadot
346d412c076SJohn Baldwin return (0);
3470a05676bSEmmanuel Vadot }
3480a05676bSEmmanuel Vadot
3490a05676bSEmmanuel Vadot static int
dwwdt_shutdown(device_t dev)3500a05676bSEmmanuel Vadot dwwdt_shutdown(device_t dev)
3510a05676bSEmmanuel Vadot {
3520a05676bSEmmanuel Vadot struct dwwdt_softc *sc;
3530a05676bSEmmanuel Vadot
3540a05676bSEmmanuel Vadot sc = device_get_softc(dev);
3550a05676bSEmmanuel Vadot
3560a05676bSEmmanuel Vadot /* Prevent restarts during shutdown. */
357ee900888SAndriy Gapon dwwdt_prevent_restart = true;
3580a05676bSEmmanuel Vadot dwwdt_stop(sc);
3590a05676bSEmmanuel Vadot return (bus_generic_shutdown(dev));
3600a05676bSEmmanuel Vadot }
3610a05676bSEmmanuel Vadot
3620a05676bSEmmanuel Vadot static device_method_t dwwdt_methods[] = {
3630a05676bSEmmanuel Vadot DEVMETHOD(device_probe, dwwdt_probe),
3640a05676bSEmmanuel Vadot DEVMETHOD(device_attach, dwwdt_attach),
3650a05676bSEmmanuel Vadot DEVMETHOD(device_detach, dwwdt_detach),
3660a05676bSEmmanuel Vadot DEVMETHOD(device_shutdown, dwwdt_shutdown),
3670a05676bSEmmanuel Vadot
3680a05676bSEmmanuel Vadot {0, 0}
3690a05676bSEmmanuel Vadot };
3700a05676bSEmmanuel Vadot
3710a05676bSEmmanuel Vadot static driver_t dwwdt_driver = {
3720a05676bSEmmanuel Vadot "dwwdt",
3730a05676bSEmmanuel Vadot dwwdt_methods,
3740a05676bSEmmanuel Vadot sizeof(struct dwwdt_softc),
3750a05676bSEmmanuel Vadot };
3760a05676bSEmmanuel Vadot
37736f54203SJohn Baldwin DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, NULL, NULL);
3780a05676bSEmmanuel Vadot MODULE_VERSION(dwwdt, 1);
3796003bf92SEmmanuel Vadot OFWBUS_PNP_INFO(compat_data);
380