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

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [watchdog/] [machzwd.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *  MachZ ZF-Logic Watchdog Timer driver for Linux
3
 *
4
 *
5
 *  This program is free software; you can redistribute it and/or
6
 *  modify it under the terms of the GNU General Public License
7
 *  as published by the Free Software Foundation; either version
8
 *  2 of the License, or (at your option) any later version.
9
 *
10
 *  The author does NOT admit liability nor provide warranty for
11
 *  any of this software. This material is provided "AS-IS" in
12
 *  the hope that it may be useful for others.
13
 *
14
 *  Author: Fernando Fuganti <fuganti@conectiva.com.br>
15
 *
16
 *  Based on sbc60xxwdt.c by Jakob Oestergaard
17
 *
18
 *
19
 *  We have two timers (wd#1, wd#2) driven by a 32 KHz clock with the
20
 *  following periods:
21
 *      wd#1 - 2 seconds;
22
 *      wd#2 - 7.2 ms;
23
 *  After the expiration of wd#1, it can generate a NMI, SCI, SMI, or
24
 *  a system RESET and it starts wd#2 that unconditionaly will RESET
25
 *  the system when the counter reaches zero.
26
 *
27
 *  14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
28
 *      Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
29
 */
30
 
31
#include <linux/module.h>
32
#include <linux/moduleparam.h>
33
#include <linux/types.h>
34
#include <linux/timer.h>
35
#include <linux/jiffies.h>
36
#include <linux/miscdevice.h>
37
#include <linux/watchdog.h>
38
#include <linux/fs.h>
39
#include <linux/ioport.h>
40
#include <linux/notifier.h>
41
#include <linux/reboot.h>
42
#include <linux/init.h>
43
 
44
#include <asm/io.h>
45
#include <asm/uaccess.h>
46
#include <asm/system.h>
47
 
48
/* ports */
49
#define ZF_IOBASE       0x218
50
#define INDEX           0x218
51
#define DATA_B          0x219
52
#define DATA_W          0x21A
53
#define DATA_D          0x21A
54
 
55
/* indexes */                   /* size */
56
#define ZFL_VERSION     0x02    /* 16   */
57
#define CONTROL         0x10    /* 16   */
58
#define STATUS          0x12    /* 8    */
59
#define COUNTER_1       0x0C    /* 16   */
60
#define COUNTER_2       0x0E    /* 8    */
61
#define PULSE_LEN       0x0F    /* 8    */
62
 
63
/* controls */
64
#define ENABLE_WD1      0x0001
65
#define ENABLE_WD2      0x0002
66
#define RESET_WD1       0x0010
67
#define RESET_WD2       0x0020
68
#define GEN_SCI         0x0100
69
#define GEN_NMI         0x0200
70
#define GEN_SMI         0x0400
71
#define GEN_RESET       0x0800
72
 
73
 
74
/* utilities */
75
 
76
#define WD1     0
77
#define WD2     1
78
 
79
#define zf_writew(port, data)  { outb(port, INDEX); outw(data, DATA_W); }
80
#define zf_writeb(port, data)  { outb(port, INDEX); outb(data, DATA_B); }
81
#define zf_get_ZFL_version()   zf_readw(ZFL_VERSION)
82
 
83
 
84
static unsigned short zf_readw(unsigned char port)
85
{
86
        outb(port, INDEX);
87
        return inw(DATA_W);
88
}
89
 
90
 
91
MODULE_AUTHOR("Fernando Fuganti <fuganti@conectiva.com.br>");
92
MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver");
93
MODULE_LICENSE("GPL");
94
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
95
 
96
static int nowayout = WATCHDOG_NOWAYOUT;
97
module_param(nowayout, int, 0);
98
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
99
 
100
#define PFX "machzwd"
101
 
102
static struct watchdog_info zf_info = {
103
        .options                = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
104
        .firmware_version       = 1,
105
        .identity               = "ZF-Logic watchdog",
106
};
107
 
108
 
109
/*
110
 * action refers to action taken when watchdog resets
111
 * 0 = GEN_RESET
112
 * 1 = GEN_SMI
113
 * 2 = GEN_NMI
114
 * 3 = GEN_SCI
115
 * defaults to GEN_RESET (0)
116
 */
117
static int action = 0;
118
module_param(action, int, 0);
119
MODULE_PARM_DESC(action, "after watchdog resets, generate: 0 = RESET(*)  1 = SMI  2 = NMI  3 = SCI");
120
 
121
static void zf_ping(unsigned long data);
122
 
123
static int zf_action = GEN_RESET;
124
static unsigned long zf_is_open;
125
static char zf_expect_close;
126
static DEFINE_SPINLOCK(zf_lock);
127
static DEFINE_SPINLOCK(zf_port_lock);
128
static DEFINE_TIMER(zf_timer, zf_ping, 0, 0);
129
static unsigned long next_heartbeat = 0;
130
 
131
 
132
/* timeout for user land heart beat (10 seconds) */
133
#define ZF_USER_TIMEO (HZ*10)
134
 
135
/* timeout for hardware watchdog (~500ms) */
136
#define ZF_HW_TIMEO (HZ/2)
137
 
138
/* number of ticks on WD#1 (driven by a 32KHz clock, 2s) */
139
#define ZF_CTIMEOUT 0xffff
140
 
141
#ifndef ZF_DEBUG
142
#       define dprintk(format, args...)
143
#else
144
#       define dprintk(format, args...) printk(KERN_DEBUG PFX ":%s:%d: " format, __FUNCTION__, __LINE__ , ## args)
145
#endif
146
 
147
 
148
static inline void zf_set_status(unsigned char new)
149
{
150
        zf_writeb(STATUS, new);
151
}
152
 
153
 
154
/* CONTROL register functions */
155
 
156
static inline unsigned short zf_get_control(void)
157
{
158
        return zf_readw(CONTROL);
159
}
160
 
161
static inline void zf_set_control(unsigned short new)
162
{
163
        zf_writew(CONTROL, new);
164
}
165
 
166
 
167
/* WD#? counter functions */
168
/*
169
 *      Just set counter value
170
 */
171
 
172
static inline void zf_set_timer(unsigned short new, unsigned char n)
173
{
174
        switch(n){
175
                case WD1:
176
                        zf_writew(COUNTER_1, new);
177
                case WD2:
178
                        zf_writeb(COUNTER_2, new > 0xff ? 0xff : new);
179
                default:
180
                        return;
181
        }
182
}
183
 
184
/*
185
 * stop hardware timer
186
 */
187
static void zf_timer_off(void)
188
{
189
        unsigned int ctrl_reg = 0;
190
        unsigned long flags;
191
 
192
        /* stop internal ping */
193
        del_timer_sync(&zf_timer);
194
 
195
        spin_lock_irqsave(&zf_port_lock, flags);
196
        /* stop watchdog timer */
197
        ctrl_reg = zf_get_control();
198
        ctrl_reg |= (ENABLE_WD1|ENABLE_WD2);    /* disable wd1 and wd2 */
199
        ctrl_reg &= ~(ENABLE_WD1|ENABLE_WD2);
200
        zf_set_control(ctrl_reg);
201
        spin_unlock_irqrestore(&zf_port_lock, flags);
202
 
203
        printk(KERN_INFO PFX ": Watchdog timer is now disabled\n");
204
}
205
 
206
 
207
/*
208
 * start hardware timer
209
 */
210
static void zf_timer_on(void)
211
{
212
        unsigned int ctrl_reg = 0;
213
        unsigned long flags;
214
 
215
        spin_lock_irqsave(&zf_port_lock, flags);
216
 
217
        zf_writeb(PULSE_LEN, 0xff);
218
 
219
        zf_set_timer(ZF_CTIMEOUT, WD1);
220
 
221
        /* user land ping */
222
        next_heartbeat = jiffies + ZF_USER_TIMEO;
223
 
224
        /* start the timer for internal ping */
225
        mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
226
 
227
        /* start watchdog timer */
228
        ctrl_reg = zf_get_control();
229
        ctrl_reg |= (ENABLE_WD1|zf_action);
230
        zf_set_control(ctrl_reg);
231
        spin_unlock_irqrestore(&zf_port_lock, flags);
232
 
233
        printk(KERN_INFO PFX ": Watchdog timer is now enabled\n");
234
}
235
 
236
 
237
static void zf_ping(unsigned long data)
238
{
239
        unsigned int ctrl_reg = 0;
240
        unsigned long flags;
241
 
242
        zf_writeb(COUNTER_2, 0xff);
243
 
244
        if(time_before(jiffies, next_heartbeat)){
245
 
246
                dprintk("time_before: %ld\n", next_heartbeat - jiffies);
247
 
248
                /*
249
                 * reset event is activated by transition from 0 to 1 on
250
                 * RESET_WD1 bit and we assume that it is already zero...
251
                 */
252
 
253
                spin_lock_irqsave(&zf_port_lock, flags);
254
                ctrl_reg = zf_get_control();
255
                ctrl_reg |= RESET_WD1;
256
                zf_set_control(ctrl_reg);
257
 
258
                /* ...and nothing changes until here */
259
                ctrl_reg &= ~(RESET_WD1);
260
                zf_set_control(ctrl_reg);
261
                spin_unlock_irqrestore(&zf_port_lock, flags);
262
 
263
                mod_timer(&zf_timer, jiffies + ZF_HW_TIMEO);
264
        }else{
265
                printk(KERN_CRIT PFX ": I will reset your machine\n");
266
        }
267
}
268
 
269
static ssize_t zf_write(struct file *file, const char __user *buf, size_t count,
270
                                                                loff_t *ppos)
271
{
272
        /* See if we got the magic character */
273
        if(count){
274
 
275
                /*
276
                 * no need to check for close confirmation
277
                 * no way to disable watchdog ;)
278
                 */
279
                if (!nowayout) {
280
                        size_t ofs;
281
 
282
                        /*
283
                         * note: just in case someone wrote the magic character
284
                         * five months ago...
285
                         */
286
                        zf_expect_close = 0;
287
 
288
                        /* now scan */
289
                        for (ofs = 0; ofs != count; ofs++){
290
                                char c;
291
                                if (get_user(c, buf + ofs))
292
                                        return -EFAULT;
293
                                if (c == 'V'){
294
                                        zf_expect_close = 42;
295
                                        dprintk("zf_expect_close = 42\n");
296
                                }
297
                        }
298
                }
299
 
300
                /*
301
                 * Well, anyhow someone wrote to us,
302
                 * we should return that favour
303
                 */
304
                next_heartbeat = jiffies + ZF_USER_TIMEO;
305
                dprintk("user ping at %ld\n", jiffies);
306
 
307
        }
308
 
309
        return count;
310
}
311
 
312
static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
313
        unsigned long arg)
314
{
315
        void __user *argp = (void __user *)arg;
316
        int __user *p = argp;
317
        switch (cmd) {
318
        case WDIOC_GETSUPPORT:
319
                if (copy_to_user(argp, &zf_info, sizeof(zf_info)))
320
                        return -EFAULT;
321
                break;
322
 
323
        case WDIOC_GETSTATUS:
324
        case WDIOC_GETBOOTSTATUS:
325
                return put_user(0, p);
326
 
327
        case WDIOC_KEEPALIVE:
328
                zf_ping(0);
329
                break;
330
 
331
        default:
332
                return -ENOTTY;
333
        }
334
 
335
        return 0;
336
}
337
 
338
static int zf_open(struct inode *inode, struct file *file)
339
{
340
        spin_lock(&zf_lock);
341
        if(test_and_set_bit(0, &zf_is_open)) {
342
                spin_unlock(&zf_lock);
343
                return -EBUSY;
344
        }
345
 
346
        if (nowayout)
347
                __module_get(THIS_MODULE);
348
 
349
        spin_unlock(&zf_lock);
350
 
351
        zf_timer_on();
352
 
353
        return nonseekable_open(inode, file);
354
}
355
 
356
static int zf_close(struct inode *inode, struct file *file)
357
{
358
        if(zf_expect_close == 42){
359
                zf_timer_off();
360
        } else {
361
                del_timer(&zf_timer);
362
                printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n");
363
        }
364
 
365
        spin_lock(&zf_lock);
366
        clear_bit(0, &zf_is_open);
367
        spin_unlock(&zf_lock);
368
 
369
        zf_expect_close = 0;
370
 
371
        return 0;
372
}
373
 
374
/*
375
 * Notifier for system down
376
 */
377
 
378
static int zf_notify_sys(struct notifier_block *this, unsigned long code,
379
                                                                void *unused)
380
{
381
        if(code == SYS_DOWN || code == SYS_HALT){
382
                zf_timer_off();
383
        }
384
 
385
        return NOTIFY_DONE;
386
}
387
 
388
 
389
 
390
 
391
static const struct file_operations zf_fops = {
392
        .owner          = THIS_MODULE,
393
        .llseek         = no_llseek,
394
        .write          = zf_write,
395
        .ioctl          = zf_ioctl,
396
        .open           = zf_open,
397
        .release        = zf_close,
398
};
399
 
400
static struct miscdevice zf_miscdev = {
401
        .minor = WATCHDOG_MINOR,
402
        .name = "watchdog",
403
        .fops = &zf_fops,
404
};
405
 
406
 
407
/*
408
 * The device needs to learn about soft shutdowns in order to
409
 * turn the timebomb registers off.
410
 */
411
static struct notifier_block zf_notifier = {
412
        .notifier_call = zf_notify_sys,
413
};
414
 
415
static void __init zf_show_action(int act)
416
{
417
        char *str[] = { "RESET", "SMI", "NMI", "SCI" };
418
 
419
        printk(KERN_INFO PFX ": Watchdog using action = %s\n", str[act]);
420
}
421
 
422
static int __init zf_init(void)
423
{
424
        int ret;
425
 
426
        printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n");
427
 
428
        ret = zf_get_ZFL_version();
429
        if ((!ret) || (ret == 0xffff)) {
430
                printk(KERN_WARNING PFX ": no ZF-Logic found\n");
431
                return -ENODEV;
432
        }
433
 
434
        if((action <= 3) && (action >= 0)){
435
                zf_action = zf_action>>action;
436
        } else
437
                action = 0;
438
 
439
        zf_show_action(action);
440
 
441
        if(!request_region(ZF_IOBASE, 3, "MachZ ZFL WDT")){
442
                printk(KERN_ERR "cannot reserve I/O ports at %d\n",
443
                                                        ZF_IOBASE);
444
                ret = -EBUSY;
445
                goto no_region;
446
        }
447
 
448
        ret = register_reboot_notifier(&zf_notifier);
449
        if(ret){
450
                printk(KERN_ERR "can't register reboot notifier (err=%d)\n",
451
                                                                        ret);
452
                goto no_reboot;
453
        }
454
 
455
        ret = misc_register(&zf_miscdev);
456
        if (ret){
457
                printk(KERN_ERR "can't misc_register on minor=%d\n",
458
                                                        WATCHDOG_MINOR);
459
                goto no_misc;
460
        }
461
 
462
        zf_set_status(0);
463
        zf_set_control(0);
464
 
465
        return 0;
466
 
467
no_misc:
468
        unregister_reboot_notifier(&zf_notifier);
469
no_reboot:
470
        release_region(ZF_IOBASE, 3);
471
no_region:
472
        return ret;
473
}
474
 
475
 
476
static void __exit zf_exit(void)
477
{
478
        zf_timer_off();
479
 
480
        misc_deregister(&zf_miscdev);
481
        unregister_reboot_notifier(&zf_notifier);
482
        release_region(ZF_IOBASE, 3);
483
}
484
 
485
module_init(zf_init);
486
module_exit(zf_exit);

powered by: WebSVN 2.1.0

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