mini2440看门狗驱动

来源:互联网 发布:软件开发验收标准 编辑:程序博客网 时间:2024/06/06 19:57
#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小水管"); 

原创粉丝点击