drivers: watchdog: Add Andestech ATCWDT200 driver.
Support the Andes atcwdt200 watchdog driver. Signed-off-by: Kevin Wang <kevinwang821020@google.com>
This commit is contained in:
parent
bd9cee0a9f
commit
a9955d3e17
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Andes Technology Corporation
|
||||
* Copyright (c) 2023 Andes Technology Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -180,3 +180,7 @@
|
|||
status = "okay";
|
||||
cs-gpios = <&gpio0 7 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
&wdt {
|
||||
status = "okay";
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ supported:
|
|||
- i2c
|
||||
- spi
|
||||
- eeprom
|
||||
- watchdog
|
||||
testing:
|
||||
ignore_tags:
|
||||
- bluetooth
|
||||
|
|
|
@ -40,5 +40,6 @@ zephyr_library_sources_ifdef(CONFIG_WDT_OPENTITAN wdt_opentitan.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)
|
||||
zephyr_library_sources_ifdef(CONFIG_WDT_ANDES_ATCWDT200 wdt_andes_atcwdt200.c)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_USERSPACE wdt_handlers.c)
|
||||
|
|
|
@ -110,4 +110,6 @@ source "drivers/watchdog/Kconfig.ifx_cat1"
|
|||
|
||||
source "drivers/watchdog/Kconfig.opentitan"
|
||||
|
||||
source "drivers/watchdog/Kconfig.andes_atcwdt200"
|
||||
|
||||
endif # WATCHDOG
|
||||
|
|
14
drivers/watchdog/Kconfig.andes_atcwdt200
Normal file
14
drivers/watchdog/Kconfig.andes_atcwdt200
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Kconfig Andes Watchdog configuration options
|
||||
#
|
||||
# Copyright (c) 2023 Andes Technology Corporation.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
config WDT_ANDES_ATCWDT200
|
||||
bool "Andes Watchdog driver"
|
||||
default y
|
||||
depends on DT_HAS_ANDESTECH_ATCWDT200_ENABLED
|
||||
select COUNTER
|
||||
help
|
||||
Enable driver for the Andes Watchdog driver.
|
366
drivers/watchdog/wdt_andes_atcwdt200.c
Normal file
366
drivers/watchdog/wdt_andes_atcwdt200.c
Normal file
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Andes Technology Corporation.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT andestech_atcwdt200
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <soc.h>
|
||||
#include <zephyr/drivers/watchdog.h>
|
||||
|
||||
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
|
||||
#include <zephyr/logging/log.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/drivers/counter.h>
|
||||
#include <zephyr/drivers/syscon.h>
|
||||
LOG_MODULE_REGISTER(wdt_andes);
|
||||
|
||||
/* Watchdog register */
|
||||
#define REG_IDR 0x00
|
||||
#define REG_CTRL 0x10
|
||||
#define REG_RESTAR 0x14
|
||||
#define REG_WREN 0x18
|
||||
#define REG_STATUS 0x1c
|
||||
|
||||
#define WDT_CTRL(addr) (addr + REG_CTRL)
|
||||
#define WDT_RESTAR(addr) (addr + REG_RESTAR)
|
||||
#define WDT_WREN(addr) (addr + REG_WREN)
|
||||
#define WDT_STATUS(addr) (addr + REG_STATUS)
|
||||
|
||||
/* Atcwdt200 magic number */
|
||||
/* 0x10 Control Register */
|
||||
|
||||
#define WDT_CTRL_RSTTIME_POW_2_7 0x000
|
||||
#define WDT_CTRL_RSTTIME_POW_2_8 0x100
|
||||
#define WDT_CTRL_RSTTIME_POW_2_9 0x200
|
||||
#define WDT_CTRL_RSTTIME_POW_2_10 0x300
|
||||
#define WDT_CTRL_RSTTIME_POW_2_11 0x400
|
||||
#define WDT_CTRL_RSTTIME_POW_2_12 0x500
|
||||
#define WDT_CTRL_RSTTIME_POW_2_13 0x600
|
||||
#define WDT_CTRL_RSTTIME_POW_2_14 0x700
|
||||
|
||||
#define WDT_CTRL_INTTIME_POW_2_6 0x000
|
||||
#define WDT_CTRL_INTTIME_POW_2_8 0x010
|
||||
#define WDT_CTRL_INTTIME_POW_2_10 0x020
|
||||
#define WDT_CTRL_INTTIME_POW_2_11 0x030
|
||||
#define WDT_CTRL_INTTIME_POW_2_12 0x040
|
||||
#define WDT_CTRL_INTTIME_POW_2_13 0x050
|
||||
#define WDT_CTRL_INTTIME_POW_2_14 0x060
|
||||
#define WDT_CTRL_INTTIME_POW_2_15 0x070
|
||||
#define WDT_CTRL_INTTIME_POW_2_17 0x080
|
||||
#define WDT_CTRL_INTTIME_POW_2_19 0x090
|
||||
#define WDT_CTRL_INTTIME_POW_2_21 0x0A0
|
||||
#define WDT_CTRL_INTTIME_POW_2_23 0x0B0
|
||||
#define WDT_CTRL_INTTIME_POW_2_25 0x0C0
|
||||
#define WDT_CTRL_INTTIME_POW_2_27 0x0D0
|
||||
#define WDT_CTRL_INTTIME_POW_2_29 0x0E0
|
||||
#define WDT_CTRL_INTTIME_POW_2_31 0x0F0
|
||||
|
||||
#define WDT_CTRL_RSTEN 0x8
|
||||
#define WDT_CTRL_INTEN 0x4
|
||||
#define WDT_CTRL_APBCLK 0x2
|
||||
#define WDT_CTRL_EXTCLK 0x0
|
||||
#define WDT_CTRL_EN 0x1
|
||||
|
||||
/* Magic Number for Restart Register */
|
||||
#define WDT_RESTART_NUM 0xcafe
|
||||
|
||||
/* Magic Number for Write Enable Register */
|
||||
#define WDT_WREN_NUM 0x5aa5
|
||||
|
||||
/* 0x1C Status Register */
|
||||
#define WDT_ST_INTEXPIRED 0x1
|
||||
#define WDT_ST_INTEXPIRED_CLR 0x1
|
||||
|
||||
/*
|
||||
* SMU(System Management Unit) Registers for hwinfo driver
|
||||
*/
|
||||
|
||||
/* Register offset*/
|
||||
#define SMU_RESET_WRSR 0x10
|
||||
#define SMU_RESET_REGHI 0x60
|
||||
#define SMU_RESET_REGLO 0x50
|
||||
#define SMU_CMD 0x14
|
||||
|
||||
#define SMU_RESET_CMD 0x3c
|
||||
|
||||
#define WDOGCFG_PERIOD_MIN BIT(7)
|
||||
#define WDOGCFG_PERIOD_MAX BIT(14)
|
||||
#define EXT_CLOCK_FREQ BIT(15)
|
||||
|
||||
static const struct device *const syscon_dev =
|
||||
DEVICE_DT_GET(DT_NODELABEL(syscon));
|
||||
static const struct device *const counter_dev =
|
||||
DEVICE_DT_GET(DT_NODELABEL(pit0));
|
||||
|
||||
struct counter_alarm_cfg alarm_cfg;
|
||||
|
||||
struct wdt_atcwdt200_config {
|
||||
uintptr_t base;
|
||||
};
|
||||
|
||||
struct wdt_atcwdt200_dev_data {
|
||||
bool timeout_valid;
|
||||
counter_alarm_callback_t counter_callback;
|
||||
struct k_spinlock lock;
|
||||
};
|
||||
|
||||
static int wdt_atcwdt200_disable(const struct device *dev);
|
||||
|
||||
static void wdt_counter_cb(const struct device *counter_dev, uint8_t chan_id,
|
||||
uint32_t counter,
|
||||
void *user_data)
|
||||
{
|
||||
const struct device *dev = DEVICE_DT_INST_GET(0);
|
||||
struct wdt_atcwdt200_dev_data *wdt_data = dev->data;
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
k_spinlock_key_t key;
|
||||
|
||||
key = k_spin_lock(&wdt_data->lock);
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(WDT_RESTART_NUM, WDT_RESTAR(wdt_addr));
|
||||
|
||||
counter_set_channel_alarm(counter_dev, 2, &alarm_cfg);
|
||||
|
||||
k_spin_unlock(&wdt_data->lock, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set maximum length of timeout to watchdog
|
||||
*
|
||||
* @param dev Watchdog device struct
|
||||
*/
|
||||
static void wdt_atcwdt200_set_max_timeout(const struct device *dev)
|
||||
{
|
||||
struct wdt_atcwdt200_dev_data *data = dev->data;
|
||||
k_spinlock_key_t key;
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
uint32_t reg, counter_freq;
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
counter_freq = counter_get_frequency(counter_dev);
|
||||
|
||||
alarm_cfg.flags = 0;
|
||||
alarm_cfg.callback = wdt_counter_cb;
|
||||
alarm_cfg.user_data = &alarm_cfg;
|
||||
alarm_cfg.ticks = ((WDOGCFG_PERIOD_MAX * counter_freq) / EXT_CLOCK_FREQ) >> 1;
|
||||
|
||||
reg = WDT_CTRL_RSTTIME_POW_2_14;
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(reg, WDT_CTRL(wdt_addr));
|
||||
|
||||
data->timeout_valid = true;
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
}
|
||||
|
||||
static int wdt_atcwdt200_disable(const struct device *dev)
|
||||
{
|
||||
struct wdt_atcwdt200_dev_data *data = dev->data;
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
k_spinlock_key_t key;
|
||||
uint32_t reg;
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
reg = sys_read32(WDT_CTRL(wdt_addr));
|
||||
reg &= ~(WDT_CTRL_RSTEN | WDT_CTRL_EN);
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(reg, WDT_CTRL(wdt_addr));
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
|
||||
wdt_atcwdt200_set_max_timeout(dev);
|
||||
counter_cancel_channel_alarm(counter_dev, 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_atcwdt200_setup(const struct device *dev, uint8_t options)
|
||||
{
|
||||
struct wdt_atcwdt200_dev_data *data = dev->data;
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
k_spinlock_key_t key;
|
||||
uint32_t reg;
|
||||
uint32_t ret = 0;
|
||||
|
||||
if (!data->timeout_valid) {
|
||||
LOG_ERR("No valid timeouts installed");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
reg = sys_read32(WDT_CTRL(wdt_addr));
|
||||
reg |= (WDT_CTRL_RSTEN | WDT_CTRL_EN);
|
||||
|
||||
if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) ==
|
||||
WDT_OPT_PAUSE_HALTED_BY_DBG) {
|
||||
counter_cancel_channel_alarm(counter_dev, 2);
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(reg, WDT_CTRL(wdt_addr));
|
||||
goto out;
|
||||
} else {
|
||||
ret = counter_set_channel_alarm(counter_dev, 2, &alarm_cfg);
|
||||
if (ret != 0) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(reg, WDT_CTRL(wdt_addr));
|
||||
}
|
||||
|
||||
out:
|
||||
k_spin_unlock(&data->lock, key);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Calculates the watchdog counter value (wdogcmp0) and
|
||||
* scaler (wdogscale) to be installed in the watchdog timer
|
||||
*
|
||||
* @param timeout Timeout value in milliseconds.
|
||||
* @param scaler Pointer to return scaler power of 2
|
||||
*
|
||||
* @return Watchdog counter value
|
||||
*/
|
||||
static uint32_t wdt_atcwdt200_convtime(uint32_t timeout, uint32_t *scaler)
|
||||
{
|
||||
int i;
|
||||
uint32_t rst_period, cnt;
|
||||
|
||||
cnt = (uint32_t)((timeout * EXT_CLOCK_FREQ) / 1000);
|
||||
rst_period = cnt;
|
||||
|
||||
for (i = 0; i < 14 && cnt > 0; i++) {
|
||||
cnt >>= 1;
|
||||
}
|
||||
|
||||
*scaler = i;
|
||||
|
||||
return rst_period;
|
||||
}
|
||||
|
||||
static int wdt_atcwdt200_install_timeout(const struct device *dev,
|
||||
const struct wdt_timeout_cfg *cfg)
|
||||
{
|
||||
struct wdt_atcwdt200_dev_data *data = dev->data;
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
k_spinlock_key_t key;
|
||||
uint32_t rst_period, reg, counter_freq, scaler;
|
||||
|
||||
if (cfg->window.min != 0U || cfg->window.max == 0U) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
counter_freq = counter_get_frequency(counter_dev);
|
||||
rst_period = wdt_atcwdt200_convtime(cfg->window.max, &scaler);
|
||||
|
||||
if (rst_period < 0 || WDOGCFG_PERIOD_MAX < rst_period) {
|
||||
LOG_ERR("Unsupported watchdog timeout\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
wdt_atcwdt200_disable(dev);
|
||||
|
||||
key = k_spin_lock(&data->lock);
|
||||
|
||||
switch (cfg->flags) {
|
||||
case WDT_FLAG_RESET_SOC:
|
||||
if (scaler < 7) {
|
||||
reg = WDT_CTRL_RSTTIME_POW_2_7;
|
||||
} else {
|
||||
scaler = scaler - 7;
|
||||
reg = scaler << 8;
|
||||
}
|
||||
|
||||
alarm_cfg.flags = 0;
|
||||
alarm_cfg.callback = wdt_counter_cb;
|
||||
alarm_cfg.user_data = &alarm_cfg;
|
||||
alarm_cfg.ticks = (((cfg->window.max * counter_freq) / 1000) >> 1);
|
||||
|
||||
break;
|
||||
case WDT_FLAG_RESET_NONE:
|
||||
case WDT_FLAG_RESET_CPU_CORE:
|
||||
default:
|
||||
LOG_ERR("Unsupported watchdog config flags\n");
|
||||
k_spin_unlock(&data->lock, key);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(reg, WDT_CTRL(wdt_addr));
|
||||
|
||||
k_spin_unlock(&data->lock, key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt_atcwdt200_feed(const struct device *dev, int channel_id)
|
||||
{
|
||||
uint32_t wdt_addr = ((const struct wdt_atcwdt200_config *)(dev->config))->base;
|
||||
|
||||
ARG_UNUSED(channel_id);
|
||||
|
||||
sys_write32(WDT_WREN_NUM, WDT_WREN(wdt_addr));
|
||||
sys_write32(WDT_RESTART_NUM, WDT_RESTAR(wdt_addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct wdt_driver_api wdt_atcwdt200_api = {
|
||||
.setup = wdt_atcwdt200_setup,
|
||||
.disable = wdt_atcwdt200_disable,
|
||||
.install_timeout = wdt_atcwdt200_install_timeout,
|
||||
.feed = wdt_atcwdt200_feed,
|
||||
};
|
||||
|
||||
static int wdt_atcwdt200_init(const struct device *dev)
|
||||
{
|
||||
struct wdt_atcwdt200_dev_data *data = dev->data;
|
||||
|
||||
data->timeout_valid = false;
|
||||
data->counter_callback = wdt_counter_cb;
|
||||
uint32_t ret;
|
||||
|
||||
counter_start(counter_dev);
|
||||
|
||||
ret = syscon_write_reg(syscon_dev, SMU_RESET_REGLO,
|
||||
((uint32_t)((unsigned long)
|
||||
Z_MEM_PHYS_ADDR(CONFIG_KERNEL_ENTRY))));
|
||||
if (ret < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = syscon_write_reg(syscon_dev, SMU_RESET_REGHI,
|
||||
((uint32_t)((uint64_t)((unsigned long)
|
||||
Z_MEM_PHYS_ADDR(CONFIG_KERNEL_ENTRY)) >> 32)));
|
||||
if (ret < 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WDT_DISABLE_AT_BOOT
|
||||
wdt_atcwdt200_disable(dev);
|
||||
#else
|
||||
data->timeout_valid = true;
|
||||
wdt_atcwdt200_set_max_timeout(dev);
|
||||
wdt_atcwdt200_setup(dev, 0x0);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct wdt_atcwdt200_dev_data wdt_atcwdt200_data;
|
||||
|
||||
static const struct wdt_atcwdt200_config wdt_atcwdt200_cfg = {
|
||||
.base = DT_INST_REG_ADDR(0),
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, wdt_atcwdt200_init, NULL,
|
||||
&wdt_atcwdt200_data, &wdt_atcwdt200_cfg, PRE_KERNEL_2,
|
||||
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &wdt_atcwdt200_api);
|
18
dts/bindings/watchdog/andestech,atcwdt200.yaml
Normal file
18
dts/bindings/watchdog/andestech,atcwdt200.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Copyright (c) 2023, Andes Technology Corporation.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
description: Andes Watchdog driver
|
||||
|
||||
compatible: "andestech,atcwdt200"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
interrupts:
|
||||
required: true
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Andes Technology Corporation
|
||||
* Copyright (c) 2023 Andes Technology Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
@ -326,6 +326,14 @@
|
|||
status = "disabled";
|
||||
};
|
||||
|
||||
wdt: wdt@f0500000 {
|
||||
compatible = "andestech,atcwdt200";
|
||||
reg = <0xf0500000 0x1000>;
|
||||
interrupts = <20 1>;
|
||||
interrupt-parent = <&plic0>;
|
||||
status = "disabled";
|
||||
};
|
||||
|
||||
smc0: smc@e0400000 {
|
||||
compatible = "andestech,atfsmc020";
|
||||
reg = <0xe0400000 0x1000>;
|
||||
|
|
Loading…
Reference in a new issue