123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /*
- * 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 <linux/uaccess.h>
- #include <mach/hardware.h>
- #define WDT_VERSION "0.3"
- #define PFX "ep93xx_wdt: "
- /* default timeout (secs) */
- #define WDT_TIMEOUT 30
- static 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_t
- ep93xx_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 long ep93xx_wdt_ioctl(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_KEEPALIVE:
- wdt_keepalive();
- ret = 0;
- break;
- case WDIOC_GETTIMEOUT:
- /* actually, it is 0.250 seconds.... */
- ret = put_user(1, (int __user *)arg);
- 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,
- .unlocked_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);
|