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>master
parent
17c5fb1953
commit
0753bc2d30
@ -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 |
||||
|
@ -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, |
||||
}; |
@ -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", |
||||
}; |
@ -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_ */ |
@ -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); |
Loading…
Reference in new issue