summaryrefslogtreecommitdiffstats
path: root/sys/arm/at91/at91_wdt.c
diff options
context:
space:
mode:
authorcognet <cognet@FreeBSD.org>2010-10-06 22:25:21 +0000
committercognet <cognet@FreeBSD.org>2010-10-06 22:25:21 +0000
commit2edabad8a47c024ddbf097f91202a71b88cff6fc (patch)
treec231f180320438f262879021e2e02bbace0911b3 /sys/arm/at91/at91_wdt.c
parent87987160d6251c2bc94a904a9995d519ac8df5fd (diff)
downloadFreeBSD-src-2edabad8a47c024ddbf097f91202a71b88cff6fc.zip
FreeBSD-src-2edabad8a47c024ddbf097f91202a71b88cff6fc.tar.gz
if_ate.c:
* Support for sam9 "EMAC" controller. * Support for rmii interface to phy. at91.c & at91sam9.c: * Eliminate separate at91sam9.c file. * Add new devices to at91sam9_devs table. at91_machdep.c & at at91sam9_machdep.c: * Automatic chip type determination. * Remove compile time chip dependencies. * Eliminate separate at91sam9_machdep.c file. at91_pmc.c: * Corrected support for all of the sam926? and sam9g20 chips. * Remove compile time chip dependencies. My apologies to Greg for taking so long to take care of it.
Diffstat (limited to 'sys/arm/at91/at91_wdt.c')
-rw-r--r--sys/arm/at91/at91_wdt.c222
1 files changed, 222 insertions, 0 deletions
diff --git a/sys/arm/at91/at91_wdt.c b/sys/arm/at91/at91_wdt.c
new file mode 100644
index 0000000..6ae01ef
--- /dev/null
+++ b/sys/arm/at91/at91_wdt.c
@@ -0,0 +1,222 @@
+/*-
+ * Copyright (c) 2010 Greg Ansley. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * The sam9 watchdog hardware can be programed only once. So we set the hardware
+ * watchdog to 16s in wdt_attach and only reset it in the wdt_tick
+ * handler. The watchdog is halted in processor debug mode.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+
+#include <arm/at91/at91var.h>
+#include <arm/at91/at91_wdtreg.h>
+
+struct wdt_softc {
+ struct mtx sc_mtx;
+ device_t sc_dev;
+ struct resource *mem_res;
+ struct callout tick_ch;
+ eventhandler_tag sc_wet;
+ void *intrhand;
+ u_int cmd;
+ u_int interval;
+};
+
+static inline uint32_t
+RD4(struct wdt_softc *sc, bus_size_t off)
+{
+ return (bus_read_4(sc->mem_res, off));
+}
+
+static inline void
+WR4(struct wdt_softc *sc, bus_size_t off, uint32_t val)
+{
+ bus_write_4(sc->mem_res, off, val);
+}
+
+static int
+wdt_intr(void *argp)
+{
+ struct wdt_softc *sc = argp;
+
+
+ if (RD4(sc, WDT_SR) & (WDT_WDUNF | WDT_WDERR)) {
+#if defined(KDB) && !defined(KDB_UNATTENDED)
+ kdb_backtrace();
+ kdb_enter(KDB_WHY_WATCHDOG, "watchdog timeout");
+#else
+ panic("watchdog timeout");
+#endif
+ }
+ return (FILTER_STRAY);
+}
+
+/* User interface, see watchdog(9) */
+static void
+wdt_watchdog(void *argp, u_int cmd, int *error)
+{
+ struct wdt_softc *sc = argp;
+ u_int interval;
+
+ mtx_lock(&sc->sc_mtx);
+
+ *error = 0;
+ sc->cmd = 0;
+ interval = cmd & WD_INTERVAL;
+ if (interval > WD_TO_16SEC)
+ *error = EOPNOTSUPP;
+ else if (interval > 0)
+ sc->cmd = interval | WD_ACTIVE;
+
+ /* We cannot turn of our watchdog so if user
+ * fails to turn us on go to passive mode. */
+ if ((sc->cmd & WD_ACTIVE) == 0)
+ sc->cmd = WD_PASSIVE;
+
+ mtx_unlock(&sc->sc_mtx);
+}
+
+/* This routine is called no matter what state the user sets the
+ * watchdog mode to. Called at a rate that is slightly less than
+ * half the hardware timeout. */
+static void
+wdt_tick(void *argp)
+{
+ struct wdt_softc *sc = argp;
+
+ mtx_assert(&sc->sc_mtx, MA_OWNED);
+ if (sc->cmd & (WD_ACTIVE | WD_PASSIVE))
+ WR4(sc, WDT_CR, WDT_KEY|WDT_WDRSTT);
+
+ sc->cmd &= WD_PASSIVE;
+ callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc);
+}
+
+static int
+wdt_probe(device_t dev)
+{
+
+ if (at91_is_sam9()) {
+ device_set_desc(dev, "WDT");
+ return (0);
+ }
+ return (ENXIO);
+}
+
+static int
+wdt_attach(device_t dev)
+{
+ static struct wdt_softc *sc;
+ struct resource *irq;
+ uint32_t wdt_mr;
+ int rid, err;
+
+ sc = device_get_softc(dev);
+ sc->cmd = WD_PASSIVE;
+ sc->sc_dev = dev;
+
+ mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "at91_wdt", MTX_DEF);
+ callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0);
+
+ rid = 0;
+ sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+
+ if (sc->mem_res == NULL)
+ panic("couldn't allocate wdt register resources");
+
+ wdt_mr = RD4(sc, WDT_MR);
+ if ((wdt_mr & WDT_WDRSTEN) == 0)
+ device_printf(dev, "Watchdog disabled! (Boot ROM?)\n");
+ else {
+#ifdef WDT_RESET
+ /* Rude, full reset of whole system on watch dog timeout */
+ WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)|
+ WDT_WDRSTEN| WDT_WDV(0xFFF));
+#else
+ /* Generate stack trace and panic on watchdog timeout*/
+ WR4(sc, WDT_MR, WDT_WDDBGHLT | WDT_WDD(0xC00)|
+ WDT_WDFIEN| WDT_WDV(0xFFF));
+#endif
+ /* This may have been set by Boot ROM so register value
+ * may not be what we just requested since this is a
+ * write once register. */
+ wdt_mr = RD4(sc, WDT_MR);
+ if (wdt_mr & WDT_WDFIEN) {
+ rid = 0;
+ irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+ RF_ACTIVE | RF_SHAREABLE);
+ if (!irq)
+ panic("could not allocate interrupt.\n");
+
+ err = bus_setup_intr(dev, irq, INTR_TYPE_CLK, wdt_intr,
+ NULL, sc, &sc->intrhand);
+ }
+
+ /* interval * hz */
+ sc->interval = (((wdt_mr & WDT_WDV(~0)) + 1) * WDT_DIV) /
+ (WDT_CLOCK/hz);
+
+ device_printf(dev, "watchdog timeout: %d seconds\n",
+ sc->interval/hz);
+
+ /* Slightly less than 1/2 of watchdog hardware timeout */
+ sc->interval = (sc->interval/2) - (sc->interval/20);
+ callout_reset(&sc->tick_ch, sc->interval, wdt_tick, sc);
+
+ /* Register us as a watchdog */
+ sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list,
+ wdt_watchdog, sc, 0);
+ }
+ return (0);
+}
+
+static device_method_t wdt_methods[] = {
+ DEVMETHOD(device_probe, wdt_probe),
+ DEVMETHOD(device_attach, wdt_attach),
+ {0,0},
+};
+
+static driver_t wdt_driver = {
+ "at91_wdt",
+ wdt_methods,
+ sizeof(struct wdt_softc),
+};
+
+static devclass_t wdt_devclass;
+
+DRIVER_MODULE(at91_wdt, atmelarm, wdt_driver, wdt_devclass, 0, 0);
OpenPOWER on IntegriCloud