Index: head/sys/arm/conf/BEAGLEBONE =================================================================== --- head/sys/arm/conf/BEAGLEBONE +++ head/sys/arm/conf/BEAGLEBONE @@ -108,6 +108,12 @@ # ADC support device ti_adc +# Watchdog support +# If we don't enable the watchdog driver, the system could potentially +# reboot automatically because the boot loader might have enabled the +# watchdog. +device ti_wdt + # USB support device usb options USB_HOST_ALIGN=64 # Align usb buffers to cache line size. Index: head/sys/arm/ti/files.ti =================================================================== --- head/sys/arm/ti/files.ti +++ head/sys/arm/ti/files.ti @@ -19,6 +19,8 @@ arm/ti/ti_mbox.c standard arm/ti/ti_pruss.c standard +arm/ti/ti_wdt.c optional ti_wdt + arm/ti/ti_adc.c optional ti_adc arm/ti/ti_gpio.c optional gpio arm/ti/ti_i2c.c optional ti_i2c Index: head/sys/arm/ti/ti_wdt.h =================================================================== --- head/sys/arm/ti/ti_wdt.h +++ head/sys/arm/ti/ti_wdt.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2014 Rui Paulo + * 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 THE AUTHOR ``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 THE AUTHOR 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. + * + * $FreeBSD$ + */ +#ifndef _TI_WDT_H_ +#define _TI_WDT_H_ + +/* TI WDT registers */ +#define TI_WDT_WIDR 0x00 /* Watchdog Identification Register */ +#define TI_WDT_WDSC 0x10 /* Watchdog System Control Register */ +#define TI_WDT_WDST 0x14 /* Watchdog Status Register */ +#define TI_WDT_WISR 0x18 /* Watchdog Interrupt Status Register */ +#define TI_WDT_WIER 0x1c /* Watchdog Interrupt Enable Register */ +#define TI_WDT_WCLR 0x24 /* Watchdog Control Register */ +#define TI_WDT_WCRR 0x28 /* Watchdog Counter Register */ +#define TI_WDT_WLDR 0x2c /* Watchdog Load Register */ +#define TI_WDT_WTGR 0x30 /* Watchdog Trigger Register */ +#define TI_WDT_WWPS 0x34 /* Watchdog Write Posting Register */ +#define TI_WDT_WDLY 0x44 /* Watchdog Delay Configuration Reg */ +#define TI_WDT_WSPR 0x48 /* Watchdog Start/Stop Register */ +#define TI_WDT_WIRQSTATRAW 0x54 /* Watchdog Raw Interrupt Status Reg. */ +#define TI_WDT_WIRQSTAT 0x58 /* Watchdog Int. Status Register */ +#define TI_WDT_WIRQENSET 0x5c /* Watchdog Int. Enable Set Register */ +#define TI_WDT_WIRQENCLR 0x60 /* Watchdog Int. Enable Clear Reg. */ + +/* WDT_WDSC Register */ +#define TI_WDSC_SR (1 << 1) /* Soft reset */ + +/* + * WDT_WWPS Register + * + * Writes to some registers require synchronisation with a different clock + * domain. The WDT_WWPS register is the place where this synchronisation + * happens. + */ +#define TI_W_PEND_WCLR (1 << 0) +#define TI_W_PEND_WCRR (1 << 1) +#define TI_W_PEND_WLDR (1 << 2) +#define TI_W_PEND_WTGR (1 << 3) +#define TI_W_PEND_WSPR (1 << 4) +#define TI_W_PEND_WDLY (1 << 5) + +/* WDT_WIRQENSET Register */ +#define TI_IRQ_EN_OVF (1 << 0) /* Overflow interrupt */ +#define TI_IRQ_EN_DLY (1 << 1) /* Delay interrupt */ + +/* WDT_WIRQSTAT Register */ +#define TI_IRQ_EV_OVF (1 << 0) /* Overflow event */ +#define TI_IRQ_EV_DLY (1 << 1) /* Delay event */ + +#endif /* _TI_WDT_H_ */ Index: head/sys/arm/ti/ti_wdt.c =================================================================== --- head/sys/arm/ti/ti_wdt.c +++ head/sys/arm/ti/ti_wdt.c @@ -0,0 +1,275 @@ +/*- + * Copyright (c) 2014 Rui Paulo + * 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 THE AUTHOR ``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 THE AUTHOR 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. + */ +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef DEBUG +#define DPRINTF(fmt, ...) do { \ + printf("%s: ", __func__); \ + printf(fmt, __VA_ARGS__); \ +} while (0) +#else +#define DPRINTF(fmt, ...) +#endif + +static device_probe_t ti_wdt_probe; +static device_attach_t ti_wdt_attach; +static device_detach_t ti_wdt_detach; +static void ti_wdt_intr(void *); +static void ti_wdt_event(void *, unsigned int, int *); + +struct ti_wdt_softc { + struct mtx sc_mtx; + struct resource *sc_mem_res; + struct resource *sc_irq_res; + void *sc_intr; + bus_space_tag_t sc_bt; + bus_space_handle_t sc_bh; + eventhandler_tag sc_ev_tag; +}; + +static device_method_t ti_wdt_methods[] = { + DEVMETHOD(device_probe, ti_wdt_probe), + DEVMETHOD(device_attach, ti_wdt_attach), + DEVMETHOD(device_detach, ti_wdt_detach), + + DEVMETHOD_END +}; + +static driver_t ti_wdt_driver = { + "ti_wdt", + ti_wdt_methods, + sizeof(struct ti_wdt_softc) +}; + +static devclass_t ti_wdt_devclass; + +DRIVER_MODULE(ti_wdt, simplebus, ti_wdt_driver, ti_wdt_devclass, 0, 0); + +static volatile __inline uint32_t +ti_wdt_reg_read(struct ti_wdt_softc *sc, uint32_t reg) +{ + return (bus_space_read_4(sc->sc_bt, sc->sc_bh, reg)); +} + +static __inline void +ti_wdt_reg_write(struct ti_wdt_softc *sc, uint32_t reg, uint32_t val) +{ + bus_space_write_4(sc->sc_bt, sc->sc_bh, reg, val); +} + +/* + * Wait for the write to a specific synchronised register to complete. + */ +static __inline void +ti_wdt_reg_wait(struct ti_wdt_softc *sc, uint32_t bit) +{ + while (ti_wdt_reg_read(sc, TI_WDT_WWPS) & bit) + DELAY(10); + +} + +static __inline void +ti_wdt_disable(struct ti_wdt_softc *sc) +{ + DPRINTF("disabling watchdog %p\n", sc); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xAAAA); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x5555); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); +} + +static __inline void +ti_wdt_enable(struct ti_wdt_softc *sc) +{ + DPRINTF("enabling watchdog %p\n", sc); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0xBBBB); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); + ti_wdt_reg_write(sc, TI_WDT_WSPR, 0x4444); + ti_wdt_reg_wait(sc, TI_W_PEND_WSPR); +} + +static int +ti_wdt_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + if (ofw_bus_is_compatible(dev, "ti,omap3-wdt")) { + device_set_desc(dev, "TI Watchdog Timer"); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +ti_wdt_attach(device_t dev) +{ + struct ti_wdt_softc *sc; + int rid; + + sc = device_get_softc(dev); + rid = 0; + mtx_init(&sc->sc_mtx, "TI WDT", NULL, MTX_DEF); + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "could not allocate memory resource\n"); + return (ENXIO); + } + sc->sc_bt = rman_get_bustag(sc->sc_mem_res); + sc->sc_bh = rman_get_bushandle(sc->sc_mem_res); + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "could not allocate interrupt resource\n"); + ti_wdt_detach(dev); + return (ENXIO); + } + if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC, + NULL, ti_wdt_intr, sc, &sc->sc_intr) != 0) { + device_printf(dev, + "unable to setup the interrupt handler\n"); + ti_wdt_detach(dev); + return (ENXIO); + } + /* Reset, enable interrupts and stop the watchdog. */ + ti_wdt_reg_write(sc, TI_WDT_WDSC, + ti_wdt_reg_read(sc, TI_WDT_WDSC) | TI_WDSC_SR); + while (ti_wdt_reg_read(sc, TI_WDT_WDSC) & TI_WDSC_SR) + DELAY(10); + ti_wdt_reg_write(sc, TI_WDT_WIRQENSET, TI_IRQ_EN_OVF | TI_IRQ_EN_DLY); + ti_wdt_disable(sc); + if (bootverbose) + device_printf(dev, "revision: 0x%x\n", + ti_wdt_reg_read(sc, TI_WDT_WIDR)); + sc->sc_ev_tag = EVENTHANDLER_REGISTER(watchdog_list, ti_wdt_event, sc, + 0); + + return (0); +} + +static int +ti_wdt_detach(device_t dev) +{ + struct ti_wdt_softc *sc; + + sc = device_get_softc(dev); + if (sc->sc_ev_tag) + EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_ev_tag); + if (sc->sc_intr) + bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr); + if (sc->sc_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); + if (sc->sc_mem_res) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->sc_mem_res), sc->sc_mem_res); + + return (0); +} + +static void +ti_wdt_intr(void *arg) +{ + struct ti_wdt_softc *sc; + + sc = arg; + DPRINTF("interrupt %p", sc); + ti_wdt_reg_write(sc, TI_WDT_WIRQSTAT, TI_IRQ_EV_OVF | TI_IRQ_EV_DLY); + /* TODO: handle interrupt */ +} + +static void +ti_wdt_event(void *arg, unsigned int cmd, int *error) +{ + struct ti_wdt_softc *sc; + uint8_t s; + uint32_t wldr; + uint32_t ptv; + + sc = arg; + ti_wdt_disable(sc); + if (cmd == WD_TO_NEVER) { + *error = 0; + return; + } + DPRINTF("cmd 0x%x\n", cmd); + cmd &= WD_INTERVAL; + if (cmd < WD_TO_1SEC) { + *error = EINVAL; + return; + } + s = 1 << (cmd - WD_TO_1SEC); + DPRINTF("seconds %u\n", s); + /* + * Leave the pre-scaler with its default values: + * PTV = 0 == 2**0 == 1 + * PRE = 1 (enabled) + * + * Compute the load register value assuming a 32kHz clock. + * See OVF_Rate in the WDT section of the AM335x TRM. + */ + ptv = 0; + wldr = 0xffffffff - (s * (32768 / (1 << ptv))) + 1; + DPRINTF("wldr 0x%x\n", wldr); + ti_wdt_reg_write(sc, TI_WDT_WLDR, wldr); + /* + * Trigger a timer reload. + */ + ti_wdt_reg_write(sc, TI_WDT_WTGR, + ti_wdt_reg_read(sc, TI_WDT_WTGR) + 1); + ti_wdt_reg_wait(sc, TI_W_PEND_WTGR); + ti_wdt_enable(sc); + *error = 0; +}