1fc45d6483
A common external watchdog circuit is kept alive by triggering a short pulse on the reset pin. This patch adds support for this use case, while making the algorithm configurable in the devicetree. The "linux,wdt-gpio" driver being modified is based off the equivalent driver in the Linux kernel, which provides support for this algorithm. This patch brings parity to this driver, and is kept aligned with the functionality and devicetree configuration in the kernel. It should be noted that this adds a required property named 'hw_algo' to the devicetree binding, following suit with the kernel. I'm happy to make this backward-compatible if preferred. Signed-off-by: Paul Doelle <paaull.git@gmail.com> Reviewed-by: Stefan Roese <sr@denx.de>
162 lines
4.7 KiB
C
162 lines
4.7 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2017 Google, Inc
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <wdt.h>
|
|
#include <asm/gpio.h>
|
|
#include <asm/state.h>
|
|
#include <asm/test.h>
|
|
#include <dm/test.h>
|
|
#include <test/test.h>
|
|
#include <test/ut.h>
|
|
#include <linux/delay.h>
|
|
#include <watchdog.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_by_driver(UCLASS_WDT,
|
|
DM_DRIVER_GET(wdt_sandbox), &dev));
|
|
ut_assertnonnull(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, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
|
|
|
|
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 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_name(UCLASS_WDT,
|
|
"wdt-gpio-toggle", &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_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)
|
|
{
|
|
struct sandbox_state *state = state_get_current();
|
|
struct udevice *gpio_wdt, *sandbox_wdt;
|
|
struct udevice *gpio;
|
|
const u64 timeout = 42;
|
|
const int offset = 7;
|
|
uint reset_count;
|
|
int val;
|
|
|
|
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));
|
|
ut_assertnonnull(sandbox_wdt);
|
|
ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio));
|
|
ut_assertnonnull(gpio);
|
|
|
|
/* Neither device should be "started", so watchdog_reset() should be a no-op. */
|
|
reset_count = state->wdt.reset_count;
|
|
val = sandbox_gpio_get_value(gpio, offset);
|
|
watchdog_reset();
|
|
ut_asserteq(reset_count, state->wdt.reset_count);
|
|
ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
|
|
|
|
/* Start both devices. */
|
|
ut_assertok(wdt_start(gpio_wdt, timeout, 0));
|
|
ut_assertok(wdt_start(sandbox_wdt, timeout, 0));
|
|
|
|
/* Make sure both devices have just been pinged. */
|
|
timer_test_add_offset(100);
|
|
watchdog_reset();
|
|
reset_count = state->wdt.reset_count;
|
|
val = sandbox_gpio_get_value(gpio, offset);
|
|
|
|
/* The gpio watchdog should be pinged, the sandbox one not. */
|
|
timer_test_add_offset(30);
|
|
watchdog_reset();
|
|
ut_asserteq(reset_count, state->wdt.reset_count);
|
|
ut_asserteq(!val, sandbox_gpio_get_value(gpio, offset));
|
|
|
|
/* After another ~30ms, both devices should get pinged. */
|
|
timer_test_add_offset(30);
|
|
watchdog_reset();
|
|
ut_asserteq(reset_count + 1, state->wdt.reset_count);
|
|
ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
|
|
|
|
return 0;
|
|
}
|
|
DM_TEST(dm_test_wdt_watchdog_reset, UT_TESTF_SCAN_FDT);
|