~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/watchdog/eurotechwdt.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      Eurotech CPU-1220/1410/1420 on board WDT driver
 
3
 *
 
4
 *      (c) Copyright 2001 Ascensit <support@ascensit.com>
 
5
 *      (c) Copyright 2001 Rodolfo Giometti <giometti@ascensit.com>
 
6
 *      (c) Copyright 2002 Rob Radez <rob@osinvestor.com>
 
7
 *
 
8
 *      Based on wdt.c.
 
9
 *      Original copyright messages:
 
10
 *
 
11
 *      (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
 
12
 *                                              All Rights Reserved.
 
13
 *
 
14
 *      This program is free software; you can redistribute it and/or
 
15
 *      modify it under the terms of the GNU General Public License
 
16
 *      as published by the Free Software Foundation; either version
 
17
 *      2 of the License, or (at your option) any later version.
 
18
 *
 
19
 *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
 
20
 *      warranty for any of this software. This material is provided
 
21
 *      "AS-IS" and at no charge.
 
22
 *
 
23
 *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>*
 
24
 */
 
25
 
 
26
/* Changelog:
 
27
 *
 
28
 * 2001 - Rodolfo Giometti
 
29
 *      Initial release
 
30
 *
 
31
 * 2002/04/25 - Rob Radez
 
32
 *      clean up #includes
 
33
 *      clean up locking
 
34
 *      make __setup param unique
 
35
 *      proper options in watchdog_info
 
36
 *      add WDIOC_GETSTATUS and WDIOC_SETOPTIONS ioctls
 
37
 *      add expect_close support
 
38
 *
 
39
 * 2002.05.30 - Joel Becker <joel.becker@oracle.com>
 
40
 *      Added Matt Domsch's nowayout module option.
 
41
 */
 
42
 
 
43
/*
 
44
 *      The eurotech CPU-1220/1410/1420's watchdog is a part
 
45
 *      of the on-board SUPER I/O device SMSC FDC 37B782.
 
46
 */
 
47
 
 
48
#include <linux/interrupt.h>
 
49
#include <linux/module.h>
 
50
#include <linux/moduleparam.h>
 
51
#include <linux/types.h>
 
52
#include <linux/miscdevice.h>
 
53
#include <linux/watchdog.h>
 
54
#include <linux/fs.h>
 
55
#include <linux/ioport.h>
 
56
#include <linux/notifier.h>
 
57
#include <linux/reboot.h>
 
58
#include <linux/init.h>
 
59
#include <linux/io.h>
 
60
#include <linux/uaccess.h>
 
61
 
 
62
#include <asm/system.h>
 
63
 
 
64
static unsigned long eurwdt_is_open;
 
65
static int eurwdt_timeout;
 
66
static char eur_expect_close;
 
67
static spinlock_t eurwdt_lock;
 
68
 
 
69
/*
 
70
 * You must set these - there is no sane way to probe for this board.
 
71
 */
 
72
 
 
73
static int io = 0x3f0;
 
74
static int irq = 10;
 
75
static char *ev = "int";
 
76
 
 
77
#define WDT_TIMEOUT             60                /* 1 minute */
 
78
 
 
79
static int nowayout = WATCHDOG_NOWAYOUT;
 
80
module_param(nowayout, int, 0);
 
81
MODULE_PARM_DESC(nowayout,
 
82
                "Watchdog cannot be stopped once started (default="
 
83
                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
84
 
 
85
/*
 
86
 * Some symbolic names
 
87
 */
 
88
 
 
89
#define WDT_CTRL_REG            0x30
 
90
#define WDT_OUTPIN_CFG          0xe2
 
91
#define WDT_EVENT_INT           0x00
 
92
#define WDT_EVENT_REBOOT        0x08
 
93
#define WDT_UNIT_SEL            0xf1
 
94
#define WDT_UNIT_SECS           0x80
 
95
#define WDT_TIMEOUT_VAL         0xf2
 
96
#define WDT_TIMER_CFG           0xf3
 
97
 
 
98
 
 
99
module_param(io, int, 0);
 
100
MODULE_PARM_DESC(io, "Eurotech WDT io port (default=0x3f0)");
 
101
module_param(irq, int, 0);
 
102
MODULE_PARM_DESC(irq, "Eurotech WDT irq (default=10)");
 
103
module_param(ev, charp, 0);
 
104
MODULE_PARM_DESC(ev, "Eurotech WDT event type (default is `int')");
 
105
 
 
106
 
 
107
/*
 
108
 * Programming support
 
109
 */
 
110
 
 
111
static inline void eurwdt_write_reg(u8 index, u8 data)
 
112
{
 
113
        outb(index, io);
 
114
        outb(data, io+1);
 
115
}
 
116
 
 
117
static inline void eurwdt_lock_chip(void)
 
118
{
 
119
        outb(0xaa, io);
 
120
}
 
121
 
 
122
static inline void eurwdt_unlock_chip(void)
 
123
{
 
124
        outb(0x55, io);
 
125
        eurwdt_write_reg(0x07, 0x08);   /* set the logical device */
 
126
}
 
127
 
 
128
static inline void eurwdt_set_timeout(int timeout)
 
129
{
 
130
        eurwdt_write_reg(WDT_TIMEOUT_VAL, (u8) timeout);
 
131
}
 
132
 
 
133
static inline void eurwdt_disable_timer(void)
 
134
{
 
135
        eurwdt_set_timeout(0);
 
136
}
 
137
 
 
138
static void eurwdt_activate_timer(void)
 
139
{
 
140
        eurwdt_disable_timer();
 
141
        eurwdt_write_reg(WDT_CTRL_REG, 0x01);   /* activate the WDT */
 
142
        eurwdt_write_reg(WDT_OUTPIN_CFG,
 
143
                !strcmp("int", ev) ? WDT_EVENT_INT : WDT_EVENT_REBOOT);
 
144
 
 
145
        /* Setting interrupt line */
 
146
        if (irq == 2 || irq > 15 || irq < 0) {
 
147
                printk(KERN_ERR ": invalid irq number\n");
 
148
                irq = 0;        /* if invalid we disable interrupt */
 
149
        }
 
150
        if (irq == 0)
 
151
                printk(KERN_INFO ": interrupt disabled\n");
 
152
 
 
153
        eurwdt_write_reg(WDT_TIMER_CFG, irq << 4);
 
154
 
 
155
        eurwdt_write_reg(WDT_UNIT_SEL, WDT_UNIT_SECS);  /* we use seconds */
 
156
        eurwdt_set_timeout(0);  /* the default timeout */
 
157
}
 
158
 
 
159
 
 
160
/*
 
161
 * Kernel methods.
 
162
 */
 
163
 
 
164
static irqreturn_t eurwdt_interrupt(int irq, void *dev_id)
 
165
{
 
166
        printk(KERN_CRIT "timeout WDT timeout\n");
 
167
 
 
168
#ifdef ONLY_TESTING
 
169
        printk(KERN_CRIT "Would Reboot.\n");
 
170
#else
 
171
        printk(KERN_CRIT "Initiating system reboot.\n");
 
172
        emergency_restart();
 
173
#endif
 
174
        return IRQ_HANDLED;
 
175
}
 
176
 
 
177
 
 
178
/**
 
179
 * eurwdt_ping:
 
180
 *
 
181
 * Reload counter one with the watchdog timeout.
 
182
 */
 
183
 
 
184
static void eurwdt_ping(void)
 
185
{
 
186
        /* Write the watchdog default value */
 
187
        eurwdt_set_timeout(eurwdt_timeout);
 
188
}
 
189
 
 
190
/**
 
191
 * eurwdt_write:
 
192
 * @file: file handle to the watchdog
 
193
 * @buf: buffer to write (unused as data does not matter here
 
194
 * @count: count of bytes
 
195
 * @ppos: pointer to the position to write. No seeks allowed
 
196
 *
 
197
 * A write to a watchdog device is defined as a keepalive signal. Any
 
198
 * write of data will do, as we we don't define content meaning.
 
199
 */
 
200
 
 
201
static ssize_t eurwdt_write(struct file *file, const char __user *buf,
 
202
size_t count, loff_t *ppos)
 
203
{
 
204
        if (count) {
 
205
                if (!nowayout) {
 
206
                        size_t i;
 
207
 
 
208
                        eur_expect_close = 0;
 
209
 
 
210
                        for (i = 0; i != count; i++) {
 
211
                                char c;
 
212
                                if (get_user(c, buf + i))
 
213
                                        return -EFAULT;
 
214
                                if (c == 'V')
 
215
                                        eur_expect_close = 42;
 
216
                        }
 
217
                }
 
218
                spin_lock(&eurwdt_lock);
 
219
                eurwdt_ping();  /* the default timeout */
 
220
                spin_unlock(&eurwdt_lock);
 
221
        }
 
222
        return count;
 
223
}
 
224
 
 
225
/**
 
226
 * eurwdt_ioctl:
 
227
 * @file: file handle to the device
 
228
 * @cmd: watchdog command
 
229
 * @arg: argument pointer
 
230
 *
 
231
 * The watchdog API defines a common set of functions for all watchdogs
 
232
 * according to their available features.
 
233
 */
 
234
 
 
235
static long eurwdt_ioctl(struct file *file,
 
236
                                        unsigned int cmd, unsigned long arg)
 
237
{
 
238
        void __user *argp = (void __user *)arg;
 
239
        int __user *p = argp;
 
240
        static const struct watchdog_info ident = {
 
241
                .options          = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT
 
242
                                                        | WDIOF_MAGICCLOSE,
 
243
                .firmware_version = 1,
 
244
                .identity         = "WDT Eurotech CPU-1220/1410",
 
245
        };
 
246
 
 
247
        int time;
 
248
        int options, retval = -EINVAL;
 
249
 
 
250
        switch (cmd) {
 
251
        case WDIOC_GETSUPPORT:
 
252
                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 
253
 
 
254
        case WDIOC_GETSTATUS:
 
255
        case WDIOC_GETBOOTSTATUS:
 
256
                return put_user(0, p);
 
257
 
 
258
        case WDIOC_SETOPTIONS:
 
259
                if (get_user(options, p))
 
260
                        return -EFAULT;
 
261
                spin_lock(&eurwdt_lock);
 
262
                if (options & WDIOS_DISABLECARD) {
 
263
                        eurwdt_disable_timer();
 
264
                        retval = 0;
 
265
                }
 
266
                if (options & WDIOS_ENABLECARD) {
 
267
                        eurwdt_activate_timer();
 
268
                        eurwdt_ping();
 
269
                        retval = 0;
 
270
                }
 
271
                spin_unlock(&eurwdt_lock);
 
272
                return retval;
 
273
 
 
274
        case WDIOC_KEEPALIVE:
 
275
                spin_lock(&eurwdt_lock);
 
276
                eurwdt_ping();
 
277
                spin_unlock(&eurwdt_lock);
 
278
                return 0;
 
279
 
 
280
        case WDIOC_SETTIMEOUT:
 
281
                if (copy_from_user(&time, p, sizeof(int)))
 
282
                        return -EFAULT;
 
283
 
 
284
                /* Sanity check */
 
285
                if (time < 0 || time > 255)
 
286
                        return -EINVAL;
 
287
 
 
288
                spin_lock(&eurwdt_lock);
 
289
                eurwdt_timeout = time;
 
290
                eurwdt_set_timeout(time);
 
291
                spin_unlock(&eurwdt_lock);
 
292
                /* Fall */
 
293
 
 
294
        case WDIOC_GETTIMEOUT:
 
295
                return put_user(eurwdt_timeout, p);
 
296
 
 
297
        default:
 
298
                return -ENOTTY;
 
299
        }
 
300
}
 
301
 
 
302
/**
 
303
 * eurwdt_open:
 
304
 * @inode: inode of device
 
305
 * @file: file handle to device
 
306
 *
 
307
 * The misc device has been opened. The watchdog device is single
 
308
 * open and on opening we load the counter.
 
309
 */
 
310
 
 
311
static int eurwdt_open(struct inode *inode, struct file *file)
 
312
{
 
313
        if (test_and_set_bit(0, &eurwdt_is_open))
 
314
                return -EBUSY;
 
315
        eurwdt_timeout = WDT_TIMEOUT;   /* initial timeout */
 
316
        /* Activate the WDT */
 
317
        eurwdt_activate_timer();
 
318
        return nonseekable_open(inode, file);
 
319
}
 
320
 
 
321
/**
 
322
 * eurwdt_release:
 
323
 * @inode: inode to board
 
324
 * @file: file handle to board
 
325
 *
 
326
 * The watchdog has a configurable API. There is a religious dispute
 
327
 * between people who want their watchdog to be able to shut down and
 
328
 * those who want to be sure if the watchdog manager dies the machine
 
329
 * reboots. In the former case we disable the counters, in the latter
 
330
 * case you have to open it again very soon.
 
331
 */
 
332
 
 
333
static int eurwdt_release(struct inode *inode, struct file *file)
 
334
{
 
335
        if (eur_expect_close == 42)
 
336
                eurwdt_disable_timer();
 
337
        else {
 
338
                printk(KERN_CRIT
 
339
                        "eurwdt: Unexpected close, not stopping watchdog!\n");
 
340
                eurwdt_ping();
 
341
        }
 
342
        clear_bit(0, &eurwdt_is_open);
 
343
        eur_expect_close = 0;
 
344
        return 0;
 
345
}
 
346
 
 
347
/**
 
348
 * eurwdt_notify_sys:
 
349
 * @this: our notifier block
 
350
 * @code: the event being reported
 
351
 * @unused: unused
 
352
 *
 
353
 * Our notifier is called on system shutdowns. We want to turn the card
 
354
 * off at reboot otherwise the machine will reboot again during memory
 
355
 * test or worse yet during the following fsck. This would suck, in fact
 
356
 * trust me - if it happens it does suck.
 
357
 */
 
358
 
 
359
static int eurwdt_notify_sys(struct notifier_block *this, unsigned long code,
 
360
        void *unused)
 
361
{
 
362
        if (code == SYS_DOWN || code == SYS_HALT)
 
363
                eurwdt_disable_timer(); /* Turn the card off */
 
364
 
 
365
        return NOTIFY_DONE;
 
366
}
 
367
 
 
368
/*
 
369
 * Kernel Interfaces
 
370
 */
 
371
 
 
372
 
 
373
static const struct file_operations eurwdt_fops = {
 
374
        .owner          = THIS_MODULE,
 
375
        .llseek         = no_llseek,
 
376
        .write          = eurwdt_write,
 
377
        .unlocked_ioctl = eurwdt_ioctl,
 
378
        .open           = eurwdt_open,
 
379
        .release        = eurwdt_release,
 
380
};
 
381
 
 
382
static struct miscdevice eurwdt_miscdev = {
 
383
        .minor  = WATCHDOG_MINOR,
 
384
        .name   = "watchdog",
 
385
        .fops   = &eurwdt_fops,
 
386
};
 
387
 
 
388
/*
 
389
 * The WDT card needs to learn about soft shutdowns in order to
 
390
 * turn the timebomb registers off.
 
391
 */
 
392
 
 
393
static struct notifier_block eurwdt_notifier = {
 
394
        .notifier_call = eurwdt_notify_sys,
 
395
};
 
396
 
 
397
/**
 
398
 * cleanup_module:
 
399
 *
 
400
 * Unload the watchdog. You cannot do this with any file handles open.
 
401
 * If your watchdog is set to continue ticking on close and you unload
 
402
 * it, well it keeps ticking. We won't get the interrupt but the board
 
403
 * will not touch PC memory so all is fine. You just have to load a new
 
404
 * module in 60 seconds or reboot.
 
405
 */
 
406
 
 
407
static void __exit eurwdt_exit(void)
 
408
{
 
409
        eurwdt_lock_chip();
 
410
 
 
411
        misc_deregister(&eurwdt_miscdev);
 
412
 
 
413
        unregister_reboot_notifier(&eurwdt_notifier);
 
414
        release_region(io, 2);
 
415
        free_irq(irq, NULL);
 
416
}
 
417
 
 
418
/**
 
419
 * eurwdt_init:
 
420
 *
 
421
 * Set up the WDT watchdog board. After grabbing the resources
 
422
 * we require we need also to unlock the device.
 
423
 * The open() function will actually kick the board off.
 
424
 */
 
425
 
 
426
static int __init eurwdt_init(void)
 
427
{
 
428
        int ret;
 
429
 
 
430
        ret = request_irq(irq, eurwdt_interrupt, 0, "eurwdt", NULL);
 
431
        if (ret) {
 
432
                printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
 
433
                goto out;
 
434
        }
 
435
 
 
436
        if (!request_region(io, 2, "eurwdt")) {
 
437
                printk(KERN_ERR "eurwdt: IO %X is not free.\n", io);
 
438
                ret = -EBUSY;
 
439
                goto outirq;
 
440
        }
 
441
 
 
442
        ret = register_reboot_notifier(&eurwdt_notifier);
 
443
        if (ret) {
 
444
                printk(KERN_ERR
 
445
                    "eurwdt: can't register reboot notifier (err=%d)\n", ret);
 
446
                goto outreg;
 
447
        }
 
448
 
 
449
        spin_lock_init(&eurwdt_lock);
 
450
 
 
451
        ret = misc_register(&eurwdt_miscdev);
 
452
        if (ret) {
 
453
                printk(KERN_ERR "eurwdt: can't misc_register on minor=%d\n",
 
454
                WATCHDOG_MINOR);
 
455
                goto outreboot;
 
456
        }
 
457
 
 
458
        eurwdt_unlock_chip();
 
459
 
 
460
        ret = 0;
 
461
        printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)"
 
462
                " - timeout event: %s\n",
 
463
                io, irq, (!strcmp("int", ev) ? "int" : "reboot"));
 
464
 
 
465
out:
 
466
        return ret;
 
467
 
 
468
outreboot:
 
469
        unregister_reboot_notifier(&eurwdt_notifier);
 
470
 
 
471
outreg:
 
472
        release_region(io, 2);
 
473
 
 
474
outirq:
 
475
        free_irq(irq, NULL);
 
476
        goto out;
 
477
}
 
478
 
 
479
module_init(eurwdt_init);
 
480
module_exit(eurwdt_exit);
 
481
 
 
482
MODULE_AUTHOR("Rodolfo Giometti");
 
483
MODULE_DESCRIPTION("Driver for Eurotech CPU-1220/1410 on board watchdog");
 
484
MODULE_LICENSE("GPL");
 
485
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);