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