diff options
-rw-r--r-- | arch/sandbox/dts/test.dts | 11 | ||||
-rw-r--r-- | doc/device-tree-bindings/watchdog/gpio-wdt.txt | 8 | ||||
-rw-r--r-- | drivers/watchdog/gpio_wdt.c | 40 | ||||
-rw-r--r-- | test/dm/wdt.c | 46 |
4 files changed, 90 insertions, 15 deletions
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 0194b9b30ef..d1a8cc7bfb7 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -850,10 +850,19 @@ }; }; - gpio-wdt { + wdt-gpio-toggle { gpios = <&gpio_a 7 0>; compatible = "linux,wdt-gpio"; hw_margin_ms = <100>; + hw_algo = "toggle"; + always-running; + }; + + wdt-gpio-level { + gpios = <&gpio_a 7 0>; + compatible = "linux,wdt-gpio"; + hw_margin_ms = <100>; + hw_algo = "level"; always-running; }; diff --git a/doc/device-tree-bindings/watchdog/gpio-wdt.txt b/doc/device-tree-bindings/watchdog/gpio-wdt.txt index c9a8559a3ef..746c2c081ea 100644 --- a/doc/device-tree-bindings/watchdog/gpio-wdt.txt +++ b/doc/device-tree-bindings/watchdog/gpio-wdt.txt @@ -5,7 +5,12 @@ Describes a simple watchdog timer which is reset by toggling a gpio. Required properties: - compatible: Must be "linux,wdt-gpio". -- gpios: gpio to toggle when wdt driver reset method is called. +- gpios: From common gpio binding; gpio connection to WDT reset pin. +- hw_algo: The algorithm used by the driver. Should be one of the + following values: + - toggle: Toggle from high-to-low or low-to-high when resetting the watchdog. + - level: Maintain a constant high/low level, and trigger a short pulse when + resetting the watchdog. Active level is determined by the GPIO flags. - always-running: Boolean property indicating that the watchdog cannot be disabled. At present, U-Boot only supports this kind of GPIO watchdog. @@ -15,5 +20,6 @@ Example: gpio-wdt { gpios = <&gpio0 1 0>; compatible = "linux,wdt-gpio"; + hw_algo = "toggle"; always-running; }; diff --git a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c index 982a66b3f9d..fe06ec8cc90 100644 --- a/drivers/watchdog/gpio_wdt.c +++ b/drivers/watchdog/gpio_wdt.c @@ -4,20 +4,38 @@ #include <dm/device_compat.h> #include <wdt.h> #include <asm/gpio.h> +#include <linux/delay.h> + +enum { + HW_ALGO_TOGGLE, + HW_ALGO_LEVEL, +}; struct gpio_wdt_priv { - struct gpio_desc gpio; - bool always_running; - int state; + struct gpio_desc gpio; + unsigned int hw_algo; + bool always_running; + int state; }; static int gpio_wdt_reset(struct udevice *dev) { struct gpio_wdt_priv *priv = dev_get_priv(dev); - priv->state = !priv->state; - - return dm_gpio_set_value(&priv->gpio, priv->state); + switch (priv->hw_algo) { + case HW_ALGO_TOGGLE: + /* Toggle output pin */ + priv->state = !priv->state; + dm_gpio_set_value(&priv->gpio, priv->state); + break; + case HW_ALGO_LEVEL: + /* Pulse */ + dm_gpio_set_value(&priv->gpio, 1); + udelay(1); + dm_gpio_set_value(&priv->gpio, 0); + break; + } + return 0; } static int gpio_wdt_start(struct udevice *dev, u64 timeout, ulong flags) @@ -34,6 +52,16 @@ static int dm_probe(struct udevice *dev) { struct gpio_wdt_priv *priv = dev_get_priv(dev); int ret; + const char *algo = dev_read_string(dev, "hw_algo"); + + if (!algo) + return -EINVAL; + if (!strcmp(algo, "toggle")) + priv->hw_algo = HW_ALGO_TOGGLE; + else if (!strcmp(algo, "level")) + priv->hw_algo = HW_ALGO_LEVEL; + else + return -EINVAL; priv->always_running = dev_read_bool(dev, "always-running"); ret = gpio_request_by_name(dev, "gpios", 0, &priv->gpio, GPIOD_IS_OUT); diff --git a/test/dm/wdt.c b/test/dm/wdt.c index ee615f0e148..535f00a8740 100644 --- a/test/dm/wdt.c +++ b/test/dm/wdt.c @@ -44,20 +44,20 @@ static int dm_test_wdt_base(struct unit_test_state *uts) } DM_TEST(dm_test_wdt_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); -static int dm_test_wdt_gpio(struct unit_test_state *uts) +static int dm_test_wdt_gpio_toggle(struct unit_test_state *uts) { /* * The sandbox wdt gpio is "connected" to gpio bank a, offset * 7. Use the sandbox back door to verify that the gpio-wdt - * driver behaves as expected. + * driver behaves as expected when using the 'toggle' algorithm. */ struct udevice *wdt, *gpio; const u64 timeout = 42; const int offset = 7; int val; - ut_assertok(uclass_get_device_by_driver(UCLASS_WDT, - DM_DRIVER_GET(wdt_gpio), &wdt)); + ut_assertok(uclass_get_device_by_name(UCLASS_WDT, + "wdt-gpio-toggle", &wdt)); ut_assertnonnull(wdt); ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio)); @@ -74,7 +74,39 @@ static int dm_test_wdt_gpio(struct unit_test_state *uts) return 0; } -DM_TEST(dm_test_wdt_gpio, UT_TESTF_SCAN_FDT); +DM_TEST(dm_test_wdt_gpio_toggle, UT_TESTF_SCAN_FDT); + +static int dm_test_wdt_gpio_level(struct unit_test_state *uts) +{ + /* + * The sandbox wdt gpio is "connected" to gpio bank a, offset + * 7. Use the sandbox back door to verify that the gpio-wdt + * driver behaves as expected when using the 'level' algorithm. + */ + struct udevice *wdt, *gpio; + const u64 timeout = 42; + const int offset = 7; + int val; + + ut_assertok(uclass_get_device_by_name(UCLASS_WDT, + "wdt-gpio-level", &wdt)); + ut_assertnonnull(wdt); + + ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio)); + ut_assertnonnull(gpio); + ut_assertok(wdt_start(wdt, timeout, 0)); + + val = sandbox_gpio_get_value(gpio, offset); + ut_assertok(wdt_reset(wdt)); + ut_asserteq(val, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(wdt_reset(wdt)); + ut_asserteq(val, sandbox_gpio_get_value(gpio, offset)); + + ut_asserteq(-ENOSYS, wdt_stop(wdt)); + + return 0; +} +DM_TEST(dm_test_wdt_gpio_level, UT_TESTF_SCAN_FDT); static int dm_test_wdt_watchdog_reset(struct unit_test_state *uts) { @@ -86,8 +118,8 @@ static int dm_test_wdt_watchdog_reset(struct unit_test_state *uts) uint reset_count; int val; - ut_assertok(uclass_get_device_by_driver(UCLASS_WDT, - DM_DRIVER_GET(wdt_gpio), &gpio_wdt)); + ut_assertok(uclass_get_device_by_name(UCLASS_WDT, + "wdt-gpio-toggle", &gpio_wdt)); ut_assertnonnull(gpio_wdt); ut_assertok(uclass_get_device_by_driver(UCLASS_WDT, DM_DRIVER_GET(wdt_sandbox), &sandbox_wdt)); |