]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
dm: Simple Watchdog uclass
authormaxims@google.com <maxims@google.com>
Mon, 17 Apr 2017 19:00:21 +0000 (12:00 -0700)
committerTom Rini <trini@konsulko.com>
Mon, 8 May 2017 15:57:30 +0000 (11:57 -0400)
This is a simple uclass for Watchdog Timers. It has four operations:
start, restart, reset, stop. Drivers must implement start, restart and
stop operations, while implementing reset is optional: It's default
implementation expires watchdog timer in one clock tick.

Signed-off-by: Maxim Sloyko <maxims@google.com>
Reviewed-by: Simon Glass <sjg@chromium.org>
arch/sandbox/dts/test.dts
arch/sandbox/include/asm/state.h
configs/sandbox_defconfig
drivers/watchdog/Kconfig
drivers/watchdog/Makefile
drivers/watchdog/sandbox_wdt.c [new file with mode: 0644]
drivers/watchdog/wdt-uclass.c [new file with mode: 0644]
include/dm/uclass-id.h
include/wdt.h [new file with mode: 0644]
test/dm/Makefile
test/dm/wdt.c [new file with mode: 0644]

index 50bcdebf74d01a641bbd91077741ada6fdef609b..094c5aaf61a12e2930bcaacb87237dbac9368e4b 100644 (file)
                        };
                };
        };
+
+       wdt0: wdt@0 {
+               compatible = "sandbox,wdt";
+       };
 };
 
 #include "sandbox_pmic.dtsi"
index 149f28d8732f980a3676a3bcefa71511458d4c40..987cc7b49dcd70dad7e341f3e87dc1f2f0a2d2fb 100644 (file)
@@ -39,6 +39,12 @@ struct sandbox_spi_info {
        struct udevice *emul;
 };
 
+struct sandbox_wdt_info {
+       unsigned long long counter;
+       uint reset_count;
+       bool running;
+};
+
 /* The complete state of the test system */
 struct sandbox_state {
        const char *cmd;                /* Command to execute */
@@ -69,6 +75,9 @@ struct sandbox_state {
        /* Pointer to information for each SPI bus/cs */
        struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS]
                                        [CONFIG_SANDBOX_SPI_MAX_CS];
+
+       /* Information about Watchdog */
+       struct sandbox_wdt_info wdt;
 };
 
 /* Minimum space we guarantee in the state FDT when calling read/write*/
index d3fee89f48ca6ca47164111ba954c81d2ad47dc4..64bb923c13af19dc2876b6c830b11d29b1134553 100644 (file)
@@ -180,3 +180,5 @@ CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
 CONFIG_UT_ENV=y
+CONFIG_WDT=y
+CONFIG_WDT_SANDBOX=y
index dbdaafc149835c5a0b2cd6d4ce0c6f607a144fd2..e8d2dba835ca997dd92d9f607f8e94f182e70406 100644 (file)
@@ -1,8 +1,26 @@
-menu "WATCHDOG support"
+menu "Watchdog Timer Support"
 
 config ULP_WATCHDOG
        bool "i.MX7ULP watchdog"
        help
          Say Y here to enable i.MX7ULP watchdog driver.
 
+config WDT
+       bool "Enable driver model for watchdog timer drivers"
+       depends on DM
+       help
+         Enable driver model for watchdog timer. At the moment the API
+         is very simple and only supports four operations:
+         start, restart, stop and reset (expire immediately).
+         What exactly happens when the timer expires is up to a particular
+         device/driver.
+
+config WDT_SANDBOX
+       bool "Enable Watchdog Timer support for Sandbox"
+       depends on SANDBOX && WDT
+       help
+               Enable Watchdog Timer support in Sandbox. This is a dummy device that
+               can be probed and supports all of the methods of WDT, but does not
+               really do anything.
+
 endmenu
index 36745ca9c9974b47ba62e84b431cd9a79a339f7e..e891d6484968cf72fbad9f61dc2acda8d2e7b12b 100644 (file)
@@ -15,3 +15,5 @@ obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o
 obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
 obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o
 obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.o
+obj-$(CONFIG_WDT) += wdt-uclass.o
+obj-$(CONFIG_WDT_SANDBOX) += sandbox_wdt.o
diff --git a/drivers/watchdog/sandbox_wdt.c b/drivers/watchdog/sandbox_wdt.c
new file mode 100644 (file)
index 0000000..34d90be
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2017 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/state.h>
+#include <wdt.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int sandbox_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+       struct sandbox_state *state = state_get_current();
+
+       state->wdt.counter = timeout;
+       state->wdt.running = true;
+
+       return 0;
+}
+
+static int sandbox_wdt_stop(struct udevice *dev)
+{
+       struct sandbox_state *state = state_get_current();
+
+       state->wdt.running = false;
+
+       return 0;
+}
+
+static int sandbox_wdt_reset(struct udevice *dev)
+{
+       struct sandbox_state *state = state_get_current();
+
+       state->wdt.reset_count++;
+
+       return 0;
+}
+
+static int sandbox_wdt_expire_now(struct udevice *dev, ulong flags)
+{
+       sandbox_wdt_start(dev, 1, flags);
+
+       return 0;
+}
+
+static int sandbox_wdt_probe(struct udevice *dev)
+{
+       struct sandbox_state *state = state_get_current();
+
+       memset(&state->wdt, 0, sizeof(state->wdt));
+
+       return 0;
+}
+
+static const struct wdt_ops sandbox_wdt_ops = {
+       .start = sandbox_wdt_start,
+       .reset = sandbox_wdt_reset,
+       .stop = sandbox_wdt_stop,
+       .expire_now = sandbox_wdt_expire_now,
+};
+
+static const struct udevice_id sandbox_wdt_ids[] = {
+       { .compatible = "sandbox,wdt" },
+       {}
+};
+
+U_BOOT_DRIVER(wdt_sandbox) = {
+       .name = "wdt_sandbox",
+       .id = UCLASS_WDT,
+       .of_match = sandbox_wdt_ids,
+       .ops = &sandbox_wdt_ops,
+       .probe = sandbox_wdt_probe,
+};
diff --git a/drivers/watchdog/wdt-uclass.c b/drivers/watchdog/wdt-uclass.c
new file mode 100644 (file)
index 0000000..ab8a64c
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2017 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <wdt.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+       const struct wdt_ops *ops = device_get_ops(dev);
+
+       if (!ops->start)
+               return -ENOSYS;
+
+       return ops->start(dev, timeout, flags);
+}
+
+int wdt_stop(struct udevice *dev)
+{
+       const struct wdt_ops *ops = device_get_ops(dev);
+
+       if (!ops->stop)
+               return -ENOSYS;
+
+       return ops->stop(dev);
+}
+
+int wdt_reset(struct udevice *dev)
+{
+       const struct wdt_ops *ops = device_get_ops(dev);
+
+       if (!ops->reset)
+               return -ENOSYS;
+
+       return ops->reset(dev);
+}
+
+int wdt_expire_now(struct udevice *dev, ulong flags)
+{
+       int ret = 0;
+       const struct wdt_ops *ops;
+
+       debug("WDT Resettting: %lu\n", flags);
+       ops = device_get_ops(dev);
+       if (ops->expire_now) {
+               return ops->expire_now(dev, flags);
+       } else {
+               if (!ops->start)
+                       return -ENOSYS;
+
+               ret = ops->start(dev, 1, flags);
+               if (ret < 0)
+                       return ret;
+
+               hang();
+       }
+
+       return ret;
+}
+
+UCLASS_DRIVER(wdt) = {
+       .id             = UCLASS_WDT,
+       .name           = "wdt",
+};
index 1b635e41103779ecd2955c34ab7c8f37f8ee7ead..4e7cc935bd2d2784ab319b47b404f275e129c601 100644 (file)
@@ -84,6 +84,7 @@ enum uclass_id {
        UCLASS_VIDEO,           /* Video or LCD device */
        UCLASS_VIDEO_BRIDGE,    /* Video bridge, e.g. DisplayPort to LVDS */
        UCLASS_VIDEO_CONSOLE,   /* Text console driver for video device */
+       UCLASS_WDT,             /* Watchdot Timer driver */
 
        UCLASS_COUNT,
        UCLASS_INVALID = -1,
diff --git a/include/wdt.h b/include/wdt.h
new file mode 100644 (file)
index 0000000..0b5f058
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2017 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#ifndef _WDT_H_
+#define _WDT_H_
+
+/*
+ * Implement a simple watchdog uclass. Watchdog is basically a timer that
+ * is used to detect or recover from malfunction. During normal operation
+ * the watchdog would be regularly reset to prevent it from timing out.
+ * If, due to a hardware fault or program error, the computer fails to reset
+ * the watchdog, the timer will elapse and generate a timeout signal.
+ * The timeout signal is used to initiate corrective action or actions,
+ * which typically include placing the system in a safe, known state.
+ */
+
+/*
+ * Start the timer
+ *
+ * @dev: WDT Device
+ * @timeout: Number of ticks before timer expires
+ * @flags: Driver specific flags. This might be used to specify
+ * which action needs to be executed when the timer expires
+ * @return: 0 if OK, -ve on error
+ */
+int wdt_start(struct udevice *dev, u64 timeout, ulong flags);
+
+/*
+ * Stop the timer, thus disabling the Watchdog. Use wdt_start to start it again.
+ *
+ * @dev: WDT Device
+ * @return: 0 if OK, -ve on error
+ */
+int wdt_stop(struct udevice *dev);
+
+/*
+ * Reset the timer, typically restoring the counter to
+ * the value configured by start()
+ *
+ * @dev: WDT Device
+ * @return: 0 if OK, -ve on error
+ */
+int wdt_reset(struct udevice *dev);
+
+/*
+ * Expire the timer, thus executing its action immediately.
+ * This is typically used to reset the board or peripherals.
+ *
+ * @dev: WDT Device
+ * @flags: Driver specific flags
+ * @return 0 if OK -ve on error. If wdt action is system reset,
+ * this function may never return.
+ */
+int wdt_expire_now(struct udevice *dev, ulong flags);
+
+/*
+ * struct wdt_ops - Driver model wdt operations
+ *
+ * The uclass interface is implemented by all wdt devices which use
+ * driver model.
+ */
+struct wdt_ops {
+       /*
+        * Start the timer
+        *
+        * @dev: WDT Device
+        * @timeout: Number of ticks before the timer expires
+        * @flags: Driver specific flags. This might be used to specify
+        * which action needs to be executed when the timer expires
+        * @return: 0 if OK, -ve on error
+        */
+       int (*start)(struct udevice *dev, u64 timeout, ulong flags);
+       /*
+        * Stop the timer
+        *
+        * @dev: WDT Device
+        * @return: 0 if OK, -ve on error
+        */
+       int (*stop)(struct udevice *dev);
+       /*
+        * Reset the timer, typically restoring the counter to
+        * the value configured by start()
+        *
+        * @dev: WDT Device
+        * @return: 0 if OK, -ve on error
+        */
+       int (*reset)(struct udevice *dev);
+       /*
+        * Expire the timer, thus executing the action immediately (optional)
+        *
+        * If this function is not provided, a default implementation
+        * will be used, which sets the counter to 1
+        * and waits forever. This is good enough for system level
+        * reset, where the function is not expected to return, but might not be
+        * good enough for other use cases.
+        *
+        * @dev: WDT Device
+        * @flags: Driver specific flags
+        * @return 0 if OK -ve on error. May not return.
+        */
+       int (*expire_now)(struct udevice *dev, ulong flags);
+};
+
+#endif  /* _WDT_H_ */
index e956915bc3d287efb15fecd30ab025ac4e1fd6ee..b15f1d0535aaebdeddfe3cafa872561363911982 100644 (file)
@@ -42,4 +42,5 @@ obj-$(CONFIG_TIMER) += timer.o
 obj-$(CONFIG_DM_VIDEO) += video.o
 obj-$(CONFIG_ADC) += adc.o
 obj-$(CONFIG_SPMI) += spmi.o
+obj-$(CONFIG_WDT) += wdt.o
 endif
diff --git a/test/dm/wdt.c b/test/dm/wdt.c
new file mode 100644 (file)
index 0000000..2ecfcea
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017 Google, Inc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <wdt.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Test that watchdog driver functions are called */
+static int dm_test_wdt_base(struct unit_test_state *uts)
+{
+       struct sandbox_state *state = state_get_current();
+       struct udevice *dev;
+       const u64 timeout = 42;
+
+       ut_assertok(uclass_get_device(UCLASS_WDT, 0, &dev));
+       ut_asserteq(0, state->wdt.counter);
+       ut_asserteq(false, state->wdt.running);
+
+       ut_assertok(wdt_start(dev, timeout, 0));
+       ut_asserteq(timeout, state->wdt.counter);
+       ut_asserteq(true, state->wdt.running);
+
+       uint reset_count = state->wdt.reset_count;
+       ut_assertok(wdt_reset(dev));
+       ut_asserteq(reset_count + 1, state->wdt.reset_count);
+       ut_asserteq(true, state->wdt.running);
+
+       ut_assertok(wdt_stop(dev));
+       ut_asserteq(false, state->wdt.running);
+
+       return 0;
+}
+DM_TEST(dm_test_wdt_base, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);