Blame view

drivers/watchdog/cpu5wdt.c 6.76 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  /*
   * sma cpu5 watchdog driver
   *
   * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
   *
   */
  
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/miscdevice.h>
  #include <linux/fs.h>
  #include <linux/init.h>
  #include <linux/ioport.h>
  #include <linux/timer.h>
3fe0c2776   Steven Rostedt   [PATCH] mutex sub...
31
  #include <linux/completion.h>
4e57b6817   Tim Schmielau   [PATCH] fix missi...
32
  #include <linux/jiffies.h>
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
33
34
  #include <linux/io.h>
  #include <linux/uaccess.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
35
36
37
  #include <linux/watchdog.h>
  
  /* adjustable parameters */
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
38
  static int verbose;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
39
40
  static int port = 0x91;
  static int ticks = 10000;
1334f3293   Axel Lin   watchdog: Use DEF...
41
  static DEFINE_SPINLOCK(cpu5wdt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  
  #define PFX			"cpu5wdt: "
  
  #define CPU5WDT_EXTENT          0x0A
  
  #define CPU5WDT_STATUS_REG      0x00
  #define CPU5WDT_TIME_A_REG      0x02
  #define CPU5WDT_TIME_B_REG      0x03
  #define CPU5WDT_MODE_REG        0x04
  #define CPU5WDT_TRIGGER_REG     0x07
  #define CPU5WDT_ENABLE_REG      0x08
  #define CPU5WDT_RESET_REG       0x09
  
  #define CPU5WDT_INTERVAL	(HZ/10+1)
  
  /* some device data */
  
  static struct {
3fe0c2776   Steven Rostedt   [PATCH] mutex sub...
60
  	struct completion stop;
996d62d44   Florian Fainelli   [WATCHDOG] Remove...
61
  	int running;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
62
  	struct timer_list timer;
996d62d44   Florian Fainelli   [WATCHDOG] Remove...
63
  	int queue;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
64
65
66
67
68
69
70
71
  	int default_ticks;
  	unsigned long inuse;
  } cpu5wdt_device;
  
  /* generic helper functions */
  
  static void cpu5wdt_trigger(unsigned long unused)
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
72
  	if (verbose > 2)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
73
74
  		printk(KERN_DEBUG PFX "trigger at %i ticks
  ", ticks);
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
75
  	if (cpu5wdt_device.running)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  		ticks--;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
77
  	spin_lock(&cpu5wdt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
78
79
80
81
  	/* keep watchdog alive */
  	outb(1, port + CPU5WDT_TRIGGER_REG);
  
  	/* requeue?? */
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
82
83
  	if (cpu5wdt_device.queue && ticks)
  		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84
85
  	else {
  		/* ticks doesn't matter anyway */
3fe0c2776   Steven Rostedt   [PATCH] mutex sub...
86
  		complete(&cpu5wdt_device.stop);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87
  	}
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
88
  	spin_unlock(&cpu5wdt_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
89
90
91
92
93
94
  
  }
  
  static void cpu5wdt_reset(void)
  {
  	ticks = cpu5wdt_device.default_ticks;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
95
  	if (verbose)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
96
97
98
99
100
101
102
  		printk(KERN_DEBUG PFX "reset (%i ticks)
  ", (int) ticks);
  
  }
  
  static void cpu5wdt_start(void)
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
103
104
105
106
  	unsigned long flags;
  
  	spin_lock_irqsave(&cpu5wdt_lock, flags);
  	if (!cpu5wdt_device.queue) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
107
108
109
110
111
112
  		cpu5wdt_device.queue = 1;
  		outb(0, port + CPU5WDT_TIME_A_REG);
  		outb(0, port + CPU5WDT_TIME_B_REG);
  		outb(1, port + CPU5WDT_MODE_REG);
  		outb(0, port + CPU5WDT_RESET_REG);
  		outb(0, port + CPU5WDT_ENABLE_REG);
82eb7c505   Jiri Slaby   [WATCHDOG] timers...
113
  		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
114
115
116
  	}
  	/* if process dies, counter is not decremented */
  	cpu5wdt_device.running++;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
117
  	spin_unlock_irqrestore(&cpu5wdt_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
118
119
120
121
  }
  
  static int cpu5wdt_stop(void)
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
122
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
123

6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
124
125
126
  	spin_lock_irqsave(&cpu5wdt_lock, flags);
  	if (cpu5wdt_device.running)
  		cpu5wdt_device.running = 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
127
  	ticks = cpu5wdt_device.default_ticks;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
128
129
  	spin_unlock_irqrestore(&cpu5wdt_lock, flags);
  	if (verbose)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
130
131
  		printk(KERN_CRIT PFX "stop not possible
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
132
133
134
135
136
137
138
  	return -EIO;
  }
  
  /* filesystem operations */
  
  static int cpu5wdt_open(struct inode *inode, struct file *file)
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
139
  	if (test_and_set_bit(0, &cpu5wdt_device.inuse))
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
140
  		return -EBUSY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
141
142
143
144
145
146
147
148
  	return nonseekable_open(inode, file);
  }
  
  static int cpu5wdt_release(struct inode *inode, struct file *file)
  {
  	clear_bit(0, &cpu5wdt_device.inuse);
  	return 0;
  }
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
149
150
  static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,
  						unsigned long arg)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
151
152
  {
  	void __user *argp = (void __user *)arg;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
153
  	int __user *p = argp;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
154
  	unsigned int value;
42747d712   Wim Van Sebroeck   [WATCHDOG] watchd...
155
  	static const struct watchdog_info ident = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
157
158
  		.options = WDIOF_CARDRESET,
  		.identity = "CPU5 WDT",
  	};
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
159
  	switch (cmd) {
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
160
161
162
  	case WDIOC_GETSUPPORT:
  		if (copy_to_user(argp, &ident, sizeof(ident)))
  			return -EFAULT;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
163
164
165
166
167
168
169
  		break;
  	case WDIOC_GETSTATUS:
  		value = inb(port + CPU5WDT_STATUS_REG);
  		value = (value >> 2) & 1;
  		return put_user(value, p);
  	case WDIOC_GETBOOTSTATUS:
  		return put_user(0, p);
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
170
171
172
173
174
175
176
177
  	case WDIOC_SETOPTIONS:
  		if (get_user(value, p))
  			return -EFAULT;
  		if (value & WDIOS_ENABLECARD)
  			cpu5wdt_start();
  		if (value & WDIOS_DISABLECARD)
  			cpu5wdt_stop();
  		break;
0c06090c9   Wim Van Sebroeck   [WATCHDOG] Coding...
178
179
180
  	case WDIOC_KEEPALIVE:
  		cpu5wdt_reset();
  		break;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
181
182
  	default:
  		return -ENOTTY;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
183
184
185
  	}
  	return 0;
  }
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
186
187
  static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,
  						size_t count, loff_t *ppos)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
188
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
189
  	if (!count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
191
  	cpu5wdt_reset();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
192
193
  	return count;
  }
62322d255   Arjan van de Ven   [PATCH] make more...
194
  static const struct file_operations cpu5wdt_fops = {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
195
196
  	.owner		= THIS_MODULE,
  	.llseek		= no_llseek,
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
197
  	.unlocked_ioctl	= cpu5wdt_ioctl,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  	.open		= cpu5wdt_open,
  	.write		= cpu5wdt_write,
  	.release	= cpu5wdt_release,
  };
  
  static struct miscdevice cpu5wdt_misc = {
  	.minor	= WATCHDOG_MINOR,
  	.name	= "watchdog",
  	.fops	= &cpu5wdt_fops,
  };
  
  /* init/exit function */
  
  static int __devinit cpu5wdt_init(void)
  {
  	unsigned int val;
  	int err;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
215
216
217
218
  	if (verbose)
  		printk(KERN_DEBUG PFX
  				"port=0x%x, verbose=%i
  ", port, verbose);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
219

6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
220
  	init_completion(&cpu5wdt_device.stop);
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
221
222
223
224
225
  	cpu5wdt_device.queue = 0;
  	setup_timer(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);
  	cpu5wdt_device.default_ticks = ticks;
  
  	if (!request_region(port, CPU5WDT_EXTENT, PFX)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
226
227
228
229
230
231
232
233
234
  		printk(KERN_ERR PFX "request_region failed
  ");
  		err = -EBUSY;
  		goto no_port;
  	}
  
  	/* watchdog reboot? */
  	val = inb(port + CPU5WDT_STATUS_REG);
  	val = (val >> 2) & 1;
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
235
  	if (!val)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
237
  		printk(KERN_INFO PFX "sorry, was my fault
  ");
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
238
239
240
241
242
243
  	err = misc_register(&cpu5wdt_misc);
  	if (err < 0) {
  		printk(KERN_ERR PFX "misc_register failed
  ");
  		goto no_misc;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
244

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
245
246
247
  
  	printk(KERN_INFO PFX "init success
  ");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
248
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
249
  no_misc:
fb8f7ba07   Alexey Dobriyan   [WATCHDOG] Semi-t...
250
251
  	release_region(port, CPU5WDT_EXTENT);
  no_port:
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
253
254
255
256
257
258
259
260
261
  	return err;
  }
  
  static int __devinit cpu5wdt_init_module(void)
  {
  	return cpu5wdt_init();
  }
  
  static void __devexit cpu5wdt_exit(void)
  {
6f932f18d   Alan Cox   [WATCHDOG 07/57] ...
262
  	if (cpu5wdt_device.queue) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  		cpu5wdt_device.queue = 0;
3fe0c2776   Steven Rostedt   [PATCH] mutex sub...
264
  		wait_for_completion(&cpu5wdt_device.stop);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
  	}
  
  	misc_deregister(&cpu5wdt_misc);
  
  	release_region(port, CPU5WDT_EXTENT);
  
  }
  
  static void __devexit cpu5wdt_exit_module(void)
  {
  	cpu5wdt_exit();
  }
  
  /* module entry points */
  
  module_init(cpu5wdt_init_module);
  module_exit(cpu5wdt_exit_module);
  
  MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
  MODULE_DESCRIPTION("sma cpu5 watchdog driver");
  MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  
  module_param(port, int, 0);
  MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");
  
  module_param(verbose, int, 0);
  MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");
  
  module_param(ticks, int, 0);
  MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");