Sfoglia il codice sorgente

添加看门狗驱动

liu qidong [url ssh://qidong.liu@10.2.90.253:29418/] 3 settimane fa
parent
commit
fe9aeb6980
2 ha cambiato i file con 624 aggiunte e 247 eliminazioni
  1. 306 0
      test_app/watchdog_app.c
  2. 318 247
      watchdog.c

+ 306 - 0
test_app/watchdog_app.c

@@ -0,0 +1,306 @@
+
+/*
+ * it87_wdt_test.c - IT87 看门狗驱动测试程序
+ *
+ * 编译: gcc -o it87_wdt_test it87_wdt_test.c -Wall
+ * 运行: sudo ./it87_wdt_test [选项]
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <linux/watchdog.h>
+
+#define WATCHDOG_DEV "/dev/watchdog"
+#define DEFAULT_KEEPALIVE 5  // 默认喂狗间隔(秒)
+
+static int wdt_fd = -1;
+static volatile int running = 1;
+static int test_mode = 0;      // 0=正常喂狗, 1=停止喂狗测试重启, 2=触发喂狗后退出
+
+/* 信号处理:优雅退出 */
+static void signal_handler(int sig)
+{
+    printf("\n[%d] 收到信号 %d,准备退出...\n", getpid(), sig);
+    running = 0;
+}
+
+/* 打印使用说明 */
+static void print_usage(const char *prog)
+{
+    printf("IT87 看门狗驱动测试程序\n\n");
+    printf("用法: %s [选项]\n\n", prog);
+    printf("选项:\n");
+    printf("  -h           显示帮助\n");
+    printf("  -t <秒>      设置喂狗间隔 (默认: %d秒)\n", DEFAULT_KEEPALIVE);
+    printf("  -T <秒>      设置看门狗超时时间 (需要驱动支持)\n");
+    printf("  -m           测试模式: 启动后停止喂狗,测试系统重启\n");
+    printf("  -e           测试模式: 喂狗一次后立即退出(测试 nowayout)\n");
+    printf("  -i           获取看门狗信息\n");
+    printf("  -c           启用魔法关闭字符 'V'\n");
+    printf("  -d           禁用看门狗(如果支持)\n");
+    printf("\n示例:\n");
+    printf("  %s                    # 正常测试,每5秒喂狗\n", prog);
+    printf("  %s -t 2               # 每2秒喂狗一次\n", prog);
+    printf("  %s -m -t 10          # 10秒后停止喂狗,系统应该重启\n", prog);
+    printf("  %s -e                 # 测试 nowayout 模式\n", prog);
+    printf("\n警告: 使用 -m 或 -e 选项会导致系统重启!\n");
+}
+
+/* 获取看门狗信息 */
+static void get_watchdog_info(int fd)
+{
+    struct watchdog_info info;
+
+    if (ioctl(fd, WDIOC_GETSUPPORT, &info) < 0) {
+        perror("WDIOC_GETSUPPORT 失败");
+        return;
+    }
+
+    printf("\n========== 看门狗信息 ==========\n");
+    printf("标识: %s\n", info.identity);
+    printf("固件版本: %u\n", info.firmware_version);
+    printf("选项支持:\n");
+    if (info.options & WDIOF_OVERHEAT)    printf("  - 过热检测\n");
+    if (info.options & WDIOF_FANFAULT)    printf("  - 风扇故障\n");
+    if (info.options & WDIOF_EXTERN1)     printf("  - 外部信号1\n");
+    if (info.options & WDIOF_EXTERN2)     printf("  - 外部信号2\n");
+    if (info.options & WDIOF_POWERUNDER)  printf("  - 电源不足\n");
+    if (info.options & WDIOF_CARDRESET)   printf("  - 卡片重启\n");
+    if (info.options & WDIOF_POWEROVER)   printf("  - 电源过载\n");
+    if (info.options & WDIOF_SETTIMEOUT)  printf("  - 设置超时时间 ✓\n");
+    if (info.options & WDIOF_MAGICCLOSE)  printf("  - 魔法关闭 ✓\n");
+    if (info.options & WDIOF_PRETIMEOUT)  printf("  - 预超时\n");
+    if (info.options & WDIOF_ALARMONLY)   printf("  - 仅报警\n");
+    if (info.options & WDIOF_KEEPALIVEPING) printf("  - 喂狗支持 ✓\n");
+    printf("================================\n\n");
+}
+
+/* 获取当前超时时间 */
+static int get_timeout(int fd)
+{
+    int timeout = 0;
+    if (ioctl(fd, WDIOC_GETTIMEOUT, &timeout) < 0) {
+        perror("WDIOC_GETTIMEOUT 失败");
+        return -1;
+    }
+    return timeout;
+}
+
+/* 设置超时时间 */
+static int set_timeout(int fd, int timeout)
+{
+    printf("设置超时时间为 %d 秒...\n", timeout);
+    if (ioctl(fd, WDIOC_SETTIMEOUT, &timeout) < 0) {
+        perror("WDIOC_SETTIMEOUT 失败");
+        return -1;
+    }
+    printf("实际设置的超时时间: %d 秒\n", timeout);
+    return 0;
+}
+
+/* 喂狗(keepalive) */
+static int keepalive(int fd)
+{
+    int dummy;
+    if (ioctl(fd, WDIOC_KEEPALIVE, &dummy) < 0) {
+        perror("WDIOC_KEEPALIVE 失败");
+        return -1;
+    }
+    return 0;
+}
+
+/* 禁用看门狗 */
+static int disable_watchdog(int fd)
+{
+    printf("尝试禁用看门狗...\n");
+    /* 方法1: 魔法关闭字符 */
+    if (write(fd, "V", 1) != 1) {
+        perror("魔法关闭字符写入失败");
+        return -1;
+    }
+    printf("已发送魔法关闭字符 'V'\n");
+    return 0;
+}
+
+/* 主测试循环 */
+static void test_loop(int fd, int keepalive_interval, int mode)
+{
+    int count = 0;
+    time_t start_time = time(NULL);
+
+    printf("\n========== 开始测试 ==========\n");
+    printf("PID: %d\n", getpid());
+    printf("喂狗间隔: %d 秒\n", keepalive_interval);
+    printf("当前超时设置: %d 秒\n", get_timeout(fd));
+
+    if (mode == 1) {
+        printf("\n⚠️  测试模式: 将在 %d 秒后停止喂狗,系统应该重启!\n",
+               keepalive_interval * 3);
+    } else if (mode == 2) {
+        printf("\n⚠️  测试模式: 喂狗一次后立即退出,测试 nowayout\n");
+    } else {
+        printf("\n正常模式: 按 Ctrl+C 停止\n");
+    }
+    printf("==============================\n\n");
+
+    /* 第一次喂狗(启动看门狗) */
+    if (keepalive(fd) < 0) {
+        fprintf(stderr, "初始喂狗失败\n");
+        return;
+    }
+    printf("[%3d] 初始喂狗完成,看门狗已启动\n", count++);
+
+    if (mode == 2) {
+        /* 测试模式: 立即退出 */
+        printf("立即退出,如果 nowayout=1,系统将在超时后重启\n");
+        return;
+    }
+
+    /* 主循环 */
+    while (running) {
+        sleep(keepalive_interval);
+
+        if (!running)
+            break;
+
+        /* 测试模式1: 在指定次数后停止喂狗 */
+        if (mode == 1 && count >= 3) {
+            printf("\n⚠️  故意停止喂狗!系统应在 %d 秒后重启...\n",
+                   get_timeout(fd));
+            printf("等待重启中(按 Ctrl+C 可尝试中断)...\n");
+
+            /* 继续循环但不喂狗 */
+            while (1) {
+                sleep(1);
+            }
+        }
+
+        /* 正常喂狗 */
+        if (keepalive(fd) < 0) {
+            fprintf(stderr, "喂狗失败\n");
+            break;
+        }
+
+        time_t elapsed = time(NULL) - start_time;
+        printf("[%3d] 已喂狗 (运行时间: %ld 秒)\n", count++, elapsed);
+        fflush(stdout);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+    int opt;
+    int keepalive_interval = DEFAULT_KEEPALIVE;
+    int new_timeout = 0;
+    int show_info = 0;
+    int magic_close = 0;
+    int disable = 0;
+
+    /* 解析命令行参数 */
+    while ((opt = getopt(argc, argv, "ht:T:meicd")) != -1) {
+        switch (opt) {
+        case 'h':
+            print_usage(argv[0]);
+            return 0;
+        case 't':
+            keepalive_interval = atoi(optarg);
+            if (keepalive_interval < 1) {
+                fprintf(stderr, "错误: 喂狗间隔必须 >= 1 秒\n");
+                return 1;
+            }
+            break;
+        case 'T':
+            new_timeout = atoi(optarg);
+            break;
+        case 'm':
+            test_mode = 1;  // 停止喂狗测试重启
+            break;
+        case 'e':
+            test_mode = 2;  // 立即退出测试 nowayout
+            break;
+        case 'i':
+            show_info = 1;
+            break;
+        case 'c':
+            magic_close = 1;
+            break;
+        case 'd':
+            disable = 1;
+            break;
+        default:
+            print_usage(argv[0]);
+            return 1;
+        }
+    }
+
+    /* 注册信号处理 */
+    signal(SIGINT, signal_handler);
+    signal(SIGTERM, signal_handler);
+
+    printf("IT87 看门狗测试程序\n");
+    printf("打开设备: %s\n", WATCHDOG_DEV);
+
+    /* 打开看门狗设备 */
+    /* O_CLOEXEC: 防止子进程继承,但看门狗通常需要保持打开 */
+    wdt_fd = open(WATCHDOG_DEV, O_WRONLY);
+    if (wdt_fd < 0) {
+        perror("无法打开看门狗设备(需要 root 权限?)");
+        fprintf(stderr, "提示: 尝试 sudo %s\n", argv[0]);
+        return 1;
+    }
+
+    printf("设备打开成功 (fd=%d)\n", wdt_fd);
+
+    /* 获取信息 */
+    if (show_info) {
+        get_watchdog_info(wdt_fd);
+    }
+
+    /* 设置新的超时时间 */
+    if (new_timeout > 0) {
+        if (set_timeout(wdt_fd, new_timeout) < 0) {
+            close(wdt_fd);
+            return 1;
+        }
+    }
+
+    /* 显示当前超时 */
+    int current_timeout = get_timeout(wdt_fd);
+    if (current_timeout > 0) {
+        printf("当前看门狗超时: %d 秒\n", current_timeout);
+    }
+
+    /* 禁用看门狗 */
+    if (disable) {
+        if (disable_watchdog(wdt_fd) < 0) {
+            fprintf(stderr, "禁用看门狗失败(可能 nowayout=1)\n");
+        }
+        close(wdt_fd);
+        return 0;
+    }
+
+    /* 主测试循环 */
+    test_loop(wdt_fd, keepalive_interval, test_mode);
+
+    /* 清理 */
+    printf("\n关闭看门狗设备...\n");
+
+    if (magic_close) {
+        /* 尝试魔法关闭 */
+        printf("发送魔法关闭字符 'V'...\n");
+        if (write(wdt_fd, "V", 1) != 1) {
+            perror("魔法关闭失败");
+        }
+    }
+
+    close(wdt_fd);
+    printf("测试程序结束\n");
+
+    return 0;
+}

+ 318 - 247
watchdog.c

@@ -1,279 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *	Watchdog Timer Driver
+ *	   for ITE IT87xx Environment Control - Low Pin Count Input / Output
+ *
+ *	(c) Copyright 2007  Oliver Schuster <olivers137@aol.com>
+ *
+ *	Based on softdog.c	by Alan Cox,
+ *		 83977f_wdt.c	by Jose Goncalves,
+ *		 it87.c		by Chris Gauthron, Jean Delvare
+ *
+ *	Data-sheets: Publicly available at the ITE website
+ *		    http://www.ite.com.tw/
+ *
+ *	Support of the watchdog timers, which are available on
+ *	IT8607, IT8620, IT8622, IT8625, IT8628, IT8655, IT8665, IT8686,
+ *	IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726, IT8728,
+ *	IT8772, IT8783 and IT8784.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/cdev.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-#include <linux/device.h>
-#include <linux/stat.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/watchdog.h>
+
+#define WATCHDOG_NAME		"IT87 WDT"
+
+/* Defaults for Module Parameter */
+#define DEFAULT_TIMEOUT		60
+#define DEFAULT_TESTMODE	0
+#define DEFAULT_NOWAYOUT	WATCHDOG_NOWAYOUT
+
+/* IO Ports */
+#define REG		0x4e
+#define VAL		0x4f
+
+/* Logical device Numbers LDN */
+#define GPIO		0x07
+
+/* Configuration Registers and Functions */
+#define LDNREG		0x07
+#define CHIPID		0x20
+#define CHIPREV		0x22
+
+/* Chip Id numbers */
+#define NO_DEV_ID	0xffff
+#define IT8607_ID	0x8607
+#define IT8620_ID	0x8620
+#define IT8622_ID	0x8622
+#define IT8625_ID	0x8625
+#define IT8628_ID	0x8628
+#define IT8655_ID	0x8655
+#define IT8665_ID	0x8665
+#define IT8686_ID	0x8686
+#define IT8702_ID	0x8702
+#define IT8705_ID	0x8705
+#define IT8712_ID	0x8712
+#define IT8716_ID	0x8716
+#define IT8718_ID	0x8718
+#define IT8720_ID	0x8720
+#define IT8721_ID	0x8721
+#define IT8726_ID	0x8726	/* the data sheet suggest wrongly 0x8716 */
+#define IT8728_ID	0x8728
+#define IT8772_ID	0x8772
+#define IT8783_ID	0x8783
+#define IT8784_ID	0x8784
+#define IT8786_ID	0x8786
+
+/* GPIO Configuration Registers LDN=0x07 */
+#define WDTCTRL		0x71
+#define WDTCFG		0x72
+#define WDTVALLSB	0x73
+#define WDTVALMSB	0x74
+
+/* GPIO Bits WDTCFG */
+#define WDT_TOV1	0x80
+#define WDT_KRST	0x40
+#define WDT_TOVE	0x20
+#define WDT_PWROK	0x10 /* not in it8721 */
+#define WDT_INT_MASK	0x0f
+
+static unsigned int max_units, chip_type;
+
+static unsigned int timeout = DEFAULT_TIMEOUT;
+static int testmode = DEFAULT_TESTMODE;
+static bool nowayout = DEFAULT_NOWAYOUT;
+
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
+		__MODULE_STRING(DEFAULT_TIMEOUT));
+module_param(testmode, int, 0);
+MODULE_PARM_DESC(testmode, "Watchdog test mode (1 = no reboot), default="
+		__MODULE_STRING(DEFAULT_TESTMODE));
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
+		__MODULE_STRING(WATCHDOG_NOWAYOUT));
+
+/* Superio Chip */
+
+static inline int superio_enter(void)
+{
+	/*
+	 * Try to reserve REG and REG + 1 for exclusive access.
+	 */
+	if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
+		return -EBUSY;
+
+	outb(0x87, REG);
+	outb(0x01, REG);
+	outb(0x55, REG);
+	outb(0xaa, REG);
+	return 0;
+}
 
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/workqueue.h>
+static inline void superio_exit(void)
+{
+	outb(0x02, REG);
+	outb(0x02, VAL);
+	release_region(REG, 2);
+}
 
-#include <linux/io.h>
-#include "gpioregs.h"
-
-#define DEVICE_NAME "watchdog"
-#define CLASS_NAME "watchdog_class"
-#define BUFFER_SIZE 1024
-
-// 可以通过模块参数指定主设备号和次设备号
-static int watchdog_major = 56; // 默认主设备号
-static int watchdog_minor = 30;   // 默认次设备号
-module_param(watchdog_major, int, S_IRUGO);
-module_param(watchdog_minor, int, S_IRUGO);
-MODULE_PARM_DESC(watchdog_major, "Major device number");
-MODULE_PARM_DESC(watchdog_minor, "Minor device number");
-
-static struct class *char_class = NULL;
-static struct device *char_device = NULL;
-static struct cdev my_cdev;
-static dev_t dev_num;
-
-// 设备结构体
-struct watchdog_dev
+static inline void superio_select(int ldn)
 {
-    char *buffer;
-    size_t size;
-    struct mutex lock;
-    struct cdev cdev;
-    struct delayed_work delay_work1;
-};
+	outb(LDNREG, REG);
+	outb(ldn, VAL);
+}
 
-static struct watchdog_dev *dev = NULL;
+static inline int superio_inb(int reg)
+{
+	outb(reg, REG);
+	return inb(VAL);
+}
 
-static void delay_work_func(struct work_struct *work)
-{    
-    printk(KERN_INFO "delay_work_func\n");
+static inline void superio_outb(int val, int reg)
+{
+	outb(reg, REG);
+	outb(val, VAL);
+}
+
+static inline int superio_inw(int reg)
+{
+	int val;
+	outb(reg++, REG);
+	val = inb(VAL) << 8;
+	outb(reg, REG);
+	val |= inb(VAL);
+	return val;
 }
 
-// 文件打开操作
-static int watchdog_open(struct inode *inode, struct file *filp)
+/* Internal function, should be called after superio_select(GPIO) */
+static void _wdt_update_timeout(unsigned int t)
 {
-    struct watchdog_dev *dev;
+	unsigned char cfg = WDT_KRST;
 
-    dev = container_of(inode->i_cdev, struct watchdog_dev, cdev);
-    filp->private_data = dev;
+	if (testmode)
+		cfg = 0;
 
-    printk(KERN_INFO "watchdog: Device opened (major=%d, minor=%d)\n",
-           imajor(inode), iminor(inode));
-    return 0;
+	if (t <= max_units)
+		cfg |= WDT_TOV1;
+	else
+		t /= 60;
+
+	if (chip_type != IT8721_ID)
+		cfg |= WDT_PWROK;
+
+	superio_outb(cfg, WDTCFG);
+	superio_outb(t, WDTVALLSB);
+	if (max_units > 255)
+		superio_outb(t >> 8, WDTVALMSB);
 }
 
-// 文件释放操作
-static int watchdog_release(struct inode *inode, struct file *filp)
+static int wdt_update_timeout(unsigned int t)
 {
-    // release_region(PORT_80, 1);
-    printk(KERN_INFO "watchdog: Device closed\n");
-    return 0;
+	int ret;
+
+	ret = superio_enter();
+	if (ret)
+		return ret;
+
+	superio_select(GPIO);
+	_wdt_update_timeout(t);
+	superio_exit();
+
+	return 0;
 }
 
-// 读操作 - 支持cat命令
-static ssize_t watchdog_read(struct file *filp, char __user *buf,
-                             size_t count, loff_t *f_pos)
+static int wdt_round_time(int t)
+{
+	t += 59;
+	t -= t % 60;
+	return t;
+}
+
+/* watchdog timer handling */
+
+static int wdt_start(struct watchdog_device *wdd)
 {
-    struct watchdog_dev *dev = filp->private_data;
-    ssize_t retval = 0;
-    size_t available;
-    int read_count = 0;
-    int ret = 0;
-
-    if (mutex_lock_interruptible(&dev->lock))
-        return -ERESTARTSYS;
-
-    // 这里read_count没有固定为4个字节,是为了便于后续扩展
-    if (count > dev->size)
-    {
-        read_count = dev->size;
-    }
-    else
-    {
-        read_count = count;
-    }
-    ret = copy_to_user(buf, dev->buffer, read_count);
-    if (ret != 0)
-    {
-        printk(KERN_INFO "watchdog: copy_to_user failed\n");
-        goto out;
-    }
-
-    printk(KERN_INFO "watchdog: Read %zu bytes\n", count);
-
-out:
-    mutex_unlock(&dev->lock);
-    return ret;
+	return wdt_update_timeout(wdd->timeout);
 }
 
-// 写操作
-static ssize_t watchdog_write(struct file *filp, const char __user *buf,
-                              size_t count, loff_t *f_pos)
+static int wdt_stop(struct watchdog_device *wdd)
 {
-    struct watchdog_dev *dev = filp->private_data;
-    ssize_t retval = 0;
-    size_t available;
-    int ret = 0;
-
-    // 加锁
-    if (mutex_lock_interruptible(&dev->lock))
-    {
-        return -ERESTARTSYS;
-    }
-
-    // 计算可写数据量
-    if (count > BUFFER_SIZE)
-    {
-        count = BUFFER_SIZE;
-    }
-
-    // 拷贝数据
-    ret = copy_from_user(dev->buffer, buf, count);
-    if (ret != 0)
-    {
-        printk(KERN_INFO "watchdog: copy_from_user failed\n");
-        goto out;
-    }
-
-    dev->size = count;
-
-    printk(KERN_INFO "watchdog: Written %zu bytes\n", count);
-
-out:
-    mutex_unlock(&dev->lock);
-
-    // 调度延迟工作
-    if (ret == 0)
-    {
-        schedule_delayed_work(&dev->delay_work1, msecs_to_jiffies(10));
-    }
-    return count;
+	return wdt_update_timeout(0);
+}
+
+/**
+ *	wdt_set_timeout - set a new timeout value with watchdog ioctl
+ *	@t: timeout value in seconds
+ *
+ *	The hardware device has a 8 or 16 bit watchdog timer (depends on
+ *	chip version) that can be configured to count seconds or minutes.
+ *
+ *	Used within WDIOC_SETTIMEOUT watchdog device ioctl.
+ */
+
+static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
+{
+	int ret = 0;
+
+	if (t > max_units)
+		t = wdt_round_time(t);
+
+	wdd->timeout = t;
+
+	if (watchdog_hw_running(wdd))
+		ret = wdt_update_timeout(t);
+
+	return ret;
 }
 
-// 文件操作结构体
-static struct file_operations fops = {
-    .owner = THIS_MODULE,
-    .open = watchdog_open,
-    .release = watchdog_release,
-    .read = watchdog_read,
-    .write = watchdog_write,
+static const struct watchdog_info ident = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
+	.firmware_version = 1,
+	.identity = WATCHDOG_NAME,
+};
+
+static const struct watchdog_ops wdt_ops = {
+	.owner = THIS_MODULE,
+	.start = wdt_start,
+	.stop = wdt_stop,
+	.set_timeout = wdt_set_timeout,
+};
+
+static struct watchdog_device wdt_dev = {
+	.info = &ident,
+	.ops = &wdt_ops,
+	.min_timeout = 1,
 };
 
-// 模块初始化
 int watchdog_init(void)
 {
-    int result;
-
-    printk(KERN_INFO "watchdog: Initializing driver with major=%d, minor=%d\n",
-           watchdog_major, watchdog_minor);
-
-    // 检查主设备号是否有效
-    if (watchdog_major <= 0)
-    {
-        printk(KERN_ALERT "watchdog: Invalid major number %d\n", watchdog_major);
-        return -EINVAL;
-    }
-
-    // 构建设备号
-    dev_num = MKDEV(watchdog_major, watchdog_minor);
-
-    // 注册设备号 - 使用指定的主设备号
-    result = register_chrdev_region(dev_num, 1, DEVICE_NAME);
-    if (result < 0)
-    {
-        printk(KERN_ALERT "watchdog: Failed to register major number %d\n", watchdog_major);
-        printk(KERN_ALERT "watchdog: Try using a different major number\n");
-        return result;
-    }
-
-    printk(KERN_INFO "watchdog: Registered with major=%d, minor=%d\n",
-           MAJOR(dev_num), MINOR(dev_num));
-
-    // 分配设备结构体
-    dev = kmalloc(sizeof(struct watchdog_dev), GFP_KERNEL);
-    if (!dev)
-    {
-        result = -ENOMEM;
-        goto fail_malloc;
-    }
-    memset(dev, 0, sizeof(struct watchdog_dev));
-
-    // 分配缓冲区
-    dev->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);
-    if (!dev->buffer)
-    {
-        result = -ENOMEM;
-        goto fail_buffer;
-    }
-
-    // 初始化互斥锁
-    mutex_init(&dev->lock);
-
-    INIT_DELAYED_WORK(&dev->delay_work1, delay_work_func);
-
-    // 初始化字符设备
-    cdev_init(&dev->cdev, &fops);
-    dev->cdev.owner = THIS_MODULE;
-
-    // 添加字符设备到系统
-    result = cdev_add(&dev->cdev, dev_num, 1);
-    if (result)
-    {
-        printk(KERN_ALERT "watchdog: Failed to add cdev\n");
-        goto fail_cdev;
-    }
-
-    // 创建设备类
-    char_class = class_create(THIS_MODULE, CLASS_NAME);
-    if (IS_ERR(char_class))
-    {
-        result = PTR_ERR(char_class);
-        printk(KERN_ALERT "watchdog: Failed to create class\n");
-        goto fail_class;
-    }
-
-    // 创建设备
-    char_device = device_create(char_class, NULL, dev_num, NULL, DEVICE_NAME);
-    if (IS_ERR(char_device))
-    {
-        result = PTR_ERR(char_device);
-        printk(KERN_ALERT "watchdog: Failed to create device\n");
-        goto fail_device;
-    }
-
-    printk(KERN_INFO "watchdog: Driver initialized successfully\n");
-    printk(KERN_INFO "watchdog: Device node: /dev/%s (major=%d, minor=%d)\n",
-           DEVICE_NAME, watchdog_major, watchdog_minor);
-    return 0;
-
-fail_device:
-    class_destroy(char_class);
-fail_class:
-    cdev_del(&dev->cdev);
-fail_cdev:
-    kfree(dev->buffer);
-fail_buffer:
-    kfree(dev);
-fail_malloc:
-    unregister_chrdev_region(dev_num, 1);
-    return result;
+	u8  chip_rev;
+	int rc;
+
+	rc = superio_enter();
+	if (rc)
+		return rc;
+
+	chip_type = superio_inw(CHIPID);
+    // chip_type = 0x8786;
+	chip_rev  = superio_inb(CHIPREV) & 0x0f;
+	superio_exit();
+
+	switch (chip_type) {
+	case IT8702_ID:
+		max_units = 255;
+		break;
+	case IT8712_ID:
+		max_units = (chip_rev < 8) ? 255 : 65535;
+		break;
+	case IT8716_ID:
+	case IT8726_ID:
+		max_units = 65535;
+		break;
+	case IT8607_ID:
+	case IT8620_ID:
+	case IT8622_ID:
+	case IT8625_ID:
+	case IT8628_ID:
+	case IT8655_ID:
+	case IT8665_ID:
+	case IT8686_ID:
+	case IT8718_ID:
+	case IT8720_ID:
+	case IT8721_ID:
+	case IT8728_ID:
+	case IT8772_ID:
+	case IT8783_ID:
+	case IT8784_ID:
+	case IT8786_ID:
+		max_units = 65535;
+		break;
+	case IT8705_ID:
+		pr_err("Unsupported Chip found, Chip %04x Revision %02x\n",
+		       chip_type, chip_rev);
+		return -ENODEV;
+	case NO_DEV_ID:
+		pr_err("no device\n");
+		return -ENODEV;
+	default:
+		pr_err("Unknown Chip found, Chip %04x Revision %04x\n",
+		       chip_type, chip_rev);
+		return -ENODEV;
+	}
+
+	rc = superio_enter();
+	if (rc)
+		return rc;
+
+	superio_select(GPIO);
+	superio_outb(WDT_TOV1, WDTCFG);
+	superio_outb(0x00, WDTCTRL);
+	superio_exit();
+
+	if (timeout < 1 || timeout > max_units * 60) {
+		timeout = DEFAULT_TIMEOUT;
+		pr_warn("Timeout value out of range, use default %d sec\n",
+			DEFAULT_TIMEOUT);
+	}
+
+	if (timeout > max_units)
+		timeout = wdt_round_time(timeout);
+
+	wdt_dev.timeout = timeout;
+	wdt_dev.max_timeout = max_units * 60;
+
+	watchdog_stop_on_reboot(&wdt_dev);
+	rc = watchdog_register_device(&wdt_dev);
+	if (rc) {
+		pr_err("Cannot register watchdog device (err=%d)\n", rc);
+		return rc;
+	}
+
+	pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d)\n",
+		chip_type, chip_rev, timeout, nowayout, testmode);
+
+	return 0;
 }
 
-// 模块退出
 void watchdog_exit(void)
 {
-    cancel_delayed_work_sync(&dev->delay_work1);
-    device_destroy(char_class, dev_num);
-    class_destroy(char_class);
-
-    if (dev)
-    {
-        cdev_del(&dev->cdev);
-        if (dev->buffer)
-            kfree(dev->buffer);
-        kfree(dev);
-    }
-
-    unregister_chrdev_region(dev_num, 1);
-    printk(KERN_INFO "watchdog: Driver removed (major=%d, minor=%d)\n",
-           watchdog_major, watchdog_minor);
+	watchdog_unregister_device(&wdt_dev);
 }
+
+