OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [sbc60xxwdt.c] - Blame information for rev 1275

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1275 phoenix
/*
2
 *      60xx Single Board Computer Watchdog Timer driver for Linux 2.2.x
3
 *
4
 *      Based on acquirewdt.c by Alan Cox.
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
 *      The author does NOT admit liability nor provide warranty for
12
 *      any of this software. This material is provided "AS-IS" in
13
 *      the hope that it may be useful for others.
14
 *
15
 *      (c) Copyright 2000    Jakob Oestergaard <jakob@ostenfeld.dk>
16
 *
17
 *           12/4 - 2000      [Initial revision]
18
 *           25/4 - 2000      Added /dev/watchdog support
19
 *           09/5 - 2001      [smj@oro.net] fixed fop_write to "return 1" on success
20
 *
21
 *
22
 *  Theory of operation:
23
 *  A Watchdog Timer (WDT) is a hardware circuit that can
24
 *  reset the computer system in case of a software fault.
25
 *  You probably knew that already.
26
 *
27
 *  Usually a userspace daemon will notify the kernel WDT driver
28
 *  via the /proc/watchdog special device file that userspace is
29
 *  still alive, at regular intervals.  When such a notification
30
 *  occurs, the driver will usually tell the hardware watchdog
31
 *  that everything is in order, and that the watchdog should wait
32
 *  for yet another little while to reset the system.
33
 *  If userspace fails (RAM error, kernel bug, whatever), the
34
 *  notifications cease to occur, and the hardware watchdog will
35
 *  reset the system (causing a reboot) after the timeout occurs.
36
 *
37
 *  This WDT driver is different from the other Linux WDT
38
 *  drivers in several ways:
39
 *  *)  The driver will ping the watchdog by itself, because this
40
 *      particular WDT has a very short timeout (one second) and it
41
 *      would be insane to count on any userspace daemon always
42
 *      getting scheduled within that time frame.
43
 *  *)  This driver expects the userspace daemon to send a specific
44
 *      character code ('V') to /dev/watchdog before closing the
45
 *      /dev/watchdog file.  If the userspace daemon closes the file
46
 *      without sending this special character, the driver will assume
47
 *      that the daemon (and userspace in general) died, and will
48
 *      stop pinging the WDT without disabling it first.  This will
49
 *      cause a reboot.
50
 *
51
 *  Why `V' ?  Well, `V' is the character in ASCII for the value 86,
52
 *  and we all know that 86 is _the_ most random number in the universe.
53
 *  Therefore it is the letter that has the slightest chance of occuring
54
 *  by chance, when the system becomes corrupted.
55
 *
56
 */
57
 
58
#include <linux/module.h>
59
#include <linux/version.h>
60
#include <linux/types.h>
61
#include <linux/errno.h>
62
#include <linux/kernel.h>
63
#include <linux/timer.h>
64
#include <linux/sched.h>
65
#include <linux/miscdevice.h>
66
#include <linux/watchdog.h>
67
#include <linux/slab.h>
68
#include <linux/ioport.h>
69
#include <linux/fcntl.h>
70
#include <linux/smp_lock.h>
71
#include <asm/io.h>
72
#include <asm/uaccess.h>
73
#include <asm/system.h>
74
#include <linux/notifier.h>
75
#include <linux/reboot.h>
76
#include <linux/init.h>
77
 
78
#define OUR_NAME "sbc60xxwdt"
79
 
80
/*
81
 * You must set these - The driver cannot probe for the settings
82
 */
83
 
84
#define WDT_STOP 0x45
85
#define WDT_START 0x443
86
 
87
/*
88
 * The 60xx board can use watchdog timeout values from one second
89
 * to several minutes.  The default is one second, so if we reset
90
 * the watchdog every ~250ms we should be safe.
91
 */
92
 
93
#define WDT_INTERVAL (HZ/4+1)
94
 
95
/*
96
 * We must not require too good response from the userspace daemon.
97
 * Here we require the userspace daemon to send us a heartbeat
98
 * char to /dev/watchdog every 10 seconds.
99
 * If the daemon pulses us every 5 seconds, we can still afford
100
 * a 5 second scheduling delay on the (high priority) daemon. That
101
 * should be sufficient for a box under any load.
102
 */
103
 
104
#define WDT_HEARTBEAT (HZ * 10)
105
 
106
static void wdt_timer_ping(unsigned long);
107
static struct timer_list timer;
108
static unsigned long next_heartbeat;
109
static int wdt_is_open;
110
static int wdt_expect_close;
111
 
112
/*
113
 *      Whack the dog
114
 */
115
 
116
static void wdt_timer_ping(unsigned long data)
117
{
118
        /* If we got a heartbeat pulse within the WDT_US_INTERVAL
119
         * we agree to ping the WDT
120
         */
121
        if(time_before(jiffies, next_heartbeat))
122
        {
123
                /* Ping the WDT by reading from WDT_START */
124
                inb_p(WDT_START);
125
                /* Re-set the timer interval */
126
                timer.expires = jiffies + WDT_INTERVAL;
127
                add_timer(&timer);
128
        } else {
129
                printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n");
130
        }
131
}
132
 
133
/*
134
 * Utility routines
135
 */
136
 
137
static void wdt_startup(void)
138
{
139
        next_heartbeat = jiffies + WDT_HEARTBEAT;
140
 
141
        /* Start the timer */
142
        timer.expires = jiffies + WDT_INTERVAL;
143
        add_timer(&timer);
144
        printk(OUR_NAME ": Watchdog timer is now enabled.\n");
145
}
146
 
147
static void wdt_turnoff(void)
148
{
149
        /* Stop the timer */
150
        del_timer(&timer);
151
        inb_p(WDT_STOP);
152
        printk(OUR_NAME ": Watchdog timer is now disabled...\n");
153
}
154
 
155
 
156
/*
157
 * /dev/watchdog handling
158
 */
159
 
160
static ssize_t fop_write(struct file * file, const char * buf, size_t count, loff_t * ppos)
161
{
162
        /* We can't seek */
163
        if(ppos != &file->f_pos)
164
                return -ESPIPE;
165
 
166
        /* See if we got the magic character */
167
        if(count)
168
        {
169
                size_t ofs;
170
 
171
                /* note: just in case someone wrote the magic character
172
                 * five months ago... */
173
                wdt_expect_close = 0;
174
 
175
                /* now scan */
176
                for(ofs = 0; ofs != count; ofs++)
177
                {
178
                        char c;
179
                        if(get_user(c, buf+ofs))
180
                                return -EFAULT;
181
                        if(c == 'V')
182
                                wdt_expect_close = 1;
183
                }
184
                /* Well, anyhow someone wrote to us, we should return that favour */
185
                next_heartbeat = jiffies + WDT_HEARTBEAT;
186
                return 1;
187
        }
188
        return 0;
189
}
190
 
191
static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos)
192
{
193
        /* No can do */
194
        return -EINVAL;
195
}
196
 
197
static int fop_open(struct inode * inode, struct file * file)
198
{
199
        switch(MINOR(inode->i_rdev))
200
        {
201
                case WATCHDOG_MINOR:
202
                        /* Just in case we're already talking to someone... */
203
                        if(wdt_is_open)
204
                                return -EBUSY;
205
                        /* Good, fire up the show */
206
                        wdt_is_open = 1;
207
                        wdt_startup();
208
                        return 0;
209
 
210
                default:
211
                        return -ENODEV;
212
        }
213
}
214
 
215
static int fop_close(struct inode * inode, struct file * file)
216
{
217
        lock_kernel();
218
        if(MINOR(inode->i_rdev) == WATCHDOG_MINOR)
219
        {
220
                if(wdt_expect_close)
221
                        wdt_turnoff();
222
                else {
223
                        del_timer(&timer);
224
                        printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n");
225
                }
226
        }
227
        wdt_is_open = 0;
228
        unlock_kernel();
229
        return 0;
230
}
231
 
232
static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
233
        unsigned long arg)
234
{
235
        static struct watchdog_info ident=
236
        {
237
                WDIOF_MAGICCLOSE,
238
                1,
239
                "SB60xx"
240
        };
241
 
242
        switch(cmd)
243
        {
244
                default:
245
                        return -ENOTTY;
246
                case WDIOC_GETSUPPORT:
247
                        return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0;
248
                case WDIOC_KEEPALIVE:
249
                        next_heartbeat = jiffies + WDT_HEARTBEAT;
250
                        return 0;
251
        }
252
}
253
 
254
static struct file_operations wdt_fops = {
255
        owner:          THIS_MODULE,
256
        llseek:         no_llseek,
257
        read:           fop_read,
258
        write:          fop_write,
259
        open:           fop_open,
260
        release:        fop_close,
261
        ioctl:          fop_ioctl
262
};
263
 
264
static struct miscdevice wdt_miscdev = {
265
        WATCHDOG_MINOR,
266
        "watchdog",
267
        &wdt_fops
268
};
269
 
270
/*
271
 *      Notifier for system down
272
 */
273
 
274
static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
275
        void *unused)
276
{
277
        if(code==SYS_DOWN || code==SYS_HALT)
278
                wdt_turnoff();
279
        return NOTIFY_DONE;
280
}
281
 
282
/*
283
 *      The WDT needs to learn about soft shutdowns in order to
284
 *      turn the timebomb registers off.
285
 */
286
 
287
static struct notifier_block wdt_notifier=
288
{
289
        wdt_notify_sys,
290
        0,
291
 
292
};
293
 
294
static void __exit sbc60xxwdt_unload(void)
295
{
296
        wdt_turnoff();
297
 
298
        /* Deregister */
299
        misc_deregister(&wdt_miscdev);
300
 
301
        unregister_reboot_notifier(&wdt_notifier);
302
        release_region(WDT_START,1);
303
//      release_region(WDT_STOP,1);
304
}
305
 
306
static int __init sbc60xxwdt_init(void)
307
{
308
        int rc = -EBUSY;
309
 
310
//      We cannot reserve 0x45 - the kernel already has!
311
//      if (!request_region(WDT_STOP, 1, "SBC 60XX WDT"))
312
//              goto err_out;
313
        if (!request_region(WDT_START, 1, "SBC 60XX WDT"))
314
                goto err_out_region1;
315
 
316
        init_timer(&timer);
317
        timer.function = wdt_timer_ping;
318
        timer.data = 0;
319
 
320
        rc = misc_register(&wdt_miscdev);
321
        if (rc)
322
                goto err_out_region2;
323
 
324
        rc = register_reboot_notifier(&wdt_notifier);
325
        if (rc)
326
                goto err_out_miscdev;
327
 
328
        printk(KERN_INFO OUR_NAME ": WDT driver for 60XX single board computer initialised.\n");
329
 
330
        return 0;
331
 
332
err_out_miscdev:
333
        misc_deregister(&wdt_miscdev);
334
err_out_region2:
335
        release_region(WDT_START,1);
336
err_out_region1:
337
        release_region(WDT_STOP,1);
338
/* err_out: */
339
        return rc;
340
}
341
 
342
module_init(sbc60xxwdt_init);
343
module_exit(sbc60xxwdt_unload);
344
 
345
MODULE_LICENSE("GPL");
346
EXPORT_NO_SYMBOLS;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.