/*
* Copyright (c) HiSilicon (Shanghai) Technologies Co., Ltd. 2022-2022. All rights reserved.
* Description: adm706_wdt.c
* Author: BSP
* Create: 2022/09/30
*/
/*
* drivers/watchdog/adm706_wdt.c
*
* WDT driver for adm706
* Copyright (C) 2010, Huawei Corporation.
*
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307 USA.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/of_gpio.h>
#include <linux/types.h>
static int nowayout = WATCHDOG_NOWAYOUT;
static unsigned long wdt_status;
static long boot_status;
int wdt_reboot_time = 6000; /* 内核停止喂狗倒计时10分钟 */
int wdt__min_reboot_time = 600; /* 内核至少喂狗600*100ms:60秒 */
int wdt_timer_stop = 0;
int disable_wdt_file = 0; /* 0表示记文件,1表示不记文件 */
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
#define WDT_ENABLED 2
#define WDT_REBOOT_TIME 50 /* 上层接管后50*100ms不喂狗系统则重启 */
#define WDT_HIGH_LEVEL 1
#define WDT_LOW_LEVEL 0
/* 喂狗寄存器 */
static uintptr_t g_wtd_reg_base_addr_mapped;
void set_wdt_reg_val(unsigned int val)
{
if (g_wtd_reg_base_addr_mapped) {
writel(val, (volatile void __iomem *)g_wtd_reg_base_addr_mapped);
}
}
static long adm706_watchdog_timeout(void)
{
return (1600); // 1600 超时时间
}
static void wdt_enable(void)
{
set_wdt_reg_val(WDT_HIGH_LEVEL);
msleep(10); // 时间缓冲10ms
set_wdt_reg_val(WDT_LOW_LEVEL);
}
/* returns 0 if the timer was successfully disabled */
static int wdt_disable(void)
{
printk(KERN_INFO "WATCHDOG: can't be Disabled by software\n");
return 0;
}
static int adm706_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
wdt_enable();
set_bit(WDT_ENABLED, &wdt_status);
return nonseekable_open(inode, file);
}
static ssize_t adm706_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
wdt_reboot_time = WDT_REBOOT_TIME;
}
return len;
}
static const struct watchdog_info ident = {
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.identity = "adm706 watchdog",
};
static long adm706_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int options;
int ret = -ENOTTY;
int __user *argp = (int __user *)compat_ptr(arg);
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof ident)) {
ret = -EFAULT;
} else {
ret = 0;
}
break;
case WDIOC_GETSTATUS:
ret = put_user(0, argp);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, argp);
break;
case WDIOC_SETOPTIONS:
options = (int) arg;
if ((unsigned int)options & WDIOS_DISABLECARD) {
if (!nowayout) {
if (wdt_disable() == 0) {
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
ret = 0;
} else {
ret = -ENXIO;
}
} else {
ret = 0;
}
}
if ((unsigned int)options & WDIOS_ENABLECARD) {
wdt_enable();
ret = 0;
}
break;
case WDIOC_KEEPALIVE:
wdt_enable();
ret = 0;
break;
case WDIOC_SETTIMEOUT:
ret = _IOC_SIZE(cmd);
break;
case WDIOC_GETTIMEOUT:
ret = put_user(adm706_watchdog_timeout(), argp);
break;
default:
break;
}
return ret;
}
static int adm706_wdt_release(struct inode *inode, struct file *file)
{
int state = 1;
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
if (test_bit(WDT_ENABLED, &wdt_status)) {
state = wdt_disable();
}
}
/* if the timer is not disbaled reload and notify that we are still
* going down
*/
if (state != 0) {
wdt_enable();
}
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
printk(KERN_INFO "watchdog released, system is going to reboot ...\n");
return 0;
}
static const struct file_operations adm706_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = adm706_wdt_write,
.unlocked_ioctl = adm706_wdt_ioctl,
.open = adm706_wdt_open,
.release = adm706_wdt_release,
};
static struct miscdevice adm706_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &adm706_wdt_fops,
};
struct task_struct *p_creat_file;
static void kick_wdt_fn(struct timer_list *timer)
{
static unsigned int count = 0;
set_wdt_reg_val(count & WDT_HIGH_LEVEL);
count++;
mod_timer(timer, jiffies + msecs_to_jiffies(100)); // 100 定时器每次递加时间为100s
}
DEFINE_TIMER(kick_wdt_timer, kick_wdt_fn);
/* 内核中通用喂狗函数,可以放置在内核任何需要喂狗的地方 */
void adm706_wdt_common(void)
{
static int val = 0;
val = (val ? WDT_LOW_LEVEL : WDT_HIGH_LEVEL);
if (wdt_timer_stop == 0) {
set_wdt_reg_val(val);
}
}
EXPORT_SYMBOL(adm706_wdt_common);
static void adm706_wdt_init(void)
{
wdt_timer_stop = 0;
printk("kernel start kick dog...\n");
add_timer(&kick_wdt_timer);
mod_timer(&kick_wdt_timer, jiffies);
}
/* 注意:hi1230平台读写寄存器,通过WDG_WDI 喂外狗 */
static int adm706_wdt_probe(struct platform_device *pdev)
{
int ret;
struct resource *res = NULL;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
printk("watchdog get res fail.\n");
return -ENXIO;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (res == NULL) {
printk("watchdog request mem fail.\n");
return -EBUSY;
}
g_wtd_reg_base_addr_mapped = (uintptr_t)ioremap(res->start, resource_size(res));
adm706_wdt_init();
ret = misc_register(&adm706_wdt_miscdev);
if (ret == 0) {
printk(KERN_INFO "adm706 watchdog timer: timeout 1.6 sec\n");
if (!nowayout) {
printk("Watchdog can be stoped.\n");
} else {
printk("Watchdog can not be stopped once started.\n");
}
}
return ret;
}
static int adm706_wdt_remove(struct platform_device *pdev)
{
int error = 0;
misc_deregister(&adm706_wdt_miscdev);
return error;
}
static const struct of_device_id adm706_wdt_match[] = {
{
.compatible = "analog,adm706",
},
{},
};
MODULE_DEVICE_TABLE(of, adm706_wdt_match);
static struct platform_driver adm706_wdt_driver = {
.driver =
{
.name = "wdt-adm706",
.owner = THIS_MODULE,
.of_match_table = adm706_wdt_match,
},
.probe = adm706_wdt_probe,
.remove = adm706_wdt_remove,
};
/* pcs may be needed to bring up other drivers */
static int __init adm706_wdt_init_driver(void)
{
return platform_driver_register(&adm706_wdt_driver);
}
subsys_initcall(adm706_wdt_init_driver);
static void __exit adm706_wdt_exit_driver(void)
{
platform_driver_unregister(&adm706_wdt_driver);
}
module_exit(adm706_wdt_exit_driver);
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_AUTHOR("Walter");
MODULE_DESCRIPTION("ADM706 watchdog timer driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
请帮我分析每一段代码的功能逻辑
最新发布