Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F131890199
D26761.id78173.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D26761.id78173.diff
View Options
Index: sys/arm64/rockchip/clk/rk3399_cru.c
===================================================================
--- sys/arm64/rockchip/clk/rk3399_cru.c
+++ sys/arm64/rockchip/clk/rk3399_cru.c
@@ -1211,6 +1211,7 @@
106),
FRACT(DCLK_VOP1_FRAC, "dclk_vop1_frac", "dclk_vop1_div", 0,
107),
+ FRACT(PCLK_WDT, "pclk_wdt", "pclk_alive", 1, 1),
/* Not yet implemented yet
* MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc", RK3399_SDMMC_CON0, 1),
Index: sys/dev/dwwdt/dwwdt.c
===================================================================
--- sys/dev/dwwdt/dwwdt.c
+++ sys/dev/dwwdt/dwwdt.c
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 2020 BusyTech
+ *
+ * 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/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/types.h>
+#include <sys/bus.h>
+#include <sys/module.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+#include <sys/rman.h>
+#include <sys/resource.h>
+#include <sys/watchdog.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/* Registers */
+#define DWWDT_CR 0x00
+#define DWWDT_CR_WDT_EN (1 << 0)
+#define DWWDT_CR_RESP_MODE (1 << 1)
+#define DWWDT_TORR 0x04
+#define DWWDT_CCVR 0x08
+#define DWWDT_CRR 0x0C
+#define DWWDT_CRR_KICK 0x76
+#define DWWDT_STAT 0x10
+#define DWWDT_STAT_STATUS 0x01
+#define DWWDT_EOI 0x14
+
+/* Useful macros */
+#define DWWDT_READ4(sc, reg) bus_read_4((sc)->sc_mem_res, (reg))
+#define DWWDT_WRITE4(sc, reg, val) \
+ bus_write_4((sc)->sc_mem_res, (reg), (val))
+
+
+/*
+ * DWWDT has 16 different timeout periods here, however FreeBSD interface
+ * provides only 12 possible values. Let map it here.
+ */
+
+static const uint32_t dwwdt_timeout_vals[] = {
+ [WD_TO_1MS] = 0x0000, /* Actually this is 60ms. */
+ [WD_TO_125MS] = 0x0001,
+ [WD_TO_250MS] = 0x0002,
+ [WD_TO_500MS] = 0x0003,
+ [WD_TO_1SEC] = 0x0004,
+ [WD_TO_2SEC] = 0x0005,
+ [WD_TO_4SEC] = 0x0006,
+ [WD_TO_8SEC] = 0x0007,
+ [WD_TO_16SEC] = 0x0008,
+ [WD_TO_32SEC] = 0x0009,
+ [WD_TO_64SEC] = 0x000A,
+ [WD_TO_128SEC] = 0x000B,
+ [WD_TO_128SEC + 1] = 0x000C,
+ [WD_TO_128SEC + 2] = 0x000D,
+ [WD_TO_128SEC + 3] = 0x000E,
+ [WD_TO_128SEC + 4] = 0x000F,
+
+ /* Anything above cannot be set. */
+};
+
+struct dwwdt_softc {
+ device_t sc_dev;
+ struct resource *sc_mem_res;
+ struct resource *sc_irq_res;
+ void *sc_intr_cookie;
+ clk_t sc_clk;
+ uint64_t sc_clk_freq;
+ eventhandler_tag sc_evtag;
+ int sc_mem_rid;
+ int sc_irq_rid;
+};
+
+
+static devclass_t dwwdt_devclass;
+
+SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+ "Synopsys Designware watchdog timer");
+/* Setting this to 0 enables full restart mode. */
+static uint32_t dwwdt_prevent_restart = 1;
+SYSCTL_UINT(_dev_dwwdt, OID_AUTO, prevent_restart, CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &dwwdt_prevent_restart, 1,
+ "Prevent system restart (0 - Disabled; 1 - Enabled)");
+
+static uint32_t dwwdt_debug_enabled = 0;
+SYSCTL_UINT(_dev_dwwdt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_MPSAFE,
+ &dwwdt_debug_enabled, 1, "Debug mode (0 - Disabled; 1 - Enabled)");
+
+static int dwwdt_probe(device_t);
+static int dwwdt_attach(device_t);
+static int dwwdt_detach(device_t);
+static int dwwdt_shutdown(device_t);
+
+static void dwwdt_intr(void *);
+static void dwwdt_event(void *, unsigned int, int *);
+
+/* Helpers */
+static inline bool dwwdt_stop(const struct dwwdt_softc *sc);
+static inline void dwwdt_start(const struct dwwdt_softc *sc);
+static inline bool dwwdt_started(const struct dwwdt_softc *sc);
+static inline void dwwdt_kick(const struct dwwdt_softc *sc);
+
+static void dwwdt_debug(device_t);
+
+static void
+dwwdt_debug(device_t dev)
+{
+ /*
+ * Reading from EOI may clear interrupt flag.
+ */
+ const struct dwwdt_softc *sc = device_get_softc(dev);
+
+ device_printf(dev, "Registers dump: \n");
+ device_printf(dev, " CR: %08x\n", DWWDT_READ4(sc, DWWDT_CR));
+ device_printf(dev, " CCVR: %08x\n", DWWDT_READ4(sc, DWWDT_CCVR));
+ device_printf(dev, " CRR: %08x\n", DWWDT_READ4(sc, DWWDT_CRR));
+ device_printf(dev, " STAT: %08x\n", DWWDT_READ4(sc, DWWDT_STAT));
+ device_printf(dev, " EOI: %08x\n", DWWDT_READ4(sc, DWWDT_EOI));
+
+ device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk));
+ device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq);
+}
+
+static inline bool
+dwwdt_started(const struct dwwdt_softc *sc)
+{
+
+ return ((DWWDT_READ4(sc, DWWDT_CR) & DWWDT_CR_WDT_EN) != 0);
+}
+
+static inline bool
+dwwdt_stop(const struct dwwdt_softc *sc)
+{
+ uint32_t val;
+
+ /* Disable watchdog, disable interrupts. */
+ val = DWWDT_READ4(sc, DWWDT_CR);
+ val &= ~(DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE);
+ DWWDT_WRITE4(sc, DWWDT_CR, val);
+
+ /*
+ * Documentation says only system reset can stop watchdog. In any way
+ * we still can check we affected flag.
+ */
+ return (!dwwdt_started(sc));
+}
+
+static void inline
+dwwdt_start(const struct dwwdt_softc *sc)
+{
+ uint32_t val;
+
+ /* Enable watchdog */
+ val = DWWDT_READ4(sc, DWWDT_CR);
+ val |= DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE;
+ DWWDT_WRITE4(sc, DWWDT_CR, val);
+}
+
+static void inline
+dwwdt_kick(const struct dwwdt_softc *sc)
+{
+
+ DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK);
+}
+
+static void
+dwwdt_intr(void *arg)
+{
+ struct dwwdt_softc *sc = arg;
+
+ KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0,
+ ("Missing interrupt status bit?"));
+
+ /* Confirm interrupt reception. */
+ (void)DWWDT_READ4(sc, DWWDT_EOI);
+
+ if (dwwdt_prevent_restart != 0) {
+ /* Kick watchdog here if restart is prevented. */
+ dwwdt_kick(sc);
+ }
+}
+
+static void
+dwwdt_event(void *arg, unsigned int cmd, int *error)
+{
+ const struct dwwdt_softc *sc = arg;
+ uint32_t timeout;
+ uint32_t val;
+ bool stopped;
+
+ /*
+ * Try to stop watchdog. It may (surely) fail, but give it the chance.
+ */
+ timeout = cmd & WD_INTERVAL;
+
+ stopped = dwwdt_stop(sc);
+ if (cmd == 0) {
+ if (!stopped)
+ *error = EOPNOTSUPP;
+ return;
+ }
+
+ if (timeout > WD_TO_128SEC || timeout == WD_TO_NEVER)
+ return;
+
+ val = dwwdt_timeout_vals[timeout];
+ /* Set timeout. */
+ DWWDT_WRITE4(sc, DWWDT_TORR, val);
+
+ /* Enable watchdog */
+ dwwdt_start(sc);
+
+ /* Kick it. */
+ dwwdt_kick(sc);
+ *error = 0;
+ if (dwwdt_debug_enabled)
+ dwwdt_debug(sc->sc_dev);
+}
+
+static int
+dwwdt_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "snps,dw-wdt"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Synopsys Designware watchdog timer");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+dwwdt_attach(device_t dev)
+{
+ struct dwwdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ sc->sc_dev = dev;
+
+ sc->sc_mem_rid = 0;
+ sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->sc_mem_rid, RF_ACTIVE);
+ if (sc->sc_mem_res == NULL) {
+ device_printf(dev, "cannot allocate memory resource\n");
+ goto err_no_mem;
+ }
+
+ sc->sc_irq_rid = 0;
+ sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->sc_irq_rid, RF_ACTIVE);
+ if (sc->sc_irq_res == NULL) {
+ device_printf(dev, "cannot allocate ireq resource\n");
+ goto err_no_irq;
+ }
+
+ sc->sc_intr_cookie = NULL;
+ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
+ NULL, dwwdt_intr, sc, &sc->sc_intr_cookie) != 0) {
+ device_printf(dev, "cannot setup interrupt routine\n");
+ goto err_no_intr;
+ }
+ if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) {
+ device_printf(dev, "cannot find clock\n");
+ goto err_no_clock;
+ }
+
+ if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) {
+ device_printf(dev, "cannot get clock frequency\n");
+ goto err_no_freq;
+ }
+
+ /* Just out of curious. */
+ if (dwwdt_debug_enabled != 0)
+ dwwdt_debug(dev);
+
+ sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0);
+ return (bus_generic_attach(dev));
+
+err_no_freq:
+ clk_release(sc->sc_clk);
+err_no_clock:
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
+
+err_no_intr:
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
+err_no_irq:
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+err_no_mem:
+ return (ENXIO);
+}
+
+static int
+dwwdt_detach(device_t dev)
+{
+ struct dwwdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ if (dwwdt_started(sc))
+ return (EBUSY);
+
+ EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag);
+ sc->sc_evtag = NULL;
+
+ if (sc->sc_clk != NULL)
+ clk_release(sc->sc_clk);
+
+ if (sc->sc_intr_cookie != NULL)
+ bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
+
+ if (sc->sc_irq_res) {
+ bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
+ sc->sc_irq_res);
+ }
+
+ if (sc->sc_mem_res) {
+ bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
+ sc->sc_mem_res);
+ }
+
+ return (bus_generic_detach(dev));
+}
+
+static int dwwdt_shutdown(device_t dev)
+{
+ struct dwwdt_softc *sc;
+
+ sc = device_get_softc(dev);
+ /* Prevent restarts during shutdown. */
+ dwwdt_prevent_restart = 1;
+
+ /* Set also maximum timeout. Just for case. */
+ DWWDT_WRITE4(sc, DWWDT_TORR, 0x000F);
+ dwwdt_kick(sc);
+
+ return (bus_generic_shutdown(dev));
+}
+
+static device_method_t dwwdt_methods[] = {
+ DEVMETHOD(device_probe, dwwdt_probe),
+ DEVMETHOD(device_attach, dwwdt_attach),
+
+ DEVMETHOD(device_detach, dwwdt_detach),
+ DEVMETHOD(device_shutdown, dwwdt_shutdown),
+
+ {0, 0}
+};
+
+static driver_t dwwdt_driver = {
+ "dwwdt",
+ dwwdt_methods,
+ sizeof(struct dwwdt_softc),
+};
+
+DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, dwwdt_devclass, NULL, NULL);
+MODULE_VERSION(dwwdt, 1);
Index: sys/modules/Makefile
===================================================================
--- sys/modules/Makefile
+++ sys/modules/Makefile
@@ -106,6 +106,7 @@
dcons_crom \
${_dpms} \
dummynet \
+ dwwdt \
${_efirt} \
${_em} \
${_ena} \
Index: sys/modules/dwwdt/Makefile
===================================================================
--- sys/modules/dwwdt/Makefile
+++ sys/modules/dwwdt/Makefile
@@ -0,0 +1,7 @@
+.PATH: ${SRCTOP}/sys/dev/dwwdt
+
+KMOD = dwwdt
+SRCS = dwwdt.c \
+ device_if.h bus_if.h ofw_bus_if.h
+
+.include<bsd.kmod.mk>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Oct 13, 12:22 AM (10 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
23647323
Default Alt Text
D26761.id78173.diff (10 KB)
Attached To
Mode
D26761: Add driver for Synopsys Designware Watchdog timer.
Attached
Detach File
Event Timeline
Log In to Comment