1 /*
2 * IndyDog 0.2 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/config.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/smp_lock.h>
23 #include <linux/init.h>
24 #include <asm/uaccess.h>
25 #include <asm/sgi/mc.h>
26
27 static unsigned long indydog_alive;
28 static int expect_close = 0;
29
30 #ifdef CONFIG_WATCHDOG_NOWAYOUT
31 static int nowayout = 1;
32 #else
33 static int nowayout = 0;
34 #endif
35
36 MODULE_PARM(nowayout,"i");
37 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
38
indydog_ping(void)39 static inline void indydog_ping(void)
40 {
41 sgimc->watchdogt = 0;
42 }
43
44 /*
45 * Allow only one person to hold it open
46 */
indydog_open(struct inode * inode,struct file * file)47 static int indydog_open(struct inode *inode, struct file *file)
48 {
49 u32 mc_ctrl0;
50
51 if (test_and_set_bit(0,&indydog_alive))
52 return -EBUSY;
53
54 if (nowayout) {
55 MOD_INC_USE_COUNT;
56 }
57
58 /* Activate timer */
59 mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
60 sgimc->cpuctrl0 = mc_ctrl0;
61 indydog_ping();
62
63 indydog_alive = 1;
64 printk(KERN_INFO "Started watchdog timer.\n");
65
66 return 0;
67 }
68
indydog_release(struct inode * inode,struct file * file)69 static int indydog_release(struct inode *inode, struct file *file)
70 {
71 /* Shut off the timer.
72 * Lock it in if it's a module and we set nowayout. */
73 lock_kernel();
74 if (expect_close) {
75 u32 mc_ctrl0 = sgimc->cpuctrl0;
76 mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
77 sgimc->cpuctrl0 = mc_ctrl0;
78 printk(KERN_INFO "Stopped watchdog timer.\n");
79 } else
80 printk(KERN_CRIT "WDT device closed unexpectedly. WDT will not stop!\n");
81 clear_bit(0, &indydog_alive);
82 unlock_kernel();
83
84 return 0;
85 }
86
indydog_write(struct file * file,const char * data,size_t len,loff_t * ppos)87 static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
88 {
89 /* Can't seek (pwrite) on this device */
90 if (ppos != &file->f_pos)
91 return -ESPIPE;
92
93 /*
94 * Refresh the timer.
95 */
96 if (len) {
97 if (!nowayout) {
98 size_t i;
99
100 /* In case it was set long ago */
101 expect_close = 0;
102
103 for (i = 0; i != len; i++) {
104 char c;
105 if (get_user(c, data + i))
106 return -EFAULT;
107 if (c == 'V')
108 expect_close = 1;
109 }
110 }
111 indydog_ping();
112 return 1;
113 }
114 return 0;
115 }
116
indydog_ioctl(struct inode * inode,struct file * file,unsigned int cmd,unsigned long arg)117 static int indydog_ioctl(struct inode *inode, struct file *file,
118 unsigned int cmd, unsigned long arg)
119 {
120 static struct watchdog_info ident = {
121 options: WDIOF_MAGICCLOSE,
122 identity: "Hardware Watchdog for SGI IP22",
123 };
124 switch (cmd) {
125 default:
126 return -ENOIOCTLCMD;
127 case WDIOC_GETSUPPORT:
128 if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
129 return -EFAULT;
130 return 0;
131 case WDIOC_GETSTATUS:
132 case WDIOC_GETBOOTSTATUS:
133 return put_user(0,(int *)arg);
134 case WDIOC_KEEPALIVE:
135 indydog_ping();
136 return 0;
137 }
138 }
139
140 static struct file_operations indydog_fops = {
141 owner: THIS_MODULE,
142 write: indydog_write,
143 ioctl: indydog_ioctl,
144 open: indydog_open,
145 release: indydog_release,
146 };
147
148 static struct miscdevice indydog_miscdev = {
149 minor: WATCHDOG_MINOR,
150 name: "watchdog",
151 fops: &indydog_fops,
152 };
153
154 static const char banner[] __initdata = KERN_INFO "Hardware Watchdog Timer for SGI IP22: 0.2\n";
155
watchdog_init(void)156 static int __init watchdog_init(void)
157 {
158 int ret = misc_register(&indydog_miscdev);
159
160 if (ret)
161 return ret;
162
163 printk(banner);
164
165 return 0;
166 }
167
watchdog_exit(void)168 static void __exit watchdog_exit(void)
169 {
170 misc_deregister(&indydog_miscdev);
171 }
172
173 module_init(watchdog_init);
174 module_exit(watchdog_exit);
175 MODULE_LICENSE("GPL");
176