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" | #include "sandbox_pmic.dtsi" | ||||||
|  |  | ||||||
|  | @ -39,6 +39,12 @@ struct sandbox_spi_info { | ||||||
| 	struct udevice *emul; | 	struct udevice *emul; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | struct sandbox_wdt_info { | ||||||
|  | 	unsigned long long counter; | ||||||
|  | 	uint reset_count; | ||||||
|  | 	bool running; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| /* The complete state of the test system */ | /* The complete state of the test system */ | ||||||
| struct sandbox_state { | struct sandbox_state { | ||||||
| 	const char *cmd;		/* Command to execute */ | 	const char *cmd;		/* Command to execute */ | ||||||
|  | @ -69,6 +75,9 @@ struct sandbox_state { | ||||||
| 	/* Pointer to information for each SPI bus/cs */ | 	/* Pointer to information for each SPI bus/cs */ | ||||||
| 	struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS] | 	struct sandbox_spi_info spi[CONFIG_SANDBOX_SPI_MAX_BUS] | ||||||
| 					[CONFIG_SANDBOX_SPI_MAX_CS]; | 					[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*/ | /* 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_TIME=y | ||||||
| CONFIG_UT_DM=y | CONFIG_UT_DM=y | ||||||
| CONFIG_UT_ENV=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 | config ULP_WATCHDOG | ||||||
| 	bool "i.MX7ULP watchdog" | 	bool "i.MX7ULP watchdog" | ||||||
| 	help | 	help | ||||||
| 	  Say Y here to enable i.MX7ULP watchdog driver. | 	  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 | endmenu | ||||||
|  |  | ||||||
|  | @ -15,3 +15,5 @@ obj-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o | ||||||
| obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o | ||||||
| obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o | obj-$(CONFIG_DESIGNWARE_WATCHDOG) += designware_wdt.o | ||||||
| obj-$(CONFIG_ULP_WATCHDOG) += ulp_wdog.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,		/* Video or LCD device */ | ||||||
| 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */ | 	UCLASS_VIDEO_BRIDGE,	/* Video bridge, e.g. DisplayPort to LVDS */ | ||||||
| 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */ | 	UCLASS_VIDEO_CONSOLE,	/* Text console driver for video device */ | ||||||
|  | 	UCLASS_WDT,		/* Watchdot Timer driver */ | ||||||
| 
 | 
 | ||||||
| 	UCLASS_COUNT, | 	UCLASS_COUNT, | ||||||
| 	UCLASS_INVALID = -1, | 	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_DM_VIDEO) += video.o | ||||||
| obj-$(CONFIG_ADC) += adc.o | obj-$(CONFIG_ADC) += adc.o | ||||||
| obj-$(CONFIG_SPMI) += spmi.o | obj-$(CONFIG_SPMI) += spmi.o | ||||||
|  | obj-$(CONFIG_WDT) += wdt.o | ||||||
| endif | 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