aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/watchdog/ibmasr.c
diff options
context:
space:
mode:
authorAndrey Panin <pazke@donpac.ru>2005-08-19 17:15:11 -0400
committerWim Van Sebroeck <wim@iguana.be>2005-09-11 16:07:34 -0400
commitd532134d4cad1da0ad0efc1d5db9f74475df80c6 (patch)
tree4993dda18b6f8408b404666a026561778f2c7bc1 /drivers/char/watchdog/ibmasr.c
parentabda5c8bd20d3bd42718b0438b8a81a73ffa4372 (diff)
[WATCHDOG] driver-for-ibm-automatic-server-restart-watchdog.patch
This patch adds driver for IBM Automatic Server Restart watchdog hardware found in some IBM eServer xSeries machines. This driver is based on the ugly driver provided by IBM. Driver was tested on IBM eServer 226. Signed-off-by: Andrey Panin <pazke@donpac.ru> Signed-off-by: Wim Van Sebroeck <wim@iguana.be> Signed-off-by: Andrew Morton <akpm@osdl.org>
Diffstat (limited to 'drivers/char/watchdog/ibmasr.c')
-rw-r--r--drivers/char/watchdog/ibmasr.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c
new file mode 100644
index 000000000000..a32c073d29bc
--- /dev/null
+++ b/drivers/char/watchdog/ibmasr.c
@@ -0,0 +1,407 @@
1/*
2 * IBM Automatic Server Restart driver.
3 *
4 * Copyright (c) 2005 Andrey Panin <pazke@donpac.ru>
5 *
6 * Based on driver written by Pete Reynolds.
7 * Copyright (c) IBM Corporation, 1998-2004.
8 *
9 * This software may be used and distributed according to the terms
10 * of the GNU Public License, incorporated herein by reference.
11 */
12
13#include <linux/config.h>
14#include <linux/fs.h>
15#include <linux/kernel.h>
16#include <linux/slab.h>
17#include <linux/module.h>
18#include <linux/pci.h>
19#include <linux/timer.h>
20#include <linux/miscdevice.h>
21#include <linux/watchdog.h>
22#include <linux/dmi.h>
23
24#include <asm/io.h>
25#include <asm/uaccess.h>
26
27
28enum {
29 ASMTYPE_UNKNOWN,
30 ASMTYPE_TOPAZ,
31 ASMTYPE_JASPER,
32 ASMTYPE_PEARL,
33 ASMTYPE_JUNIPER,
34 ASMTYPE_SPRUCE,
35};
36
37#define PFX "ibmasr: "
38
39#define TOPAZ_ASR_REG_OFFSET 4
40#define TOPAZ_ASR_TOGGLE 0x40
41#define TOPAZ_ASR_DISABLE 0x80
42
43/* PEARL ASR S/W REGISTER SUPERIO PORT ADDRESSES */
44#define PEARL_BASE 0xe04
45#define PEARL_WRITE 0xe06
46#define PEARL_READ 0xe07
47
48#define PEARL_ASR_DISABLE_MASK 0x80 /* bit 7: disable = 1, enable = 0 */
49#define PEARL_ASR_TOGGLE_MASK 0x40 /* bit 6: 0, then 1, then 0 */
50
51/* JASPER OFFSET FROM SIO BASE ADDR TO ASR S/W REGISTERS. */
52#define JASPER_ASR_REG_OFFSET 0x38
53
54#define JASPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1, enable = 0 */
55#define JASPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */
56
57#define JUNIPER_BASE_ADDRESS 0x54b /* Base address of Juniper ASR */
58#define JUNIPER_ASR_DISABLE_MASK 0x01 /* bit 0: disable = 1 enable = 0 */
59#define JUNIPER_ASR_TOGGLE_MASK 0x02 /* bit 1: 0, then 1, then 0 */
60
61#define SPRUCE_BASE_ADDRESS 0x118e /* Base address of Spruce ASR */
62#define SPRUCE_ASR_DISABLE_MASK 0x01 /* bit 1: disable = 1 enable = 0 */
63#define SPRUCE_ASR_TOGGLE_MASK 0x02 /* bit 0: 0, then 1, then 0 */
64
65
66static int nowayout = WATCHDOG_NOWAYOUT;
67
68static unsigned long asr_is_open;
69static char asr_expect_close;
70
71static unsigned int asr_type, asr_base, asr_length;
72static unsigned int asr_read_addr, asr_write_addr;
73static unsigned char asr_toggle_mask, asr_disable_mask;
74
75static void asr_toggle(void)
76{
77 unsigned char reg = inb(asr_read_addr);
78
79 outb(reg & ~asr_toggle_mask, asr_write_addr);
80 reg = inb(asr_read_addr);
81
82 outb(reg | asr_toggle_mask, asr_write_addr);
83 reg = inb(asr_read_addr);
84
85 outb(reg & ~asr_toggle_mask, asr_write_addr);
86 reg = inb(asr_read_addr);
87}
88
89static void asr_enable(void)
90{
91 unsigned char reg;
92
93 if (asr_type == ASMTYPE_TOPAZ) {
94 /* asr_write_addr == asr_read_addr */
95 reg = inb(asr_read_addr);
96 outb(reg & ~(TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE),
97 asr_read_addr);
98 } else {
99 /*
100 * First make sure the hardware timer is reset by toggling
101 * ASR hardware timer line.
102 */
103 asr_toggle();
104
105 reg = inb(asr_read_addr);
106 outb(reg & ~asr_disable_mask, asr_write_addr);
107 }
108 reg = inb(asr_read_addr);
109}
110
111static void asr_disable(void)
112{
113 unsigned char reg = inb(asr_read_addr);
114
115 if (asr_type == ASMTYPE_TOPAZ)
116 /* asr_write_addr == asr_read_addr */
117 outb(reg | TOPAZ_ASR_TOGGLE | TOPAZ_ASR_DISABLE,
118 asr_read_addr);
119 else {
120 outb(reg | asr_toggle_mask, asr_write_addr);
121 reg = inb(asr_read_addr);
122
123 outb(reg | asr_disable_mask, asr_write_addr);
124 }
125 reg = inb(asr_read_addr);
126}
127
128static int __init asr_get_base_address(void)
129{
130 unsigned char low, high;
131 const char *type = "";
132
133 asr_length = 1;
134
135 switch (asr_type) {
136 case ASMTYPE_TOPAZ:
137 /* SELECT SuperIO CHIP FOR QUERYING (WRITE 0x07 TO BOTH 0x2E and 0x2F) */
138 outb(0x07, 0x2e);
139 outb(0x07, 0x2f);
140
141 /* SELECT AND READ THE HIGH-NIBBLE OF THE GPIO BASE ADDRESS */
142 outb(0x60, 0x2e);
143 high = inb(0x2f);
144
145 /* SELECT AND READ THE LOW-NIBBLE OF THE GPIO BASE ADDRESS */
146 outb(0x61, 0x2e);
147 low = inb(0x2f);
148
149 asr_base = (high << 16) | low;
150 asr_read_addr = asr_write_addr =
151 asr_base + TOPAZ_ASR_REG_OFFSET;
152 asr_length = 5;
153
154 break;
155
156 case ASMTYPE_JASPER:
157 type = "Jaspers ";
158
159 /* FIXME: need to use pci_config_lock here, but it's not exported */
160
161/* spin_lock_irqsave(&pci_config_lock, flags);*/
162
163 /* Select the SuperIO chip in the PCI I/O port register */
164 outl(0x8000f858, 0xcf8);
165
166 /*
167 * Read the base address for the SuperIO chip.
168 * Only the lower 16 bits are valid, but the address is word
169 * aligned so the last bit must be masked off.
170 */
171 asr_base = inl(0xcfc) & 0xfffe;
172
173/* spin_unlock_irqrestore(&pci_config_lock, flags);*/
174
175 asr_read_addr = asr_write_addr =
176 asr_base + JASPER_ASR_REG_OFFSET;
177 asr_toggle_mask = JASPER_ASR_TOGGLE_MASK;
178 asr_disable_mask = JASPER_ASR_DISABLE_MASK;
179 asr_length = JASPER_ASR_REG_OFFSET + 1;
180
181 break;
182
183 case ASMTYPE_PEARL:
184 type = "Pearls ";
185 asr_base = PEARL_BASE;
186 asr_read_addr = PEARL_READ;
187 asr_write_addr = PEARL_WRITE;
188 asr_toggle_mask = PEARL_ASR_TOGGLE_MASK;
189 asr_disable_mask = PEARL_ASR_DISABLE_MASK;
190 asr_length = 4;
191 break;
192
193 case ASMTYPE_JUNIPER:
194 type = "Junipers ";
195 asr_base = JUNIPER_BASE_ADDRESS;
196 asr_read_addr = asr_write_addr = asr_base;
197 asr_toggle_mask = JUNIPER_ASR_TOGGLE_MASK;
198 asr_disable_mask = JUNIPER_ASR_DISABLE_MASK;
199 break;
200
201 case ASMTYPE_SPRUCE:
202 type = "Spruce's ";
203 asr_base = SPRUCE_BASE_ADDRESS;
204 asr_read_addr = asr_write_addr = asr_base;
205 asr_toggle_mask = SPRUCE_ASR_TOGGLE_MASK;
206 asr_disable_mask = SPRUCE_ASR_DISABLE_MASK;
207 break;
208 }
209
210 if (!request_region(asr_base, asr_length, "ibmasr")) {
211 printk(KERN_ERR PFX "address %#x already in use\n",
212 asr_base);
213 return -EBUSY;
214 }
215
216 printk(KERN_INFO PFX "found %sASR @ addr %#x\n", type, asr_base);
217
218 return 0;
219}
220
221
222static ssize_t asr_write(struct file *file, const char __user *buf,
223 size_t count, loff_t *ppos)
224{
225 if (count) {
226 if (!nowayout) {
227 size_t i;
228
229 /* In case it was set long ago */
230 asr_expect_close = 0;
231
232 for (i = 0; i != count; i++) {
233 char c;
234 if (get_user(c, buf + i))
235 return -EFAULT;
236 if (c == 'V')
237 asr_expect_close = 42;
238 }
239 }
240 asr_toggle();
241 }
242 return count;
243}
244
245static int asr_ioctl(struct inode *inode, struct file *file,
246 unsigned int cmd, unsigned long arg)
247{
248 static const struct watchdog_info ident = {
249 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
250 WDIOF_MAGICCLOSE,
251 .identity = "IBM ASR"
252 };
253 void __user *argp = (void __user *)arg;
254 int __user *p = argp;
255 int heartbeat;
256
257 switch (cmd) {
258 case WDIOC_GETSUPPORT:
259 return copy_to_user(argp, &ident, sizeof(ident)) ?
260 -EFAULT : 0;
261
262 case WDIOC_GETSTATUS:
263 case WDIOC_GETBOOTSTATUS:
264 return put_user(0, p);
265
266 case WDIOC_KEEPALIVE:
267 asr_toggle();
268 return 0;
269
270
271 case WDIOC_SETTIMEOUT:
272 if (get_user(heartbeat, p))
273 return -EFAULT;
274 /* Fall */
275
276 case WDIOC_GETTIMEOUT:
277 heartbeat = 256;
278 return put_user(heartbeat, p);
279
280 case WDIOC_SETOPTIONS: {
281 int new_options, retval = -EINVAL;
282
283 if (get_user(new_options, p))
284 return -EFAULT;
285
286 if (new_options & WDIOS_DISABLECARD) {
287 asr_disable();
288 retval = 0;
289 }
290
291 if (new_options & WDIOS_ENABLECARD) {
292 asr_enable();
293 asr_toggle();
294 retval = 0;
295 }
296
297 return retval;
298 }
299 }
300
301 return -ENOIOCTLCMD;
302}
303
304static int asr_open(struct inode *inode, struct file *file)
305{
306 if(test_and_set_bit(0, &asr_is_open))
307 return -EBUSY;
308
309 asr_toggle();
310 asr_enable();
311
312 return nonseekable_open(inode, file);
313}
314
315static int asr_release(struct inode *inode, struct file *file)
316{
317 if (asr_expect_close == 42)
318 asr_disable();
319 else {
320 printk(KERN_CRIT PFX "unexpected close, not stopping watchdog!\n");
321 asr_toggle();
322 }
323 clear_bit(0, &asr_is_open);
324 asr_expect_close = 0;
325 return 0;
326}
327
328static struct file_operations asr_fops = {
329 .owner = THIS_MODULE,
330 .llseek = no_llseek,
331 .write = asr_write,
332 .ioctl = asr_ioctl,
333 .open = asr_open,
334 .release = asr_release,
335};
336
337static struct miscdevice asr_miscdev = {
338 .minor = WATCHDOG_MINOR,
339 .name = "watchdog",
340 .fops = &asr_fops,
341};
342
343
344struct ibmasr_id {
345 const char *desc;
346 int type;
347};
348
349static struct ibmasr_id __initdata ibmasr_id_table[] = {
350 { "IBM Automatic Server Restart - eserver xSeries 220", ASMTYPE_TOPAZ },
351 { "IBM Automatic Server Restart - Machine Type 8673", ASMTYPE_PEARL },
352 { "IBM Automatic Server Restart - Machine Type 8480", ASMTYPE_JASPER },
353 { "IBM Automatic Server Restart - Machine Type 8482", ASMTYPE_JUNIPER },
354 { "IBM Automatic Server Restart - Machine Type 8648", ASMTYPE_SPRUCE },
355 { NULL }
356};
357
358static int __init ibmasr_init(void)
359{
360 struct ibmasr_id *id;
361 int rc;
362
363 for (id = ibmasr_id_table; id->desc; id++) {
364 if (dmi_find_device(DMI_DEV_TYPE_OTHER, id->desc, NULL)) {
365 asr_type = id->type;
366 break;
367 }
368 }
369
370 if (!asr_type)
371 return -ENODEV;
372
373 rc = misc_register(&asr_miscdev);
374 if (rc < 0) {
375 printk(KERN_ERR PFX "failed to register misc device\n");
376 return rc;
377 }
378
379 rc = asr_get_base_address();
380 if (rc) {
381 misc_deregister(&asr_miscdev);
382 return rc;
383 }
384
385 return 0;
386}
387
388static void __exit ibmasr_exit(void)
389{
390 if (!nowayout)
391 asr_disable();
392
393 misc_deregister(&asr_miscdev);
394
395 release_region(asr_base, asr_length);
396}
397
398module_init(ibmasr_init);
399module_exit(ibmasr_exit);
400
401module_param(nowayout, int, 0);
402MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
403
404MODULE_DESCRIPTION("IBM Automatic Server Restart driver");
405MODULE_AUTHOR("Andrey Panin");
406MODULE_LICENSE("GPL");
407MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);