xref: /freebsd/sys/dev/dwwdt/dwwdt.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
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