硬件看门狗MAX6369设置—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
灯的抢脚见过了。今天看外部看门狗:软件卡死时它负责把整机拉起来,缺一行配置会变成隔几分钟静默重启。
桌面起来了,什么都没做,大约三分钟整板复位。串口没有 panic,没有 sysrq,没有 reboot: Restarting system。U-Boot 又从头跑。这种「静默复位」不是内核内部 dw_wdt 的 44 秒,是底板上那颗 MAX6369 在拉 RESET。
MAX6369 是外部芯片。WDI 接到 GPIO0_C2。内核要用 gpio-wdt 按芯片要求的方式翻转这根脚,芯片才认为「还活着」。设备树里 必须 有 hw_algo = "toggle"。缺这一行,gpio_wdt_probe() 读属性失败,返回 -EINVAL,也就是 -22,驱动不绑定,没人喂 WDI。芯片按自己的超时把整板复位。这块板拨码默认档,现场测到大约 188 秒。
拨码拨到「接入看门狗」,故意把应用挂死,超过 4 分钟不复位,才说明内核在喂。关拨码等于把芯片电气上拿掉,再挂 10 分钟不复位也不能证明驱动好。
产品 rk3568_evb:
device/board/rk/rk3568_evb/kernel/rk3568-evb-linux.dts


官方流程图是「打开设备 → 设超时 → 周期喂狗 → 关掉」。外部这颗在 always-running 下,内核 probe 成功就开始喂,用户态不必打开 /dev/watchdog。图上的 open/ioctl 路径留给内部 dw_wdt,或者留给你故意去掉 always-running、改成用户态守护的演示。
1. 外部芯片和内部控制器不是同一条狗
RK3568 内部有 dw_wdt,会出 /dev/watchdog0 一类节点,超时常见 44 秒,而且常常被 watchdog_service 喂着。你看到 /dev/watchdog 存在,不能说明 MAX6369 在工作。
外部 MAX6369:
内核 gpio-wdt ──toggle──► GPIO0_C2 (WDI)
│
MAX6369
│ RESET 拉 SoC
特征:
- 整板复位,dmesg 来不及留下「我要重启了」
- 间隔大约 188 秒(随拨码超时档变,不是 dw_wdt 的 44)
dmesg | grep gpio-wdt若有probe ... error -22,就是没绑上
hdc shell "ls -l /dev/watchdog*"
hdc shell "cat /sys/class/watchdog/watchdog*/identity"
hdc shell "dmesg | grep -iE 'gpio-wdt|wdt-gpio|watchdog'"
identity 里能看到 GPIO Watchdog 才是这颗。dw_wdt 是另一条。板上可能同时有三个节点 watchdog、watchdog0、watchdog1,按 identity 认,不要按序号猜。
拨码在底板,丝印常是 WDT ON/OFF。关掉之后 WDI 悬空或芯片不参与复位,软件怎么配都不会 188 秒死一次。验收必须拨到开。
Linux 编号:GPIO0_C2 = 0×32 + 16 + 2 = 18。
hdc shell "cat /sys/kernel/debug/gpio" | grep gpio-18
绑上之后 consumer 是 wdt-gpio 或 gpio-wdt。
2. 驱动认什么属性
drivers/watchdog/gpio_wdt.c probe 开头:
ret = of_property_read_string(np, "hw_algo", &algo);
if (ret)
return ret;
if (!strcmp(algo, "toggle")) {
priv->hw_algo = HW_ALGO_TOGGLE;
gflags = GPIOD_IN;
} else if (!strcmp(algo, "level")) {
priv->hw_algo = HW_ALGO_LEVEL;
gflags = GPIOD_OUT_LOW;
} else {
return -EINVAL;
}
of_property_read_string 找不到属性就返回 -EINVAL(-22)。dmesg:
gpio-wdt: probe of wdt-gpio failed with error -22
MAX6369 的 WDI 是 边沿复位计时:每次高低跳变清零内部计数器。对应算法是 toggle,不是 level(level 是拉高一小段再拉低,给另一类芯片)。toggle 下 GPIO 极性几乎无所谓,驱动自己翻 priv->state。
hw_margin_ms 是硬件超时的安全边距。驱动把它填进 max_hw_heartbeat_ms。内核看门狗核心会在这个时间之内调用 ping。本板写 600:大约每 300~600 ms toggle 一次 WDI,远小于 188 秒,足够。
always-running 是布尔属性。probe 末尾:
priv->always_running = of_property_read_bool(np, "always-running");
...
if (priv->always_running)
gpio_wdt_start(&priv->wdd);
写成 always-enabled 驱动不认,等于没开,用户态若不 open /dev/watchdog,仍然没人喂——表现又回到 188 秒复位。属性名必须是 always-running。
ping 对 toggle:
case HW_ALGO_TOGGLE:
priv->state = !priv->state;
gpiod_set_value_cansleep(priv->gpiod, priv->state);
break;
内核在喂的时候,示波器看 GPIO0_C2 是方波,周期约几百毫秒。没有方波:要么没 probe,要么拨码把 WDI 隔开了。
stop 在 always-running 时不会把脚改成输入去停喂,只把 WDOG_HW_RUNNING 再置上。所以用户态 close /dev/watchdog 也停不掉这颗,这是属性的本意。
内核配置:
CONFIG_WATCHDOG=y
CONFIG_GPIO_WATCHDOG=y
CONFIG_WATCHDOG_HANDLE_BOOT_ENABLED=y
HANDLE_BOOT_ENABLED 让启动早期也维持喂,避免刚进内核、用户态还没起来的那一段被芯片咬死。
3. 设备树
wdt_gpio: wdt-gpio {
compatible = "linux,wdt-gpio";
gpios = <&gpio0 RK_PC2 GPIO_ACTIVE_LOW>;
hw_algo = "toggle";
hw_margin_ms = <600>;
always-running;
};
&pinctrl {
ext_wdt {
ext_wdt_wdi: ext-wdt-wdi {
rockchip,pins = <0 RK_PC2 RK_FUNC_GPIO &pcfg_pull_up>;
};
};
};
WDI = GPIO0_C2。pinctrl 强制 GPIO,不要停在别的复用。SET 脚(超时档)有的板接到 GPIO4_C0,由拨码或硬件电阻决定超时,软件不必去 bit-bang SET,除非你要改档。
compatible 必须是 linux,wdt-gpio。写成 gpio-wdt 对不上 of_device_id。
改完刷 resource + boot_linux。
hdc shell "cat /proc/partitions"
hdc shell "dd if=/data/resource.img of=/dev/block/mmcblk1p4 bs=4M conv=fsync"
hdc shell "dd if=/data/boot_linux.img of=/dev/block/mmcblk1p5 bs=4M conv=fsync"
of= 必须 /dev/block/...。
4. 怎么证明内核在喂
拨码打开。不要杀 init,不要 reboot -f。只要桌面挂着,超过 4 分钟 不复位,外部芯片就被喂着。188 秒那一档如果没人喂,四分钟之内一定已经死过一回。
对照实验(会复位,只在你能接受重启时做):
- 临时把节点
status = "disabled",刷进去,拨码开,等 188 秒级复位——证明芯片和拨码是活的。 - 再打开节点并带齐三个属性,同样拨码,挂 4 分钟不死——证明驱动在喂。
不要用「关拨码」当对照,那只能证明拨码能把芯片拿掉。
看方波:逻辑分析仪或示波器探 GPIO0_C2,toggle 在跳。没有仪器时,4 分钟挂机就是仪器。
hdc shell "cat /sys/class/watchdog/watchdog*/identity"
hdc shell "cat /sys/class/watchdog/watchdog*/state"
不要 在 always-running 已经生效时再写一个用户态 while sleep; ioctl KEEPALIVE 抢同一颗,除非你拿掉 always-running 做「杀守护就复位」的演示。两套同时喂也无害,但验收说不清是谁在喂。
内部 dw_wdt 被 watchdog_service 喂着,44 秒不对齐现场 188 秒。排查静默复位时先问间隔:不到一分钟偏内部,三分多钟偏 MAX6369。
5. 完整 C:只查询,不抢喂
always-running 下用户态打开 /dev/watchdog 要小心 nowayout:有的配置 close 而不写 V 会被当成停喂。查询用 sysfs,不要随便 open。
/* wdt_info.c
* armv7-unknown-linux-ohos-clang --target=arm-linux-ohos \
* --sysroot=${OHOS_NATIVE}/sysroot \
* -o wdt_info wdt_info.c \
* -Wl,--dynamic-linker=/system/lib/ld-musl-arm.so.1
*
* hdc file send wdt_info /data/wdt_info
* hdc shell "chmod 755 /data/wdt_info; /data/wdt_info"
*/
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
static void dump_file(const char *path)
{
char buf[128];
int fd = open(path, O_RDONLY);
int n;
if (fd < 0) {
return;
}
n = (int)read(fd, buf, sizeof(buf) - 1);
close(fd);
if (n > 0) {
buf[n] = 0;
printf(" %s = %s", path, buf);
if (buf[n - 1] != '\n') {
putchar('\n');
}
}
}
int main(void)
{
DIR *d = opendir("/sys/class/watchdog");
struct dirent *e;
int gpio_wdt = 0;
if (!d) {
fprintf(stderr, "没有 /sys/class/watchdog。CONFIG_WATCHDOG?\n");
return 1;
}
while ((e = readdir(d)) != NULL) {
char p[192], id[64];
int fd, n;
if (e->d_name[0] == '.') {
continue;
}
snprintf(p, sizeof(p), "/sys/class/watchdog/%s/identity", e->d_name);
fd = open(p, O_RDONLY);
if (fd < 0) {
continue;
}
n = (int)read(fd, id, sizeof(id) - 1);
close(fd);
if (n <= 0) {
continue;
}
id[n] = 0;
printf("[%s] %s", e->d_name, id);
if (strstr(id, "GPIO")) {
gpio_wdt = 1;
snprintf(p, sizeof(p), "/sys/class/watchdog/%s/state", e->d_name);
dump_file(p);
snprintf(p, sizeof(p), "/sys/class/watchdog/%s/timeout", e->d_name);
dump_file(p);
}
}
closedir(d);
if (!gpio_wdt) {
fprintf(stderr,
"没有 identity=GPIO Watchdog。\n"
"dmesg 搜 gpio-wdt 和 error -22,补 hw_algo=toggle\n");
return 2;
}
printf("拨码打开,挂机 >4 min 不复位 = 内核在喂 WDI。\n");
printf("约 188s 就重启 = 没喂上,不是 dw_wdt 的 44s。\n");
return 0;
}
若要做「停喂就复位」演示,去掉 dts 的 always-running,用户态周期 ioctl(fd, WDIOC_KEEPALIVE, 0),正常退出前 write(fd, "V", 1)。X 用 identity 对上 GPIO 的那一个。杀这个进程后,MAX6369 按拨码超时复位。always-running 还在时杀用户态进程不会复位。
6. ArkTS 页面只做状态,不负责喂狗
import evbio from 'libevbio.so';
@Entry
@Component
struct GpioWdt {
@State id: string = '';
@State hint: string = '拨码打开。内核 always-running 在喂,页面不 ioctl。';
build() {
Column() {
Text('MAX6369 / gpio-wdt').fontSize(22).fontWeight(FontWeight.Bold)
Text('identity: ' + this.id).margin({ top: 12 }).fontSize(16)
Text(this.hint).margin({ top: 8 }).fontSize(14)
Button('刷新').onClick(() => { this.refresh(); }).margin({ top: 16 })
}.padding(20)
}
refresh() {
let found: string = '';
for (let i = 0; i < 4; i++) {
let base: string = '/sys/class/watchdog/watchdog' + i.toString();
let s: string = evbio.readText(base + '/identity');
if (s.indexOf('GPIO') >= 0) {
found = s.replace('\n', '');
break;
}
}
if (found.length === 0) {
this.id = '(没有 GPIO Watchdog)';
this.hint = 'probe -22 的话补 hw_algo=toggle,属性名是 always-running 不是 always-enabled';
} else {
this.id = found;
this.hint = '拨码开 + 挂机超过 4 分钟不复位,即内核在喂。约 188 秒就重启是没喂上。';
}
}
aboutToAppear() {
this.refresh();
}
}
页面上不要放「开始喂狗」按钮,除非 dts 已经拿掉 always-running。否则按钮按下去你以为是应用在救机器,其实一直是内核在救。
7. 现象对照
| 现象 | 原因 | 处理 |
|---|---|---|
| ~188 s 静默复位 | gpio-wdt 没绑,芯片自己计时 | 补 `hw_algo="toggle"` |
| probe error -22 | 缺 hw_algo,或写成了别的字符串 | 只能是 toggle 或 level;本芯片用 toggle |
| 有 /dev/watchdog 仍 188 s 复位 | 那是 dw_wdt,不是这颗 | identity 找 GPIO Watchdog |
| 写了 always-enabled 仍复位 | 驱动认 always-running | 改属性名 |
| 关拨码就不复位 | 芯片没接入 | 验收必须拨开 |
| 挂 4 min 不复位 | 内核在喂 | 通过 |
| 用户态守护杀掉才复位 | 你拿掉了 always-running | 演示路径,和默认交付不同 |
| 44 s 复位且有 panic/log | 内部 dw_wdt / 别的原因 | 不要和 MAX6369 混 |
flowchart TD
A["约 188s 静默复位"] --> B{dmesg gpio-wdt -22?}
B -->|是| C[补 hw_algo=toggle]
B -->|否, 有 GPIO Watchdog| D{拨码开了?}
D -->|否| E[拨开再验]
D -->|是仍复位| F[always-running 写成了 always-enabled?]
F --> G[改属性名, 刷 resource]
B -->|否, 只有 dw_wdt| H[节点 compatible / GPIO0_C2 / CONFIG_GPIO_WATCHDOG]
外部看门狗靠 linux,wdt-gpio + hw_algo=toggle + hw_margin_ms=600 + always-running。缺算法就 -22,没人喂,大约 188 秒硬复位。拨码打开、挂机超过 4 分钟还活着,内核就在喂。
系列第 27 篇 · 芯片:瑞芯微 RK3568 · OpenHarmony 4.1(API 11) · Linux 5.10
更多推荐

所有评论(0)