1/*- 2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * 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. 14 * 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 25 * SUCH DAMAGE. 26 */ 27 28/* 29 * Watchdog driver for AR71xx 30 */ 31 32#include <sys/cdefs.h> 33__FBSDID("$FreeBSD$"); 34 35#include <sys/param.h> 36#include <sys/systm.h> 37#include <sys/watchdog.h> 38#include <sys/bus.h> 39#include <sys/kernel.h> 40#include <sys/module.h> 41#include <sys/sysctl.h> 42 43#include <mips/atheros/ar71xxreg.h> 44#include <mips/atheros/ar71xx_cpudef.h> 45 46struct ar71xx_wdog_softc { 47 device_t dev; 48 int armed; 49 int reboot_from_watchdog; 50 int debug; 51}; 52 53static void 54ar71xx_wdog_watchdog_fn(void *private, u_int cmd, int *error) 55{ 56 struct ar71xx_wdog_softc *sc = private; 57 uint64_t timer_val; 58 59 cmd &= WD_INTERVAL; 60 if (sc->debug) 61 device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: cmd: %x\n", cmd); 62 if (cmd > 0) { 63 timer_val = (uint64_t)(1ULL << cmd) * ar71xx_ahb_freq() / 64 1000000000; 65 if (sc->debug) 66 device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val); 67 /* 68 * Load timer with large enough value to prevent spurious 69 * reset 70 */ 71 ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER, 72 ar71xx_ahb_freq() * 10); 73 ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, 74 RST_WDOG_ACTION_RESET); 75 ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER, 76 (timer_val & 0xffffffff)); 77 sc->armed = 1; 78 *error = 0; 79 } else { 80 if (sc->debug) 81 device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: disarming\n"); 82 if (sc->armed) { 83 ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, 84 RST_WDOG_ACTION_NOACTION); 85 sc->armed = 0; 86 } 87 } 88} 89 90static int 91ar71xx_wdog_probe(device_t dev) 92{ 93 94 device_set_desc(dev, "Atheros AR71XX watchdog timer"); 95 return (BUS_PROBE_NOWILDCARD); 96} 97 98static void 99ar71xx_wdog_sysctl(device_t dev) 100{ 101 struct ar71xx_wdog_softc *sc = device_get_softc(dev); 102 103 struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); 104 struct sysctl_oid *tree = device_get_sysctl_tree(sc->dev); 105 106 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 107 "debug", CTLFLAG_RW, &sc->debug, 0, 108 "enable watchdog debugging"); 109 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 110 "armed", CTLFLAG_RD, &sc->armed, 0, 111 "whether the watchdog is armed"); 112 SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 113 "reboot_from_watchdog", CTLFLAG_RD, &sc->reboot_from_watchdog, 0, 114 "whether the system rebooted from the watchdog"); 115} 116 117 118static int 119ar71xx_wdog_attach(device_t dev) 120{ 121 struct ar71xx_wdog_softc *sc = device_get_softc(dev); 122 123 /* Initialise */ 124 sc->reboot_from_watchdog = 0; 125 sc->armed = 0; 126 sc->debug = 0; 127 128 if (ATH_READ_REG(AR71XX_RST_WDOG_CONTROL) & RST_WDOG_LAST) { 129 device_printf (dev, 130 "Previous reset was due to watchdog timeout\n"); 131 sc->reboot_from_watchdog = 1; 132 } 133 134 ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, RST_WDOG_ACTION_NOACTION); 135 136 sc->dev = dev; 137 EVENTHANDLER_REGISTER(watchdog_list, ar71xx_wdog_watchdog_fn, sc, 0); 138 ar71xx_wdog_sysctl(dev); 139 140 return (0); 141} 142 143static device_method_t ar71xx_wdog_methods[] = { 144 DEVMETHOD(device_probe, ar71xx_wdog_probe), 145 DEVMETHOD(device_attach, ar71xx_wdog_attach), 146 {0, 0}, 147}; 148 149static driver_t ar71xx_wdog_driver = { 150 "ar71xx_wdog", 151 ar71xx_wdog_methods, 152 sizeof(struct ar71xx_wdog_softc), 153}; 154static devclass_t ar71xx_wdog_devclass; 155 156DRIVER_MODULE(ar71xx_wdog, nexus, ar71xx_wdog_driver, ar71xx_wdog_devclass, 0, 0); 157