/* $NetBSD$ */ /*- * Copyright (c) 2010 SHIMIZU Ryo * 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 __KERNEL_RCSID(0, "$NetBSD$"); #include #include #include #include #include #include #include #include #define IMX51_WDOG_HZ 2 /* timer resolution of 0.5 sec */ #define IMX51_WDOG_PERIOD_DEFAULT 120 #define IMX51_WDOG_PERIOD_MAX 127 struct imx51wdog_softc { device_t sc_dev; bus_addr_t sc_addr; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; void *sc_ih; uint32_t sc_period; struct sysmon_wdog sc_smw; }; void imx51_wdog_restart_all(void); static int imx51wdog_match(device_t, struct cfdata *, void *); static void imx51wdog_attach(device_t, device_t, void *); static void imx51wdog_init(struct imx51wdog_softc *); static void imx51wdog_enable(struct imx51wdog_softc *); static void imx51wdog_disable(struct imx51wdog_softc *); static void imx51wdog_restart(struct imx51wdog_softc *); static int imx51wdog_intr(void *); static int imx51wdog_setmode(struct sysmon_wdog *); static int imx51wdog_tickle(struct sysmon_wdog *); CFATTACH_DECL_NEW(imx51wdog, sizeof(struct imx51wdog_softc), imx51wdog_match, imx51wdog_attach, NULL, NULL); /* XXX: statically saved softc */ #define MAXWATCHDOG 2 static struct imx51wdog_softc *sc_wdog[MAXWATCHDOG]; /* ARGSUSED */ static int imx51wdog_match(device_t parent __unused, struct cfdata *match __unused, void *aux) { struct axi_attach_args *aa; aa = aux; aa->aa_size = IMX51_WDOG_SIZE; return 1; } void imx51_wdog_restart_all(void) { int i; for (i = 0; i < MAXWATCHDOG; i++) if (sc_wdog[i] != NULL) imx51wdog_restart(sc_wdog[i]); } /* ARGSUSED */ static void imx51wdog_attach(device_t parent __unused, device_t self, void *aux) { struct imx51wdog_softc *sc; struct axi_attach_args *aa; uint16_t v; aa = aux; sc = device_private(self); sc->sc_dev = self; sc->sc_iot = aa->aa_iot; sc->sc_addr = aa->aa_addr; /* XXX: statically saved softc */ if (sc->sc_dev->dv_unit < MAXWATCHDOG) sc_wdog[sc->sc_dev->dv_unit] = sc; aprint_normal(": Watch Dog Timer\n"); aprint_naive("\n"); if (bus_space_map(sc->sc_iot, sc->sc_addr, aa->aa_size, 0, &sc->sc_ioh)) { aprint_error(": can't map registers\n"); return; } /* IMPORTANT: disable Power Down Counter */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_MISCCTRL_REG, 0); /* read last reset reason */ v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_RESET_REG); if (v & IMX51_WDOG_RESET_SFTW) aprint_normal_dev(self, "reset by Software Reset\n"); if (v & IMX51_WDOG_RESET_TOUT) aprint_normal_dev(self, "reset by Watchdog Timeout\n"); sc->sc_period = IMX51_WDOG_PERIOD_DEFAULT; imx51wdog_init(sc); sc->sc_smw.smw_cookie = sc; sc->sc_smw.smw_name = device_xname(sc->sc_dev); sc->sc_smw.smw_setmode = imx51wdog_setmode; sc->sc_smw.smw_tickle = imx51wdog_tickle; sc->sc_smw.smw_period = sc->sc_period; if (sysmon_wdog_register(&sc->sc_smw) != 0) aprint_error_dev(self, "unable to register with sysmon\n"); /* XXX: disable interrupt */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_INTCTRL_REG, 0); sc->sc_ih = intr_establish(aa->aa_irq, IPL_HIGH, 0, imx51wdog_intr, sc); } static void imx51wdog_init(struct imx51wdog_softc *sc) { uint16_t v; imx51wdog_restart(sc); /* setup to suspend watchdog */ #if 0 /* OK */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG, IMX51_WDOG_CONTROL_WDZST | IMX51_WDOG_CONTROL_WDBG | IMX51_WDOG_CONTROL_WDE | IMX51_WDOG_CONTROL_WDT | IMX51_WDOG_CONTROL_SRS | IMX51_WDOG_CONTROL_WDA | IMX51_WDOG_CONTROL_WDW | IMX51_WDOG_CONTROL_WT(sc->sc_period * 2)); #endif v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG); bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG, v | IMX51_WDOG_CONTROL_WT_MASK | IMX51_WDOG_CONTROL_WOE | IMX51_WDOG_CONTROL_WDA | /* must be set: no assert */ IMX51_WDOG_CONTROL_SRS | /* must be set: no assert */ IMX51_WDOG_CONTROL_WDT | IMX51_WDOG_CONTROL_WDBG | IMX51_WDOG_CONTROL_WDZST); /* restart watchdog */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_SERVICE_REG, IMX51_WDOG_SERVICE_MAGIC1); bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_SERVICE_REG, IMX51_WDOG_SERVICE_MAGIC2); } static void imx51wdog_enable(struct imx51wdog_softc *sc) { uint16_t val; printf("%s:%d\n", __func__, __LINE__); /* enable watchdog */ val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG); val &= ~IMX51_WDOG_CONTROL_WT_MASK; val |= sc->sc_period << 9; val |= IMX51_WDOG_CONTROL_WDE; bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG, val); } static void imx51wdog_disable(struct imx51wdog_softc *sc) { uint16_t val; printf("%s:%d\n", __func__, __LINE__); /* disable watchdog. didnt work??? */ val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG); val &= ~IMX51_WDOG_CONTROL_WDE; bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_CONTROL_REG, val); } static void imx51wdog_restart(struct imx51wdog_softc *sc) { /* restart watchdog */ bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_SERVICE_REG, IMX51_WDOG_SERVICE_MAGIC1); bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMX51_WDOG_SERVICE_REG, IMX51_WDOG_SERVICE_MAGIC2); } static int imx51wdog_intr(void *arg) { struct imx51wdog_softc *sc; sc = (struct imx51wdog_softc *)arg; printf("%s: interrupt occured\n", device_xname(sc->sc_dev)); return 1; } static int imx51wdog_setmode(struct sysmon_wdog *smw) { struct imx51wdog_softc *sc; sc = smw->smw_cookie; if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { imx51wdog_disable(sc); return 0; } if (smw->smw_period == WDOG_PERIOD_DEFAULT) smw->smw_period = sc->sc_period; if (smw->smw_period > IMX51_WDOG_PERIOD_MAX) return EINVAL; sc->sc_period = smw->smw_period; imx51wdog_enable(sc); return 0; } static int imx51wdog_tickle(struct sysmon_wdog *smw) { struct imx51wdog_softc *sc; sc = smw->smw_cookie; printf("%s:%d\n", __func__, __LINE__); imx51wdog_restart(sc); return 0; }