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>
This commit is contained in:
		
							parent
							
								
									17c5fb1953
								
							
						
					
					
						commit
						0753bc2d30
					
				|  | @ -426,6 +426,10 @@ | |||
| 			}; | ||||
| 		}; | ||||
| 	}; | ||||
| 
 | ||||
| 	wdt0: wdt@0 { | ||||
| 		compatible = "sandbox,wdt"; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| #include "sandbox_pmic.dtsi" | ||||
|  |  | |||
|  | @ -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*/ | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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", | ||||
| }; | ||||
|  | @ -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, | ||||
|  |  | |||
|  | @ -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_ */ | ||||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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