Hi,
this is a driver for the watchdog found in RockChip SoCs (at least
RockChip 3399). Note that it attaches to "snps,dw-wdt", so it may be
found elsewhere and maybe another name may be more appropriate.
I have no idea how to figure out the frequency of the driving clock so
the driver measures the frequency on first use.
The timeout can be programmed only to about powers of two (1,2,5,10,21s)
on by RockPro64, where the watchdog is driven by a ridiculously fast
clock of 100 MHz (100 KHz would be more adequate).
On my RockPro64 the watchdog cannot be disabled once it has been
enabled. To work around this I (ab)use the interrupt mechanism of the
watchdog and would recommend to simlpy use kern.watchdog.auto=1 to
"disable" the watchdog. This won't save ddb(4) though.
While tracing the driver I noticed that interrupts are delivered twice.
I'm looking for reviews and OKs. Thanks!
Christopher
Index: sys/dev/fdt/files.fdt
===================================================================
RCS file: /cvs/src/sys/dev/fdt/files.fdt,v
retrieving revision 1.160
diff -u -p -r1.160 files.fdt
--- sys/dev/fdt/files.fdt 21 Nov 2021 11:02:21 -0000 1.160
+++ sys/dev/fdt/files.fdt 8 Dec 2021 09:37:23 -0000
@@ -383,6 +383,10 @@ device rkvop
attach rkvop at fdt
file dev/fdt/rkvop.c rkvop
+device rkdog
+attach rkdog at fdt
+file dev/fdt/rkdog.c rkdog
+
device dwmmc: sdmmcbus
attach dwmmc at fdt
file dev/fdt/dwmmc.c dwmmc
Index: sys/dev/fdt/rkdog.c
===================================================================
RCS file: sys/dev/fdt/rkdog.c
diff -N sys/dev/fdt/rkdog.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/dev/fdt/rkdog.c 8 Dec 2021 09:37:23 -0000
@@ -0,0 +1,263 @@
+/* $OpenBSD: rkdog.c,v 1.3 2020/05/29 04:42:25 deraadt Exp $ */
+/*
+ * Copyright (c) 2021 Christopher Zimmermann <chr...@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/timeout.h>
+
+#include <machine/intr.h>
+#include <machine/bus.h>
+#include <machine/fdt.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_clock.h>
+#include <dev/ofw/fdt.h>
+
+/* Registers */
+#define RKDOG_CR 0x0000 /* control */
+#define RKDOG_CR_ENABLE 0x0001 /* enable counter */
+#define RKDOG_CR_INTERRUPT 0x0002
+/*
+ * On some devices RKDOG_CR_ENABLE can only be set, but not be cleared.
+ *
+ * If RKDOG_CR_INTERRUPT is enabled the watchdog will count twice. After the
+ * first count the interrupt is raised as a warning. After the second count
+ * reset happens only when the interrupt is not cleared.
+ */
+#define RKDOG_CR_RST_PULSE_LENGTH_MASK 0x001c
+#define RKDOG_CR_RST_PULSE_LENGTH_SHIFT 2
+/*
+ * 000: 2 pclk cycles
+ * 001: 4 pclk cycles
+ * 010: 8 pclk cycles DEFAULT
+ * 011: 16 pclk cycles
+ * 100: 32 pclk cycles
+ * 101: 64 pclk cycles
+ * 110: 128 pclk cycles
+ * 111: 256 pclk cycles
+ */
+/* timeout range n={0..15}. Timeout is 2^(16+n)-1 ticks */
+#define RKDOG_TORR 0x0004
+#define RKDOG_CCVR 0x0008 /* current counter value */
+#define RKDOG_CRR 0x000c /* counter restart by writing code
number */
+#define RKDOG_CRR_MAGIC 0x76
+#define RKDOG_STAT 0x0010 /* interrupt status */
+#define RKDOG_EOI 0x0014 /* clear interrupt */
+
+#define HREAD4(sc, reg)
\
+ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
+#define HWRITE4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
+
+struct rkdog_softc {
+ struct device sc_dev;
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ struct timeout sc_to;
+ uint32_t sc_freq;
+ int sc_period;
+};
+
+int rkdog_match(struct device *, void *, void *);
+void rkdog_attach(struct device *, struct device *, void *);
+static void rkdog_dump(struct rkdog_softc *, const char *);
+int rkdog_intr(void *);
+int rkdog_cb(void *, int);
+
+struct cfattach rkdog_ca = {
+ sizeof (struct rkdog_softc), rkdog_match, rkdog_attach
+};
+
+struct cfdriver rkdog_cd = {
+ NULL, "rkdog", DV_DULL
+};
+
+int
+rkdog_match(struct device *parent, void *match, void *aux)
+{
+ struct fdt_attach_args *faa = aux;
+
+ return OF_is_compatible(faa->fa_node, "snps,dw-wdt");
+}
+
+void
+rkdog_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct rkdog_softc *sc = (struct rkdog_softc *)self;
+ struct fdt_attach_args *faa = aux;
+
+ if (faa->fa_nreg < 1) {
+ printf(": no registers\n");
+ return;
+ }
+
+ sc->sc_iot = faa->fa_iot;
+ if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
+ faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
+ printf(": can't map registers\n");
+ return;
+ }
+
+ if (HREAD4(sc, RKDOG_CR) != 0x0a) {
+ printf(" unexpected (expected 0xa)\n");
+ return;
+ }
+
+ fdt_intr_establish(faa->fa_node, IPL_CLOCK, rkdog_intr,
+ sc, sc->sc_dev.dv_xname);
+ if (NULL == fdt_intr_establish(faa->fa_node, IPL_BIO,
+ rkdog_intr, sc, sc->sc_dev.dv_xname)) {
+ printf(": unable to establish interrupt\n");
+ return;
+ }
+
+ printf("\n");
+
+ sc->sc_freq = 0;
+
+ rkdog_dump(sc, "initial dump");
+
+#ifndef SMALL_KERNEL
+ wdog_register(rkdog_cb, sc);
+#endif
+
+ clock_set_assigned(faa->fa_node);
+ clock_enable_all(faa->fa_node);
+}
+
+static void
+rkdog_dump(struct rkdog_softc *sc, const char *msg) {
+#ifdef DEBUG
+ printf("%s: %s\n CR=%#x TORR=%#x(%u) CCVR=%#x STAT=%#x\n",
+ sc->sc_dev.dv_xname, msg,
+ HREAD4(sc, RKDOG_CR),
+ HREAD4(sc, RKDOG_TORR),
+ HREAD4(sc, RKDOG_TORR),
+ HREAD4(sc, RKDOG_CCVR),
+ HREAD4(sc, RKDOG_STAT));
+#endif
+}
+
+int
+rkdog_intr(void *self) {
+ struct rkdog_softc *sc = self;
+
+ rkdog_dump(sc, "interrupt");
+
+ /* clear all interrupts */
+ HREAD4(sc, RKDOG_EOI);
+
+ return 1;
+}
+
+int
+rkdog_cb(void *self, int period)
+{
+ struct rkdog_softc *sc = self;
+ unsigned tor = 0;
+ uint32_t control;
+
+ rkdog_dump(sc, "watchdog callback");
+
+ /*
+ * to enable set RC_ENABLE and clear RC_INTERRUPT
+ * to disable clear RC_ENABLE and set RC_INTERRUPT
+ * Even when RC_ENABLE cannot be cleared this prevents a reset because
+ * we acknowledge all interrupts.
+ */
+
+ if (period == 0) {
+ /* in case counter cannot be disabled enable interrupt */
+ control = HREAD4(sc, RKDOG_CR);
+ if ((control & RKDOG_CR_ENABLE) == 0)
+ return 0;
+ HWRITE4(sc, RKDOG_TORR, 15);
+ control &= ~RKDOG_CR_ENABLE;
+ control |= RKDOG_CR_INTERRUPT;
+ HWRITE4(sc, RKDOG_CR, control);
+ control = HREAD4(sc, RKDOG_CR);
+
+ if (control & RKDOG_CR_ENABLE) {
+ /* we don't enable without measuring freq */
+ printf("%s: Cannot disable watchdog timer. "
+ "Please enable kern.watchdog.auto as a
workaround\n",
+ sc->sc_dev.dv_xname);
+ return (0x7fffffff / sc->sc_freq);
+ }
+ else
+ return 0;
+ }
+
+ if (period == sc->sc_period) {
+#ifdef DEBUG
+ printf("%s: just tickling\n", sc->sc_dev.dv_xname);
+#endif
+ HWRITE4(sc, RKDOG_CRR, RKDOG_CRR_MAGIC);
+ return sc->sc_period;
+ }
+
+ control = HREAD4(sc, RKDOG_CR);
+
+ if (sc->sc_freq == 0) {
+ uint32_t ticks, dt = 1000; /* us */
+
+ HWRITE4(sc, RKDOG_TORR, 15);
+ control |= RKDOG_CR_ENABLE | RKDOG_CR_INTERRUPT;
+ HWRITE4(sc, RKDOG_CR, control);
+ HWRITE4(sc, RKDOG_CRR, RKDOG_CRR_MAGIC);
+
+ ticks = HREAD4(sc, RKDOG_CCVR);
+ delay(dt);
+ ticks -= HREAD4(sc, RKDOG_CCVR);
+ sc->sc_freq = ticks * (1000000 / dt);
+#ifdef DEBUG
+ printf("%s: %d / %u us => frequency %u\n",
+ sc->sc_dev.dv_xname, ticks, dt, sc->sc_freq);
+#endif
+ }
+
+ while (tor < 15 &&
+ 1 << tor < (sc->sc_freq >> 16) * period)
+ tor++;
+
+#ifdef DEBUG
+ printf("%s: target period=%ds ticks_wanted=%u tor=%u ticks=%#x(%u /
%us)\n",
+ sc->sc_dev.dv_xname,
+ period,
+ sc->sc_freq * period - 1,
+ tor,
+ (1 << (16+tor)) - 1,
+ (1 << (16+tor)) - 1,
+ ((1 << (16+tor)) - 1) / sc->sc_freq);
+#endif
+
+ assert((1 << (16+tor)) - 1 >= sc->sc_freq);
+ sc->sc_period = ((1 << (16+tor)) - 1) / sc->sc_freq;
+
+ HWRITE4(sc, RKDOG_TORR, tor);
+ control = HREAD4(sc, RKDOG_CR);
+ control |= RKDOG_CR_ENABLE;
+ control &= ~RKDOG_CR_INTERRUPT;
+ HWRITE4(sc, RKDOG_CR, control);
+ HWRITE4(sc, RKDOG_CRR, RKDOG_CRR_MAGIC);
+
+ rkdog_dump(sc, "started watchdog");
+
+ return sc->sc_period;
+}
Index: share/man/man4/rkdog.4
===================================================================
RCS file: share/man/man4/rkdog.4
diff -N share/man/man4/rkdog.4
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ share/man/man4/rkdog.4 8 Dec 2021 09:37:23 -0000
@@ -0,0 +1,38 @@
+.\" $OpenBSD: rkdog.4,v 1.1 2019/10/16 22:24:45 kettenis Exp $
+.\"
+.\" Copyright (c) 2021 Christopher Zimmermann <chr...@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: December 7 2021 $
+.Dt rkdog 4
+.Os
+.Sh NAME
+.Nm rkdog
+.Nd watchdog timer device used on RockChip SoCs
+.Sh SYNOPSIS
+.Cd "rkdog* at fdt?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the watchdog timer device integrated in RockChip
SoCs.
+The watchdog timeout can only be programmed on a log2 scale.
+The driver will round up the timeout to the next longer possible setting.
+The actually selected timeout can and should be checked by
+.Xr sysctl 4 to make sure the watchdog gets tickled in time in case the
+requested period was longer than supported.
+.Sh HISTORY
+The
+.Nm
+device driver first appeared in
+.Ox 7.0 .
Index: sys/arch/arm64/conf/GENERIC
===================================================================
RCS file: /cvs/src/sys/arch/arm64/conf/GENERIC,v
retrieving revision 1.214
diff -u -p -r1.214 GENERIC
--- sys/arch/arm64/conf/GENERIC 22 Nov 2021 20:25:50 -0000 1.214
+++ sys/arch/arm64/conf/GENERIC 8 Dec 2021 09:37:23 -0000
@@ -277,6 +277,7 @@ rkpwm* at fdt?
rkrng* at fdt?
rktemp* at fdt?
rkvop* at fdt?
+rkdog* at fdt?
rkdwusb* at fdt?
dwmmc* at fdt?
sdmmc* at dwmmc?