s3c6410在linux下的WATCHDOG TIMER(看门狗定时器)驱动(1)的链接地址
s3c6410在linux下的WATCHDOG TIMER(看门狗定时器)驱动(2)的地址链接
前面两篇一篇分析了看门狗定时器的驱动架构,另一篇分析了平台设备对应的probe函数,虽然对应的remove函数没分析,其实和别的平台设备驱动一样,做和probe函数相反的工作。这一篇要说点啥呢?
S3c2410_wdt.c (linux2.6.28\drivers\watchdog)在这个文件中还有很多函数,我们都没有分析过,这一篇就找些重要的函数分析下。
1、那就先从s3c2410wdt_start函数开始,此函数开启看门狗,源码如下:
static void s3c2410wdt_start(void)
{
unsigned long wtcon;
spin_lock(&wdt_lock);
__s3c2410wdt_stop();先停止看门狗便于设置
wtcon = readl(wdt_base + S3C2410_WTCON);读控制寄存器WTCON
wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
这里的定义:
#define S3C2410_WTCON_ENABLE (1<<5)
#define S3C2410_WTCON_DIV128 (3<<3)
对照下面的寄存器图,更直观:
if (soft_noboot) { 这个标志第一篇中说明了它的作用,不明的可以回过去查看。等于1,作为定时器使用。
2、接着说s3c2410wdt_stop函数,停止看门狗工作。
static void s3c2410wdt_stop(void)
{
spin_lock(&wdt_lock);
__s3c2410wdt_stop();
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);
}
3、s3c2410wdt_shutdown此函数用于停止看门狗
static void s3c2410wdt_shutdown(struct platform_device *dev)
{
s3c2410wdt_stop();
}
4、s3c2410wdt_suspend该函数用于电源管理,挂起设备
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;
}
5、s3c2410wdt_resume也与电源管理有关,挂起后的恢复
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;
}
6、再来看s3c2410wdt_fops结构体中的函数:
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,
};
6.1、先看s3c2410wdt_open函数:在用户调用open函数时被调用。源码如下:
/*
* /dev/watchdog handling
*/
static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
printk("s3c2410wdt_open.\n");
if (test_and_set_bit(0, &open_lock))
return -EBUSY;
调用此函数测试open_lock的第0位。为0则表示此函数返回0,表示设备没有被另外的程序打开。如果第0位为1,则表示设备已经被打开,返回EBUSY
if (nowayout)不为0,表示看门狗不允许关闭,则增加看门狗模块的引用计数。
__module_get(THIS_MODULE);
allow_close = CLOSE_STATE_NOT;设为不允许关闭
/* start the timer */
s3c2410wdt_start();打开设备
return nonseekable_open(inode, file);不允许调用seek函数,即不允许对设备进行定位。
}
6.2、接着看s3c2410wdt_release函数,在close函数中被调用。函数源码如下:
static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
/*
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/
if (allow_close == CLOSE_STATE_ALLOW)看门狗为允许关闭
s3c2410wdt_stop();关闭看门狗
else {
dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
s3c2410wdt_keepalive();使看门狗为活动状态
}
其中s3c2410wdt_keepalive函数源码如下:
static void s3c2410wdt_keepalive(void)
{
spin_lock(&wdt_lock);
writel(wdt_count, wdt_base + S3C2410_WTCNT);重新写计数寄存器
spin_unlock(&wdt_lock);
}
allow_close = CLOSE_STATE_NOT;设为不允许关闭
clear_bit(0, &open_lock);将open_lock的第0位设为0,允许打开
return 0;
}
6.3、再来看s3c2410wdt_write函数,该函数主要用来设置allow_close变量为允许关闭状态。如果向设备中写入V,那么就允许关闭设备。源码如下:
static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
/*
* Refresh the timer.
*/
printk("s3c2410wdt_write.\n");
if (len) {
if (!nowayout) { 允许关闭
size_t i;
/* In case it was set long ago */
allow_close = CLOSE_STATE_NOT 不允许关闭
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
allow_close = CLOSE_STATE_ALLOW;
}如果往看门狗设备中写入V,允许关闭
}
s3c2410wdt_keepalive();
}
return len;
}
6.4、现在来看一个比较重要的函数,s3c2410wdt_ioctl函数用来接受系统的命令,设置看门狗的状态。
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;
printk("s3c2410wdt_ioctl.\n");
switch (cmd) {
case WDIOC_GETSUPPORT:获取看门狗的信息
return copy_to_user(argp, &s3c2410_wdt_ident,
sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
其中有定义:
#define OPTIONS WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE
static const struct watchdog_info s3c2410_wdt_ident = {
.options = OPTIONS,
.firmware_version = 0,
.identity = "S3C2410 Watchdog",
};
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;
}
}
6.5、不知道你还记得吗?我们在probe函数中,申请的中断,并设置中断处理函数是s3c2410wdt_irq,现在来说这个函数,源码如下:
/* 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;
}
其实就是喂狗。
这里我就有个疑问了,在看门狗用作定时器时,WTDAT寄存器用于指定超时时间。当看门狗作为定时器使用时,当计数器WTCNT的值到0时,WTDAT寄存器的值会被自动装入WTCNT,并不会发出复位信号。这里WTDAT寄存器的值会被自动装入WTCNT,那我们还喂狗干什么?这不是自动喂狗吗?
当用作看门狗时,到时间都复位了,中断还有用吗?
那位帮我前辈帮我解惑,在这里先谢了。