当前位置: 首页 > 工具软件 > WDT > 使用案例 >

Linux查看watchdog超时信息,Linux驱动学习之:WDT(watchdog)驱动

澹台硕
2023-12-01

/* linux/drivers/char/watchdog/s3c2410_wdt.c

*

* Copyright (c) 2004 Simtec Electronics

*  Ben Dooks

*

* S3C2410 Watchdog Timer Support

*

* Based on, softdog.c by Alan Cox,

*(c) Copyright 1996 Alan Cox

*

* This program is free software; you can redistribute it and/or modify

* it under the terms of the GNU General Public License as published by

* the Free Software Foundation; either version 2 of the License,or

*(at your option) any later version.

*

* This program is distributed in the hope that 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;ifnot, write to the Free Software

* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#undef S3C_VA_WATCHDOG

#define S3C_VA_WATCHDOG (0)

#include

#define PFX "s3c2410-wdt: "

#define CONFIG_S3C2410_WATCHDOG_ATBOOT      (0)

#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME    (15)

static int nowayout = WATCHDOG_NOWAYOUT;

static int tmr_margin   = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;

static int tmr_atboot   = CONFIG_S3C2410_WATCHDOG_ATBOOT;

static int soft_noboot;

static int debug;

module_param(tmr_margin,int, 0);

module_param(tmr_atboot,int, 0);

module_param(nowayout,int, 0);

module_param(soft_noboot,int, 0);

module_param(debug,int, 0);

MODULE_PARM_DESC(tmr_margin,"Watchdog tmr_margin in seconds. default="

__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME)")");

MODULE_PARM_DESC(tmr_atboot,

"Watchdog is started at boot time if set to 1, default="

__MODULE_STRING(CONFIG_S3C2410_WATCHDOG_ATBOOT));

MODULE_PARM_DESC(nowayout,"Watchdog cannot be stopped once started (default="

__MODULE_STRING(WATCHDOG_NOWAYOUT)")");

MODULE_PARM_DESC(soft_noboot,"Watchdog action, set to 1 to ignore reboots, "

"0 to reboot (default depends on ONLY_TESTING)");

MODULE_PARM_DESC(debug,"Watchdog debug, set to >1 for debug, (default 0)");

static unsigned long open_lock;

static struct device    *wdt_dev;/* platform device attached to*/

static struct resource  *wdt_mem;

static struct resource  *wdt_irq;

static struct clk   *wdt_clock;

static void __iomem *wdt_base;

static unsigned int  wdt_count;

static char      expect_close;

static DEFINE_SPINLOCK(wdt_lock);

/* watchdog control routines */

#define DBG(msg...)do{\

if(debug)\

printk(KERN_INFO msg);\

}while(0)

/* functions */

static void s3c2410wdt_keepalive(void)

{

spin_lock(&wdt_lock);

writel(wdt_count, wdt_base + S3C2410_WTCNT);

spin_unlock(&wdt_lock);

}

static void __s3c2410wdt_stop(void)

{

unsigned long wtcon;

wtcon = readl(wdt_base + S3C2410_WTCON);

wtcon &=~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);

writel(wtcon, wdt_base + S3C2410_WTCON);

}

static void s3c2410wdt_stop(void)

{

spin_lock(&wdt_lock);

__s3c2410wdt_stop();

spin_unlock(&wdt_lock);

}

static void s3c2410wdt_start(void)

{

unsigned long wtcon;

spin_lock(&wdt_lock);

__s3c2410wdt_stop();

wtcon = readl(wdt_base + S3C2410_WTCON);

wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;

if(soft_noboot){

wtcon |= S3C2410_WTCON_INTEN;

wtcon &=~S3C2410_WTCON_RSTEN;

}else{

wtcon &=~S3C2410_WTCON_INTEN;

wtcon |= S3C2410_WTCON_RSTEN;

}

DBG("%s: wdt_count=0x%08x, wtcon=%08lx\n",

__func__, wdt_count, wtcon);

writel(wdt_count, wdt_base + S3C2410_WTDAT);

writel(wdt_count, wdt_base + S3C2410_WTCNT);

writel(wtcon, wdt_base + S3C2410_WTCON);

spin_unlock(&wdt_lock);

}

static int s3c2410wdt_set_heartbeat(int timeout)

{

unsigned int freq = clk_get_rate(wdt_clock);

unsigned int count;

unsigned int divisor = 1;

unsigned long wtcon;

if(timeout < 1)

return -EINVAL;

freq /= 128;

count = timeout * freq;

DBG("%s: count=%d, timeout=%d, freq=%d\n",

__func__, count, timeout, freq);

/*if the count is bigger than the watchdog register,

then work out what we need todo(andif) we can

actually make this value

*/

if(count >= 0x10000){

for(divisor = 1; divisor <= 0x100; divisor++){

if((count / divisor)< 0x10000)

break;

}

if((count / divisor)>= 0x10000){

dev_err(wdt_dev,"timeout %d too big\n", timeout);

return -EINVAL;

}

}

tmr_margin = timeout;

DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",

__func__, timeout, divisor, count, count/divisor);

count /= divisor;

wdt_count = count;

/* update the pre-scaler */

wtcon = readl(wdt_base + S3C2410_WTCON);

wtcon &=~S3C2410_WTCON_PRESCALE_MASK;

wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);

writel(count, wdt_base + S3C2410_WTDAT);

writel(wtcon, wdt_base + S3C2410_WTCON);

return 0;

}

/*

*/dev/watchdog handling

*/

static int s3c2410wdt_open(struct inode *inode, struct file *file)

{

if(test_and_set_bit(0,&open_lock))

return -EBUSY;

if(nowayout)

__module_get(THIS_MODULE);

expect_close = 0;

/* start the timer */

s3c2410wdt_start();

return nonseekable_open(inode, file);

}

static int s3c2410wdt_release(struct inode *inode, struct file *file)

{

/*

*  Shut off the timer.

*  Lock it inif it's a module and we set nowayout

*/

if(expect_close == 42)

s3c2410wdt_stop();

else{

dev_err(wdt_dev,"Unexpected close, not stopping watchdog\n");

s3c2410wdt_keepalive();

}

expect_close = 0;

clear_bit(0,&open_lock);

return 0;

}

static ssize_t s3c2410wdt_write(struct file *file,const char __user *data,

size_t len, loff_t *ppos)

{

/*

*  Refresh the timer.

*/

if(len){

if(!nowayout){

size_t i;

/*Incase it was set long ago */

expect_close = 0;

for(i = 0; i !=len; i++){

char c;

if(get_user(c, data + i))

return -EFAULT;

if(c =='V')

expect_close = 42;

}

}

s3c2410wdt_keepalive();

}

return len;

}

#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)

static const struct watchdog_info s3c2410_wdt_ident ={

.options          =     OPTIONS,

.firmware_version = 0,

.identity         ="S3C2410 Watchdog",

};

static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,

unsigned long arg)

{

void __user *argp =(void __user *)arg;

int __user *p = argp;

int new_margin;

switch (cmd){

case WDIOC_GETSUPPORT:

return copy_to_user(argp,&s3c2410_wdt_ident,

sizeof(s3c2410_wdt_ident))?-EFAULT : 0;

case WDIOC_GETSTATUS:

case WDIOC_GETBOOTSTATUS:

return put_user(0, p);

case WDIOC_KEEPALIVE:

s3c2410wdt_keepalive();

return 0;

case WDIOC_SETTIMEOUT:

if(get_user(new_margin, p))

return -EFAULT;

if(s3c2410wdt_set_heartbeat(new_margin))

return -EINVAL;

s3c2410wdt_keepalive();

return put_user(tmr_margin, p);

case WDIOC_GETTIMEOUT:

return put_user(tmr_margin, p);

default:

return -ENOTTY;

}

}

/* kernel interface */

static const struct file_operations s3c2410wdt_fops ={

.owner      = THIS_MODULE,

.llseek     = no_llseek,

.write      = s3c2410wdt_write,

.unlocked_ioctl = s3c2410wdt_ioctl,

.open       = s3c2410wdt_open,

.release    = s3c2410wdt_release,

};

static struct miscdevice s3c2410wdt_miscdev ={

.minor      = WATCHDOG_MINOR,

.name       ="watchdog",

.fops       =&s3c2410wdt_fops,

};

/* interrupt handler code */

static irqreturn_t s3c2410wdt_irq(int irqno, void *param)

{

dev_info(wdt_dev,"watchdog timer expired (irq)\n");

s3c2410wdt_keepalive();

return IRQ_HANDLED;

}

/* device interface */

static int __devinit s3c2410wdt_probe(struct platform_device *pdev)

{

struct resource *res;

struct device *dev;

unsigned int wtcon;

int started = 0;

int ret;

int size;

DBG("%s: probe=%p\n", __func__, pdev);

dev =&pdev->dev;

wdt_dev =&pdev->dev;

/*get the memory region for the watchdog timer -- flags is IORESOURCE_MEM */

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

if(res ==NULL){

dev_err(dev,"no memory resource specified\n");

return -ENOENT;

}

size =(res->end- res->start)+ 1;

//请求分配指定的I/O内存资源

wdt_mem = request_mem_region(res->start, size, pdev->name);

if(wdt_mem ==NULL){

dev_err(dev,"failed to get memory region\n");

ret =-ENOENT;

goto err_req;

}

//将一个IO地址空间映射到内核的虚拟地址空间上去,便于访问

wdt_base = ioremap(res->start, size);

if(wdt_base ==NULL){

dev_err(dev,"failed to ioremap() region\n");

ret =-EINVAL;

goto err_req;

}

DBG("probe: mapped wdt_base=%p\n", wdt_base);

/*get the memory region for the watchdog timer -- flags is IORESOURCE_IRQ */

wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

if(wdt_irq ==NULL){

dev_err(dev,"no irq resource specified\n");

ret =-ENOENT;

goto err_map;

}

//注册中断服务函数s3c2410wdt_irq()

ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);

if(ret != 0){

dev_err(dev,"failed to install irq (%d)\n", ret);

goto err_map;

}

//从平台时钟队列中获取clk

wdt_clock = clk_get(&pdev->dev,"watchdog");

if(IS_ERR(wdt_clock)){

dev_err(dev,"failed to find watchdog clock source\n");

ret = PTR_ERR(wdt_clock);

goto err_irq;

}

//inform the system when the clock source should be running

clk_enable(wdt_clock);

/* see if we can actually set the requested timer margin,andif

*not, try the default value */

if(s3c2410wdt_set_heartbeat(tmr_margin)){

started = s3c2410wdt_set_heartbeat(

CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

if(started == 0)

dev_info(dev,

"tmr_margin value out of range, default %d used\n",

CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);

else

dev_info(dev,"default timer value is out of range, "

"cannot start\n");

}

ret = misc_register(&s3c2410wdt_miscdev);

if(ret){

dev_err(dev,"cannot register miscdev on minor=%d (%d)\n",

WATCHDOG_MINOR, ret);

goto err_clk;

}

if(tmr_atboot && started == 0){

dev_info(dev,"starting watchdog timer\n");

s3c2410wdt_start();

}elseif(!tmr_atboot){

/*if we're not enabling the watchdog,then ensure it is

* disabled if it has been left running from the bootloader

*or other source */

s3c2410wdt_stop();

}

/* print out a statement of readiness */

wtcon = readl(wdt_base + S3C2410_WTCON);

dev_info(dev,"watchdog %sactive, reset %sabled, irq %sabled\n",

(wtcon & S3C2410_WTCON_ENABLE)?"":"in",

(wtcon & S3C2410_WTCON_RSTEN)?"":"dis",

(wtcon & S3C2410_WTCON_INTEN)?"":"en");

return 0;

err_clk:

clk_disable(wdt_clock);

clk_put(wdt_clock);

err_irq:

free_irq(wdt_irq->start, pdev);

err_map:

iounmap(wdt_base);

err_req:

release_resource(wdt_mem);

kfree(wdt_mem);

return ret;

}

static int __devexit s3c2410wdt_remove(struct platform_device *dev)

{

release_resource(wdt_mem);

kfree(wdt_mem);

wdt_mem =NULL;

free_irq(wdt_irq->start, dev);

wdt_irq =NULL;

clk_disable(wdt_clock);

clk_put(wdt_clock);

wdt_clock =NULL;

iounmap(wdt_base);

misc_deregister(&s3c2410wdt_miscdev);

return 0;

}

static void s3c2410wdt_shutdown(struct platform_device *dev)

{

s3c2410wdt_stop();

}

#ifdef CONFIG_PM

static unsigned long wtcon_save;

static unsigned long wtdat_save;

static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)

{

/* Save watchdog state,and turn it off.*/

wtcon_save = readl(wdt_base + S3C2410_WTCON);

wtdat_save = readl(wdt_base + S3C2410_WTDAT);

/* Note that WTCNT doesn't need to be saved.*/

s3c2410wdt_stop();

return 0;

}

static int s3c2410wdt_resume(struct platform_device *dev)

{

/* Restore watchdog state.*/

writel(wtdat_save, wdt_base + S3C2410_WTDAT);

writel(wtdat_save, wdt_base + S3C2410_WTCNT);/* Reset count */

writel(wtcon_save, wdt_base + S3C2410_WTCON);

printk(KERN_INFO PFX "watchdog %sabled\n",

(wtcon_save & S3C2410_WTCON_ENABLE)?"en":"dis");

return 0;

}

#else

#define s3c2410wdt_suspend NULL

#define s3c2410wdt_resume  NULL

#endif /* CONFIG_PM */

/*

*platform_driver s3c2410wdt_driver 与 platform_device s3c_device_wdt 对应

*s3c_device_wdt 在arch/arm/plat-s3c24xx/devs.c中定义

*两者的工作顺序是先定义platform_device -> 注册 platform_device->

*在mini2440_machine_init()中完成

*再定义 platform_driver-> 注册 platform_driver

*/

static struct platform_driver s3c2410wdt_driver ={

.probe      = s3c2410wdt_probe,//设备的检测,所以需要先注册设备

.remove     = __devexit_p(s3c2410wdt_remove),//删除该设备

.shutdown   = s3c2410wdt_shutdown,//关闭该设备

.suspend    = s3c2410wdt_suspend,

.resume= s3c2410wdt_resume,

.driver     ={//设备驱动

.owner  = THIS_MODULE,

/*

*对应 struct platform_device s3c_device_wdt = {

*     .name         = "s3c2410-wdt",

*       ...

*    };

*/

.name   ="s3c2410-wdt",

},

};

static char banner[] __initdata =

KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronics\n";

static int __init watchdog_init(void)//模块初始化

{

printk(banner);//打印信息

return platform_driver_register(&s3c2410wdt_driver);//注册设备的驱动程序

}

static void __exit watchdog_exit(void)//移除模块

{

platform_driver_unregister(&s3c2410wdt_driver);//unregister a driver for platform-level devices

}

module_init(watchdog_init);

module_exit(watchdog_exit);

MODULE_AUTHOR("Ben Dooks , "

"Dimitry Andric ");

MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");

MODULE_LICENSE("GPL");

MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

MODULE_ALIAS("platform:s3c2410-wdt");

 类似资料: