Skip to content
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
59 changes: 36 additions & 23 deletions drivers/watchdog/wdt_xilinx_wwdt.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2025 Advanced Micro Devices, Inc.
* Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -9,7 +9,6 @@
#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>
Expand Down Expand Up @@ -41,20 +40,21 @@ LOG_MODULE_REGISTER(xilinx_wwdt, CONFIG_WDT_LOG_LEVEL);
#define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK64(32, 1)

struct xilinx_wwdt_config {
DEVICE_MMIO_ROM;
uint32_t wdt_clock_freq;
mem_addr_t base;
};

struct xilinx_wwdt_data {
DEVICE_MMIO_RAM;
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;
mm_reg_t reg = DEVICE_MMIO_GET(dev);
uint32_t reg_value;
int ret = 0;

Expand All @@ -76,10 +76,10 @@ static int wdt_xilinx_wwdt_setup(const struct device *dev, uint8_t options)
*/

/* Read enable status register and update WEN bit */
reg_value = sys_read32(config->base + XWWDT_ESR_OFFSET) | XWWDT_ESR_WEN_MASK;
reg_value = sys_read32(reg + XWWDT_ESR_OFFSET) | XWWDT_ESR_WEN_MASK;

/* Write enable status register with updated WEN value */
sys_write32(reg_value, config->base + XWWDT_ESR_OFFSET);
sys_write32(reg_value, reg + XWWDT_ESR_OFFSET);
data->wdt_started = true;
out:
k_spin_unlock(&data->lock, key);
Expand All @@ -91,6 +91,7 @@ static int wdt_xilinx_wwdt_install_timeout(const struct device *dev,
{
const struct xilinx_wwdt_config *config = dev->config;
struct xilinx_wwdt_data *data = dev->data;
mm_reg_t reg = DEVICE_MMIO_GET(dev);
uint64_t closed_window_ms_count;
uint64_t open_window_ms_count;
uint64_t max_hw_timeout_ms;
Expand All @@ -106,12 +107,23 @@ static int wdt_xilinx_wwdt_install_timeout(const struct device *dev,
goto out;
}

if (cfg->flags != WDT_FLAG_RESET_SOC) {
ret = -ENOTSUP;
/*
* The expiry/reset action is owned by platform firmware (PLM/CDO) and is
* routed through the Error Aggregation Module; the driver never issues a
* reset itself and cannot honor any WDT_FLAG_RESET_* value (including
* WDT_FLAG_RESET_NONE). Accept the timeout regardless and warn that the
* flag has no effect.
*/
LOG_WRN("WDT_FLAG_RESET_* has no effect; "
"reset action is owned by firmware (PLM/CDO)");

timeout_ms = cfg->window.max;

if (timeout_ms == 0) {
ret = -EINVAL;
goto out;
}

timeout_ms = cfg->window.max;
max_hw_timeout_ms = (XWWDT_MAX_COUNT_WINDOW_COMBINED * 1000) / config->wdt_clock_freq;

/* Timeout greater than the maximum hardware timeout is invalid. */
Expand All @@ -138,10 +150,10 @@ static int wdt_xilinx_wwdt_install_timeout(const struct device *dev,
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);
sys_write32(XWWDT_MWR_MASK, reg + XWWDT_MWR_OFFSET);
sys_write32(~(uint32_t)XWWDT_ESR_WEN_MASK, reg + XWWDT_ESR_OFFSET);
sys_write32(closed_window_ms_count, reg + XWWDT_FWR_OFFSET);
sys_write32(open_window_ms_count, reg + XWWDT_SWR_OFFSET);

data->timeout_active = true;
out:
Expand All @@ -151,8 +163,8 @@ static int wdt_xilinx_wwdt_install_timeout(const struct device *dev,

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;
mm_reg_t reg = DEVICE_MMIO_GET(dev);
uint32_t control_status_reg;
uint32_t is_sec_window;
int ret = 0;
Expand All @@ -165,10 +177,10 @@ static int wdt_xilinx_wwdt_feed(const struct device *dev, int channel_id)
}

/* Enable write access control bit for the WWDT. */
sys_write32(XWWDT_MWR_MASK, config->base + XWWDT_MWR_OFFSET);
sys_write32(XWWDT_MWR_MASK, reg + XWWDT_MWR_OFFSET);

/* Trigger restart kick to WWDT. */
control_status_reg = sys_read32(config->base + XWWDT_ESR_OFFSET);
control_status_reg = sys_read32(reg + 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;
Expand All @@ -180,32 +192,32 @@ static int wdt_xilinx_wwdt_feed(const struct device *dev, int channel_id)
}

control_status_reg |= (uint32_t)XWWDT_ESR_WSW_MASK;
sys_write32(control_status_reg, config->base + XWWDT_ESR_OFFSET);
sys_write32(control_status_reg, reg + 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;
mm_reg_t reg = DEVICE_MMIO_GET(dev);
uint32_t is_wwdt_enable;
uint32_t is_sec_window;
uint32_t reg_value;
int 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;
is_wwdt_enable = sys_read32(reg + 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) >>
is_sec_window = (sys_read32(reg + XWWDT_ESR_OFFSET) & XWWDT_ESR_WSW_MASK) >>
XWWDT_ESR_WSW_SHIFT;

if (is_sec_window != 1) {
Expand All @@ -215,10 +227,10 @@ static int wdt_xilinx_wwdt_disable(const struct device *dev)
}

/* Read enable status register and update WEN bit. */
reg_value = sys_read32(config->base + XWWDT_ESR_OFFSET) & (~XWWDT_ESR_WEN_MASK);
reg_value = sys_read32(reg + XWWDT_ESR_OFFSET) & (~XWWDT_ESR_WEN_MASK);

/* Write enable status register with updated WEN and WSW value. */
sys_write32(reg_value, config->base + XWWDT_ESR_OFFSET);
sys_write32(reg_value, reg + XWWDT_ESR_OFFSET);

data->wdt_started = false;
out:
Expand All @@ -235,6 +247,7 @@ static int wdt_xilinx_wwdt_init(const struct device *dev)
return -EINVAL;
}

DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
return ret;
}

Expand All @@ -249,7 +262,7 @@ static DEVICE_API(wdt, wdt_xilinx_wwdt_api) = {
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), \
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(inst)), \
.wdt_clock_freq = DT_INST_PROP_BY_PHANDLE(inst, clocks, clock_frequency), \
}; \
\
Expand Down
16 changes: 14 additions & 2 deletions dts/bindings/watchdog/xlnx,versal-wwdt.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
# Copyright (c) 2025 Advanced Micro Devices, Inc.
# Copyright (c) 2025-2026 Advanced Micro Devices, Inc.
# SPDX-License-Identifier: Apache-2.0

description: Xilinx window watchdog
description: |
Xilinx window watchdog
The WWDT implements a two-window (closed + open) watchdog. It must be
fed only during the open window. A feed in the closed window, or a
missed feed by the end of the open window, is treated as a watchdog
failure.
The action taken on a failure (no reset, CPU/subsystem reset, or SoC
reset) is routed through the platform Error Aggregation Module and
configured by firmware (PLM) via the boot-time CDO. Zephyr running on
the APU/RPU cannot observe or change this routing, so the driver does
not honor the WDT_FLAG_RESET_* flags (including WDT_FLAG_RESET_NONE)
passed to wdt_install_timeout(); the reset behavior is defined entirely
by platform firmware.

compatible: "xlnx,versal-wwdt"

Expand Down
Loading