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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 *      w83697hf/hg WDT driver
3
 *
4
 *      (c) Copyright 2006 Samuel Tardieu <sam@rfc1149.net>
5
 *      (c) Copyright 2006 Marcus Junker <junker@anduras.de>
6
 *
7
 *      Based on w83627hf_wdt.c which is based on advantechwdt.c
8
 *      which is based on wdt.c.
9
 *      Original copyright messages:
10
 *
11
 *      (c) Copyright 2003 Pádraig Brady <P@draigBrady.com>
12
 *
13
 *      (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl>
14
 *
15
 *      (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
16
 *                              http://www.redhat.com
17
 *
18
 *      This program is free software; you can redistribute it and/or
19
 *      modify it under the terms of the GNU General Public License
20
 *      as published by the Free Software Foundation; either version
21
 *      2 of the License, or (at your option) any later version.
22
 *
23
 *      Neither Marcus Junker nor ANDURAS AG admit liability nor provide
24
 *      warranty for any of this software. This material is provided
25
 *      "AS-IS" and at no charge.
26
 */
27
 
28
#include <linux/module.h>
29
#include <linux/moduleparam.h>
30
#include <linux/types.h>
31
#include <linux/miscdevice.h>
32
#include <linux/watchdog.h>
33
#include <linux/fs.h>
34
#include <linux/ioport.h>
35
#include <linux/notifier.h>
36
#include <linux/reboot.h>
37
#include <linux/init.h>
38
#include <linux/spinlock.h>
39
 
40
#include <asm/io.h>
41
#include <asm/uaccess.h>
42
#include <asm/system.h>
43
 
44
#define WATCHDOG_NAME "w83697hf/hg WDT"
45
#define PFX WATCHDOG_NAME ": "
46
#define WATCHDOG_TIMEOUT 60             /* 60 sec default timeout */
47
 
48
static unsigned long wdt_is_open;
49
static char expect_close;
50
static DEFINE_SPINLOCK(io_lock);
51
 
52
/* You must set this - there is no sane way to probe for this board. */
53
static int wdt_io = 0x2e;
54
module_param(wdt_io, int, 0);
55
MODULE_PARM_DESC(wdt_io, "w83697hf/hg WDT io port (default 0x2e, 0 = autodetect)");
56
 
57
static int timeout = WATCHDOG_TIMEOUT;  /* in seconds */
58
module_param(timeout, int, 0);
59
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. 1<= timeout <=255, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ".");
60
 
61
static int nowayout = WATCHDOG_NOWAYOUT;
62
module_param(nowayout, int, 0);
63
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
64
 
65
/*
66
 *      Kernel methods.
67
 */
68
 
69
#define W83697HF_EFER (wdt_io+0)        /* Extended Function Enable Register */
70
#define W83697HF_EFIR (wdt_io+0)        /* Extended Function Index Register (same as EFER) */
71
#define W83697HF_EFDR (wdt_io+1)        /* Extended Function Data Register */
72
 
73
static inline void
74
w83697hf_unlock(void)
75
{
76
        outb_p(0x87, W83697HF_EFER);    /* Enter extended function mode */
77
        outb_p(0x87, W83697HF_EFER);    /* Again according to manual */
78
}
79
 
80
static inline void
81
w83697hf_lock(void)
82
{
83
        outb_p(0xAA, W83697HF_EFER);    /* Leave extended function mode */
84
}
85
 
86
/*
87
 *      The three functions w83697hf_get_reg(), w83697hf_set_reg() and
88
 *      w83697hf_write_timeout() must be called with the device unlocked.
89
 */
90
 
91
static unsigned char
92
w83697hf_get_reg(unsigned char reg)
93
{
94
        outb_p(reg, W83697HF_EFIR);
95
        return inb_p(W83697HF_EFDR);
96
}
97
 
98
static void
99
w83697hf_set_reg(unsigned char reg, unsigned char data)
100
{
101
        outb_p(reg, W83697HF_EFIR);
102
        outb_p(data, W83697HF_EFDR);
103
}
104
 
105
static void
106
w83697hf_write_timeout(int timeout)
107
{
108
        w83697hf_set_reg(0xF4, timeout);        /* Write Timeout counter to CRF4 */
109
}
110
 
111
static void
112
w83697hf_select_wdt(void)
113
{
114
        w83697hf_unlock();
115
        w83697hf_set_reg(0x07, 0x08);   /* Switch to logic device 8 (GPIO2) */
116
}
117
 
118
static inline void
119
w83697hf_deselect_wdt(void)
120
{
121
        w83697hf_lock();
122
}
123
 
124
static void
125
w83697hf_init(void)
126
{
127
        unsigned char bbuf;
128
 
129
        w83697hf_select_wdt();
130
 
131
        bbuf = w83697hf_get_reg(0x29);
132
        bbuf &= ~0x60;
133
        bbuf |= 0x20;
134
        w83697hf_set_reg(0x29, bbuf);   /* Set pin 119 to WDTO# mode (= CR29, WDT0) */
135
 
136
        bbuf = w83697hf_get_reg(0xF3);
137
        bbuf &= ~0x04;
138
        w83697hf_set_reg(0xF3, bbuf);   /* Count mode is seconds */
139
 
140
        w83697hf_deselect_wdt();
141
}
142
 
143
static int
144
wdt_ping(void)
145
{
146
        spin_lock(&io_lock);
147
        w83697hf_select_wdt();
148
 
149
        w83697hf_write_timeout(timeout);
150
 
151
        w83697hf_deselect_wdt();
152
        spin_unlock(&io_lock);
153
        return 0;
154
}
155
 
156
static int
157
wdt_enable(void)
158
{
159
        spin_lock(&io_lock);
160
        w83697hf_select_wdt();
161
 
162
        w83697hf_write_timeout(timeout);
163
        w83697hf_set_reg(0x30, 1);      /* Enable timer */
164
 
165
        w83697hf_deselect_wdt();
166
        spin_unlock(&io_lock);
167
        return 0;
168
}
169
 
170
static int
171
wdt_disable(void)
172
{
173
        spin_lock(&io_lock);
174
        w83697hf_select_wdt();
175
 
176
        w83697hf_set_reg(0x30, 0);       /* Disable timer */
177
        w83697hf_write_timeout(0);
178
 
179
        w83697hf_deselect_wdt();
180
        spin_unlock(&io_lock);
181
        return 0;
182
}
183
 
184
static int
185
wdt_set_heartbeat(int t)
186
{
187
        if ((t < 1) || (t > 255))
188
                return -EINVAL;
189
 
190
        timeout = t;
191
        return 0;
192
}
193
 
194
static ssize_t
195
wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
196
{
197
        if (count) {
198
                if (!nowayout) {
199
                        size_t i;
200
 
201
                        expect_close = 0;
202
 
203
                        for (i = 0; i != count; i++) {
204
                                char c;
205
                                if (get_user(c, buf+i))
206
                                        return -EFAULT;
207
                                if (c == 'V')
208
                                        expect_close = 42;
209
                        }
210
                }
211
                wdt_ping();
212
        }
213
        return count;
214
}
215
 
216
static int
217
wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
218
          unsigned long arg)
219
{
220
        void __user *argp = (void __user *)arg;
221
        int __user *p = argp;
222
        int new_timeout;
223
        static struct watchdog_info ident = {
224
                .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
225
                .firmware_version = 1,
226
                .identity = "W83697HF WDT",
227
        };
228
 
229
        switch (cmd) {
230
        case WDIOC_GETSUPPORT:
231
                if (copy_to_user(argp, &ident, sizeof(ident)))
232
                        return -EFAULT;
233
                break;
234
 
235
        case WDIOC_GETSTATUS:
236
        case WDIOC_GETBOOTSTATUS:
237
                return put_user(0, p);
238
 
239
        case WDIOC_KEEPALIVE:
240
                wdt_ping();
241
                break;
242
 
243
        case WDIOC_SETTIMEOUT:
244
                if (get_user(new_timeout, p))
245
                        return -EFAULT;
246
                if (wdt_set_heartbeat(new_timeout))
247
                        return -EINVAL;
248
                wdt_ping();
249
                /* Fall */
250
 
251
        case WDIOC_GETTIMEOUT:
252
                return put_user(timeout, p);
253
 
254
        case WDIOC_SETOPTIONS:
255
        {
256
                int options, retval = -EINVAL;
257
 
258
                if (get_user(options, p))
259
                        return -EFAULT;
260
 
261
                if (options & WDIOS_DISABLECARD) {
262
                        wdt_disable();
263
                        retval = 0;
264
                }
265
 
266
                if (options & WDIOS_ENABLECARD) {
267
                        wdt_enable();
268
                        retval = 0;
269
                }
270
 
271
                return retval;
272
        }
273
 
274
        default:
275
                return -ENOTTY;
276
        }
277
        return 0;
278
}
279
 
280
static int
281
wdt_open(struct inode *inode, struct file *file)
282
{
283
        if (test_and_set_bit(0, &wdt_is_open))
284
                return -EBUSY;
285
        /*
286
         *      Activate
287
         */
288
 
289
        wdt_enable();
290
        return nonseekable_open(inode, file);
291
}
292
 
293
static int
294
wdt_close(struct inode *inode, struct file *file)
295
{
296
        if (expect_close == 42) {
297
                wdt_disable();
298
        } else {
299
                printk (KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
300
                wdt_ping();
301
        }
302
        expect_close = 0;
303
        clear_bit(0, &wdt_is_open);
304
        return 0;
305
}
306
 
307
/*
308
 *      Notifier for system down
309
 */
310
 
311
static int
312
wdt_notify_sys(struct notifier_block *this, unsigned long code,
313
        void *unused)
314
{
315
        if (code == SYS_DOWN || code == SYS_HALT) {
316
                /* Turn the WDT off */
317
                wdt_disable();
318
        }
319
        return NOTIFY_DONE;
320
}
321
 
322
/*
323
 *      Kernel Interfaces
324
 */
325
 
326
static const struct file_operations wdt_fops = {
327
        .owner          = THIS_MODULE,
328
        .llseek         = no_llseek,
329
        .write          = wdt_write,
330
        .ioctl          = wdt_ioctl,
331
        .open           = wdt_open,
332
        .release        = wdt_close,
333
};
334
 
335
static struct miscdevice wdt_miscdev = {
336
        .minor = WATCHDOG_MINOR,
337
        .name = "watchdog",
338
        .fops = &wdt_fops,
339
};
340
 
341
/*
342
 *      The WDT needs to learn about soft shutdowns in order to
343
 *      turn the timebomb registers off.
344
 */
345
 
346
static struct notifier_block wdt_notifier = {
347
        .notifier_call = wdt_notify_sys,
348
};
349
 
350
static int
351
w83697hf_check_wdt(void)
352
{
353
        if (!request_region(wdt_io, 2, WATCHDOG_NAME)) {
354
                printk (KERN_ERR PFX "I/O address 0x%x already in use\n", wdt_io);
355
                return -EIO;
356
        }
357
 
358
        printk (KERN_DEBUG PFX "Looking for watchdog at address 0x%x\n", wdt_io);
359
        w83697hf_unlock();
360
        if (w83697hf_get_reg(0x20) == 0x60) {
361
                printk (KERN_INFO PFX "watchdog found at address 0x%x\n", wdt_io);
362
                w83697hf_lock();
363
                return 0;
364
        }
365
        w83697hf_lock();        /* Reprotect in case it was a compatible device */
366
 
367
        printk (KERN_INFO PFX "watchdog not found at address 0x%x\n", wdt_io);
368
        release_region(wdt_io, 2);
369
        return -EIO;
370
}
371
 
372
static int w83697hf_ioports[] = { 0x2e, 0x4e, 0x00 };
373
 
374
static int __init
375
wdt_init(void)
376
{
377
        int ret, i, found = 0;
378
 
379
        printk (KERN_INFO PFX "WDT driver for W83697HF/HG initializing\n");
380
 
381
        if (wdt_io == 0) {
382
                /* we will autodetect the W83697HF/HG watchdog */
383
                for (i = 0; ((!found) && (w83697hf_ioports[i] != 0)); i++) {
384
                        wdt_io = w83697hf_ioports[i];
385
                        if (!w83697hf_check_wdt())
386
                                found++;
387
                }
388
        } else {
389
                if (!w83697hf_check_wdt())
390
                        found++;
391
        }
392
 
393
        if (!found) {
394
                printk (KERN_ERR PFX "No W83697HF/HG could be found\n");
395
                ret = -EIO;
396
                goto out;
397
        }
398
 
399
        w83697hf_init();
400
        wdt_disable();  /* Disable watchdog until first use */
401
 
402
        if (wdt_set_heartbeat(timeout)) {
403
                wdt_set_heartbeat(WATCHDOG_TIMEOUT);
404
                printk (KERN_INFO PFX "timeout value must be 1<=timeout<=255, using %d\n",
405
                        WATCHDOG_TIMEOUT);
406
        }
407
 
408
        ret = register_reboot_notifier(&wdt_notifier);
409
        if (ret != 0) {
410
                printk (KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
411
                        ret);
412
                goto unreg_regions;
413
        }
414
 
415
        ret = misc_register(&wdt_miscdev);
416
        if (ret != 0) {
417
                printk (KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
418
                        WATCHDOG_MINOR, ret);
419
                goto unreg_reboot;
420
        }
421
 
422
        printk (KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d)\n",
423
                timeout, nowayout);
424
 
425
out:
426
        return ret;
427
unreg_reboot:
428
        unregister_reboot_notifier(&wdt_notifier);
429
unreg_regions:
430
        release_region(wdt_io, 2);
431
        goto out;
432
}
433
 
434
static void __exit
435
wdt_exit(void)
436
{
437
        misc_deregister(&wdt_miscdev);
438
        unregister_reboot_notifier(&wdt_notifier);
439
        release_region(wdt_io, 2);
440
}
441
 
442
module_init(wdt_init);
443
module_exit(wdt_exit);
444
 
445
MODULE_LICENSE("GPL");
446
MODULE_AUTHOR("Marcus Junker <junker@anduras.de>, Samuel Tardieu <sam@rfc1149.net>");
447
MODULE_DESCRIPTION("w83697hf/hg WDT driver");
448
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

powered by: WebSVN 2.1.0

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