Zephyr AXI WDT Driver Implementation
This page provides details on the implementation of the AXI WDT Zephyr driver.
Watchdog Subsystem
Zephyr uses watchdog subsystem along with supported driver to enable, set timeout, kick, disable the Axi Timebase WDT. The APIs which are defined in the subsystem are exposed to the application layer. Based on the compatible string, corresponding driver API is called.
The watchdog subsystem has a structure where drivers need to register their APIs by following the same protocol used in the structure mentioned.
Zephyr AXI watchdog driver stack.
Data structures
/** * @brief Watchdog timeout window. * * Each installed timeout needs feeding within the specified time window, * otherwise the watchdog will trigger. If the watchdog instance does not * support window timeouts then min value must be equal to 0. * * @note If specified values can not be precisely set they are always rounded * up. */ struct wdt_window { /** Lower limit of watchdog feed timeout in milliseconds. */ uint32_t min; /** Upper limit of watchdog feed timeout in milliseconds. */ uint32_t max; }; /** @brief Watchdog timeout configuration. */ struct wdt_timeout_cfg { /** Timing parameters of watchdog timeout. */ struct wdt_window window; /** Timeout callback (can be `NULL`). */ wdt_callback_t callback; #if defined(CONFIG_WDT_MULTISTAGE) || defined(__DOXYGEN__) /** * Pointer to the next timeout configuration. * * This field is only available if @kconfig{CONFIG_WDT_MULTISTAGE} is * enabled (watchdogs with staged timeouts functionality). Value must be * `NULL` for single stage timeout. */ struct wdt_timeout_cfg *next; #endif /** Flags (see @ref WDT_FLAGS). */ uint8_t flags; };
Typedefs
/** * @brief Watchdog callback. * * @param dev Watchdog device instance. * @param channel_id Channel identifier. */ typedef void (*wdt_callback_t)(const struct device *dev, int channel_id);
Subsystem wdt_driver_api
__subsystem struct wdt_driver_api { wdt_api_setup setup; wdt_api_disable disable; wdt_api_install_timeout install_timeout; wdt_api_feed feed; };
Watchdog options
#define WDT_OPT_PAUSE_IN_SLEEP BIT(0) /* Pause watchdog timer when CPU is in sleep state. */ #define WDT_OPT_PAUSE_HALTED_BY_DBG BIT(1) /* Pause watchdog timer when CPU is halted by the debugger. */
Watchdog Behavior Flags
#define WDT_FLAG_RESET_NONE (0 << WDT_FLAG_RESET_SHIFT) Reset: none. #define WDT_FLAG_RESET_CPU_CORE (1 << WDT_FLAG_RESET_SHIFT) Reset: CPU core. #define WDT_FLAG_RESET_SOC (2 << WDT_FLAG_RESET_SHIFT) Reset: SoC.
wdt_setup()
int wdt_setup(const struct device *dev, uint8_t options);
Setup the watchdog instance
This function is used for configuring global watchdog settings that affect all timeouts. Call it after installing timeouts. After successful return, all installed timeouts are valid and must be serviced periodically by calling wdt_feed().
Parameters
dev |
Watchdog device instance. |
options |
Configuration options (see Watchdog Options). |
Return Values for wdt_setup()
0 |
If successful. |
-ENOTSUP |
If any of the set options is not supported. |
-EBUSY |
If the watchdog instance is already set up. |
-errno |
In case of any other failure. |
wdt_install_timeout()
static int wdt_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
Install a new timeout.
Note: This function must be used before wdt_setup(). Changes applied here have no effects until wdt_setup() is called.
Parameters
dev |
Watchdog device instance |
cfg |
Timeout configuration |
Return Values for wdt_install_timeout()
channel_id |
If successful, a non-negative value indicating the index of the channel to which the timeout was assigned. This value is supposed to be used as the parameter in calls to wdt_feed(). |
-EBUSY |
If timeout can not be installed while watchdog has already been setup. |
-ENOMEM |
If no more timeouts can be installed. |
-ENOTSUP |
If any of the set flags is not supported. |
-EINVAL |
If any of the window timeout value is out of possible range. This value is also returned if watchdog supports only one timeout value for all timeouts and the supplied timeout window differs from windows for alarms installed so far. |
-errno |
In case of any other failure. |
wdt_feed()
int wdt_feed (const struct device *dev, int channel_id)
Feed specified watchdog timeout.
Parameters
dev |
Watchdog device instance. |
channel_id |
Channel index. |
Return Values for wdt_feed()
0 |
If successful. |
-EAGAIN |
If completing the feed operation stalls the caller, for example due to an in-progress watchdog operation such as a previous wdt_feed() call. |
-EINVAL |
If there is no installed timeout for supplied channel. |
-errno |
In case of any other failure. |
wdt_disable()
int wdt_disable (const struct device *dev)
Disable watchdog instance.
This function disables the watchdog instance and automatically uninstalls all timeouts. To set up a new watchdog, install timeouts and call wdt_setup() again.
Not all watchdogs can be restarted after they are disabled.
Parameters
dev |
Watchdog device instance. |
Return Values for wdt_disable()
0 |
If successful. |
-EFAULT |
If watchdog instance is not enabled. |
-EPERM |
If the watchdog cannot be disabled directly by application code. |
-errno |
In case of any other failure. |
axi-timebase-wdt Node
DT Node
axi_timebase_wdt_0: watchdog@41a00000 { compatible = "xlnx,axi-timebase-wdt-3.0" , "xlnx,xps-timebase-wdt-1.00.a"; reg = <0x41a00000 0x10000>; clocks = <&clk100>; xlnx,wdt-enable-once = <0x1>; status = "okay"; xlnx,wdt-interval = <0x1e>; };
zephyr/drivers/watchdog/CMakeLists.txt
zephyr_library_sources_ifdef(CONFIG_WDT_XILINX_AXI wdt_xilinx_axi.c)
zephyr/drivers/watchdog/Kconfig
source "drivers/watchdog/Kconfig.xlnx"
zephyr/drivers/watchdog/Kconfig.xlnx
config WDT_XILINX_AXI bool "Xilinx AXI Timebase WDT driver" default y depends on DT_HAS_XLNX_XPS_TIMEBASE_WDT_1_00_A_ENABLED help Enable the Xilinx AXI Timebase WDT driver. if WDT_XILINX_AXI config WDT_XILINX_AXI_HWINFO_API bool "Expose HWINFO API in Xilinx AXI Timebase WDT driver" default y select HWINFO help Controls whether the Xilinx AXI Timebase WDT driver exposes a HWINFO API which allows determining whether the WDT initiated the last system reset. This may need to be disabled if using a device or SoC which already implements this API. endif # WDT_XILINX_AXI
zephyr/dts/bindings/watchdog/xlnx,xps-timebase-wdt-1.00.a.yaml
# Copyright (c) 2023, Calian # SPDX-License-Identifier: Apache-2.0 description: Xilinx AXI timebase WDT core compatible: "xlnx,xps-timebase-wdt-1.00.a" include: base.yaml properties: reg: required: true clocks: description: Reference to AXI clock for watchdog core required: true xlnx,wdt-interval: description: Configured bit width of watchdog counter in core, controls watchdog expiry duration. required: true type: int xlnx,wdt-enable-once: description: Indicates whether WDT can only be enabled once and not disabled required: true type: int enum: - 0 - 1
Probe
#define WDT_XILINX_AXI_INIT(inst) \ static struct xilinx_wdt_axi_data wdt_xilinx_axi_##inst##_dev_data; \ \ static const struct xilinx_wdt_axi_config wdt_xilinx_axi_##inst##_cfg = { \ .base = DT_INST_REG_ADDR(inst), \ .clock_rate = DT_INST_PROP_BY_PHANDLE(inst, clocks, clock_frequency), \ .timer_width_max = DT_INST_PROP(inst, xlnx_wdt_interval), \ .enable_once = DT_INST_PROP(inst, xlnx_wdt_enable_once), \ }; \ \ DEVICE_DT_INST_DEFINE(inst, &wdt_xilinx_axi_init, NULL, &wdt_xilinx_axi_##inst##_dev_data, \ &wdt_xilinx_axi_##inst##_cfg, PRE_KERNEL_1, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_xilinx_api); DT_INST_FOREACH_STATUS_OKAY(WDT_XILINX_AXI_INIT)
Functional Implementation
wdt_xilinx_axi_install_timeout
WDT expiry happens after two wraps of the timer (first raises an interrupt which is not used, second triggers a reset) so add 1 to timer_width.
Check if timer width is good width or bad width.
If good timer width, write the width to MWR register.
If bad width, return -EINVAL.
wdt_xilinx_axi_setup
wdt_xilinx_axi_feed
Feed the wdt if wdt is already started and by writing to TWCSR0.
wdt_xilinx_axi_disable
Disable the wdt by writing the mask of CSR0_WDS to TWCSR0 and 0 to TWCSR1.