Once i.MX's watchdog is enabled, it can never be disabled. Some 64-bit
authorpatrick <patrick@openbsd.org>
Fri, 28 May 2021 13:03:55 +0000 (13:03 +0000)
committerpatrick <patrick@openbsd.org>
Fri, 28 May 2021 13:03:55 +0000 (13:03 +0000)
i.MX machines with a recent U-Boot come up with the watchdog enabled, so
we have to regularly ping it to make sure the watchdog doesn't reset us.

The watchdog's timeout can be configured in 0.5s steps to a maximum of
128s.  Set it to the maximum, and schedule a timeout which reloads the
counter every 120s.

This only needs to be done if the watchdog is enabled when we boot up.

Tested on Cubox-i (armv7) and MNT Reform (arm64)
ok kettenis@

sys/dev/fdt/imxdog.c

index 69663af..82f4a12 100644 (file)
@@ -1,6 +1,6 @@
-/* $OpenBSD: imxdog.c,v 1.1 2021/05/28 11:50:18 patrick Exp $ */
+/* $OpenBSD: imxdog.c,v 1.2 2021/05/28 13:03:55 patrick Exp $ */
 /*
- * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2012-2013,2021 Patrick Wildt <patrick@blueri.se>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
 #include <sys/param.h>
 #include <sys/systm.h>
 #include <sys/device.h>
+#include <sys/timeout.h>
 
 #include <machine/bus.h>
 #include <machine/fdt.h>
 
-#include <armv7/armv7/armv7_machdep.h>
-
 #include <dev/ofw/openfirm.h>
 #include <dev/ofw/fdt.h>
 
+extern void (*cpuresetfn)(void);
+
 /* registers */
 #define WCR            0x00
+#define  WCR_WDE               (1 << 2)
+#define  WCR_WT_SEC(x)         (((x) * 2 - 1) << 8)
+#define  WCR_WT_MASK           (0xff << 8)
 #define WSR            0x02
 #define WRSR           0x04
 #define WICR           0x06
 #define WMCR           0x08
 
+#define WDOG_TIMEOUT_CALLBACK          120
+#define WDOG_MAX_TIMEOUT_SEC           128
+
 struct imxdog_softc {
        struct device           sc_dev;
        bus_space_tag_t         sc_iot;
        bus_space_handle_t      sc_ioh;
+       struct timeout          sc_tmo;
 };
 
 struct imxdog_softc *imxdog_sc;
@@ -45,6 +53,7 @@ struct imxdog_softc *imxdog_sc;
 int    imxdog_match(struct device *, void *, void *);
 void   imxdog_attach(struct device *, struct device *, void *);
 void   imxdog_reset(void);
+void   imxdog_timeout(void *);
 
 struct cfattach        imxdog_ca = {
        sizeof (struct imxdog_softc), imxdog_match, imxdog_attach
@@ -67,6 +76,7 @@ imxdog_attach(struct device *parent, struct device *self, void *aux)
 {
        struct fdt_attach_args *faa = aux;
        struct imxdog_softc *sc = (struct imxdog_softc *) self;
+       uint16_t reg;
 
        if (faa->fa_nreg < 1)
                return;
@@ -78,27 +88,55 @@ imxdog_attach(struct device *parent, struct device *self, void *aux)
 
        printf("\n");
 
+       timeout_set(&sc->sc_tmo, imxdog_timeout, sc);
+
+       /* Adjust timeout to maximum seconds */
+       reg = bus_space_read_2(sc->sc_iot, sc->sc_ioh, WCR);
+       reg &= ~WCR_WT_MASK;
+       reg |= WCR_WT_SEC(WDOG_MAX_TIMEOUT_SEC);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WCR, reg);
+
+       /* Watchdog cannot be disabled, ping the watchdog if enabled */
+       if (bus_space_read_2(sc->sc_iot, sc->sc_ioh, WCR) & WCR_WDE)
+               imxdog_timeout(sc);
+
        imxdog_sc = sc;
-       cpuresetfn = imxdog_reset;
+       if (cpuresetfn == NULL)
+               cpuresetfn = imxdog_reset;
 }
 
 void
 imxdog_reset(void)
 {
-       if (imxdog_sc == NULL)
+       struct imxdog_softc *sc = imxdog_sc;
+
+       if (sc == NULL)
                return;
 
        /* disable watchdog and set timeout to 0 */
-       bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 0);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WCR, 0);
 
        /* sequence to reset timeout counter */
-       bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0x5555);
-       bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0xaaaa);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WSR, 0x5555);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WSR, 0xaaaa);
 
        /* enable watchdog */
-       bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WCR, 1);
        /* errata TKT039676 */
-       bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WCR, 1);
 
        delay(100000);
 }
+
+void
+imxdog_timeout(void *args)
+{
+       struct imxdog_softc *sc = args;
+
+       /* Reload timeout counter */
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WSR, 0x5555);
+       bus_space_write_2(sc->sc_iot, sc->sc_ioh, WSR, 0xaaaa);
+
+       /* Schedule reload to trigger before counter runs out */
+       timeout_add_sec(&sc->sc_tmo, WDOG_TIMEOUT_CALLBACK);
+}