Skip to content
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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/watchdog/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ zephyr_library_sources_ifdef(CONFIG_WDT_AMBIQ wdt_ambiq.c)
zephyr_library_sources_ifdef(CONFIG_WDT_XMC4XXX wdt_xmc4xxx.c)
zephyr_library_sources_ifdef(CONFIG_WWDT_NUMAKER wdt_wwdt_numaker.c)
zephyr_library_sources_ifdef(CONFIG_WDT_ENE_KB1200 wdt_ene_kb1200.c)
zephyr_library_sources_ifdef(CONFIG_XILINX_WINDOW_WATCHDOG wdt_xilinx_wwdt.c)

zephyr_library_sources_ifdef(CONFIG_WDT_DW wdt_dw.c wdt_dw_common.c)
zephyr_library_sources_ifdef(CONFIG_WDT_INTEL_ADSP wdt_intel_adsp.c wdt_dw_common.c)
Expand Down
2 changes: 2 additions & 0 deletions drivers/watchdog/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,6 @@ source "drivers/watchdog/Kconfig.ene"

source "drivers/watchdog/Kconfig.litex"

source "drivers/watchdog/Kconfig.xilinx_wwdt"

endif # WATCHDOG
16 changes: 16 additions & 0 deletions drivers/watchdog/Kconfig.xilinx_wwdt
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.
274 changes: 274 additions & 0 deletions drivers/watchdog/wdt_xilinx_wwdt.c
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)
21 changes: 21 additions & 0 deletions dts/bindings/watchdog/xlnx,versal-wwdt.yaml
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.
Loading