#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/types.h> #include <linux/timer.h> #include <linux/miscdevice.h> #include <linux/watchdog.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/clk.h> #include <linux/uaccess.h> #include <linux/io.h> #include <mach/regs-gpio.h> #include <mach/map.h>module_param(tmr_margin, int, 0); // 默认的看门狗喂狗时间module_param(tmr_atboot, int, 0); // 系统启动时就使能看门狗,为1表示使能,为0表示关闭;module_param(nowayout, int, 0); // 不允许看门狗关闭,为1表示不允许关闭,为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)"); typedef enum close_state { CLOSE_STATE_NOT, CLOSE_STATE_ALLOW = 0x4021 } close_state_t; static unsigned long open_lock; static struct device *wdt_dev; /* platform 设备 */ static struct resource *wdt_mem;//用来保存IO端口占用的内存资源 static struct resource *wdt_irq;//保存wdt中断号 static struct clk *wdt_clock;//保存从平台获取的watchdog时钟 static void __iomem *wdt_base;//经过ioremap后的内存基地址 static unsigned int wdt_count;//保存向WTCNT写的计数值 static close_state_t allow_close; static DEFINE_SPINLOCK(wdt_lock);//定义一个自旋锁 /* watchdog control routines */ #define DBG(msg...) do { \ if (debug) \ printk(KERN_INFO msg); \ } while (0) 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_keepalive(void){spin_lock(&wdt_lock);//给资源上锁writel(wdt_count,wdt_base+S3C2410_WTCNT);//把值写到WTCNT寄存器spin_unlock(&wdt_lock);//解锁资源,下同}static void s3c241wdt_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;//看门狗定时器使能、时钟除数因子128 if(soft_noboot)//使用参数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);//将wdt_count写入WTDAT writel(wdt_count,wdt_base+S3C2410_WTCNT);//将wdt_count写入WTCNTwritel(wtcon,wdt_base+S3C2410_WTCON);//设置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 wton;if(timerout<1)return -EINVAL;freq/=128;count=timeout*freq;//如果计时的时间过大,则适当加大预分频值if(cout>=0x10000){for(divisor=1;divisor<=0x100;divisor++){if((count/drivisor)<0x10000)break;}//若预分频最大仍不能满足计时周期,则报错 if((count/divisor)>=0x10000){dev_err(wdt_dev,"timeout %s too big \n",timeout);return -EINVAL;}}tmr_margin=timeout;//把预分频后的值保存到tmr_margincount/=divisor;wdt_count=count;wtcon=readl(wdt_base +S3C2410_WTCON);wtcon&=~S3C2410_WTCON_PRESCALE_MASK;wtcon|=S3C2410_WTCON_PRESCALE(divisor-1);writel(count, wdt_base + S3C2410_WTDAT);//把值写到WTDAT寄存器 writel(wtcon, wdt_base + S3C2410_WTCON);//设置预分频值 return 0; }static int mini2440_watch_open(struct inode*inode,struct file *file){if(test_and_set_bit(0,&open_lock))//测试并设置open_lock第0位为1 return -EBUSY;if(nowayout)__module_get(THIS_MODULE);allow_close=CLOSE_START_NOT;s3c2410wdt_start();//看门狗开始return nonseekable_open( inode, file);//不能llseek打开}static ssize_t mini2440_wathdog_write(struct file*file,const char __user*data,size_t len ,loff_t *ppos){if(len){if(!nowayout){allow_close=CLOSE_STATE_NOT;for(i=0;i!=len;i++){char c;if(get_user(c,data+i))//从用户空间copy数据return -EFAULT;if(c=='V')//当输入V时,关闭watchdog allow_close=CLOSE_STATE_ALLOW;}}//手动喂狗 s3c2410wdt_keepalive();//把值写到WTCNT寄存器 ,由于将allow_close设置成CLOSE_STATE_ALLOW后,当release时无法再次喂狗 }return len;}static int mini2440_watch_release(struct inode *inode ,struct file*file){if(allow_close==CLOSE_STATE_ALLOW)s3c2410wdt_stop();else{dev_err(wdt_dev,"Unexpected close ,not stopping watchdog ");s3c2410wdt_keepalive();//手动喂狗 }allow_close=CLOSE_STATE_NOT;clear_bit(0,&open_lock);return 0;}static long mini2440_watch_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 WDTOC_GETSUPRORT: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();//把值写到WTCNT寄存器return 0;case WDIOC_SETTIMEOUT:if(get_user(new_margin,p))?return -EFAULT; if(s3c2410wdt_set_heartbeat(new_margin))/*设置看门狗的计数值和设置分频使能*/ return -EFAULT; s3c2410wdt_keepalive();//把值写到WTCNT寄存器 return put_user(tmr_margin,p);case WDIOC_GETTIMEOUT:return put_user(tmr_margin,p);default:return -ENOTTY; }}static const struct file_operations mini2440_watch_fops={.owner=THIS_MODULE,.llseek=no_llseek,.write =mini2440_wathdog_write,.unlocked_ioctl=mini2440_watch_ioctl,.open =mini2440_watch_open,.release =mini2440_watch_release,}static struct miscdevice s3c2410wdt_miscdev={.minor=WATCHWOG_MINOR,.name="watchdog",.fops=&mini2440_watch_fops,}static int mini2440_watchdog_probe(struct platform_device *pdev) { struct resource *res; struct device *dev; unsigned int wtcon; int started = 0; int ret; int size; dev = &pdev->dev; wdt_dev = &pdev->dev; //获取IO端口资源 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; //申请内存空间 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; } //地址映射,wdt_base为基地址 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); //从平台设备中获取中断号 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; } //获取系统时钟 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; } //使能时钟 clk_enable(wdt_clock); if (s3c2410wdt_set_heartbeat(tmr_margin)) {//这里第一次设置计时周期为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();//启动看门狗 } else if (!tmr_atboot) { 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 mini2440_watchdog_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 mini2440_watchdog_shutdown(struct platform_device *dev){s3c2410wdt_stop();}static struct platform_driver mini2440_watchdog={.probe=mini2440_watchdog_probe,.remove =mini2440_watchdog_remove,.shutdown=mini2440_watchdog_shutdown,.driver={.owner=THIS_MODULE,.name="s3c2410-wdt",},};static int mini2440_watchdog_init(void){return platform_driver_register(mini2440_watchdog);}static void mini2440_watchdog_exit(void){platform_driver_unregister(mini2440_watchdog);}module_init(mini2440_watchdog_init);module_init(mini2440_watchdog_init);MODULE_LICENSE("GPL");MODULE_AUTHOR("4M小水管");