S3C2440驅(qū)動篇—看門狗驅(qū)動分析
驅(qū)動實現(xiàn):
本文引用地址:http://m.butianyuan.cn/article/201611/318173.htm#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=%08lxn",__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) //根據(jù)timeout時間設(shè)置看門狗定時器初始值{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=%dn",__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 bign", 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是內(nèi)涵配置選項,如果配置則看門狗不能被關(guān)閉*/__module_get(THIS_MODULE);/*如果內(nèi)核配置了CONFIG_WATCHDOG_NOWAYOUT項,則使模塊使用計數(shù)加1*/expect_close = 0;/* start the timer */s3c2410wdt_start();return nonseekable_open(inode, file);/*表示返回的這個設(shè)備文件是不可以被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 watchdogn");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) { /*如果沒有配置內(nèi)核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;}}/*上面的意思是想要看門狗定時器可以被關(guān)閉,則內(nèi)核不要配置CONFIG_WATCHDOG_NOWAYOUT選項,對于下面這里還要“喂狗”一次,我剛開始覺得不需要,因為在看門狗定時器中斷里面不斷的在“喂狗”。后來想想,這里還必須要“喂狗”一次,因為當(dāng)上面我們判斷到寫入的數(shù)據(jù)是"V"時,看門狗定時器的當(dāng)前操作狀態(tài)馬上被設(shè)置為關(guān)閉,再當(dāng)驅(qū)動去調(diào)用看門狗設(shè)備驅(qū)動的關(guān)閉接口函數(shù)時,看門狗定時器中斷被禁止,無法再實現(xiàn)“喂狗”,所以這里要手動“喂狗”一次,否則定時器溢出系統(tǒng)被復(fù)位*/s3c2410wdt_keepalive();}return len;}#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)/*用于支持看門狗IO控制中獲取看門狗信息的命令WDIOC_GETSUPPORT,下面的宏和看門狗信息結(jié)構(gòu)體定義在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=%pn", __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中已經(jīng)引用,所以這里就不需再引用*/dev_err(dev, "no memory resource specifiedn");return -ENOENT;}size = (res->end - res->start) + 1;/*申請watchdog的IO端口資源所占用的IO內(nèi)存,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 regionn");ret = -ENOENT;goto err_req;}wdt_base = ioremap(res->start, size);if (wdt_base == NULL) {dev_err(dev, "failed to ioremap() regionn");ret = -EINVAL;goto err_req;}DBG("probe: mapped wdt_base=%pn", wdt_base);wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);if (wdt_irq == NULL) {dev_err(dev, "no irq resource specifiedn");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 sourcen");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 usedn",CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);elsedev_info(dev, "default timer value is out of range, ""cannot startn");}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 timern");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 %sabledn",(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平臺設(shè)備驅(qū)動電源管理的支持。CONFIG_PM這個宏定義在內(nèi)核中,當(dāng)配置內(nèi)核時選上電源管理,則Watchdog平臺驅(qū)動的設(shè)備掛起和恢復(fù)功能均有效*/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 %sabledn",(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內(nèi)核中,使用了大量不同的宏來標(biāo)記具有不同作用的函數(shù)和數(shù)據(jù)結(jié)構(gòu),這些宏在include/linux/init.h頭文件中定義,編譯器通過這些宏可以把代碼優(yōu)化放到合適的內(nèi)存位置,以減少內(nèi)存占用和提高內(nèi)核效率。__devinit、__devexit就是這些宏之一,在probe()和remove()函數(shù)中應(yīng)該使用__devinit和__devexit宏。又當(dāng)remove()函數(shù)使用了__devexit宏時,則在驅(qū)動結(jié)構(gòu)體中一定要使用__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", /*注意這里的名稱一定要和系統(tǒng)中定義平臺設(shè)備的名稱一致,這樣才能把平臺設(shè)備與該平臺設(shè)備的驅(qū)動關(guān)聯(lián)起來*/},};static char banner[] __initdata =KERN_INFO "S3C2410 Watchdog Timer, (c) 2004 Simtec Electronicsn";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");
配置內(nèi)核,默認(rèn)情況下已經(jīng)配置
Devide drivers --->
<*> Watchdog Timer Support --->
<*> s3c2410 wathcdog
測試:
我們可以使用echo向/dev/watchdog設(shè)備隨便寫入一些數(shù)據(jù)即可開啟看門狗,比如:echo 0 > /dev/watchdog如果等待15秒鐘,系統(tǒng)自動重啟。
由驅(qū)動程序知道,只要寫入“V”就可以停止看門狗,echo –n V > /dev/watchdog,-n是去掉回車。
下面是一個圖形化各個主要函數(shù)之間層次關(guān)系,更清晰的了解驅(qū)動程序。
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)
評論