Files
arcs/arcs-sdk/drivers/lisa_wdt/lisa_wdt_arcs.c
2026-08-13 16:50:52 +08:00

561 lines
20 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2025, LISTENAI
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file lisa_wdt_arcs.c
* @brief LISA WDT ARCS 平台适配层
*
* 此文件实现 ARCS 芯片平台的看门狗定时器硬件适配
*/
#include "lisa_wdt.h"
#include "Driver_WDT.h"
#include "arcs_ap.h"
#include <stddef.h>
#include <string.h>
#include <lisa_mutex.h>
#define LOG_TAG "lisa_wdt_arcs"
#include <lisa_log.h>
#define DEVICE_LOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_lock(priv->mutex, LISA_OS_WAIT_FOREVER); \
} \
} while (0)
#define DEVICE_UNLOCK(priv) \
do { \
if (priv->mutex) { \
lisa_mutex_unlock(priv->mutex); \
} \
} while (0)
/* ===== WDT 时钟频率定义 ===== */
#define WDT_CLK_32K_HZ 32000UL /* 32K 时钟频率 (Hz) */
#define WDT_CLK_APB_HZ PCLKFREQ() /* APB 时钟频率 (Hz),运行时获取 */
/* ===== WDT 设备私有数据 ===== */
typedef struct {
void *hal_handler; /* HAL WDT 句柄 */
lisa_mutex_t *mutex; /* 互斥锁 */
lisa_wdt_config_t config; /* 当前配置 */
lisa_wdt_state_t state; /* 当前状态 */
lisa_wdt_callback_t callback; /* 超时回调函数 */
void *user_data; /* 用户数据 */
hal_driver_wdt_clk_src clk_src; /* 时钟源 */
hal_driver_wdt_int_time int_time; /* 中断时间配置 */
hal_driver_wdt_rst_time rst_time; /* 复位时间配置 */
uint32_t configured_timeout_ms; /* 已配置的超时时间(毫秒)*/
} lisa_wdt_priv_t;
/* ===== WDT 设备静态实例 ===== */
static lisa_wdt_priv_t wdt0_priv;
/* ===== 内部辅助函数 ===== */
/**
* @brief HAL中断回调函数
*
* 注意:此函数在中断上下文中执行,应尽量简短快速
* 不要在中断回调中调用可能阻塞的函数如mutex操作
*/
static void wdt_hal_irq_callback(void *workspace)
{
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)workspace;
if (!priv) {
LISA_LOGE(LOG_TAG, "[IRQ] WDT callback: invalid workspace");
return;
}
/* 更新状态为已超时(在中断上下文中,直接赋值是安全的) */
priv->state = LISA_WDT_STATE_EXPIRED;
LISA_LOGW(LOG_TAG, "[IRQ] WDT interrupt triggered! State updated to EXPIRED");
/* 调用用户回调函数 */
if (priv->callback) {
LISA_LOGD(LOG_TAG, "[IRQ] Calling user callback");
priv->callback(priv->user_data);
LISA_LOGD(LOG_TAG, "[IRQ] User callback returned");
} else {
LISA_LOGW(LOG_TAG, "[IRQ] WDT interrupt triggered but no user callback registered");
}
}
/**
* @brief 将毫秒转换为中断时间枚举值
*
* @param timeout_ms 超时时间(毫秒)
* @param clk_src 时钟源
* @param int_time 输出参数,中断时间枚举值
*
* @return 0 成功
* @return LISA_DEVICE_ERR_RANGE 超时时间超出范围
*/
static int ms_to_int_time(uint32_t timeout_ms, hal_driver_wdt_clk_src clk_src, hal_driver_wdt_int_time *int_time)
{
uint32_t clk_freq = (clk_src == hal_driver_wdt_clk_src_32k) ? WDT_CLK_32K_HZ : WDT_CLK_APB_HZ;
uint64_t clk_period_ns = 1000000000ULL / clk_freq; /* 时钟周期(纳秒)*/
uint64_t timeout_ns = (uint64_t)timeout_ms * 1000000ULL; /* 超时时间(纳秒)*/
uint64_t clk_count = timeout_ns / clk_period_ns; /* 需要的时钟周期数 */
/* 查找大于等于所需周期数的最小 2^N 值(向上取整) */
/* 这样可以确保实际超时时间不会小于配置的时间 */
if (clk_count <= (1ULL << 6)) {
*int_time = hal_driver_wdt_int_time_6;
} else if (clk_count <= (1ULL << 8)) {
*int_time = hal_driver_wdt_int_time_8;
} else if (clk_count <= (1ULL << 10)) {
*int_time = hal_driver_wdt_int_time_10;
} else if (clk_count <= (1ULL << 11)) {
*int_time = hal_driver_wdt_int_time_11;
} else if (clk_count <= (1ULL << 12)) {
*int_time = hal_driver_wdt_int_time_12;
} else if (clk_count <= (1ULL << 13)) {
*int_time = hal_driver_wdt_int_time_13;
} else if (clk_count <= (1ULL << 14)) {
*int_time = hal_driver_wdt_int_time_14;
} else if (clk_count <= (1ULL << 15)) {
*int_time = hal_driver_wdt_int_time_15;
} else if (clk_count <= (1ULL << 17)) {
*int_time = hal_driver_wdt_int_time_17;
} else if (clk_count <= (1ULL << 19)) {
*int_time = hal_driver_wdt_int_time_19;
} else if (clk_count <= (1ULL << 21)) {
*int_time = hal_driver_wdt_int_time_21;
} else if (clk_count <= (1ULL << 23)) {
*int_time = hal_driver_wdt_int_time_23;
} else if (clk_count <= (1ULL << 25)) {
*int_time = hal_driver_wdt_int_time_25;
} else if (clk_count <= (1ULL << 27)) {
*int_time = hal_driver_wdt_int_time_27;
} else if (clk_count <= (1ULL << 29)) {
*int_time = hal_driver_wdt_int_time_29;
} else if (clk_count <= (1ULL << 31)) {
*int_time = hal_driver_wdt_int_time_31;
} else {
return LISA_DEVICE_ERR_RANGE;
}
/* 计算实际超时时间用于日志 - 需要根据枚举值映射到实际的2^N值 */
uint64_t actual_clk_count;
int exp;
switch ((int)*int_time) {
case 0: exp = 6; break; /* int_time_6 */
case 1: exp = 8; break; /* int_time_8 */
case 2: exp = 10; break; /* int_time_10 */
case 3: exp = 11; break; /* int_time_11 */
case 4: exp = 12; break; /* int_time_12 */
case 5: exp = 13; break; /* int_time_13 */
case 6: exp = 14; break; /* int_time_14 */
case 7: exp = 15; break; /* int_time_15 */
case 8: exp = 17; break; /* int_time_17 */
case 9: exp = 19; break; /* int_time_19 */
case 10: exp = 21; break; /* int_time_21 */
case 11: exp = 23; break; /* int_time_23 */
case 12: exp = 25; break; /* int_time_25 */
case 13: exp = 27; break; /* int_time_27 */
case 14: exp = 29; break; /* int_time_29 */
case 15: exp = 31; break; /* int_time_31 */
default: exp = 6; break;
}
actual_clk_count = (1ULL << exp);
uint64_t actual_timeout_ms = (actual_clk_count * clk_period_ns) / 1000000ULL;
LISA_LOGD(LOG_TAG, "ms_to_int_time: requested=%lu ms, clk_freq=%lu Hz, clk_count=%llu, selected=int_time_%d (2^%d=%llu cycles, ≈%llu ms)",
timeout_ms, clk_freq, clk_count, (int)*int_time, exp, actual_clk_count, actual_timeout_ms);
return LISA_DEVICE_OK;
}
/**
* @brief 将毫秒转换为复位时间枚举值
*
* @param timeout_ms 超时时间(毫秒)
* @param clk_src 时钟源
* @param rst_time 输出参数,复位时间枚举值
*
* @return 0 成功
* @return LISA_DEVICE_ERR_RANGE 超时时间超出范围
*/
static int ms_to_rst_time(uint32_t timeout_ms, hal_driver_wdt_clk_src clk_src, hal_driver_wdt_rst_time *rst_time)
{
uint32_t clk_freq = (clk_src == hal_driver_wdt_clk_src_32k) ? WDT_CLK_32K_HZ : WDT_CLK_APB_HZ;
uint64_t clk_period_ns = 1000000000ULL / clk_freq; /* 时钟周期(纳秒)*/
uint64_t timeout_ns = (uint64_t)timeout_ms * 1000000ULL; /* 超时时间(纳秒)*/
uint64_t clk_count = timeout_ns / clk_period_ns; /* 需要的时钟周期数 */
/* 查找大于等于所需周期数的最小 2^N 值(向上取整) */
/* 这样可以确保实际超时时间不会小于配置的时间 */
if (clk_count <= (1ULL << 7)) {
*rst_time = hal_driver_wdt_rst_time_7;
} else if (clk_count <= (1ULL << 8)) {
*rst_time = hal_driver_wdt_rst_time_8;
} else if (clk_count <= (1ULL << 9)) {
*rst_time = hal_driver_wdt_rst_time_9;
} else if (clk_count <= (1ULL << 10)) {
*rst_time = hal_driver_wdt_rst_time_10;
} else if (clk_count <= (1ULL << 11)) {
*rst_time = hal_driver_wdt_rst_time_11;
} else if (clk_count <= (1ULL << 12)) {
*rst_time = hal_driver_wdt_rst_time_12;
} else if (clk_count <= (1ULL << 13)) {
*rst_time = hal_driver_wdt_rst_time_13;
} else if (clk_count <= (1ULL << 14)) {
*rst_time = hal_driver_wdt_rst_time_14;
} else {
return LISA_DEVICE_ERR_RANGE;
}
/* 计算实际超时时间用于日志 - rst_time枚举值直接对应2^(7+枚举值) */
int exp = 7 + (int)*rst_time;
uint64_t actual_clk_count = (1ULL << exp);
uint64_t actual_timeout_ms = (actual_clk_count * clk_period_ns) / 1000000ULL;
LISA_LOGD(LOG_TAG, "ms_to_rst_time: requested=%lu ms, clk_freq=%lu Hz, clk_count=%llu, selected=rst_time_%d (2^%d=%llu cycles, ≈%llu ms)",
timeout_ms, clk_freq, clk_count, (int)*rst_time, exp, actual_clk_count, actual_timeout_ms);
return LISA_DEVICE_OK;
}
/* ===== ARCS平台WDT实现函数 ===== */
/**
* @brief 配置看门狗设备
*/
static int arcs_wdt_setup(lisa_device_t *dev, const lisa_wdt_config_t *config)
{
if (!lisa_device_is_initialized(dev) || !config) {
return LISA_DEVICE_ERR_INVALID;
}
/* 验证配置参数 */
if (config->int_timeout_ms == 0) {
LISA_LOGE(LOG_TAG, "Invalid int_timeout_ms: %lu ms", config->int_timeout_ms);
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
/* 如果看门狗正在运行,先停止 */
if (priv->state == LISA_WDT_STATE_RUNNING) {
if (WDT_Disable(priv->hal_handler) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to disable WDT before setup");
return LISA_DEVICE_ERR_IO;
}
priv->state = LISA_WDT_STATE_IDLE;
}
/* 保存配置 */
priv->config = *config;
/* 默认使用 32K 时钟源 */
priv->clk_src = hal_driver_wdt_clk_src_32k;
/* 计算中断时间(从启动开始) */
if (ms_to_int_time(config->int_timeout_ms, priv->clk_src, &priv->int_time) != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Invalid interrupt time for int_timeout_ms: %lu ms", config->int_timeout_ms);
return LISA_DEVICE_ERR_RANGE;
}
/* 计算复位时间(从中断触发后开始计算,到系统复位) */
/* 注意根据HAL库注释rst_time是复位阶段的时间从中断触发后开始计算 */
/* HAL库示例int_time=15(1s), rst_time=14(0.5s),时间线:启动->[1s]中断->[0.5s]复位 */
if (ms_to_rst_time(config->rst_timeout_ms, priv->clk_src, &priv->rst_time) != LISA_DEVICE_OK) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Invalid reset time for rst_timeout_ms: %lu ms", config->rst_timeout_ms);
return LISA_DEVICE_ERR_RANGE;
}
/* 确保中断回调已注册(在配置之前注册,确保配置时回调已存在) */
/* 注意即使没有用户回调我们也需要注册HAL回调来更新状态 */
if (WDT_Initialize(priv->hal_handler, wdt_hal_irq_callback, priv) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to register WDT callback before configuration");
return LISA_DEVICE_ERR_IO;
}
LISA_LOGD(LOG_TAG, "WDT callback registered: %p, workspace: %p", wdt_hal_irq_callback, priv);
/* 配置 HAL WDT */
hal_driver_wdt_cfg_t hal_cfg = {
.clk_src = priv->clk_src,
.int_time = priv->int_time,
.rst_time = priv->rst_time,
};
if (WDT_Control(priv->hal_handler, &hal_cfg) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to configure WDT");
return LISA_DEVICE_ERR_IO;
}
LISA_LOGD(LOG_TAG, "WDT configured: int_time=%d, rst_time=%d", priv->int_time, priv->rst_time);
/* 保存配置的总超时时间(用于剩余时间查询) */
priv->configured_timeout_ms = config->int_timeout_ms;
priv->state = LISA_WDT_STATE_IDLE;
DEVICE_UNLOCK(priv);
uint32_t total_rst_timeout_ms = config->int_timeout_ms + config->rst_timeout_ms;
LISA_LOGI(LOG_TAG, "WDT configured: int_timeout=%lu ms, rst_timeout=%lu ms (total=%lu ms), clk_src=%d, int_time=%d, rst_time=%d",
config->int_timeout_ms, config->rst_timeout_ms, total_rst_timeout_ms,
priv->clk_src, priv->int_time, priv->rst_time);
return LISA_DEVICE_OK;
}
/**
* @brief 启动看门狗
*/
static int arcs_wdt_start(lisa_device_t *dev)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
/* 检查是否已配置 */
if (priv->configured_timeout_ms == 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "WDT not configured, call setup first");
return LISA_DEVICE_ERR_NOT_READY;
}
/* 如果已经在运行,直接返回 */
if (priv->state == LISA_WDT_STATE_RUNNING) {
DEVICE_UNLOCK(priv);
return LISA_DEVICE_OK;
}
/* 确保回调已注册(双重检查) */
if (WDT_Initialize(priv->hal_handler, wdt_hal_irq_callback, priv) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to register WDT callback before start");
return LISA_DEVICE_ERR_IO;
}
/* 启动 HAL WDT这会同时使能中断和复位 */
if (WDT_Enable(priv->hal_handler) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to enable WDT");
return LISA_DEVICE_ERR_IO;
}
priv->state = LISA_WDT_STATE_RUNNING;
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "WDT started with int_time=%d, rst_time=%d, callback=%p",
priv->int_time, priv->rst_time, wdt_hal_irq_callback);
return LISA_DEVICE_OK;
}
/**
* @brief 停止看门狗
*/
static int arcs_wdt_stop(lisa_device_t *dev)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
if (priv->state != LISA_WDT_STATE_RUNNING) {
DEVICE_UNLOCK(priv);
return LISA_DEVICE_OK;
}
/* 停止 HAL WDT */
if (WDT_Disable(priv->hal_handler) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to disable WDT");
return LISA_DEVICE_ERR_IO;
}
priv->state = LISA_WDT_STATE_IDLE;
DEVICE_UNLOCK(priv);
LISA_LOGI(LOG_TAG, "WDT stopped");
return LISA_DEVICE_OK;
}
/**
* @brief 喂狗(重置看门狗计数器)
*/
static int arcs_wdt_feed(lisa_device_t *dev)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
if (priv->state != LISA_WDT_STATE_RUNNING) {
DEVICE_UNLOCK(priv);
LISA_LOGW(LOG_TAG, "WDT not running, feed ignored");
return LISA_DEVICE_ERR_NOT_READY;
}
/* 喂狗 */
if (WDT_Refresh(priv->hal_handler) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to refresh WDT");
return LISA_DEVICE_ERR_IO;
}
DEVICE_UNLOCK(priv);
LISA_LOGD(LOG_TAG, "WDT fed");
return LISA_DEVICE_OK;
}
/**
* @brief 获取剩余时间
*/
static int arcs_wdt_get_remaining_time(lisa_device_t *dev, uint32_t *remaining_ms)
{
if (!lisa_device_is_initialized(dev) || !remaining_ms) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
if (priv->state != LISA_WDT_STATE_RUNNING) {
DEVICE_UNLOCK(priv);
*remaining_ms = 0;
return LISA_DEVICE_ERR_NOT_READY;
}
/* ARCS WDT HAL 不支持直接读取剩余时间,返回配置的超时时间 */
*remaining_ms = priv->configured_timeout_ms;
DEVICE_UNLOCK(priv);
return LISA_DEVICE_OK;
}
/**
* @brief 获取看门狗状态
*/
static int arcs_wdt_get_state(lisa_device_t *dev, lisa_wdt_state_t *state)
{
if (!lisa_device_is_initialized(dev) || !state) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
*state = priv->state;
DEVICE_UNLOCK(priv);
return LISA_DEVICE_OK;
}
/**
* @brief 设置超时回调函数
*/
static int arcs_wdt_set_callback(lisa_device_t *dev, lisa_wdt_callback_t callback, void *user_data)
{
if (!lisa_device_is_initialized(dev)) {
return LISA_DEVICE_ERR_INVALID;
}
lisa_wdt_priv_t *priv = (lisa_wdt_priv_t *)dev->priv_data;
DEVICE_LOCK(priv);
priv->callback = callback;
priv->user_data = user_data;
/* 注意HAL回调始终注册用于更新状态和调用用户回调 */
/* 即使用户没有设置回调我们也需要HAL回调来更新状态 */
if (WDT_Initialize(priv->hal_handler, wdt_hal_irq_callback, priv) != 0) {
DEVICE_UNLOCK(priv);
LISA_LOGE(LOG_TAG, "Failed to update WDT callback");
return LISA_DEVICE_ERR_IO;
}
DEVICE_UNLOCK(priv);
LISA_LOGD(LOG_TAG, "WDT callback %s", callback ? "set" : "cleared");
return LISA_DEVICE_OK;
}
/* ===== ARCS WDT API 实例 ===== */
static const lisa_wdt_api_t arcs_wdt_api = {
.setup = arcs_wdt_setup,
.start = arcs_wdt_start,
.stop = arcs_wdt_stop,
.feed = arcs_wdt_feed,
.get_remaining_time = arcs_wdt_get_remaining_time,
.get_state = arcs_wdt_get_state,
.set_callback = arcs_wdt_set_callback,
};
/* ===== 设备初始化函数 ===== */
static int arcs_wdt0_init(void)
{
/* 清空私有数据 */
memset(&wdt0_priv, 0, sizeof(lisa_wdt_priv_t));
/* 获取 HAL WDT 句柄 */
wdt0_priv.hal_handler = WDT();
if (!wdt0_priv.hal_handler) {
LISA_LOGE(LOG_TAG, "Failed to get WDT handler");
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* 创建互斥锁 */
wdt0_priv.mutex = lisa_mutex_create();
if (!wdt0_priv.mutex) {
LISA_LOGE(LOG_TAG, "Failed to create mutex");
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* 初始化 HAL WDT注册中断回调 */
if (WDT_Initialize(wdt0_priv.hal_handler, wdt_hal_irq_callback, &wdt0_priv) != 0) {
LISA_LOGE(LOG_TAG, "Failed to initialize WDT");
return LISA_DEVICE_ERR_INIT_FAIL;
}
/* 上电 WDT */
if (WDT_PowerControl(wdt0_priv.hal_handler, CSK_POWER_FULL) != 0) {
LISA_LOGE(LOG_TAG, "Failed to power on WDT");
return LISA_DEVICE_ERR_INIT_FAIL;
}
wdt0_priv.state = LISA_WDT_STATE_IDLE;
LISA_LOGI(LOG_TAG, "WDT0 initialized successfully");
return LISA_DEVICE_OK;
}
/* ===== 设备注册 ===== */
LISA_DEVICE_REGISTER(wdt0, /* 设备名称 */
&arcs_wdt_api, /* API指针 */
&wdt0_priv, /* 私有数据指针 */
NULL, /* 用户数据 */
arcs_wdt0_init, /* 初始化函数 */
LISA_DEVICE_PRIORITY_NORMAL); /* 优先级 */