4.3.13. 看门狗驱动调试指南
4.3.13.1. Watchdog 概述
看门狗(Watchdog Timer 或者 Watchdog)是
在
4.3.13.2. Watchdog 的特点
芯片
APB3接口
支持 :兼容 APB3 接口。倒计时
超时 :计数器指示 从 预设值 倒计时 到 0,表示 发生 超时。 系统
复位 :如果触发 在 第二次 超时 发生 前 中断 未 被 清除,则 会 触发 系统 复位。 可编程
超时 :提供范围 可编程 的 超时 周期 范围,可以 在 配置 期间 选择 硬 编码 该值,以 减少 寄存器 的 需求。 双
可编程 :可超时 周期 选 的 双 可编程 超时 周期,适用 于 首次 启动 等待时间 与 后续 启动 等待时间 不同 的 情况,可以 选择 硬 编码 这些 值。 复位
脉冲 :支持长度 设置 可编程 和 硬 编码 的 复位 脉冲 长度。 防止
意外 :防止 DW_apb_wdt 计数器重启 的 意外 重启。 防止
意外 :防止 DW_apb_wdt 的禁用 意外 禁用。 暂停
模式 :可支持 选 支持 暂停 模式,通过 使用 外部 暂停 使能 信号 实现。 测试
模式 :测试信号 模式 信号 用于 减少 功能测试 所 需 的 时间。
4.3.13.3. 功能描述
Watchdog 典型应用
watchdog 是
下图

具体
正常
情况 下: 系统
运行 正常,watchdog 被 定期 喂狗。 任务
完成,没有 死锁,watchdog 保持 计时 并 不会 触发 重启。
死锁
发生 后: 模块 A 和
模块 B 发生 死锁,系统 进入 无 响应 状态。 由于
死锁,系统 无法 完成 周期性 任务,因此 也 无法 及时 喂狗。 watchdog 超时,触发
重启。
系统
重启 后: 系统
重新启动,模块 A 和 B 被 初始化,死锁 被 消除。 watchdog 重新
开始 计时,继续 监控 系统 状态。
Watchdog 功能原理
看门狗
Watchdog 的

Watchdog 工作方式
Watchdog 的
Watchdog 的
常规
模式:软件 定期 重置 Watchdog 计时器。如果 在 规定 的 时间 内 没有“喂狗”信号,Watchdog 会 触发 复位。 调试模式:Watchdog 在
调试 过程 中 可以 暂停,避免 调试 时 发生意外 复位。通常 需要 外部 信号 或 特殊 控制指令 来 启动 调试模式。 复位
保护模式:Watchdog 在 遇到 故障 时会 触发 系统 复位,但 该 复位 过程 会 受到 保护,防止 因 不必要 的 操作 导致系统 频繁 重启。
Watchdog 基本工作流程
Watchdog 工作
初始化
和 配置:在 系统启动 时,Watchdog 被 初始化 并 配置。系统 设置 超时 时间(即 Watchdog 计时器 的 倒计时 周期),通常 由 软件 设置,也 可以 通过 硬 编码 来 实现。此时 Watchdog 开始 计时。 “喂狗”机制:正常
情况 下,系统 中 的 软件程序 会 定期 给 Watchdog 发送 信号,称为“喂狗”操作。这 通常 是 通过 重置 Watchdog 的 计时器 实现 的。例如,软件 每隔 一定 时间 向 Watchdog 写入 特定 值,重置 计时器 的 倒计时 过程,确保 Watchdog 不会 超时。 超时
检测:如果 软件程序 长时间 没有 进行“喂狗”操作(例如,程序 发生 死锁、崩溃 或 被 挂 起),Watchdog 计时器 会 倒计时 到达 设定 的 超时 时间(通常 为 几秒钟 至 几分钟)。 超时
后 的 处理:当 计时器 倒计时 到 0 时,Watchdog 会 认为 系统 出现异常,自动 触发 预设 的 响应 行为,通常 是 复位 操作,重启 系统 或硬件 模块,恢复 系统 到 正常 状态。
上述

Watchdog 的工作方式细节
正常
运行 模式 在 正常 运行 时,Watchdog 会 定期 接收 到 来自 软件 的“喂狗”信号。每次 软件 触发“喂狗”时,Watchdog 的 计时器 会 被 重置,重新 开始 计时。如果 计时器 周期 内 Watchdog 没有 收到“喂狗”信号,它会 检测 到 超时 并 触发 复位,重新启动 系统。 超时
处理 超时 条件:如果 Watchdog 在 设定 的 超时 时间 内未 收到“喂狗”信号,说明 系统 可能 出现 了 故障(例如 程序 死锁、死循环 等)。 超时 后 复位:Watchdog 会 触发 系统 复位。通常 这是 硬件 级 的 复位 操作,重启 整个 系统 或 部分 硬件 模块(如 CPU、外围设备 等),清除 当前 的 错误 状态,确保 系统 能够 继续 运行。 配置
和 可编程 性 超时 时间(周期):Watchdog 的 超时 时间 通常 是 可 配置 的。开发者 可以 根据 需要 设定 适当 的 超时 时间,短 周期 适合 对 实时性 要求 高 的 系统,长 周期 则 适合 对 响应 时间 不 那么 敏感 的 系统。 双重 超时 段:一些 Watchdog 支持 配置 不同 的 超时 段,如 启动 阶段 与 正常 运行 阶段 的 超时 时间 不同。启动 阶段 通常 需要 更 长时间 的 等待,而 在 正常 运行 中,超时 周期 可以 较 短。 硬 编码 配置:为了 减少 对 寄存器 的 频繁 访问,一些 Watchdog 可以 将 配置 值硬 编码 在 硬件 中,降低 系统 的 复杂性 和 资源 消耗。 防止
误操作 防止 禁用 Watchdog:为 防止 软件 错误 或 恶意代码 禁用 Watchdog,某些 Watchdog 系统 提供“保护 机制”,比如 要求 特定 的 安全 序列 才能 禁用 Watchdog,或者 在 某些 情况 下(如 调试模式)强制 启用 Watchdog。 防止 重启:一些 Watchdog 实现 包括 保护措施,以 防止 系统 在 未经 授权 或 不必要 的 情况 下 反复 重启。例如,在 硬件 设计 中,可以 使用 额外 的“复位 保护”机制,以 防止 频繁 复位。 外部
控制 和 暂停 外部 暂停 信号:某些 Watchdog 允许 外部 信号 暂停 Watchdog 的 计时器,这 对于 调试 或 在 某些 特殊 情况 下 暂停 Watchdog 非常 有用。外部 信号 可以 控制 Watchdog 是否 继续 计时,避免 在 调试 过程 中误 触发 复位。 暂停 模式:在 特定 情况 下,Watchdog 可以 被 配置 为 进入 暂停 模式,此时 Watchdog 不会 触发 复位,直到 系统 重新 恢复正常 工作。 测试
模式 功能测试:在 开发 和 验证 过程 中,Watchdog 通常 会 提供 测试 模式,用于 模拟 超时 和 复位 行为。这 有助于 开发人员 验证 Watchdog 是否 能 在 预期 条件 下 正常 工作。
4.3.13.4. Watchdog 驱动代码说明
Watchdog 相关代码路径
drivers/watchdog/dw_wdt.c # Watchdog 驱动代码源文件
include/linux/watchdog.h # Watchdog 驱动代码头文件
Watchdog DTS配置
芯片arch/arm64/boot/dts/hobot/x5.dtsi文件
a55_apb1 {
……
watchdog: watchdog@34250000 {
compatible = "snps,dw-wdt";
status = "okay";
reg = <0x34250000 0x10000>;
clocks = <&hpsclks X5_WDT_PCLK>;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;
timeout-sec = <10>;
clk-rate-div = <2>;
resets = <&socrst SOC_WDT_APB_RESET>;
};
可以
watchdog:
watchdog: watchdog@34250000:watchdog:是该 设备 的 标签(label),可 用于 引用。 watchdog@34250000:是该 设备 的 地址,在 设备树 中,这个 表示 设备 的 基 地址。 34250000是该 设备 在 系统 内存 中 的 物理地址。
compatible:
compatible = "snps,dw-wdt";这是
一个 设备 兼容性 描述符,用于 指示 驱动程序 应当 匹配 哪些 硬件。此处 的 "snps,dw-wdt"表示该 设备 是 Synopsys™ 提供 的 dw-wdt(即设计 WARE Watchdog Timer),告诉 操作系统 应 使用 与 之 兼容 的 驱动程序 来 管理 该 硬件 设备。
status:
status = "okay";该字段
表示 设备 的 状态。 "okay"表示该 设备 是 启用 的,可以 被 内核 使用。如果 该字 段 为 "disabled",则表示 该 设备 在 内核 中 被 禁用。
reg:
reg = <0x34250000 0x10000>;该字段
指定 设备 的 寄存器 范围。 0x34250000是设备 的 基 地址, 0x10000表示该 设备 的 地址 范围(寄存器 大小 为 0x10000 字节)。这 告诉 内核 如何 映射 硬件 寄存器。
clocks:
clocks = <&hpsclks X5_WDT_PCLK>;这个
字 段 指定 该 设备 所 依赖 的 时钟 源。 &hpsclks是一个 指向 时钟 控制器 节点 的 引用, X5_WDT_PCLK是一个 具体 的 时钟 源。内核 会 根据 此 信息 来 配置 设备 的 时钟。
interrupt-parent:
interrupt-parent = <&gic>;该字段
指定 设备 的 中断 控制器。在 此例 中, &gic表示设备 使用 的 中断 控制器 是 GIC(通用 中断 控制器)。这 允许 操作系统 正确 地 配置 设备 中断。
interrupts:
interrupts = <GIC_SPI 71 IRQ_TYPE_LEVEL_HIGH>;该字段
指定 设备 的 中断 信息。 GIC_SPI表示一个 共享 外部 中断, 71是该 设备 的 中断 号, IRQ_TYPE_LEVEL_HIGH表示该 中断 是 一个 高电平 触发 的 中断。
timeout-sec:
timeout-sec = <10>;该字段
指定 看门狗 定时器 的 超时 时间(单位:秒)。在 这个 例子 中, 10表示定时器 的 超时 时间 为 10 秒。如果 系统 在 此 时间 内 没有响应(例如 没有 重置 看门狗 定时器),则 看门狗 将 触发 系统 重启。
clk-rate-div:
clk-rate-div = <2>;这个
字 段 表示 时钟 频率 的 分频 系数。在 某些 硬件平台 中,定时器 时钟 是 基于 一个 更 高频率 的 时钟 信号,分频 系数 决定 了 时钟 信号 的 实际 频率。这里 的 2表示时钟 频率 将 被 除以 2。
resets:
resets = <&socrst SOC_WDT_APB_RESET>;该字段
指定 与 设备 相关 的 复位 控制 信号。在 此例 中, &socrst是复位 控制器 的 节点 引用, SOC_WDT_APB_RESET是该 设备 的 复位 信号。这 用于 控制 设备 的 复位 操作。
备注:
x5.dtsi 中
芯片
&watchdog {
status = "disabled";
};
Watchdog 内核配置
Watchdog 设备HORIZON_WATCHDOG_ENABLE 来
默认
/* arch/arm64/configs/hobot_x5_soc_defconfig */
CONFIG_WATCHDOG=y
CONFIG_DW_WATCHDOG=y
默认
/* arch/arm64/configs/hobot_x5_soc_perf_defconfig */
...
CONFIG_WATCHDOG=y
CONFIG_DW_WATCHDOG=y
CONFIG_HORIZON_WATCHDOG_ENABLE=y
...
CONFIG_HORIZON_WATCHDOG_ENABLE 配置kernel/drivers/watchdog/dw_wdt.c 中:
static int dw_wdt_drv_probe(struct platform_device *pdev)
{
……
#ifdef CONFIG_HORIZON_WATCHDOG_ENABLE
dw_wdt->wdt_disable = 0;
#else
dw_wdt->wdt_disable = 1;
#endif
4.3.13.5. Watchdog 功能使用
Watchdog 寄存器说明
源码
#define WDOG_CONTROL_REG_OFFSET 0x00
#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
#define WDOG_CONTROL_REG_RESP_MODE_MASK 0x02
#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
#define WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT 4
#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
#define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c
#define WDOG_COUNTER_RESTART_KICK_VALUE 0x76
#define WDOG_INTERRUPT_STATUS_REG_OFFSET 0x10
#define WDOG_INTERRUPT_CLEAR_REG_OFFSET 0x14
#define WDOG_COMP_PARAMS_5_REG_OFFSET 0xe4
#define WDOG_COMP_PARAMS_4_REG_OFFSET 0xe8
#define WDOG_COMP_PARAMS_3_REG_OFFSET 0xec
#define WDOG_COMP_PARAMS_2_REG_OFFSET 0xf0
#define WDOG_COMP_PARAMS_1_REG_OFFSET 0xf4
#define WDOG_COMP_PARAMS_1_USE_FIX_TOP BIT(6)
#define WDOG_COMP_VERSION_REG_OFFSET 0xf8
#define WDOG_COMP_TYPE_REG_OFFSET 0xfc
/* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */
#define DW_WDT_NUM_TOPS 16
#define DW_WDT_FIX_TOP(_idx) (1U << (16 + _idx))
#define DW_WDT_DEFAULT_SECONDS 30
下面
| 宏 |
值 | 描述 |
|---|---|---|
WDOG_CONTROL_REG_OFFSET |
0x00 |
控制 |
WDOG_CONTROL_REG_WDT_EN_MASK |
0x01 |
用于 |
WDOG_CONTROL_REG_RESP_MODE_MASK |
0x02 |
用于 |
WDOG_TIMEOUT_RANGE_REG_OFFSET |
0x04 |
超时 |
WDOG_TIMEOUT_RANGE_TOPINIT_SHIFT |
4 |
超时 |
WDOG_CURRENT_COUNT_REG_OFFSET |
0x08 |
当前 |
WDOG_COUNTER_RESTART_REG_OFFSET |
0x0c |
计数器 |
WDOG_COUNTER_RESTART_KICK_VALUE |
0x76 |
重启 |
WDOG_INTERRUPT_STATUS_REG_OFFSET |
0x10 |
中断 |
WDOG_INTERRUPT_CLEAR_REG_OFFSET |
0x14 |
中断 |
WDOG_COMP_PARAMS_5_REG_OFFSET |
0xe4 |
组件 |
WDOG_COMP_PARAMS_4_REG_OFFSET |
0xe8 |
组件 |
WDOG_COMP_PARAMS_3_REG_OFFSET |
0xec |
组件 |
WDOG_COMP_PARAMS_2_REG_OFFSET |
0xf0 |
组件 |
WDOG_COMP_PARAMS_1_REG_OFFSET |
0xf4 |
组件 |
WDOG_COMP_PARAMS_1_USE_FIX_TOP |
BIT(6) |
组件 |
WDOG_COMP_VERSION_REG_OFFSET |
0xf8 |
组件 |
WDOG_COMP_TYPE_REG_OFFSET |
0xfc |
组件 |
DW_WDT_NUM_TOPS |
16 |
watchdog 支持 |
DW_WDT_FIX_TOP(_idx) |
1U << (16 + _idx) |
用于 |
DW_WDT_DEFAULT_SECONDS |
30 |
watchdog 的 |
Watchdog 驱动关键结构体说明
1) dw_wdt_rmod
enum dw_wdt_rmod {
DW_WDT_RMOD_RESET = 1,
DW_WDT_RMOD_IRQ = 2
};
这个 dw_wdt_rmod 枚举
DW_WDT_RMOD_RESET:值为1,表示当 watchdog 超时 时,设备 将 触发 系统 重置(reset)。 DW_WDT_RMOD_IRQ:值为2,表示当 watchdog 超时 时,设备 将 生成 一个 中断(IRQ)而 不是 重置 系统。
使用
系统
重置:在 某些 情况 下,如果 系统 出现 严重错误 或 无法 恢复 的 状态,可以 通过 设置 DW_WDT_RMOD_RESET模式使 watchdog 在 超时 时 重置 系统,以 尝试 恢复 系统 的 正常 运行。 中断
生成:在 其他 情况 下,如果 需要 对 超时 事件 进行 更 细粒度 的 控制 或 处理,可以 通过 设置 DW_WDT_RMOD_IRQ模式使 watchdog 在 超时 时 生成 一个 中断,然后 由 系统软件 处理 这个 中断,执行 相应 的 错误处理 或 日志 记录 操作。
2) dw_wdt_timeout
struct dw_wdt_timeout {
u32 top_val;
unsigned int sec;
unsigned int msec;
};
dw_wdt_timeout 结构
top_val:这是一个 无 符号32位 整数,用于 存储 与 超时 周期 相关 的 一个 值,这个 值 通常 与 硬件 寄存器 中 的 设置 有关。在 Watchdog 设备 中,top_val 代表 特定 的 超时 周期 值(TOPs),这些 值 用于 配置 watchdog 的 超时 行为。 sec:这是一个 无 符号 整数,表示 超时 周期 的 秒数 部分。 msec:这也 是 一个 无 符号 整数,表示 超时 周期 的 毫秒 数 部分。
3)dw_wdt
struct dw_wdt {
void __iomem *regs;
struct clk *clk;
struct clk *pclk;
unsigned long rate;
enum dw_wdt_rmod rmod;
struct dw_wdt_timeout timeouts[DW_WDT_NUM_TOPS];
struct watchdog_device wdd;
struct reset_control *rst;
/* Save/restore */
u32 control;
u32 timeout;
#if IS_ENABLED(CONFIG_ARCH_HOBOT_X5)
u32 clk_rate_div;
u32 wdt_disable;
#endif
#ifdef CONFIG_DEBUG_FS
struct dentry *dbgfs_dir;
#endif
};
dw_wdt 结构
regs:指向设备 寄存器 的 指针,用于 直接 访问 硬件 寄存器。 clk和pclk:分别表示 watchdog 定时器 和 APB 总线 的 时钟,用于 获取 时钟 频率 并 控制 时钟 的 启用 和 禁用。 rate:表示 watchdog 定时器的 时钟 频率。 rmod:表示 watchdog 的响应 模式,可以 是 重置(DW_WDT_RMOD_RESET)或 中断(DW_WDT_RMOD_IRQ)。 timeouts:一个数组,存储 了 不同 超时 周期 的 值,允许 watchdog 有 多个 超时 选项。 wdd:一个结构 体,用于 与 Linux 内核 的 watchdog 框架 进行 交互。 rst:一个指针,指向 reset control 结构 体,用于 执行 watchdog 相关 的 复位 操作。 control和timeout:用于保存 watchdog 控制 寄存器 和 超时 周期 的 当前 值,以便 在 需要 时 恢复。 clk_rate_div和wdt_disable:特定于 HOBOT_X5 架构 的 成员,用于 时钟 频率 分频 和 禁用 watchdog。 dbgfs_dir:当配置 了 CONFIG_DEBUG_FS 时,用于 创建 debugfs 文件系统 的 目录 项,便于 调试。
这个
Watchdog 驱动接口函数
下面
dw_wdt_is_enabled(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针,代表watchdog设备 的 实例。 功能:检查watchdog设备
是否 已经 启用。
dw_wdt_update_mode(struct dw_wdt *dw_wdt, enum dw_wdt_rmod rmod):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 rmod- 新的 响应 模式,可以 是 DW_WDT_RMOD_RESET或DW_WDT_RMOD_IRQ。
功能:更新watchdog设备
的 响应 模式。
dw_wdt_find_best_top(struct dw_wdt *dw_wdt, unsigned int timeout, u32 *top_val):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 timeout- 请求的 超时 时间(秒)。 top_val- 指向存储 最佳TOP值 的 变量 的 指针。
功能:寻找
最 适合 的 超时 周期(TOP)值。
dw_wdt_get_min_timeout(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:获取watchdog设备
的 最小 超时 时间。
dw_wdt_get_max_timeout_ms(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:获取watchdog设备
的 最大 超时 时间(毫秒)。
dw_wdt_get_timeout(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:获取
当前watchdog设备 的 超时 时间。
dw_wdt_ping(struct watchdog_device *wdd):参数:
wdd- 指向watchdog_device结构体 的 指针。 功能:刷新watchdog设备
的 计时器。
dw_wdt_set_timeout(struct watchdog_device *wdd, unsigned int top_s):参数:
wdd- 指向watchdog_device结构体 的 指针。 top_s- 请求的 超时 时间(秒)。
功能:设置watchdog设备
的 超时 时间。
dw_wdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req):参数:
wdd- 指向watchdog_device结构体 的 指针。 req- 请求的 预 超时 时间。
功能:设置
预 超时 功能。
dw_wdt_arm_system_reset(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:配置
并 启用watchdog设备,使 其 在 超时 后 触发 系统 重置。
dw_wdt_start(struct watchdog_device *wdd):参数:
wdd- 指向watchdog_device结构体 的 指针。 功能:启动watchdog设备。
dw_wdt_stop(struct watchdog_device *wdd):参数:
wdd- 指向watchdog_device结构体 的 指针。 功能:停止watchdog设备。
dw_wdt_restart(struct watchdog_device *wdd, unsigned long action, void *data):参数:
wdd- 指向watchdog_device结构体 的 指针。 action- 重启动作。 data- 重启数据。
功能:重启watchdog设备。
dw_wdt_get_timeleft(struct watchdog_device *wdd):参数:
wdd- 指向watchdog_device结构体 的 指针。 功能:获取watchdog设备
剩余 的 时间。
dw_wdt_irq(int irq, void *devid):参数:
irq- 中断号。 devid- 设备实例 指针。
功能:watchdog设备
的 中断 处理函数。
dw_wdt_suspend(struct device *dev):参数:
dev- 指向device结构体 的 指针。 功能:挂
起watchdog设备。
dw_wdt_resume(struct device *dev):参数:
dev- 指向device结构体 的 指针。 功能:恢复watchdog设备。
dw_wdt_handle_tops(struct dw_wdt *dw_wdt, const u32 *tops):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 tops- 指向TOPs数组的 指针。
功能:处理
和 排序 超时 周期(TOPs)数组。
dw_wdt_init_timeouts(struct dw_wdt *dw_wdt, struct device *dev):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 dev- 指向device结构体 的 指针。
功能:初始化watchdog设备
的 超时 周期。
dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:初始化debugfs接口。
dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt):参数:
dw_wdt- 指向dw_wdt结构体 的 指针。 功能:清除debugfs接口。
dw_wdt_drv_probe(struct platform_device *pdev):参数:
pdev- 指向platform_device结构体 的 指针。 功能:初始化
平台 设备 驱动。
dw_wdt_drv_remove(struct platform_device *pdev):参数:
pdev- 指向platform_device结构体 的 指针。 功能:卸载
平台 设备 驱动。
Watchdog 测试
注意,在CONFIG_HORIZON_WATCHDOG_ENABLE 需要
# 重新配置 kernel config 文件,使能 CONFIG_HORIZON_WATCHDOG_ENABLE
./bd.sh boot menuconfig
# 重新编译内核镜像
./bd.sh boot
watchdog debugfs
在 watchdog 函数
static const struct debugfs_reg32 dw_wdt_dbgfs_regs[] = {
DW_WDT_DBGFS_REG("cr", WDOG_CONTROL_REG_OFFSET),
DW_WDT_DBGFS_REG("torr", WDOG_TIMEOUT_RANGE_REG_OFFSET),
DW_WDT_DBGFS_REG("ccvr", WDOG_CURRENT_COUNT_REG_OFFSET),
DW_WDT_DBGFS_REG("crr", WDOG_COUNTER_RESTART_REG_OFFSET),
DW_WDT_DBGFS_REG("stat", WDOG_INTERRUPT_STATUS_REG_OFFSET),
DW_WDT_DBGFS_REG("param5", WDOG_COMP_PARAMS_5_REG_OFFSET),
DW_WDT_DBGFS_REG("param4", WDOG_COMP_PARAMS_4_REG_OFFSET),
DW_WDT_DBGFS_REG("param3", WDOG_COMP_PARAMS_3_REG_OFFSET),
DW_WDT_DBGFS_REG("param2", WDOG_COMP_PARAMS_2_REG_OFFSET),
DW_WDT_DBGFS_REG("param1", WDOG_COMP_PARAMS_1_REG_OFFSET),
DW_WDT_DBGFS_REG("version", WDOG_COMP_VERSION_REG_OFFSET),
DW_WDT_DBGFS_REG("type", WDOG_COMP_TYPE_REG_OFFSET)
};
static void dw_wdt_dbgfs_init(struct dw_wdt *dw_wdt)
{
struct device *dev = dw_wdt->wdd.parent;
struct debugfs_regset32 *regset;
regset = devm_kzalloc(dev, sizeof(*regset), GFP_KERNEL);
if (!regset)
return;
regset->regs = dw_wdt_dbgfs_regs;
regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs);
regset->base = dw_wdt->regs;
dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL);
debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset);
}
static void dw_wdt_dbgfs_clear(struct dw_wdt *dw_wdt)
{
debugfs_remove_recursive(dw_wdt->dbgfs_dir);
}
这组registers 的dw_wdt_dbgfs_regs 数组
调试
root@buildroot:/sys/kernel/debug/34250000.watchdog# cat registers
cr = 0x00000019
torr = 0x0000000e
ccvr = 0x29a5f5d9
crr = 0x00000000
stat = 0x00000000
param5 = 0x00000000
param4 = 0x00000000
param3 = 0x00000000
param2 = 0x0000ffff
param1 = 0x10001a50
version = 0x3131332a
type = 0x44570120
watchdog 测例
下面
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // UNIX Standard Function Definitions
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> // file control definition
#include <termios.h> // PPSIX terminal control definition
#include <errno.h> // Error number definition
#include <pthread.h>
#include <linux/watchdog.h>
#include <string.h>
#include <sys/ioctl.h>
int watchdogfd;
int feeddog = 1;
void* feeddogthread()
{
int feeddogvalue;
int returnval;
feeddogvalue = 65535;
while (feeddog) {
printf("feed dog\n");
returnval = write(watchdogfd, &feeddogvalue, sizeof(int));
sleep(5); // Every 5s seconds, the value of the watchdog counter register will be reloaded
}
}
void watchdog_demo_help() {
printf("Usage: watchdog_program [option]\n");
printf("Options:\n");
printf(" h Show this help message\n");
printf(" g Get the currently set watchdog timeout period\n");
printf(" e Exit the watchdog test program\n");
printf(" t Get the remaining time before the watchdog triggers timeout\n");
printf(" r Restart the watchdog\n");
// Add more options here if necessary
}
int main()
{
pthread_t watchdogThd;
//int watchdogfd;
int returnval;
char readline[32], *p;
// open watchdog device
if ((watchdogfd = open("/dev/watchdog", O_RDWR|O_NONBLOCK)) < 0) {
printf("cannot open the watchdog device\n");
exit(0);
}
int timeout = 10; // Warning! The value will be parsed as a hexadecimal number in ioctl.
int timeleft;
ioctl(watchdogfd, WDIOC_SETTIMEOUT, &timeout);
printf("The watchdog timeout was set to %d seconds\n", timeout);
// Creating a dog feeding thread
returnval = pthread_create(&watchdogThd, NULL, feeddogthread, NULL);
if (returnval < 0)
printf("cannot create feeddog thread\n");
while (1) {
printf("Command (e quit): ");
memset(readline, '\0', sizeof(readline));
fgets(readline, sizeof(readline), stdin);
/* Remove the first null character of a string */
p = readline;
while(*p == ' ' || *p == '\t')
p++;
switch(*p) {
case 'h':
watchdog_demo_help(); // Call help function to display options
break;
case 'g':
ioctl(watchdogfd, WDIOC_GETTIMEOUT, &timeout);
printf("The timeout was is %d seconds\n", timeout);
break;
case 'e':
printf("Close watchdog an exit safety!\n");
//write(watchdogfd, "V", 1);
int disable_dog = WDIOS_DISABLECARD;
ioctl(watchdogfd, WDIOC_SETOPTIONS, &disable_dog);
close(watchdogfd);
return 0;
case 's':
printf("stop feed dog\n");
feeddog = 0;
break;
case 't':
ioctl(watchdogfd, WDIOC_GETTIMELEFT, &timeleft);
printf("The timeout was is %d seconds\n", timeleft);
break;
case 'r':
printf("we don't close watchdog. The machine will reboot in a few seconds!\n");
printf("wait......\n");
break;
default:
printf("get error char: %c,it's an invalid option. Type 'h' for help.\n", *p);
}
}
return 0;
}
这/dev/watchdog 来
打开
看门狗 :设备 通过
open("/dev/watchdog", O_RDWR | O_NONBLOCK)打开/dev/watchdog设备,允许读写操作,并 以 非 阻塞 模式 打开。
设置
看门狗 :超时 时间 使用
ioctl(watchdogfd, WDIOC_SETTIMEOUT, &timeout)设置看门狗 的 超时 时间。默认设置 为 10秒。超时后,如果 没有 喂狗,系统 会 触发 重启。
喂狗
线程 :创建
一个 名为 feeddogthread的线程,这个 线程 每 5 秒钟 向 看门狗 设备 写入 一个 值(65535),用于”喂狗”,防止 看门狗 超时 触发 系统 重启。
用户
交互 :命令 主线
程 通过 一个 简单 的 命令行 界面 等待 用户 输入。用户 可以 输入 以下 命令 来 控制 看门狗: h:显示帮助 信息,列出 可用 的 命令。 g:获取当前 设置 的 看门狗 超时 时间。 e:退出程序,并 安全 关闭 看门狗 设备。 s:停止喂狗(通过 设置 feeddog为 0,停止线程 的 循环)。 t:获取当前 剩余 的 看门狗 超时 时间。 r:提示系统 将 重启,但 实际上 并 没有 关闭 看门狗,只是 输出 提示信息。
线程
管理 :pthread_create创建了 一个 独立 的 线程 用于 喂狗 操作。主线 程则 持续 等待 并 处理 用户 输入。
关闭
看门狗 :当
用户 选择 退出 ( e),程序通过 ioctl(watchdogfd, WDIOC_SETOPTIONS, &disable_dog)禁用看门狗,并 关闭 设备 文件。
测例
/opt/arm-gnu-toolchain-11.3.rel1-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc watchdog_demo.c -o watchdog_demo
将watchdog_demo 传到
测例
root@buildroot:/userdata# chmod +x watchdog_demo
root@buildroot:/userdata# ./watchdog_demo
The watchdog timeout was set to 10 seconds
Command (e quit): g
The timeout was is 10 seconds
Command (e quit): t
The timeout was is 7 seconds
Command (e quit): t
The timeout was is 6 seconds
Command (e quit): t
The timeout was is 5 seconds
Command (e quit): feed dog # 喂狗
t
The timeout was is 8 seconds # 看门狗时间重置了,再次从 10 开始倒计时
Command (e quit): t
The timeout was is 7 seconds
模拟
# 停止喂狗,倒计时10秒没有喂狗会导致系统复位
Command (e quit): s
stop feed dog
# 等待重启后,查看复位日志
root@buildroot:~# cd /userdata/log/
root@buildroot:/userdata/log# cat reset_reason.txt
……
# 最后一行就是上一次系统重启的原因
1970-01-01-00-00-03: WATCHDOG normal LNX6.1.83_PL5.1_V1.0.14_20241212-1614 0068
可以reset_reason.txt 中WATCHDOG 的
4.3.13.6. 常见问题
在
Watchdog 未能
触发 复位 问题
描述 :系统没有 在 Watchdog 超时 后 进行 复位,可能 会 导致系统 依然 停留 在 死锁 或 异常 状态。 可能
原因 :Watchdog 驱动
未 :驱动正确 加载 或 配置 可能 未 启动 或 配置 不当。 硬件
故障 :Watchdog 的硬件 可能 存在 问题,导致 它 无法 触发 复位。 内核
配置 :内核问题 未 启用 相关 的 Watchdog 驱动 或 模块。
解决
方法 :检查 Watchdog 驱动
是否 已 正确 加载,查看 dmesg日志确认 是否 有 启动 错误。 确认
硬件 连接 正常,特别 是 与 Watchdog 相关 的 电路 和 芯片。 确认
内核 配置文件( /boot/config-$(uname -r))中是否 启用 了 Watchdog 驱动 模块。
Watchdog 无法
定期 喂狗 问题
描述 :系统未能 按 预期 定期 喂狗,导致 Watchdog 触发 复位。 可能
原因 :定时
任务 :应用程序或 应用程序 未能 及时 喂狗 或 内核模块 没有 按时 喂狗,可能 是 由于 性能 瓶颈、延迟 或 死锁。 Watchdog 计时器
配置 :配置不当 的 超时 时间 过 短,系统 可能 无法 及时 完成 喂狗 操作。
解决
方法 :确保
系统 中 定时 喂狗 的 任务(如 crontab 或 内核 定时器)正常 运行,避免 任务 阻塞 或 长时间 占用 CPU。 增加 Watchdog 的
超时 时间,避免 它 触发 复位。 使用
工具(如 watchdog命令行工具)手动 检查 Watchdog 状态。
Watchdog 导致系统
频繁 重启 问题
描述 :系统被 Watchdog 多次 重启,可能 是 由于 Watchdog 提前 触发 了 复位。 可能
原因 :Watchdog 超时
时间 :超时过 短 时间 过 短,系统 还 未 完成 正常 工作 就 会 触发 复位。 系统资源
消耗 :系统过大 负载 过高,导致 无法 及时 喂狗 或 完成 其他 重要 任务。
解决
方法 :调整 Watchdog 的
超时 时间,使 其 适应 系统 的 响应 时间。 优化
系统 性能,减少 过载 情况,尤其 是 CPU 或 内存 使用 过高 的 情况。 使用
工具 监控 系统 性能,查找 资源 消耗 瓶颈。
Watchdog 监控
的 硬件 或 设备 失效 问题
描述 :Watchdog 硬件本身 出现 故障,导致 无法 正确 监控 系统。 可能
原因 :硬件
损坏 :Watchdog 芯片或 其 相关 电路 损坏。 驱动
不 :使用兼容 的 Watchdog 驱动 与 硬件 不 兼容。
解决
方法 :检查
硬件 连接 和 电源,确保 Watchdog 芯片 和 相关 电路 正常 工作。 更新
或 替换 驱动程序,确保 其 与 硬件 兼容。 使用
其他 工具(如 硬件 诊断 工具)测试 Watchdog 硬件 是否 正常。
Watchdog 被
错误 地 禁用 或 关闭 问题
描述 :Watchdog 驱动被误 禁用,导致 无法 发挥作用。 可能
原因 :用户
或 :可能程序 手动 禁用 Watchdog 是 某些 程序 或 配置 禁用 了 Watchdog 驱动。 内核模块
未 :Watchdog 驱动加载 未 被 加载 到 内核 中。
解决
方法 :检查
/etc/watchdog.conf文件或 其他 配置文件,确保 没有 禁用 Watchdog。 确认 Watchdog 驱动
已 在 内核 中 加载 并 正常 运行。 检查
dmesg日志,查看是否 有 与 Watchdog 相关 的 禁用 或 错误信息。
Watchdog 复位
时 未能 正常 重启 系统 问题
描述 :Watchdog 触发复位 后,系统 未能 如 预期 般 重启,可能 停 在 某个 状态。 可能
原因 :内核
问题 :系统可能 未能 正确处理 复位 请求,导致 无法 完成 复位。 硬件
问题 :复位信号 可能 未 被 硬件 正确 接收 或 处理。
解决
方法 :检查
内核 日志,确认 复位 过程 是否 成功。 确认 Watchdog 复位
信号 与 硬件 是否 正确 连接,确保 系统 可以 正常 重启。 确保
内核 支持 硬件 复位 功能。
Watchdog 与
系统 其他 监控 工具 冲突 问题
描述 :系统上 使用 了 多个 监控 工具,如 systemd的守护 进程、 monit等,它们可能 与 Watchdog 冲突。 可能
原因 :多重
监控 :多个监控 工具 可能 会 尝试 独立 地 重启 系统 或 管理 Watchdog,从而 造成 冲突。
解决
方法 :确保
只 使用 一个 监控 工具 来 管理 Watchdog,避免 冲突。 配置
合适 的 优先级,确保 系统监控 的 一致性。