1/* 2 * IndyDog 0.3 A Hardware Watchdog Device for SGI IP22 3 * 4 * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 * 11 * based on softdog.c by Alan Cox <alan@redhat.com> 12 */ 13 14#include <linux/module.h> 15#include <linux/moduleparam.h> 16#include <linux/types.h> 17#include <linux/kernel.h> 18#include <linux/fs.h> 19#include <linux/mm.h> 20#include <linux/miscdevice.h> 21#include <linux/watchdog.h> 22#include <linux/notifier.h> 23#include <linux/reboot.h> 24#include <linux/init.h> 25#include <asm/uaccess.h> 26#include <asm/sgi/mc.h> 27 28#define PFX "indydog: " 29static int indydog_alive; 30 31#define WATCHDOG_TIMEOUT 30 /* 30 sec default timeout */ 32 33static int nowayout = WATCHDOG_NOWAYOUT; 34module_param(nowayout, int, 0); 35MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 36 37static void indydog_start(void) 38{ 39 u32 mc_ctrl0 = sgimc->cpuctrl0; 40 41 mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG; 42 sgimc->cpuctrl0 = mc_ctrl0; 43} 44 45static void indydog_stop(void) 46{ 47 u32 mc_ctrl0 = sgimc->cpuctrl0; 48 49 mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; 50 sgimc->cpuctrl0 = mc_ctrl0; 51 52 printk(KERN_INFO PFX "Stopped watchdog timer.\n"); 53} 54 55static void indydog_ping(void) 56{ 57 sgimc->watchdogt = 0; 58} 59 60/* 61 * Allow only one person to hold it open 62 */ 63static int indydog_open(struct inode *inode, struct file *file) 64{ 65 if (indydog_alive) 66 return -EBUSY; 67 68 if (nowayout) 69 __module_get(THIS_MODULE); 70 71 /* Activate timer */ 72 indydog_start(); 73 indydog_ping(); 74 75 indydog_alive = 1; 76 printk(KERN_INFO "Started watchdog timer.\n"); 77 78 return nonseekable_open(inode, file); 79} 80 81static int indydog_release(struct inode *inode, struct file *file) 82{ 83 /* Shut off the timer. 84 * Lock it in if it's a module and we defined ...NOWAYOUT */ 85 if (!nowayout) 86 indydog_stop(); /* Turn the WDT off */ 87 88 indydog_alive = 0; 89 90 return 0; 91} 92 93static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos) 94{ 95 /* Refresh the timer. */ 96 if (len) { 97 indydog_ping(); 98 } 99 return len; 100} 101 102static int indydog_ioctl(struct inode *inode, struct file *file, 103 unsigned int cmd, unsigned long arg) 104{ 105 int options, retval = -EINVAL; 106 static struct watchdog_info ident = { 107 .options = WDIOF_KEEPALIVEPING | 108 WDIOF_MAGICCLOSE, 109 .firmware_version = 0, 110 .identity = "Hardware Watchdog for SGI IP22", 111 }; 112 113 switch (cmd) { 114 default: 115 return -ENOTTY; 116 case WDIOC_GETSUPPORT: 117 if (copy_to_user((struct watchdog_info *)arg, 118 &ident, sizeof(ident))) 119 return -EFAULT; 120 return 0; 121 case WDIOC_GETSTATUS: 122 case WDIOC_GETBOOTSTATUS: 123 return put_user(0,(int *)arg); 124 case WDIOC_KEEPALIVE: 125 indydog_ping(); 126 return 0; 127 case WDIOC_GETTIMEOUT: 128 return put_user(WATCHDOG_TIMEOUT,(int *)arg); 129 case WDIOC_SETOPTIONS: 130 { 131 if (get_user(options, (int *)arg)) 132 return -EFAULT; 133 134 if (options & WDIOS_DISABLECARD) { 135 indydog_stop(); 136 retval = 0; 137 } 138 139 if (options & WDIOS_ENABLECARD) { 140 indydog_start(); 141 retval = 0; 142 } 143 144 return retval; 145 } 146 } 147} 148 149static int indydog_notify_sys(struct notifier_block *this, unsigned long code, void *unused) 150{ 151 if (code == SYS_DOWN || code == SYS_HALT) 152 indydog_stop(); /* Turn the WDT off */ 153 154 return NOTIFY_DONE; 155} 156 157static const struct file_operations indydog_fops = { 158 .owner = THIS_MODULE, 159 .llseek = no_llseek, 160 .write = indydog_write, 161 .ioctl = indydog_ioctl, 162 .open = indydog_open, 163 .release = indydog_release, 164}; 165 166static struct miscdevice indydog_miscdev = { 167 .minor = WATCHDOG_MINOR, 168 .name = "watchdog", 169 .fops = &indydog_fops, 170}; 171 172static struct notifier_block indydog_notifier = { 173 .notifier_call = indydog_notify_sys, 174}; 175 176static char banner[] __initdata = 177 KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n"; 178 179static int __init watchdog_init(void) 180{ 181 int ret; 182 183 ret = register_reboot_notifier(&indydog_notifier); 184 if (ret) { 185 printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", 186 ret); 187 return ret; 188 } 189 190 ret = misc_register(&indydog_miscdev); 191 if (ret) { 192 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", 193 WATCHDOG_MINOR, ret); 194 unregister_reboot_notifier(&indydog_notifier); 195 return ret; 196 } 197 198 printk(banner); 199 200 return 0; 201} 202 203static void __exit watchdog_exit(void) 204{ 205 misc_deregister(&indydog_miscdev); 206 unregister_reboot_notifier(&indydog_notifier); 207} 208 209module_init(watchdog_init); 210module_exit(watchdog_exit); 211 212MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>"); 213MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22"); 214MODULE_LICENSE("GPL"); 215MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 216