1195514Sgonzo/*- 2195514Sgonzo * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3195514Sgonzo * All rights reserved. 4195514Sgonzo * 5195514Sgonzo * Redistribution and use in source and binary forms, with or without 6195514Sgonzo * modification, are permitted provided that the following conditions 7195514Sgonzo * are met: 8195514Sgonzo * 1. Redistributions of source code must retain the above copyright 9195514Sgonzo * notice unmodified, this list of conditions, and the following 10195514Sgonzo * disclaimer. 11195514Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12195514Sgonzo * notice, this list of conditions and the following disclaimer in the 13195514Sgonzo * documentation and/or other materials provided with the distribution. 14195514Sgonzo * 15195514Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16195514Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17195514Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18195514Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19195514Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20195514Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21195514Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22195514Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23195514Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24195514Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25195514Sgonzo * SUCH DAMAGE. 26195514Sgonzo */ 27195514Sgonzo 28195514Sgonzo/* 29195514Sgonzo * Watchdog driver for AR71xx 30195514Sgonzo */ 31195514Sgonzo 32195514Sgonzo#include <sys/cdefs.h> 33195514Sgonzo__FBSDID("$FreeBSD$"); 34195514Sgonzo 35195514Sgonzo#include <sys/param.h> 36195514Sgonzo#include <sys/systm.h> 37195514Sgonzo#include <sys/watchdog.h> 38195514Sgonzo#include <sys/bus.h> 39195514Sgonzo#include <sys/kernel.h> 40195514Sgonzo#include <sys/module.h> 41209338Sadrian#include <sys/sysctl.h> 42195514Sgonzo 43195514Sgonzo#include <mips/atheros/ar71xxreg.h> 44211476Sadrian#include <mips/atheros/ar71xx_cpudef.h> 45195514Sgonzo 46195514Sgonzostruct ar71xx_wdog_softc { 47195514Sgonzo device_t dev; 48195514Sgonzo int armed; 49209338Sadrian int reboot_from_watchdog; 50209338Sadrian int debug; 51195514Sgonzo}; 52195514Sgonzo 53195514Sgonzostatic void 54195514Sgonzoar71xx_wdog_watchdog_fn(void *private, u_int cmd, int *error) 55195514Sgonzo{ 56195514Sgonzo struct ar71xx_wdog_softc *sc = private; 57195514Sgonzo uint64_t timer_val; 58195514Sgonzo 59195514Sgonzo cmd &= WD_INTERVAL; 60209338Sadrian if (sc->debug) 61209338Sadrian device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: cmd: %x\n", cmd); 62195514Sgonzo if (cmd > 0) { 63195514Sgonzo timer_val = (uint64_t)(1ULL << cmd) * ar71xx_ahb_freq() / 64195514Sgonzo 1000000000; 65209338Sadrian if (sc->debug) 66209338Sadrian device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: programming timer: %jx\n", (uintmax_t) timer_val); 67195514Sgonzo /* 68195514Sgonzo * Load timer with large enough value to prevent spurious 69195514Sgonzo * reset 70195514Sgonzo */ 71195514Sgonzo ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER, 72195514Sgonzo ar71xx_ahb_freq() * 10); 73195514Sgonzo ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, 74195514Sgonzo RST_WDOG_ACTION_RESET); 75195514Sgonzo ATH_WRITE_REG(AR71XX_RST_WDOG_TIMER, 76195514Sgonzo (timer_val & 0xffffffff)); 77195514Sgonzo sc->armed = 1; 78195514Sgonzo *error = 0; 79195514Sgonzo } else { 80209338Sadrian if (sc->debug) 81209338Sadrian device_printf(sc->dev, "ar71xx_wdog_watchdog_fn: disarming\n"); 82195514Sgonzo if (sc->armed) { 83195514Sgonzo ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, 84195514Sgonzo RST_WDOG_ACTION_NOACTION); 85195514Sgonzo sc->armed = 0; 86195514Sgonzo } 87195514Sgonzo } 88195514Sgonzo} 89195514Sgonzo 90195514Sgonzostatic int 91195514Sgonzoar71xx_wdog_probe(device_t dev) 92195514Sgonzo{ 93195514Sgonzo 94195514Sgonzo device_set_desc(dev, "Atheros AR71XX watchdog timer"); 95195514Sgonzo return (0); 96195514Sgonzo} 97195514Sgonzo 98209338Sadrianstatic void 99209338Sadrianar71xx_wdog_sysctl(device_t dev) 100209338Sadrian{ 101209338Sadrian struct ar71xx_wdog_softc *sc = device_get_softc(dev); 102209338Sadrian 103209338Sadrian struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(sc->dev); 104209338Sadrian struct sysctl_oid *tree = device_get_sysctl_tree(sc->dev); 105209338Sadrian 106209338Sadrian SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 107209338Sadrian "debug", CTLFLAG_RW, &sc->debug, 0, 108209338Sadrian "enable watchdog debugging"); 109209338Sadrian SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 110209338Sadrian "armed", CTLFLAG_RD, &sc->armed, 0, 111209338Sadrian "whether the watchdog is armed"); 112209338Sadrian SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, 113209338Sadrian "reboot_from_watchdog", CTLFLAG_RD, &sc->reboot_from_watchdog, 0, 114209338Sadrian "whether the system rebooted from the watchdog"); 115209338Sadrian} 116209338Sadrian 117209338Sadrian 118195514Sgonzostatic int 119195514Sgonzoar71xx_wdog_attach(device_t dev) 120195514Sgonzo{ 121195514Sgonzo struct ar71xx_wdog_softc *sc = device_get_softc(dev); 122195514Sgonzo 123209338Sadrian /* Initialise */ 124209338Sadrian sc->reboot_from_watchdog = 0; 125209338Sadrian sc->armed = 0; 126209338Sadrian sc->debug = 0; 127209338Sadrian 128209338Sadrian if (ATH_READ_REG(AR71XX_RST_WDOG_CONTROL) & RST_WDOG_LAST) { 129195514Sgonzo device_printf (dev, 130195514Sgonzo "Previous reset was due to watchdog timeout\n"); 131209338Sadrian sc->reboot_from_watchdog = 1; 132209338Sadrian } 133195514Sgonzo 134195514Sgonzo ATH_WRITE_REG(AR71XX_RST_WDOG_CONTROL, RST_WDOG_ACTION_NOACTION); 135195514Sgonzo 136195514Sgonzo sc->dev = dev; 137195514Sgonzo EVENTHANDLER_REGISTER(watchdog_list, ar71xx_wdog_watchdog_fn, sc, 0); 138209338Sadrian ar71xx_wdog_sysctl(dev); 139195514Sgonzo 140195514Sgonzo return (0); 141195514Sgonzo} 142195514Sgonzo 143195514Sgonzostatic device_method_t ar71xx_wdog_methods[] = { 144195514Sgonzo DEVMETHOD(device_probe, ar71xx_wdog_probe), 145195514Sgonzo DEVMETHOD(device_attach, ar71xx_wdog_attach), 146195514Sgonzo {0, 0}, 147195514Sgonzo}; 148195514Sgonzo 149195514Sgonzostatic driver_t ar71xx_wdog_driver = { 150195514Sgonzo "ar71xx_wdog", 151195514Sgonzo ar71xx_wdog_methods, 152195514Sgonzo sizeof(struct ar71xx_wdog_softc), 153195514Sgonzo}; 154195514Sgonzostatic devclass_t ar71xx_wdog_devclass; 155195514Sgonzo 156195514SgonzoDRIVER_MODULE(ar71xx_wdog, nexus, ar71xx_wdog_driver, ar71xx_wdog_devclass, 0, 0); 157