📄 ep93xx_wdt.c
字号:
/* * Watchdog driver for Cirrus Logic EP93xx family of devices. * * Copyright (c) 2004 Ray Lehtiniemi * Copyright (c) 2006 Tower Technologies * Based on ep93xx driver, bits from alim7101_wdt.c * * Authors: Ray Lehtiniemi <rayl@mail.com>, * Alessandro Zummo <a.zummo@towertech.it> * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. * * This watchdog fires after 250msec, which is a too short interval * for us to rely on the user space daemon alone. So we ping the * wdt each ~200msec and eventually stop doing it if the user space * daemon dies. * * TODO: * * - Test last reset from watchdog status * - Add a few missing ioctls */#include <linux/module.h>#include <linux/fs.h>#include <linux/miscdevice.h>#include <linux/watchdog.h>#include <linux/timer.h>#include <asm/hardware.h>#include <asm/uaccess.h>#define WDT_VERSION "0.3"#define PFX "ep93xx_wdt: "/* default timeout (secs) */#define WDT_TIMEOUT 30static int nowayout = WATCHDOG_NOWAYOUT;static int timeout = WDT_TIMEOUT;static struct timer_list timer;static unsigned long next_heartbeat;static unsigned long wdt_status;static unsigned long boot_status;#define WDT_IN_USE 0#define WDT_OK_TO_CLOSE 1#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x))#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00)#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04)/* reset the wdt every ~200ms */#define WDT_INTERVAL (HZ/5)static void wdt_enable(void){ __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG);}static void wdt_disable(void){ __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG);}static inline void wdt_ping(void){ __raw_writew(0x5555, EP93XX_WDT_WATCHDOG);}static void wdt_startup(void){ next_heartbeat = jiffies + (timeout * HZ); wdt_enable(); mod_timer(&timer, jiffies + WDT_INTERVAL);}static void wdt_shutdown(void){ del_timer_sync(&timer); wdt_disable();}static void wdt_keepalive(void){ /* user land ping */ next_heartbeat = jiffies + (timeout * HZ);}static int ep93xx_wdt_open(struct inode *inode, struct file *file){ if (test_and_set_bit(WDT_IN_USE, &wdt_status)) return -EBUSY; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); wdt_startup(); return nonseekable_open(inode, file);}static ssize_tep93xx_wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos){ if (len) { if (!nowayout) { size_t i; clear_bit(WDT_OK_TO_CLOSE, &wdt_status); for (i = 0; i != len; i++) { char c; if (get_user(c, data + i)) return -EFAULT; if (c == 'V') set_bit(WDT_OK_TO_CLOSE, &wdt_status); else clear_bit(WDT_OK_TO_CLOSE, &wdt_status); } } wdt_keepalive(); } return len;}static struct watchdog_info ident = { .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, .identity = "EP93xx Watchdog",};static intep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ int ret = -ENOTTY; switch (cmd) { case WDIOC_GETSUPPORT: ret = copy_to_user((struct watchdog_info __user *)arg, &ident, sizeof(ident)) ? -EFAULT : 0; break; case WDIOC_GETSTATUS: ret = put_user(0, (int __user *)arg); break; case WDIOC_GETBOOTSTATUS: ret = put_user(boot_status, (int __user *)arg); break; case WDIOC_GETTIMEOUT: /* actually, it is 0.250 seconds.... */ ret = put_user(1, (int __user *)arg); break; case WDIOC_KEEPALIVE: wdt_keepalive(); ret = 0; break; } return ret;}static int ep93xx_wdt_release(struct inode *inode, struct file *file){ if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) wdt_shutdown(); else printk(KERN_CRIT PFX "Device closed unexpectedly - " "timer will not stop\n"); clear_bit(WDT_IN_USE, &wdt_status); clear_bit(WDT_OK_TO_CLOSE, &wdt_status); return 0;}static const struct file_operations ep93xx_wdt_fops = { .owner = THIS_MODULE, .write = ep93xx_wdt_write, .ioctl = ep93xx_wdt_ioctl, .open = ep93xx_wdt_open, .release = ep93xx_wdt_release,};static struct miscdevice ep93xx_wdt_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &ep93xx_wdt_fops,};static void ep93xx_timer_ping(unsigned long data){ if (time_before(jiffies, next_heartbeat)) wdt_ping(); /* Re-set the timer interval */ mod_timer(&timer, jiffies + WDT_INTERVAL);}static int __init ep93xx_wdt_init(void){ int err; err = misc_register(&ep93xx_wdt_miscdev); boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; printk(KERN_INFO PFX "EP93XX watchdog, driver version " WDT_VERSION "%s\n", (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) ? " (nCS1 disable detected)" : ""); if (timeout < 1 || timeout > 3600) { timeout = WDT_TIMEOUT; printk(KERN_INFO PFX "timeout value must be 1<=x<=3600, using %d\n", timeout); } setup_timer(&timer, ep93xx_timer_ping, 1); return err;}static void __exit ep93xx_wdt_exit(void){ wdt_shutdown(); misc_deregister(&ep93xx_wdt_miscdev);}module_init(ep93xx_wdt_init);module_exit(ep93xx_wdt_exit);module_param(nowayout, int, 0);MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");module_param(timeout, int, 0);MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," "Alessandro Zummo <a.zummo@towertech.it>");MODULE_DESCRIPTION("EP93xx Watchdog");MODULE_LICENSE("GPL");MODULE_VERSION(WDT_VERSION);MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -