您的位置 首页 报告

S3C2440驱动篇—看门狗驱动剖析

Linux-2.6.32.2内核自带S3C2440看门狗驱动,只需要配置一下就可以使用。驱动源代码位于drivers/watchdog/s3c2410_wdt.c,由于驱动使用…

Linux-2.6.32.2内核自带S3C2440看门狗驱动,只需要装备一下就能够运用。驱动源代码坐落drivers/watchdog/s3c2410_wdt.c,咱们驱动运用了渠道设备,有关渠道设备学习参考上一篇文章。

驱动完成:

#include #include #include #include #include #include #include #include #include #include interrupt.h>#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);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)   //依据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 to do (and if) we canactually 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)     /*nowayout是内在装备选项,假如装备则看门狗不能被封闭*/__module_get(THIS_MODULE);/*假如内核装备了CONFIG_WATCHDOG_NOWAYOUT项,则使模块运用计数加1*/expect_close = 0;/* start the timer */s3c2410wdt_start();return nonseekable_open(inode, file);/*一共回来的这个设备文件是不能够被seek操作的,nonseekable_open界说在fs.h中*/}static int s3c2410wdt_release(struct inode *inode, struct file *file){/**	Shut off the timer.* 	Lock it in if its 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) { /*假如没有装备内核CONFIG_WATCHDOG_NOWAYOUT选项,WATCHDOG_NOWAYOUT宏坐落include/linux/watchdog.h*/size_t i;/* In case 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;}}/*上面的意思是想要看门狗定时器能够被封闭,则内核不要装备CONFIG_WATCHDOG_NOWAYOUT选项,关于下面这儿还要“喂狗”一次,我刚开始觉得不需要,由于在看门狗定时器中止晒干不断的在“喂狗”。后来想想,这儿还必须要“喂狗”一次,由于当上面咱们判别到写入的数据是"V"时,看门狗定时器的当时操作状况立刻被设置为封闭,再当驱动去调用看门狗设备驱动的封闭接口函数时,看门狗定时器中止被制止,无法再完成“喂狗”,所以这儿要手动“喂狗”一次,不然定时器溢出体系被复位*/s3c2410wdt_keepalive();}return len;}#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)/*用于支撑看门狗IO操控中获取看门狗信息的指令WDIOC_GETSUPPORT,下面的宏和看门狗信息结构体界说在include/linux/watchdog.h中*/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;/*以下对看门狗定时器IO操控的指令界说在watchdog.h中*/switch (cmd) {case WDIOC_GETSUPPORT:/*获取看门狗的支撑信息,wdt_ident界说在上面*/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,  /*界说为不行定位,即屏蔽seek操作,no_llseek界说在fs.h中*/.write		= s3c2410wdt_write,.unlocked_ioctl	= s3c2410wdt_ioctl,.open		= s3c2410wdt_open,.release	= s3c2410wdt_release,};static struct miscdevice s3c2410wdt_miscdev = {.minor		= WATCHDOG_MINOR,  //坐落include/linux/miscdevic.h中.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;  /*界说一个资源,用来保存获取的watchdog的IO资源*/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 */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (res == NULL) {/*dev_err界说在device.h中,在platform_device.h中现已引证,所以这儿就不需再引证*/dev_err(dev, "no memory resource specified\n");return -ENOENT;}size = (res->end - res->start) + 1;/*请求watchdog的IO端口资源所占用的IO内存,request_mem_region界说在ioport.h中*/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 = 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;}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;}/*界说在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/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);/* see if we can actually set the requested timer margin, and if* 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);elsedev_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) {/* if were 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/*对Watchdog渠道设备驱动电源办理的支撑。CONFIG_PM这个宏界说在内核中,当装备内核时选上电源办理,则Watchdog渠道驱动的设备挂起和康复功用均有用*/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 doesnt 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 *//*留意:这是运用了一个__devexit_p,还有前面运用了__devinit和__devexit,咱们仍是先来讲讲这个:在Linux内核中,运用了很多不同的宏来符号具有不同效果的函数和数据结构,这些宏在include/linux/init.h头文件中界说,编译器经过这些宏能够把代码优化放到适宜的内存方位,以削减内存占用和进步内核功率。__devinit、__devexit便是这些宏之一,在probe()和remove()函数中应该运用__devinit和__devexit宏。又当remove()函数运用了__devexit宏时,则在驱动结构体中必定要运用__devexit_p宏来引证remove(),所以下面就用__devexit_p来引证rtc_remove*/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,.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);}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");

装备内核,默许情况下现已装备

Devide drivers —>

<*> Watchdog Timer Support —>

<*> s3c2410 wathcdog

测验:

咱们能够运用echo向/dev/watchdog设备随意写入一些数据即可舱位看门狗,比方:echo 0 > /dev/watchdog假如等候15秒钟,体系主动重启。

由驱动程序知道,只需写入“V”就能够中止看门狗,echo –n V > /dev/watchdog,-n是去掉回车。

下面是一个图形化各个首要函数之间层次联系,更明晰的了解驱动程序。

module_init(&s3c24xx_i2c_driver)

|__ platform_driver_register(&s3c24xx_i2c_driver)

|__ s3c24xx_i2c_probe

| |__ platform_get_resource

| |__ request_irq

| |__ misc_register(&s3c2410wdt_miscdev)

| |__ &s3c2410wdt_fops

| |__ s3c2410wdt_open

| |__ s3c2410wdt_release

| |__ s3c2410wdt_write

| |__ s3c2410wdt_ioctl

| |__ no_llseek

|__ s3c2410wdt_remove

|__ s3c2410wdt_shutdown

|__ s3c2410wdt_suspend

|__ s3c2410wdt_resume

module_exit(watchdog_exit)

|__ platform_driver_unregister(&s3c2410wdt_driver)

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/ceping/baogao/265057.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部