看门狗驱动1

您所在的位置:网站首页 看门狗1 看门狗驱动1

看门狗驱动1

#看门狗驱动1| 来源: 网络整理| 查看: 265

#include #include #include #include #include #include /*定义了一个用来保存watchdog的IO端口占用的IO空间和经过虚拟映射后的内存地址*/ static struct resource*wdt_mem; static void __iomem*wdt_base; /*保存watchdog中断号,NO_IRQ宏定义在irq.h中*/ static int wdt_irqno= NO_IRQ; /*保存从平台时钟队列中获取watchdog的时钟*/ static struct clk*wdt_clock; #define CONFIG_WATCHDOG_ATBOOT            (0) #define CONFIG_WATCHDOG_DEFAULT_TIME    (15) static int tmr_atboot= CONFIG_WATCHDOG_ATBOOT; static int tmr_margin= CONFIG_WATCHDOG_DEFAULT_TIME; static int soft_noboot; static unsignedint     wdt_count;/*用于保存经计算后得到的计数寄存器WTCNT的计数值*/ /*申明并初始化一个自旋锁wdt_pie_lock,对Watchdog资源进行互斥访问*/ static DEFINE_SPINLOCK(wdt_pie_lock); static int __devinit watchdog_probe(struct platform_device*pdev) {     int ret;     int started = 0;     struct resource *res;/*定义一个资源,用来保存获取的watchdog的IO资源*/     /*在系统定义的watchdog平台设备中获取watchdog中断号      platform_get_irq定义在platform_device.h中*/     wdt_irqno = platform_get_irq(pdev, 0);     if(wdt_irqno         /*错误处理*/         dev_err(dev,"IRQ%d error %d\n", wdt_irqno, ret);         return ret;     }     /*获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致*/     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);     if (res== NULL)     {         /*错误处理*/         dev_err(&pdev->dev,"failed to get memory region resource\n");         return -ENOENT;     }     /*从平台时钟队列中获取watchdog的时钟,这里为什么要取得这个时钟,因为看门狗定时器的工作周期是由这个      时钟和时钟除数因子得到的。注意这里的watchdog参数要与系统中定义的时钟名称一致才能获取得到,也就是      说,系统必须先定义得有watchdog。系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/     wdt_clock = clk_get(&pdev->dev,"watchdog");     if (IS_ERR(wdt_clock))     {         /*错误处理*/         dev_err(&pdev->dev,"failed to find watchdog clock source\n");         return PTR_ERR(wdt_clock);     }     /*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/     clk_enable(wdt_clock);     /*申请watchdog的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),      request_mem_region定义在ioport.h中*/     wdt_mem = request_mem_region(res->start, res->end- res->start+ 1, pdev->name);     if (wdt_mem== NULL)     {         /*错误处理*/         dev_err(&pdev->dev,"failed to reserve memory region\n");         ret = -ENOENT;         goto err_noclk;     }     /*将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。      注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/     wdt_base = ioremap(res->start, res->end- res->start+ 1);     if (wdt_base== NULL)     {         /*错误处理*/         dev_err(&pdev->dev,"failed ioremap()\n");         ret = -EINVAL;         goto err_noreq;     }     /*好了,通过上面的步骤已经将watchdog的资源都准备好了,下面就开始使用啦*/     /*这里是计算并设置看门狗定时器时钟周期值,wdt_set_heartbeat定义在下面。符合数据手册中要求的“在看门狗定时     器开始工作之前,一个初始值必须先写入看门狗定时器计数寄存器WTCNT中”。其实这里就是初始化看门狗定时器*/     if (wdt_set_heartbeat(pdev, tmr_margin))     {         /*这里调用两次的意思是看能不能设置成期望的值,如果不能就设置默认的值*/         started = wdt_set_heartbeat(pdev, CONFIG_WATCHDOG_DEFAULT_TIME);         /*打印设置的值信息*/         if (started== 0)             dev_info(&pdev->dev,"tmr_margin value out of range, default %d used\n", CONFIG_WATCHDOG_DEFAULT_TIME);         else             dev_info(&pdev->dev,"default timer value is out of range, cannot start\n");     }     /*device_init_wakeup该函数定义在pm_wakeup.h中,定义如下:     static inline void device_init_wakeup(struct device *dev, int val){         dev->power.can_wakeup = dev->power.should_wakeup = !!val;}     显然这个函数是让驱动支持电源管理的,这里只要知道,can_wakeup为1时表明这个设备可以被唤醒,设备驱动为了支持     Linux中的电源管理,有责任调用device_init_wakeup()来初始化can_wakeup,而should_wakeup则是在设备的电源状态     发生变化的时候被device_may_wakeup()用来测试,测试它该不该变化,因此can_wakeup表明的是一种能力,     而should_wakeup表明的是有了这种能力以后去不去做某件事。好了,我们没有必要深入研究电源管理的内容了,     要不就扯远了,电源管路以后再讲*/     device_init_wakeup(&pdev->dev, 1);     /*把看门狗设备又注册成为misc设备,misc_register定义在miscdevice.h中      wdt_miscdev结构体定义及内部接口函数在第③步中讲*/     ret = misc_register(&wdt_miscdev);     if (ret)     {         /*错误处理*/         dev_err(&pdev->dev,"cannot register miscdev on minor=%d (%d)\n", WATCHDOG_MINOR, ret);         goto err_nomap;     }     /*函数wdt_start_or_stop定义在下面*/     if (tmr_atboot&& started == 0)     {         wdt_start_or_stop(1);/*参数1表示启动看门狗定时器*/     }     else if(!tmr_atboot)     {         wdt_start_or_stop(0);/*参数0表示停止看门狗定时器*/     }     return 0;   //以下是上面错误处理的跳转点 err_noclk:     clk_disable(wdt_clock);     clk_put(wdt_clock); err_noreq:     release_resource(wdt_mem);     kfree(wdt_mem); err_nomap:     iounmap(wdt_base);     return ret; } /*看门狗定时器中断服务程序*/ static irqreturn_t wdt_irq(int irq,void *argv) {     /*主要要做的事情是在看门狗定时器计数寄存器值递减到0之前重新写入新值(即:“喂狗”)*/     wdt_keepalive();     return IRQ_HANDLED; } /*看门狗定时器“喂狗”*/ static void wdt_keepalive(void) {     spin_lock(&wdt_lock);/*获取自旋锁保护临界区资源*/     writel(wdt_count, wdt_base+ S3C2410_WTCNT);/*往计数寄存器WTCNT重新写入计数值*/     spin_unlock(&wdt_lock);/*释放自旋锁,即解锁*/ } /*计算并设置看门狗定时器时钟周期值并初始化看门狗定时器*/ static int wdt_set_heartbeat(struct platform_device*pdev, int timeout) {     unsigned int freq= clk_get_rate(wdt_clock);     unsigned intcount;     unsigned int divisor= 1;     unsigned long wtcon;     if (timeout= 0x10000)     {             for (divisor= 1; divisor             dev_err(&pdev->dev,"timeout %d too big\n", timeout);             return -EINVAL;         }     }     tmr_margin = timeout;     count /= divisor;     wdt_count = count;     wtcon = readl(wdt_base+ S3C2410_WTCON);/* wtcon=1000000000100001 这是控制寄存器的默认值,看数据手册得到*/     wtcon &=~S3C2410_WTCON_PRESCALE_MASK;/* wtcon=1000000000100001 & ~1111111100000000 = 0000000000100001 */     /*S3C2410_WTCON_PRESCALE宏是将divisor-1的值向左位移8位,也就是说该值的右8位都为0,则计算如下:      wtcon=0000000000100001 | (xxxxxxxx)00000000 = (xxxxxxxx)00100001*/     wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);     /*设置看门狗定时器数据寄存器WTDAT的值,然后WTDAT的值会自动加载到WTCNT中*/     writel(count, wdt_base+ S3C2410_WTDAT);     /*根据数据手册和上面计算的wtcon值可得,下面是设置看门狗定时控制寄存器WTCON为:      看门狗定时器输出使能有效、一个保留位默认0、中断使能无效、时钟除数因子为16、      看门狗定时器使能有效、两个保留位默认00、预定标器值为xxxxxxxx的内容。*/     writel(wtcon, wdt_base+ S3C2410_WTCON);     return 0; } /*根据标志flag的值来启动或者停止看门狗定时器,1表示启动,0表示停止*/ static void wdt_start_or_stop(int flag) {     unsigned long wtcon;     spin_lock(&wdt_pie_lock);/*获取自旋锁保护临界区资源*/     /*停止看门狗定时器,以下各寄存器的位操作请参照数据手册*/     wtcon = readl(wdt_base+ S3C2410_WTCON);     wtcon &=~(S3C2410_WTCON_ENABLE| S3C2410_WTCON_RSTEN);     writel(wtcon, wdt_base+ S3C2410_WTCON);     if(!flag)     {         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;         }         writel(wdt_count, wdt_base+ S3C2410_WTDAT);         writel(wdt_count, wdt_base+ S3C2410_WTCNT);         writel(wtcon, wdt_base+ S3C2410_WTCON);     }     spin_unlock(&wdt_pie_lock);/*释放自旋锁,即解锁*/ }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3