#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <linux/ktime.h>
#include <linux/reboot.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
// 看门狗寄存器定义
#define INTERNAL_DOG_BASE_ADDR 0xaaaa0000
#define INTERNAL_DOG_OFFSET 0x1234
#define FEED_DOG_BASE_ADDR 0x1a000
#define FEED_DOG_OFFSET 0x011
#define WCV_L_OFFSET 0x00042
#define WCV_H_OFFSET 0x00023
#define WOR_OFFSET 0x00065
#define WRR_OFFSET 0x10011
#define WCS_REG_OFFSET 0x0
// 看门狗状态位
#define WDT_WS0 (1 << 0)
#define WDT_WS1 (1 << 1)
// 看门狗使能寄存器
static unsigned long wdt_is_open;
static char expect_close;
static int heartbeat = 60;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default=60)");
static bool nowayout = false;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=0)");
struct wdt_device {
struct watchdog_device wdd;
struct device *dev;
void __iomem *base;
struct clk *clk;
int irq;
unsigned long status;
bool nowayout;
u32 wdog_offset_value;
u64 last_feed_time;
u64 timeout_ns;
u64 last_feed;
u32 wcs_value;
u32 wor_value;
spinlock_t lock; // 添加自旋锁保护共享资源
};
static int wdog_start(struct watchdog_device *wdd)
{
struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd);
iowrite32(0x1, wd->base + INTERNAL_DOG_OFFSET);
return 0;
}
static int wdog_stop(struct watchdog_device *wdd)
{
struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd);
iowrite32(0x0, wd->base + INTERNAL_DOG_OFFSET);
return 0;
}
static int wdog_feed(struct watchdog_device *wdd)
{
u64 current_time;
u64 new_compare_value;
unsigned long flags;
struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd);
spin_lock_irqsave(&wd->lock, flags);
iowrite32(0x1, wd->base + FEED_DOG_OFFSET);
current_time = ktime_get_ns();
new_compare_value = current_time + wd->wdog_offset_value;
iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET);
wd->last_feed_time = current_time;
spin_unlock_irqrestore(&wd->lock, flags);
return 0;
}
static u32 read_WRR(struct wdt_device *wd)
{
u32 wrr_value;
u64 current_time;
u64 new_compare_value;
unsigned long flags;
spin_lock_irqsave(&wd->lock, flags);
wrr_value = ioread32(wd->base + WRR_OFFSET);
current_time = ktime_get_ns();
new_compare_value = current_time + wd->wdog_offset_value;
iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET);
wd->last_feed_time = current_time;
spin_unlock_irqrestore(&wd->lock, flags);
return wrr_value;
}
static void write_WOR(struct wdt_device *wd, u32 value)
{
u64 current_time;
u64 new_compare_value;
unsigned long flags;
spin_lock_irqsave(&wd->lock, flags);
iowrite32(value, wd->base + WOR_OFFSET);
wd->wor_value = value;
current_time = ktime_get_ns();
new_compare_value = current_time + wd->wdog_offset_value;
iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET);
wd->last_feed_time = current_time;
spin_unlock_irqrestore(&wd->lock, flags);
}
static void write_WCS(struct wdt_device *wd, u32 value)
{
u64 current_time;
u64 new_compare_value;
unsigned long flags;
spin_lock_irqsave(&wd->lock, flags);
iowrite32(value, wd->base + WCS_REG_OFFSET);
wd->wcs_value = value;
current_time = ktime_get_ns();
new_compare_value = current_time + wd->wdog_offset_value;
iowrite32((u32)(new_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(new_compare_value >> 32), wd->base + WCV_H_OFFSET);
wd->last_feed_time = current_time;
spin_unlock_irqrestore(&wd->lock, flags);
}
static void write_WCV(struct wdt_device *wd, u64 new_compare_value)
{
u64 current_time;
u64 final_compare_value;
unsigned long flags;
spin_lock_irqsave(&wd->lock, flags);
current_time = ktime_get_ns();
if (new_compare_value > current_time) {
final_compare_value = new_compare_value;
} else {
final_compare_value = current_time + wd->wdog_offset_value;
}
iowrite32((u32)(final_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(final_compare_value >> 32), wd->base + WCV_H_OFFSET);
wd->last_feed_time = current_time;
spin_unlock_irqrestore(&wd->lock, flags);
}
static irqreturn_t wdt_interrupt(int irq, void *dev_id)
{
struct wdt_device *wd = dev_id;
u32 status;
unsigned long flags;
spin_lock_irqsave(&wd->lock, flags);
// 读取状态寄存器
status = ioread32(wd->base + WCS_REG_OFFSET);
// 检查状态位
if (!(status & WDT_WS0)) {
status |= WDT_WS0; // 设置WS0位
iowrite32(status, wd->base + WCS_REG_OFFSET);
} else if ((status & WDT_WS0) && !(status & WDT_WS1)) {
status |= WDT_WS1; // 设置WS1位
iowrite32(status, wd->base + WCS_REG_OFFSET);
spin_unlock_irqrestore(&wd->lock, flags);
emergency_restart(); // 触发系统重启
return IRQ_HANDLED;
}
spin_unlock_irqrestore(&wd->lock, flags);
return IRQ_HANDLED;
}
// 实现文件操作函数
static int wdt_open(struct inode *inode, struct file *file)
{
struct wdt_device *wd = container_of(file->private_data, struct wdt_device, wdd);
if (test_and_set_bit(0, &wdt_is_open))
return -EBUSY;
file->private_data = wd;
return nonseekable_open(inode, file);
}
static ssize_t wdt_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos)
{
return 0;
}
static ssize_t wdt_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos)
{
struct wdt_device *wd = file->private_data;
if (len) {
if (!nowayout) {
size_t i;
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V')
expect_close = 42;
}
}
wdog_feed(&wd->wdd);
}
return len;
}
static int wdt_release(struct inode *inode, struct file *file)
{
struct wdt_device *wd = file->private_data;
if (expect_close == 42) {
wdog_stop(&wd->wdd);
} else {
pr_crit("Unexpected close, not stopping watchdog!\n");
wdog_feed(&wd->wdd);
}
clear_bit(0, &wdt_is_open);
expect_close = 0;
return 0;
}
static long wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd, unsigned long arg)
{
struct wdt_device *wd = container_of(wdd, struct wdt_device, wdd);
int ret = 0;
int time_val;
void __user *argp = (void __user *)arg;
int __user *p = argp;
switch (cmd) {
case WDIOC_KEEPALIVE:
wdog_feed(wdd);
break;
case WDIOC_SETTIMEOUT:
if (get_user(time_val, p))
return -EFAULT;
heartbeat = time_val;
wdd->timeout = heartbeat;
wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC;
wdog_feed(wdd); // 更新超时设置后立即喂狗
break;
case WDIOC_GETTIMEOUT:
return put_user(wdd->timeout, p);
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_SETOPTIONS:
if (get_user(time_val, p))
return -EFAULT;
if (time_val & WDIOS_DISABLECARD)
ret = wdog_stop(wdd);
if (time_val & WDIOS_ENABLECARD)
ret = wdog_start(wdd);
break;
default:
ret = -ENOTTY;
break;
}
return ret;
}
static const struct watchdog_info wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "Watchdog Timer",
};
static const struct watchdog_ops wdt_ops = {
.owner = THIS_MODULE,
.start = wdog_start,
.stop = wdog_stop,
.ping = wdog_feed,
.ioctl = wdt_ioctl,
};
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = wdt_open,
.read = wdt_read,
.write = wdt_write,
.release = wdt_release,
.unlocked_ioctl = wdt_ioctl,
};
static int wdt_probe(struct platform_device *pdev)
{
struct wdt_device *wd;
struct resource *res;
int ret;
u64 current_time;
u64 initial_compare_value;
u32 wcs_readback;
wd = devm_kzalloc(&pdev->dev, sizeof(*wd), GFP_KERNEL);
if (!wd)
return -ENOMEM;
// 获取内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "No memory resource\n");
return -ENODEV;
}
// 映射寄存器区域
wd->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(wd->base)) {
dev_err(&pdev->dev, "Failed to map registers\n");
return PTR_ERR(wd->base);
}
// 初始化自旋锁
spin_lock_init(&wd->lock);
// 获取中断
wd->irq = platform_get_irq(pdev, 0);
if (wd->irq < 0) {
dev_err(&pdev->dev, "No IRQ resource\n");
return wd->irq;
}
// 注册中断处理函数
ret = devm_request_irq(&pdev->dev, wd->irq, wdt_interrupt,
IRQF_SHARED, "watchdog", wd);
if (ret) {
dev_err(&pdev->dev, "Failed to request IRQ\n");
return ret;
}
// 初始化看门狗寄存器
wd->wcs_value = ioread32(wd->base + WCS_REG_OFFSET);
wd->wcs_value &= ~(1 << 0); // 禁用看门狗
iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET);
// 设置超时值 (纳秒)
wd->wdog_offset_value = (u64)heartbeat * NSEC_PER_SEC;
wd->wor_value = wd->wdog_offset_value;
iowrite32(wd->wor_value, wd->base + WOR_OFFSET);
// 设置比较值
current_time = ktime_get_ns();
initial_compare_value = current_time + wd->wdog_offset_value;
iowrite32((u32)(initial_compare_value & 0xFFFFFFFF), wd->base + WCV_L_OFFSET);
iowrite32((u32)(initial_compare_value >> 32), wd->base + WCV_H_OFFSET);
// 启用看门狗
wd->wcs_value |= (1 << 0);
iowrite32(wd->wcs_value, wd->base + WCS_REG_OFFSET);
wcs_readback = ioread32(wd->base + WCS_REG_OFFSET);
wd->wcs_value = wcs_readback;
// 初始化看门狗设备结构
wd->wdd.ops = &wdt_ops;
wd->wdd.info = &wdt_info;
wd->wdd.timeout = heartbeat;
wd->wdd.min_timeout = 1;
wd->wdd.max_timeout = 3600;
wd->dev = &pdev->dev;
wd->wdd.parent = &pdev->dev;
wd->nowayout = nowayout;
// 注册看门狗设备
ret = devm_watchdog_register_device(&pdev->dev, &wd->wdd);
if (ret) {
dev_err(&pdev->dev, "Failed to register watchdog device (err=%d)\n", ret);
return ret;
}
// 设置驱动数据
platform_set_drvdata(pdev, wd);
dev_info(&pdev->dev, "WDT driver initialized (heartbeat=%d sec, nowayout=%d)\n",
heartbeat, nowayout);
return 0;
}
static int wdt_remove(struct platform_device *pdev)
{
struct wdt_device *wd = platform_get_drvdata(pdev);
// 停止看门狗
wdog_stop(&wd->wdd);
dev_info(&pdev->dev, "Watchdog driver removed\n");
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int wdt_suspend(struct device *dev)
{
struct wdt_device *wd = dev_get_drvdata(dev);
// 禁用看门狗
wdog_stop(&wd->wdd);
return 0;
}
static int wdt_resume(struct device *dev)
{
struct wdt_device *wd = dev_get_drvdata(dev);
// 恢复看门狗
wdog_start(&wd->wdd);
wdog_feed(&wd->wdd);
return 0;
}
static const struct dev_pm_ops wdt_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(wdt_suspend, wdt_resume)
};
#endif
static const struct of_device_id wdt_match[] = {
{ .compatible = "a,watchdog" },
{},
};
MODULE_DEVICE_TABLE(of, wdt_match);
static struct platform_driver wdt_driver = {
.probe = wdt_probe,
.remove = wdt解释一下这个代码,每一步都在干什么
最新发布