-
Notifications
You must be signed in to change notification settings - Fork 7.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for Xilinx Window Watchdog driver #86430
Closed
Closed
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright (c) 2025 Advanced Micro Devices, Inc. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config XILINX_WINDOW_WATCHDOG | ||
bool "Xilinx window watchdog driver" | ||
default y | ||
depends on DT_HAS_XLNX_VERSAL_WWDT_ENABLED | ||
help | ||
Enable Window watchdog driver for the versal_wwdt IP core. | ||
Window watchdog timer(WWDT) contains closed(first) and | ||
open(second) window with 32 bit width. Write to the watchdog | ||
timer within predefined window periods of time. This means | ||
a period that is not too soon and a period that is not too | ||
late. The WWDT has to be restarted within the open window time. | ||
If software tries to restart WWDT outside of the open window | ||
time period, it generates a SOC reset. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
/* | ||
* Copyright (c) 2025 Advanced Micro Devices, Inc. | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define DT_DRV_COMPAT xlnx_versal_wwdt | ||
|
||
#include <errno.h> | ||
#include <zephyr/device.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/drivers/hwinfo.h> | ||
#include <zephyr/drivers/watchdog.h> | ||
#include <zephyr/logging/log.h> | ||
#include <zephyr/sys/util.h> | ||
|
||
LOG_MODULE_REGISTER(xilinx_wwdt, CONFIG_WDT_LOG_LEVEL); | ||
|
||
/* Register offsets for the WWDT device */ | ||
#define XWWDT_MWR_OFFSET 0x00 | ||
#define XWWDT_ESR_OFFSET 0x04 | ||
#define XWWDT_FCR_OFFSET 0x08 | ||
#define XWWDT_FWR_OFFSET 0x0c | ||
#define XWWDT_SWR_OFFSET 0x10 | ||
|
||
/* Master Write Control Register Masks */ | ||
#define XWWDT_MWR_MASK BIT(0) | ||
|
||
/* Enable and Status Register Masks */ | ||
#define XWWDT_ESR_WINT_MASK BIT(16) | ||
#define XWWDT_ESR_WSW_MASK BIT(8) | ||
#define XWWDT_ESR_WEN_MASK BIT(0) | ||
|
||
/* Watchdog Second Window Shift */ | ||
#define XWWDT_ESR_WSW_SHIFT 8U | ||
|
||
/* Maximum count value of each 32 bit window */ | ||
#define XWWDT_MAX_COUNT_WINDOW GENMASK(31, 0) | ||
|
||
/* Maximum count value of closed and open window combined */ | ||
#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK64(32, 1) | ||
|
||
struct xilinx_wwdt_config { | ||
uint32_t wdt_clock_freq; | ||
uint32_t timeout_sec; | ||
mem_addr_t base; | ||
}; | ||
|
||
struct xilinx_wwdt_data { | ||
struct k_spinlock lock; | ||
bool timeout_active; | ||
bool wdt_started; | ||
}; | ||
|
||
static int wdt_xilinx_wwdt_setup(const struct device *dev, uint8_t options) | ||
{ | ||
const struct xilinx_wwdt_config *config = dev->config; | ||
struct xilinx_wwdt_data *data = dev->data; | ||
uint32_t reg_value; | ||
uint32_t ret = 0; | ||
|
||
k_spinlock_key_t key = k_spin_lock(&data->lock); | ||
|
||
if (!data->timeout_active) { | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
if (data->wdt_started) { | ||
ret = -EBUSY; | ||
goto out; | ||
} | ||
|
||
/* | ||
* There is no control at driver level whether the WDT pauses in CPU sleep | ||
* or when halted by debugger. Hence there is no check for the options. | ||
*/ | ||
|
||
/* Read enable status register and update WEN bit */ | ||
reg_value = sys_read32(config->base + XWWDT_ESR_OFFSET) | XWWDT_ESR_WEN_MASK; | ||
|
||
/* Write enable status register with updated WEN value */ | ||
sys_write32(reg_value, config->base + XWWDT_ESR_OFFSET); | ||
data->wdt_started = true; | ||
out: | ||
k_spin_unlock(&data->lock, key); | ||
return ret; | ||
} | ||
|
||
static int wdt_xilinx_wwdt_install_timeout(const struct device *dev, | ||
const struct wdt_timeout_cfg *cfg) | ||
{ | ||
const struct xilinx_wwdt_config *config = dev->config; | ||
struct xilinx_wwdt_data *data = dev->data; | ||
uint64_t closed_window_ms_count; | ||
uint64_t open_window_ms_count; | ||
uint64_t max_hw_timeout_ms; | ||
uint64_t timeout_ms_count; | ||
uint32_t timeout_ms; | ||
uint64_t ms_count; | ||
uint32_t ret = 0; | ||
|
||
k_spinlock_key_t key = k_spin_lock(&data->lock); | ||
|
||
if (data->wdt_started) { | ||
ret = -EBUSY; | ||
goto out; | ||
} | ||
|
||
if (cfg->flags != WDT_FLAG_RESET_SOC) { | ||
ret = -ENOTSUP; | ||
goto out; | ||
} | ||
|
||
timeout_ms = config->timeout_sec * 1000; | ||
max_hw_timeout_ms = (XWWDT_MAX_COUNT_WINDOW_COMBINED * 1000) / config->wdt_clock_freq; | ||
|
||
/* | ||
* If the maximum limit of the window is passed from the user space, | ||
* overwrite the timeout with this value. | ||
*/ | ||
if (cfg->window.max > 0) { | ||
timeout_ms = cfg->window.max; | ||
} | ||
|
||
/* Timeout greater than the maximum hardware timeout is invalid. */ | ||
if (timeout_ms > max_hw_timeout_ms) { | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
/* Calculate ticks for 1 milli-second */ | ||
ms_count = (config->wdt_clock_freq) / 1000; | ||
timeout_ms_count = timeout_ms * ms_count; | ||
|
||
closed_window_ms_count = cfg->window.min * ms_count; | ||
if (closed_window_ms_count > XWWDT_MAX_COUNT_WINDOW) { | ||
LOG_ERR("The closed window timeout is invalid."); | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
open_window_ms_count = timeout_ms_count - closed_window_ms_count; | ||
if (open_window_ms_count > XWWDT_MAX_COUNT_WINDOW) { | ||
LOG_ERR("The open window timeout is invalid."); | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
sys_write32(XWWDT_MWR_MASK, config->base + XWWDT_MWR_OFFSET); | ||
sys_write32(~(uint32_t)XWWDT_ESR_WEN_MASK, config->base + XWWDT_ESR_OFFSET); | ||
sys_write32(closed_window_ms_count, config->base + XWWDT_FWR_OFFSET); | ||
sys_write32(open_window_ms_count, config->base + XWWDT_SWR_OFFSET); | ||
|
||
data->timeout_active = true; | ||
out: | ||
k_spin_unlock(&data->lock, key); | ||
return ret; | ||
} | ||
|
||
static int wdt_xilinx_wwdt_feed(const struct device *dev, int channel_id) | ||
{ | ||
const struct xilinx_wwdt_config *config = dev->config; | ||
struct xilinx_wwdt_data *data = dev->data; | ||
uint32_t control_status_reg; | ||
uint32_t is_sec_window; | ||
uint32_t ret = 0; | ||
|
||
k_spinlock_key_t key = k_spin_lock(&data->lock); | ||
|
||
if (channel_id != 0 || !data->timeout_active) { | ||
ret = -EINVAL; | ||
goto out; | ||
} | ||
|
||
/* Enable write access control bit for the WWDT. */ | ||
sys_write32(XWWDT_MWR_MASK, config->base + XWWDT_MWR_OFFSET); | ||
|
||
/* Trigger restart kick to WWDT. */ | ||
control_status_reg = sys_read32(config->base + XWWDT_ESR_OFFSET); | ||
|
||
/* Check if WWDT is in Second window. */ | ||
is_sec_window = (control_status_reg & (uint32_t)XWWDT_ESR_WSW_MASK) >> XWWDT_ESR_WSW_SHIFT; | ||
|
||
if (is_sec_window != 1) { | ||
LOG_ERR("Feed in Closed window is not supported."); | ||
ret = -ENOTSUP; | ||
goto out; | ||
} | ||
|
||
control_status_reg |= (uint32_t)XWWDT_ESR_WSW_MASK; | ||
sys_write32(control_status_reg, config->base + XWWDT_ESR_OFFSET); | ||
out: | ||
k_spin_unlock(&data->lock, key); | ||
return ret; | ||
} | ||
|
||
static int wdt_xilinx_wwdt_disable(const struct device *dev) | ||
{ | ||
const struct xilinx_wwdt_config *config = dev->config; | ||
struct xilinx_wwdt_data *data = dev->data; | ||
uint32_t is_wwdt_enable; | ||
uint32_t is_sec_window; | ||
uint32_t reg_value; | ||
uint32_t ret = 0; | ||
|
||
k_spinlock_key_t key = k_spin_lock(&data->lock); | ||
|
||
is_wwdt_enable = sys_read32(config->base + XWWDT_ESR_OFFSET) & XWWDT_ESR_WEN_MASK; | ||
|
||
if (is_wwdt_enable == 0) { | ||
ret = -EFAULT; | ||
goto out; | ||
} | ||
|
||
/* Read enable status register and check if WWDT is in open window. */ | ||
is_sec_window = (sys_read32(config->base + XWWDT_ESR_OFFSET) & XWWDT_ESR_WSW_MASK) >> | ||
XWWDT_ESR_WSW_SHIFT; | ||
|
||
if (is_sec_window != 1) { | ||
LOG_ERR("Disabling WWDT in closed window is not allowed."); | ||
ret = -EPERM; | ||
goto out; | ||
} | ||
|
||
/* Read enable status register and update WEN bit. */ | ||
reg_value = sys_read32(config->base + XWWDT_ESR_OFFSET) & (~XWWDT_ESR_WEN_MASK); | ||
|
||
/* Set WSW bit to zero. It is RW1C bit. */ | ||
reg_value &= ~((uint32_t)XWWDT_ESR_WSW_MASK); | ||
|
||
/* Write enable status register with updated WEN and WSW value. */ | ||
sys_write32(reg_value, config->base + XWWDT_ESR_OFFSET); | ||
|
||
data->wdt_started = false; | ||
out: | ||
k_spin_unlock(&data->lock, key); | ||
return ret; | ||
} | ||
|
||
static int wdt_xilinx_wwdt_init(const struct device *dev) | ||
{ | ||
const struct xilinx_wwdt_config *config = dev->config; | ||
uint32_t ret = 0; | ||
|
||
if (config->timeout_sec == 0) { | ||
return -EINVAL; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static DEVICE_API(wdt, wdt_xilinx_wwdt_api) = { | ||
.setup = wdt_xilinx_wwdt_setup, | ||
.install_timeout = wdt_xilinx_wwdt_install_timeout, | ||
.feed = wdt_xilinx_wwdt_feed, | ||
.disable = wdt_xilinx_wwdt_disable, | ||
}; | ||
|
||
#define WDT_XILINX_WWDT_INIT(inst) \ | ||
static struct xilinx_wwdt_data wdt_xilinx_wwdt_##inst##_dev_data; \ | ||
\ | ||
static const struct xilinx_wwdt_config wdt_xilinx_wwdt_##inst##_cfg = { \ | ||
.base = DT_INST_REG_ADDR(inst), \ | ||
.timeout_sec = DT_INST_PROP(inst, timeout_sec), \ | ||
.wdt_clock_freq = DT_INST_PROP_BY_PHANDLE(inst, clocks, clock_frequency), \ | ||
}; \ | ||
\ | ||
DEVICE_DT_INST_DEFINE(inst, &wdt_xilinx_wwdt_init, NULL, \ | ||
&wdt_xilinx_wwdt_##inst##_dev_data, \ | ||
&wdt_xilinx_wwdt_##inst##_cfg, PRE_KERNEL_1, \ | ||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_xilinx_wwdt_api); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(WDT_XILINX_WWDT_INIT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright (c) 2025 Advanced Micro Devices, Inc. | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
description: Xilinx window watchdog | ||
|
||
compatible: "xlnx,versal-wwdt" | ||
|
||
include: base.yaml | ||
|
||
properties: | ||
reg: | ||
required: true | ||
|
||
timeout-sec: | ||
type: int | ||
required: true | ||
description: Timeout value in seconds. | ||
|
||
clocks: | ||
required: true | ||
description: Reference to clock for window watchdog core. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, a minor query. I see that there is no callback registered and no interrupt available after timeout. When the closed window time is given some time by the user, how will the user understand that the window is open, so that they can clear the timer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @ghostMan-pac,
I see that there is no callback registered and no interrupt available after timeout.
The WWDT error interrupts (IRQs) occur when the watchdog timer is not serviced within the predefined window periods. These IRQs are routed to the Processing System Manager (PSM) error accumulator module. The PSM is responsible for managing power and system-level errors, generating a System on Chip (SoC) reset when a WWDT error occurs. The system reset event is signaled as a system error for the PSM firmware to handle and a reset output signal to the MIO/EMIO. Refer DT-Bindings.
When the closed window time is given some time by the user, how will the user understand that the window is open, so that they can clear the timer.
In general, the feed operation is independent of whether the window is open or closed. During configuration, the user specifies the closed window value. If the user attempts to ping during the closed window, the driver will produce an error. Once the wdt is in second window, the feed works.
Consider an example in which window.max is set to 10 seconds and window.min is set to 5 seconds. If the user tries to feed/ping every 4 seconds, the first ping will not be allowed by driver as application tries to ping in first window. The next feed after 4 seconds (at 8th second) will be allowed by driver to restart the timer.
Please refer
zephyr/samples/drivers/watchdog/src/main.c
Lines 136 to 139 in d8edd78
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you @Harini-T for the detailed explanation. I have seen another watchdog peripheral which will reset the board, if pinged within the closed window state. That is why I asked whether there is any option for the peripheral to indicate to the user whether the window is opened or not. It seems your watchdog does not take the drastic measure of resetting the board, if the pinging is early (before window open).