xref: /freebsd/sys/arm64/apple/apple_wdog.c (revision 92e237e1cc278eb240424b186258cb2089455bfa)
12eace89dSKyle Evans /*-
2*92e237e1SKyle Evans  * SPDX-License-Identifier: BSD-2-Clause
32eace89dSKyle Evans  *
42eace89dSKyle Evans  * Copyright (c) 2022 Michael J. Karels <karels@freebsd.org>
52eace89dSKyle Evans  * Copyright (c) 2012 Alexander Rybalko <ray@freebsd.org>
62eace89dSKyle Evans  * All rights reserved.
72eace89dSKyle Evans  *
82eace89dSKyle Evans  * Redistribution and use in source and binary forms, with or without
92eace89dSKyle Evans  * modification, are permitted provided that the following conditions
102eace89dSKyle Evans  * are met:
112eace89dSKyle Evans  * 1. Redistributions of source code must retain the above copyright
122eace89dSKyle Evans  *    notice, this list of conditions and the following disclaimer.
132eace89dSKyle Evans  * 2. Redistributions in binary form must reproduce the above copyright
142eace89dSKyle Evans  *    notice, this list of conditions and the following disclaimer in the
152eace89dSKyle Evans  *    documentation and/or other materials provided with the distribution.
162eace89dSKyle Evans  *
172eace89dSKyle Evans  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
182eace89dSKyle Evans  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
192eace89dSKyle Evans  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202eace89dSKyle Evans  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
212eace89dSKyle Evans  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
222eace89dSKyle Evans  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
232eace89dSKyle Evans  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
242eace89dSKyle Evans  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
252eace89dSKyle Evans  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
262eace89dSKyle Evans  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
272eace89dSKyle Evans  * SUCH DAMAGE.
282eace89dSKyle Evans  */
292eace89dSKyle Evans 
302eace89dSKyle Evans #include <sys/param.h>
312eace89dSKyle Evans #include <sys/bus.h>
322eace89dSKyle Evans #include <sys/eventhandler.h>
332eace89dSKyle Evans #include <sys/kernel.h>
342eace89dSKyle Evans #include <sys/limits.h>
352eace89dSKyle Evans #include <sys/lock.h>
362eace89dSKyle Evans #include <sys/module.h>
372eace89dSKyle Evans #include <sys/mutex.h>
382eace89dSKyle Evans #include <sys/reboot.h>
392eace89dSKyle Evans #include <sys/rman.h>
402eace89dSKyle Evans #include <sys/systm.h>
412eace89dSKyle Evans #include <sys/watchdog.h>
422eace89dSKyle Evans 
432eace89dSKyle Evans #include <dev/ofw/openfirm.h>
442eace89dSKyle Evans #include <dev/ofw/ofw_bus.h>
452eace89dSKyle Evans #include <dev/ofw/ofw_bus_subr.h>
462eace89dSKyle Evans 
472eace89dSKyle Evans #include <dev/clk/clk.h>
482eace89dSKyle Evans 
492eace89dSKyle Evans #include <machine/bus.h>
502eace89dSKyle Evans #include <machine/machdep.h>
512eace89dSKyle Evans 
522eace89dSKyle Evans #define	APPLE_WDOG_WD0_TIMER		0x0000
532eace89dSKyle Evans #define	APPLE_WDOG_WD0_RESET		0x0004
542eace89dSKyle Evans #define	APPLE_WDOG_WD0_INTR		0x0008
552eace89dSKyle Evans #define	APPLE_WDOG_WD0_CNTL		0x000c
562eace89dSKyle Evans 
572eace89dSKyle Evans #define	APPLE_WDOG_WD1_TIMER		0x0010
582eace89dSKyle Evans #define	APPLE_WDOG_WD1_RESET		0x0014
592eace89dSKyle Evans #define	APPLE_WDOG_WD1_CNTL		0x001c
602eace89dSKyle Evans 
612eace89dSKyle Evans #define	APPLE_WDOG_WD2_TIMER		0x0020
622eace89dSKyle Evans #define	APPLE_WDOG_WD2_RESET		0x0024
632eace89dSKyle Evans #define	APPLE_WDOG_WD2_CNTL		0x002c
642eace89dSKyle Evans 
652eace89dSKyle Evans #define	APPLE_WDOG_CNTL_INTENABLE	0x0001
662eace89dSKyle Evans #define	APPLE_WDOG_CNTL_INTSTAT		0x0002
672eace89dSKyle Evans #define	APPLE_WDOG_CNTL_RSTENABLE	0x0004
682eace89dSKyle Evans 
692eace89dSKyle Evans #define	READ(_sc, _r) bus_space_read_4((_sc)->bst, (_sc)->bsh, (_r))
702eace89dSKyle Evans #define	WRITE(_sc, _r, _v) bus_space_write_4((_sc)->bst, (_sc)->bsh, (_r), (_v))
712eace89dSKyle Evans 
722eace89dSKyle Evans struct apple_wdog_softc {
732eace89dSKyle Evans 	device_t		dev;
742eace89dSKyle Evans 	struct resource *	res;
752eace89dSKyle Evans 	bus_space_tag_t		bst;
762eace89dSKyle Evans 	bus_space_handle_t	bsh;
772eace89dSKyle Evans 	clk_t			clk;
782eace89dSKyle Evans 	uint64_t		clk_freq;
792eace89dSKyle Evans 	struct mtx		mtx;
802eace89dSKyle Evans };
812eace89dSKyle Evans 
822eace89dSKyle Evans static struct ofw_compat_data compat_data[] = {
832eace89dSKyle Evans 	{"apple,wdt",		1},
842eace89dSKyle Evans 	{NULL,			0}
852eace89dSKyle Evans };
862eace89dSKyle Evans 
872eace89dSKyle Evans static void apple_wdog_watchdog_fn(void *private, u_int cmd, int *error);
882eace89dSKyle Evans static void apple_wdog_reboot_system(void *, int);
892eace89dSKyle Evans 
902eace89dSKyle Evans static int
apple_wdog_probe(device_t dev)912eace89dSKyle Evans apple_wdog_probe(device_t dev)
922eace89dSKyle Evans {
932eace89dSKyle Evans 
942eace89dSKyle Evans 	if (!ofw_bus_status_okay(dev))
952eace89dSKyle Evans 		return (ENXIO);
962eace89dSKyle Evans 
972eace89dSKyle Evans 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
982eace89dSKyle Evans 		return (ENXIO);
992eace89dSKyle Evans 
1002eace89dSKyle Evans 	device_set_desc(dev, "Apple Watchdog");
1012eace89dSKyle Evans 
1022eace89dSKyle Evans 	return (BUS_PROBE_DEFAULT);
1032eace89dSKyle Evans }
1042eace89dSKyle Evans 
1052eace89dSKyle Evans static int
apple_wdog_attach(device_t dev)1062eace89dSKyle Evans apple_wdog_attach(device_t dev)
1072eace89dSKyle Evans {
1082eace89dSKyle Evans 	struct apple_wdog_softc *sc;
1092eace89dSKyle Evans 	int error, rid;
1102eace89dSKyle Evans 
1112eace89dSKyle Evans 	sc = device_get_softc(dev);
1122eace89dSKyle Evans 	sc->dev = dev;
1132eace89dSKyle Evans 
1142eace89dSKyle Evans 	rid = 0;
1152eace89dSKyle Evans 	sc->res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
1162eace89dSKyle Evans 	if (sc->res == NULL) {
1172eace89dSKyle Evans 		device_printf(dev, "could not allocate memory resource\n");
1182eace89dSKyle Evans 		return (ENXIO);
1192eace89dSKyle Evans 	}
1202eace89dSKyle Evans 
1212eace89dSKyle Evans 	sc->bst = rman_get_bustag(sc->res);
1222eace89dSKyle Evans 	sc->bsh = rman_get_bushandle(sc->res);
1232eace89dSKyle Evans 
1242eace89dSKyle Evans 	error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
1252eace89dSKyle Evans 	if (error != 0) {
1262eace89dSKyle Evans 		device_printf(dev, "cannot get clock\n");
1272eace89dSKyle Evans 		goto fail;
1282eace89dSKyle Evans 	}
1292eace89dSKyle Evans 	error = clk_enable(sc->clk);
1302eace89dSKyle Evans 	if (error != 0) {
1312eace89dSKyle Evans 		device_printf(dev, "cannot enable clock\n");
1322eace89dSKyle Evans 		goto fail;
1332eace89dSKyle Evans 	}
1342eace89dSKyle Evans 	error = clk_get_freq(sc->clk, &sc->clk_freq);
1352eace89dSKyle Evans 	if (error != 0) {
1362eace89dSKyle Evans 		device_printf(dev, "cannot get base frequency\n");
1372eace89dSKyle Evans 		goto fail_clk;
1382eace89dSKyle Evans 	}
1392eace89dSKyle Evans 
1402eace89dSKyle Evans 	mtx_init(&sc->mtx, "Apple Watchdog", "apple_wdog", MTX_DEF);
1412eace89dSKyle Evans 	EVENTHANDLER_REGISTER(watchdog_list, apple_wdog_watchdog_fn, sc, 0);
1422eace89dSKyle Evans 	EVENTHANDLER_REGISTER(shutdown_final, apple_wdog_reboot_system, sc,
1432eace89dSKyle Evans 	    SHUTDOWN_PRI_LAST);
1442eace89dSKyle Evans 
1452eace89dSKyle Evans 	/* Reset the watchdog timers. */
1462eace89dSKyle Evans 	WRITE(sc, APPLE_WDOG_WD0_CNTL, 0);
1472eace89dSKyle Evans 	WRITE(sc, APPLE_WDOG_WD1_CNTL, 0);
1482eace89dSKyle Evans 
1492eace89dSKyle Evans 	return (0);
1502eace89dSKyle Evans 
1512eace89dSKyle Evans fail_clk:
1522eace89dSKyle Evans 	clk_disable(sc->clk);
1532eace89dSKyle Evans fail:
1542eace89dSKyle Evans 	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->res);
1552eace89dSKyle Evans 	return (error);
1562eace89dSKyle Evans }
1572eace89dSKyle Evans 
1582eace89dSKyle Evans static void
apple_wdog_watchdog_fn(void * private,u_int cmd,int * error)1592eace89dSKyle Evans apple_wdog_watchdog_fn(void *private, u_int cmd, int *error)
1602eace89dSKyle Evans {
1612eace89dSKyle Evans 	struct apple_wdog_softc *sc;
1622eace89dSKyle Evans 	uint64_t sec;
1632eace89dSKyle Evans 	uint32_t ticks, sec_max;
1642eace89dSKyle Evans 
1652eace89dSKyle Evans 	sc = private;
1662eace89dSKyle Evans 	mtx_lock(&sc->mtx);
1672eace89dSKyle Evans 
1682eace89dSKyle Evans 	cmd &= WD_INTERVAL;
1692eace89dSKyle Evans 
1702eace89dSKyle Evans 	if (cmd > 0) {
1712eace89dSKyle Evans 		sec = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000000;
1722eace89dSKyle Evans 		sec_max = UINT_MAX / sc->clk_freq;
1732eace89dSKyle Evans 		if (sec == 0 || sec > sec_max) {
1742eace89dSKyle Evans 			/*
1752eace89dSKyle Evans 			 * Can't arm
1762eace89dSKyle Evans 			 * disable watchdog as watchdog(9) requires
1772eace89dSKyle Evans 			 */
1782eace89dSKyle Evans 			device_printf(sc->dev,
1792eace89dSKyle Evans 			    "Can't arm, timeout must be between 1-%d seconds\n",
1802eace89dSKyle Evans 			    sec_max);
1812eace89dSKyle Evans 			WRITE(sc, APPLE_WDOG_WD1_CNTL, 0);
1822eace89dSKyle Evans 			mtx_unlock(&sc->mtx);
1832eace89dSKyle Evans 			*error = EINVAL;
1842eace89dSKyle Evans 			return;
1852eace89dSKyle Evans 		}
1862eace89dSKyle Evans 
1872eace89dSKyle Evans 		ticks = sec * sc->clk_freq;
1882eace89dSKyle Evans 		WRITE(sc, APPLE_WDOG_WD1_TIMER, 0);
1892eace89dSKyle Evans 		WRITE(sc, APPLE_WDOG_WD1_RESET, ticks);
1902eace89dSKyle Evans 		WRITE(sc, APPLE_WDOG_WD1_CNTL, APPLE_WDOG_CNTL_RSTENABLE);
1912eace89dSKyle Evans 
1922eace89dSKyle Evans 		*error = 0;
1932eace89dSKyle Evans 	} else
1942eace89dSKyle Evans 		WRITE(sc, APPLE_WDOG_WD1_CNTL, 0);
1952eace89dSKyle Evans 
1962eace89dSKyle Evans 	mtx_unlock(&sc->mtx);
1972eace89dSKyle Evans }
1982eace89dSKyle Evans 
1992eace89dSKyle Evans static void
apple_wdog_reboot_system(void * private,int howto)2002eace89dSKyle Evans apple_wdog_reboot_system(void *private, int howto)
2012eace89dSKyle Evans {
2022eace89dSKyle Evans 	struct apple_wdog_softc *sc = private;
2032eace89dSKyle Evans 
2042eace89dSKyle Evans 	/* Only handle reset. */
2052eace89dSKyle Evans 	if ((howto & (RB_HALT | RB_POWEROFF)) != 0)
2062eace89dSKyle Evans 		return;
2072eace89dSKyle Evans 
2082eace89dSKyle Evans 	printf("Resetting system ... ");
2092eace89dSKyle Evans 
2102eace89dSKyle Evans 	WRITE(sc, APPLE_WDOG_WD1_CNTL, APPLE_WDOG_CNTL_RSTENABLE);
2112eace89dSKyle Evans 	WRITE(sc, APPLE_WDOG_WD1_RESET, 1);
2122eace89dSKyle Evans 	WRITE(sc, APPLE_WDOG_WD1_TIMER, 0);
2132eace89dSKyle Evans 
2142eace89dSKyle Evans 	/* Wait for watchdog timeout; should take milliseconds. */
2152eace89dSKyle Evans 	DELAY(2000000);
2162eace89dSKyle Evans 
2172eace89dSKyle Evans 	/* Not reached ... one hopes. */
2182eace89dSKyle Evans 	printf("failed to reset.\n");
2192eace89dSKyle Evans }
2202eace89dSKyle Evans 
2212eace89dSKyle Evans static device_method_t apple_wdog_methods[] = {
2222eace89dSKyle Evans 	DEVMETHOD(device_probe, apple_wdog_probe),
2232eace89dSKyle Evans 	DEVMETHOD(device_attach, apple_wdog_attach),
2242eace89dSKyle Evans 
2252eace89dSKyle Evans 	DEVMETHOD_END
2262eace89dSKyle Evans };
2272eace89dSKyle Evans 
2282eace89dSKyle Evans static driver_t apple_wdog_driver = {
2292eace89dSKyle Evans 	"apple_wdog",
2302eace89dSKyle Evans 	apple_wdog_methods,
2312eace89dSKyle Evans 	sizeof(struct apple_wdog_softc),
2322eace89dSKyle Evans };
2332eace89dSKyle Evans 
2342eace89dSKyle Evans DRIVER_MODULE(apple_wdog, simplebus, apple_wdog_driver, 0, 0);
235