Browse Source

dm: Simple Watchdog uclass

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>
tags/2020-06-01
maxims@google.com 4 years ago
committed by Tom Rini
parent
commit
0753bc2d30
11 changed files with 333 additions and 1 deletions
  1. +4
    -0
      arch/sandbox/dts/test.dts
  2. +9
    -0
      arch/sandbox/include/asm/state.h
  3. +2
    -0
      configs/sandbox_defconfig
  4. +19
    -1
      drivers/watchdog/Kconfig
  5. +2
    -0
      drivers/watchdog/Makefile
  6. +76
    -0
      drivers/watchdog/sandbox_wdt.c
  7. +72
    -0
      drivers/watchdog/wdt-uclass.c
  8. +1
    -0
      include/dm/uclass-id.h
  9. +107
    -0
      include/wdt.h
  10. +1
    -0
      test/dm/Makefile
  11. +40
    -0
      test/dm/wdt.c

+ 4
- 0
arch/sandbox/dts/test.dts View File

@@ -426,6 +426,10 @@
};
};
};

wdt0: wdt@0 {
compatible = "sandbox,wdt";
};
};

#include "sandbox_pmic.dtsi"

+ 9
- 0
arch/sandbox/include/asm/state.h View 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*/


+ 2
- 0
configs/sandbox_defconfig View 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

+ 19
- 1
drivers/watchdog/Kconfig View 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

+ 2
- 0
drivers/watchdog/Makefile View 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

+ 76
- 0
drivers/watchdog/sandbox_wdt.c View File

@@ -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,
};

+ 72
- 0
drivers/watchdog/wdt-uclass.c View File

@@ -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",
};

+ 1
- 0
include/dm/uclass-id.h View 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,


+ 107
- 0
include/wdt.h View File

@@ -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_ */

+ 1
- 0
test/dm/Makefile View 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

+ 40
- 0
test/dm/wdt.c View File

@@ -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);