1/*- 2 * Copyright (c) 2010 Greg Ansley. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26/* 27 * The SAM9 watchdog hardware can be programed only once. So we set the 28 * hardware watchdog to 16 s in wdt_attach and only reset it in the wdt_tick 29 * handler. The watchdog is halted in processor debug mode. 30 */ 31 32#include "opt_platform.h" 33 34#include <sys/cdefs.h> 35__FBSDID("$FreeBSD: releng/10.2/sys/arm/at91/at91_wdt.c 266196 2014-05-15 21:21:47Z ian $"); 36 37#include <sys/param.h> 38#include <sys/bus.h> 39#include <sys/kdb.h> 40#include <sys/kernel.h> 41#include <sys/module.h> 42#include <sys/systm.h> 43#include <sys/watchdog.h> 44 45#include <machine/bus.h> 46 47#include <arm/at91/at91var.h> 48#include <arm/at91/at91_wdtreg.h> 49 50#ifdef FDT 51#include <dev/fdt/fdt_common.h> 52#include <dev/ofw/ofw_bus.h> 53#include <dev/ofw/ofw_bus_subr.h> 54#endif 55 56struct wdt_softc { 57 struct mtx sc_mtx; 58 device_t sc_dev; 59 struct resource *mem_res; 60 struct callout tick_ch; 61 eventhandler_tag sc_wet; 62 void *intrhand; 63 u_int cmd; 64 u_int interval; 65}; 66 67static inline uint32_t 68RD4(struct wdt_softc *sc, bus_size_t off) 69{ 70 71 return (bus_read_4(sc->mem_res, off)); 72} 73 74static inline void 75WR4(struct wdt_softc *sc, bus_size_t off, uint32_t val) 76{ 77 78 bus_write_4(sc->mem_res, off, val); 79} 80 81static int 82wdt_intr(void *argp) 83{ 84 struct wdt_softc *sc = argp; 85 86 87 if (RD4(sc, WDT_SR) & (WDT_WDUNF | WDT_WDERR)) { 88#if defined(KDB) && !defined(KDB_UNATTENDED) 89 kdb_backtrace(); 90 kdb_enter(KDB_WHY_WATCHDOG, "watchdog timeout"); 91#else 92 panic("watchdog timeout"); 93#endif 94 } 95 return (FILTER_STRAY); 96} 97 98/* User interface, see watchdog(9) */ 99static void 100wdt_watchdog(void *argp, u_int cmd, int *error) 101{ 102 struct wdt_softc *sc = argp; 103 u_int interval; 104 105 mtx_lock(&sc->sc_mtx); 106 107 *error = 0; 108 sc->cmd = 0; 109 interval = cmd & WD_INTERVAL; 110 if (interval > WD_TO_16SEC) 111 *error = EOPNOTSUPP; 112 else if (interval > 0) 113 sc->cmd = interval | WD_ACTIVE; 114 115 /* We cannot turn off our watchdog so if user 116 * fails to turn us on go to passive mode. */ 117 if ((sc->cmd & WD_ACTIVE) == 0) 118 sc->cmd = WD_PASSIVE; 119 120 mtx_unlock(&sc->sc_mtx); 121} 122 123/* This routine is called no matter what state the user sets the 124 * watchdog mode to. Called at a rate that is slightly less than 125 * half the hardware timeout. */ 126static void 127wdt_tick(void *argp) 128{ 129 struct wdt_softc *sc = argp; 130 131 mtx_assert(&sc->sc_mtx, MA_OWNED); 132 if (sc->cmd & (WD_ACTIVE | WD_PASSIVE)) 133 WR4(sc, WDT_CR, WDT_KEY|WDT_WDRSTT); 134 135 sc->cmd &= WD_PASSIVE; 136 callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc); 137} 138 139static int 140wdt_probe(device_t dev) 141{ 142#ifdef FDT 143 if (!ofw_bus_is_compatible(dev, "atmel,at91sam9260-wdt")) 144 return (ENXIO); 145#endif 146 device_set_desc(dev, "WDT"); 147 return (0); 148} 149 150static int 151wdt_attach(device_t dev) 152{ 153 static struct wdt_softc *sc; 154 struct resource *irq; 155 uint32_t wdt_mr; 156 int rid, err; 157 158 sc = device_get_softc(dev); 159 sc->cmd = WD_PASSIVE; 160 sc->sc_dev = dev; 161 162 mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "at91_wdt", MTX_DEF); 163 callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); 164 165 rid = 0; 166 sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 167 RF_ACTIVE); 168 169 if (sc->mem_res == NULL) 170 panic("couldn't allocate wdt register resources"); 171 172 wdt_mr = RD4(sc, WDT_MR); 173 if ((wdt_mr & WDT_WDRSTEN) == 0) 174 device_printf(dev, "Watchdog disabled! (Boot ROM?)\n"); 175 else { 176#ifdef WDT_RESET 177 /* Rude, full reset of whole system on watch dog timeout */ 178 WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)| 179 WDT_WDRSTEN| WDT_WDV(0xFFF)); 180#else 181 /* Generate stack trace and panic on watchdog timeout*/ 182 WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)| 183 WDT_WDFIEN| WDT_WDV(0xFFF)); 184#endif 185 /* 186 * This may have been set by Boot ROM so register value may 187 * not be what we just requested since this is a write once 188 * register. 189 */ 190 wdt_mr = RD4(sc, WDT_MR); 191 if (wdt_mr & WDT_WDFIEN) { 192 rid = 0; 193 irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 194 RF_ACTIVE | RF_SHAREABLE); 195 if (!irq) 196 panic("could not allocate interrupt.\n"); 197 198 err = bus_setup_intr(dev, irq, INTR_TYPE_CLK, wdt_intr, 199 NULL, sc, &sc->intrhand); 200 } 201 202 /* interval * hz */ 203 sc->interval = (((wdt_mr & WDT_WDV(~0)) + 1) * WDT_DIV) / 204 (WDT_CLOCK/hz); 205 206 device_printf(dev, "watchdog timeout: %d seconds\n", 207 sc->interval / hz); 208 209 /* Slightly less than 1/2 of watchdog hardware timeout */ 210 sc->interval = (sc->interval/2) - (sc->interval/20); 211 callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc); 212 213 /* Register us as a watchdog */ 214 sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list, 215 wdt_watchdog, sc, 0); 216 } 217 return (0); 218} 219 220static device_method_t wdt_methods[] = { 221 DEVMETHOD(device_probe, wdt_probe), 222 DEVMETHOD(device_attach, wdt_attach), 223 DEVMETHOD_END 224}; 225 226static driver_t wdt_driver = { 227 "at91_wdt", 228 wdt_methods, 229 sizeof(struct wdt_softc), 230}; 231 232static devclass_t wdt_devclass; 233 234#ifdef FDT 235DRIVER_MODULE(at91_wdt, simplebus, wdt_driver, wdt_devclass, NULL, NULL); 236#else 237DRIVER_MODULE(at91_wdt, atmelarm, wdt_driver, wdt_devclass, NULL, NULL); 238#endif 239