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

Subversion Repositories or1k

[/] [or1k/] [trunk/] [linux/] [linux-2.4/] [drivers/] [char/] [shwdt.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
 * drivers/char/shwdt.c
3
 *
4
 * Watchdog driver for integrated watchdog in the SuperH processors.
5
 *
6
 * Copyright (C) 2001, 2002, 2003, 2004 Paul Mundt <lethal@linux-sh.org>
7
 *
8
 * This program is free software; you can redistribute it and/or modify it
9
 * under the terms of the GNU General Public License as published by the
10
 * Free Software Foundation; either version 2 of the License, or (at your
11
 * option) any later version.
12
 *
13
 * 14-Dec-2001 Matt Domsch <Matt_Domsch@dell.com>
14
 *     Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT
15
 *
16
 * 19-Apr-2002 Rob Radez <rob@osinvestor.com>
17
 *     Added expect close support, made emulated timeout runtime changeable
18
 *     general cleanups, add some ioctls
19
 */
20
#include <linux/config.h>
21
#include <linux/module.h>
22
#include <linux/init.h>
23
#include <linux/kernel.h>
24
#include <linux/types.h>
25
#include <linux/miscdevice.h>
26
#include <linux/watchdog.h>
27
#include <linux/reboot.h>
28
#include <linux/notifier.h>
29
#include <linux/ioport.h>
30
 
31
#include <asm/io.h>
32
#include <asm/uaccess.h>
33
 
34
#if defined(CONFIG_CPU_SH5)
35
  extern unsigned long cprc_base;
36
 
37
  #define WTCNT         (cprc_base+0x10)
38
  #define WTCSR         (cprc_base+0x18)
39
#elif defined(CONFIG_CPU_SH4)
40
  #define WTCNT         0xffc00008
41
  #define WTCSR         0xffc0000c
42
#elif defined(CONFIG_CPU_SH3)
43
  #define WTCNT         0xffffff84
44
  #define WTCSR         0xffffff86
45
#else
46
  #error "Can't use SuperH watchdog on this platform"
47
#endif
48
 
49
#define WTCNT_HIGH      0x5a00
50
#define WTCSR_HIGH      0xa500
51
 
52
#define WTCSR_TME       0x80
53
#define WTCSR_WT        0x40
54
#define WTCSR_RSTS      0x20
55
#define WTCSR_WOVF      0x10
56
#define WTCSR_IOVF      0x08
57
#define WTCSR_CKS2      0x04
58
#define WTCSR_CKS1      0x02
59
#define WTCSR_CKS0      0x01
60
 
61
/*
62
 * CKS0-2 supports a number of clock division ratios. At the time the watchdog
63
 * is enabled, it defaults to a 41 usec overflow period .. we overload this to
64
 * something a little more reasonable, and really can't deal with anything
65
 * lower than WTCSR_CKS_1024, else we drop back into the usec range.
66
 *
67
 * Clock Division Ratio         Overflow Period
68
 * --------------------------------------------
69
 *     1/32 (initial value)       41 usecs
70
 *     1/64                       82 usecs
71
 *     1/128                     164 usecs
72
 *     1/256                     328 usecs
73
 *     1/512                     656 usecs
74
 *     1/1024                   1.31 msecs
75
 *     1/2048                   2.62 msecs
76
 *     1/4096                   5.25 msecs
77
 */
78
#define WTCSR_CKS_32    0x00
79
#define WTCSR_CKS_64    0x01
80
#define WTCSR_CKS_128   0x02
81
#define WTCSR_CKS_256   0x03
82
#define WTCSR_CKS_512   0x04
83
#define WTCSR_CKS_1024  0x05
84
#define WTCSR_CKS_2048  0x06
85
#define WTCSR_CKS_4096  0x07
86
 
87
/*
88
 * Default clock division ratio is 5.25 msecs. For an additional table of
89
 * values, consult the asm-sh/watchdog.h. Overload this at module load
90
 * time.
91
 *
92
 * In order for this to work reliably we need to have HZ set to 1000 or
93
 * something quite higher than 100 (or we need a proper high-res timer
94
 * implementation that will deal with this properly), otherwise the 10ms
95
 * resolution of a jiffy is enough to trigger the overflow. For things like
96
 * the SH-4 and SH-5, this isn't necessarily that big of a problem, though
97
 * for the SH-2 and SH-3, this isn't recommended unless the WDT is absolutely
98
 * necssary.
99
 *
100
 * As a result of this timing problem, the only modes that are particularly
101
 * feasible are the 4096 and the 2048 divisors, which yeild 5.25 and 2.62ms
102
 * overflow periods respectively.
103
 *
104
 * Also, since we can't really expect userspace to be responsive enough
105
 * before the overflow happens, we maintain two seperate timers .. One in
106
 * the kernel for clearing out WOVF every 2ms or so (again, this depends on
107
 * HZ == 1000), and another for monitoring userspace writes to the WDT device.
108
 *
109
 * As such, we currently use a configurable heartbeat interval which defaults
110
 * to 30s. In this case, the userspace daemon is only responsible for periodic
111
 * writes to the device before the next heartbeat is scheduled. If the daemon
112
 * misses its deadline, the kernel timer will allow the WDT to overflow.
113
 */
114
static int clock_division_ratio = WTCSR_CKS_4096;
115
 
116
#define msecs_to_jiffies(msecs) (jiffies + (HZ * msecs + 9999) / 10000)
117
#define next_ping_period(cks)   msecs_to_jiffies(cks - 4)
118
 
119
static unsigned long shwdt_is_open;
120
static struct watchdog_info sh_wdt_info;
121
static char shwdt_expect_close;
122
static struct timer_list timer;
123
static unsigned long next_heartbeat;
124
static int heartbeat = 30;
125
 
126
#ifdef CONFIG_WATCHDOG_NOWAYOUT
127
static int nowayout = 1;
128
#else
129
static int nowayout = 0;
130
#endif
131
 
132
/**
133
 *      sh_wdt_write_cnt - Write to Counter
134
 *
135
 *      @val: Value to write
136
 *
137
 *      Writes the given value @val to the lower byte of the timer counter.
138
 *      The upper byte is set manually on each write.
139
 */
140
static void sh_wdt_write_cnt(__u8 val)
141
{
142
        ctrl_outw(WTCNT_HIGH | (__u16)val, WTCNT);
143
}
144
 
145
/**
146
 *      sh_wdt_read_csr - Read from Control/Status Register
147
 *
148
 *      Reads back the WTCSR value.
149
 */
150
static inline __u8 sh_wdt_read_csr(void)
151
{
152
        /*
153
         * XXX: This is pretty straightforward on SH-3 and up, though on
154
         * anything lower we have seperate locations for reading and
155
         * writing the CSR value. We just keep this around for general
156
         * completeness.
157
         */
158
        return ctrl_inb(WTCSR);
159
}
160
 
161
/**
162
 *      sh_wdt_write_csr - Write to Control/Status Register
163
 *
164
 *      @val: Value to write
165
 *
166
 *      Writes the given value @val to the lower byte of the control/status
167
 *      register. The upper byte is set manually on each write.
168
 */
169
static void sh_wdt_write_csr(__u8 val)
170
{
171
        ctrl_outw(WTCSR_HIGH | (__u16)val, WTCSR);
172
}
173
 
174
/**
175
 *      sh_wdt_start - Start the Watchdog
176
 *
177
 *      Starts the watchdog.
178
 */
179
static void sh_wdt_start(void)
180
{
181
        __u8 csr;
182
 
183
        mod_timer(&timer, next_ping_period(clock_division_ratio));
184
        next_heartbeat = jiffies + (heartbeat * HZ);
185
 
186
        csr = sh_wdt_read_csr();
187
        csr |= WTCSR_WT | clock_division_ratio;
188
        sh_wdt_write_csr(csr);
189
 
190
        sh_wdt_write_cnt(0);
191
 
192
        /*
193
         * These processors have a bit of an inconsistent initialization
194
         * process.. starting with SH-3, RSTS was moved to WTCSR, and the
195
         * RSTCSR register was removed.
196
         *
197
         * On the SH-2 these semantics are even odder, as we have to deal
198
         * with RSTCSR outright. See the 2.6 code for this.
199
         */
200
        csr = sh_wdt_read_csr();
201
        csr |= WTCSR_TME;
202
        csr &= ~WTCSR_RSTS;
203
        sh_wdt_write_csr(csr);
204
}
205
 
206
/**
207
 *      sh_wdt_stop - Stop the Watchdog
208
 *
209
 *      Stops the watchdog.
210
 */
211
static void sh_wdt_stop(void)
212
{
213
        __u8 csr;
214
 
215
        del_timer(&timer);
216
 
217
        csr = sh_wdt_read_csr();
218
        csr &= ~WTCSR_TME;
219
        sh_wdt_write_csr(csr);
220
}
221
 
222
/**
223
 *      sh_wdt_ping - Ping the Watchdog
224
 *
225
 *      @data: Unused
226
 *
227
 *      Clears overflow bit, resets timer counter.
228
 */
229
static void sh_wdt_ping(unsigned long data)
230
{
231
        if (time_before(jiffies, next_heartbeat)) {
232
                __u8 csr;
233
 
234
                csr = sh_wdt_read_csr();
235
                csr &= ~WTCSR_IOVF;
236
                sh_wdt_write_csr(csr);
237
 
238
                sh_wdt_write_cnt(0);
239
 
240
                mod_timer(&timer, next_ping_period(clock_division_ratio));
241
        }
242
}
243
 
244
/**
245
 *      sh_wdt_open - Open the Device
246
 *
247
 *      @inode: inode of device
248
 *      @file: file handle of device
249
 *
250
 *      Watchdog device is opened and started.
251
 */
252
static int sh_wdt_open(struct inode *inode, struct file *file)
253
{
254
        if (test_and_set_bit(0, &shwdt_is_open))
255
                return -EBUSY;
256
 
257
        sh_wdt_start();
258
 
259
        return 0;
260
}
261
 
262
/**
263
 *      sh_wdt_close - Close the Device
264
 *
265
 *      @inode: inode of device
266
 *      @file: file handle of device
267
 *
268
 *      Watchdog device is closed and stopped.
269
 */
270
static int sh_wdt_close(struct inode *inode, struct file *file)
271
{
272
        if (!nowayout && shwdt_expect_close == 42) {
273
                sh_wdt_stop();
274
        } else {
275
                printk(KERN_CRIT "shwdt: Unexpected close, not stopping watchdog!\n");
276
                next_heartbeat = jiffies + (heartbeat * HZ);
277
        }
278
 
279
        clear_bit(0, &shwdt_is_open);
280
        shwdt_expect_close = 0;
281
 
282
        return 0;
283
}
284
 
285
/**
286
 *      sh_wdt_write - Write to Device
287
 *
288
 *      @file: file handle of device
289
 *      @buf: buffer to write
290
 *      @count: length of buffer
291
 *      @ppos: offset
292
 *
293
 *      Pings the watchdog on write.
294
 */
295
static ssize_t sh_wdt_write(struct file *file, const char *buf,
296
                            size_t count, loff_t *ppos)
297
{
298
        /* Can't seek (pwrite) on this device */
299
        if (ppos != &file->f_pos)
300
                return -ESPIPE;
301
 
302
        if (count) {
303
                size_t i;
304
 
305
                shwdt_expect_close = 0;
306
 
307
                for (i = 0; i != count; i++) {
308
                        char c;
309
                        if (get_user(c, buf + i))
310
                                return -EFAULT;
311
                        if (c == 'V')
312
                                shwdt_expect_close = 42;
313
                }
314
                next_heartbeat = jiffies + (heartbeat * HZ);
315
        }
316
 
317
        return count;
318
}
319
 
320
/**
321
 *      sh_wdt_ioctl - Query Device
322
 *
323
 *      @inode: inode of device
324
 *      @file: file handle of device
325
 *      @cmd: watchdog command
326
 *      @arg: argument
327
 *
328
 *      Query basic information from the device or ping it, as outlined by the
329
 *      watchdog API.
330
 */
331
static int sh_wdt_ioctl(struct inode *inode, struct file *file,
332
                        unsigned int cmd, unsigned long arg)
333
{
334
        int new_timeout;
335
 
336
        switch (cmd) {
337
                case WDIOC_GETSUPPORT:
338
                        if (copy_to_user((struct watchdog_info *)arg,
339
                                          &sh_wdt_info,
340
                                          sizeof(sh_wdt_info))) {
341
                                return -EFAULT;
342
                        }
343
 
344
                        break;
345
                case WDIOC_GETSTATUS:
346
                case WDIOC_GETBOOTSTATUS:
347
                        return put_user(0, (int *)arg);
348
                case WDIOC_KEEPALIVE:
349
                        next_heartbeat = jiffies + (heartbeat * HZ);
350
 
351
                        break;
352
                case WDIOC_SETTIMEOUT:
353
                        if (get_user(new_timeout, (int *)arg))
354
                                return -EFAULT;
355
                        if (new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */
356
                                return -EINVAL;
357
                        heartbeat = new_timeout;
358
                        next_heartbeat = jiffies + (heartbeat * HZ);
359
                        /* Fall */
360
                case WDIOC_GETTIMEOUT:
361
                        return put_user(heartbeat, (int *)arg);
362
                case WDIOC_SETOPTIONS:
363
                {
364
                        int options, retval = -EINVAL;
365
 
366
                        if (get_user(options, (int *)arg))
367
                                return -EFAULT;
368
 
369
                        if (options & WDIOS_DISABLECARD) {
370
                                sh_wdt_stop();
371
                                retval = 0;
372
                        }
373
 
374
                        if (options & WDIOS_ENABLECARD) {
375
                                sh_wdt_start();
376
                                retval = 0;
377
                        }
378
 
379
                        return retval;
380
                }
381
                default:
382
                        return -ENOTTY;
383
        }
384
 
385
        return 0;
386
}
387
 
388
/**
389
 *      sh_wdt_notify_sys - Notifier Handler
390
 *
391
 *      @this: notifier block
392
 *      @code: notifier event
393
 *      @unused: unused
394
 *
395
 *      Handles specific events, such as turning off the watchdog during a
396
 *      shutdown event.
397
 */
398
static int sh_wdt_notify_sys(struct notifier_block *this,
399
                             unsigned long code, void *unused)
400
{
401
        if (code == SYS_DOWN || code == SYS_HALT) {
402
                sh_wdt_stop();
403
        }
404
 
405
        return NOTIFY_DONE;
406
}
407
 
408
static struct file_operations sh_wdt_fops = {
409
        owner:          THIS_MODULE,
410
        llseek:         no_llseek,
411
        write:          sh_wdt_write,
412
        ioctl:          sh_wdt_ioctl,
413
        open:           sh_wdt_open,
414
        release:        sh_wdt_close,
415
};
416
 
417
static struct watchdog_info sh_wdt_info = {
418
        options:                WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
419
        firmware_version:       0,
420
        identity:               "SH WDT",
421
};
422
 
423
static struct notifier_block sh_wdt_notifier = {
424
        sh_wdt_notify_sys,
425
        NULL,
426
 
427
};
428
 
429
static struct miscdevice sh_wdt_miscdev = {
430
        minor:          WATCHDOG_MINOR,
431
        name:           "watchdog",
432
        fops:           &sh_wdt_fops,
433
};
434
 
435
/**
436
 *      sh_wdt_init - Initialize module
437
 *
438
 *      Registers the device and notifier handler. Actual device
439
 *      initialization is handled by sh_wdt_open().
440
 */
441
static int __init sh_wdt_init(void)
442
{
443
        if (misc_register(&sh_wdt_miscdev)) {
444
                printk(KERN_ERR "shwdt: Can't register misc device\n");
445
                return -EINVAL;
446
        }
447
 
448
        if (!request_region(WTCNT, 1, "shwdt")) {
449
                printk(KERN_ERR "shwdt: Can't request WTCNT region\n");
450
                misc_deregister(&sh_wdt_miscdev);
451
                return -ENXIO;
452
        }
453
 
454
        if (!request_region(WTCSR, 1, "shwdt")) {
455
                printk(KERN_ERR "shwdt: Can't request WTCSR region\n");
456
                release_region(WTCNT, 1);
457
                misc_deregister(&sh_wdt_miscdev);
458
                return -ENXIO;
459
        }
460
 
461
        if (register_reboot_notifier(&sh_wdt_notifier)) {
462
                printk(KERN_ERR "shwdt: Can't register reboot notifier\n");
463
                release_region(WTCSR, 1);
464
                release_region(WTCNT, 1);
465
                misc_deregister(&sh_wdt_miscdev);
466
                return -EINVAL;
467
        }
468
 
469
        init_timer(&timer);
470
        timer.function = sh_wdt_ping;
471
        timer.data = 0;
472
 
473
        return 0;
474
}
475
 
476
/**
477
 *      sh_wdt_exit - Deinitialize module
478
 *
479
 *      Unregisters the device and notifier handler. Actual device
480
 *      deinitialization is handled by sh_wdt_close().
481
 */
482
static void __exit sh_wdt_exit(void)
483
{
484
        unregister_reboot_notifier(&sh_wdt_notifier);
485
        release_region(WTCSR, 1);
486
        release_region(WTCNT, 1);
487
        misc_deregister(&sh_wdt_miscdev);
488
}
489
 
490
EXPORT_NO_SYMBOLS;
491
 
492
MODULE_PARM(nowayout,"i");
493
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
494
MODULE_PARM(clock_division_ratio, "i");
495
MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
496
 
497
MODULE_AUTHOR("Paul Mundt <lethal@0xd6.org>");
498
MODULE_DESCRIPTION("SuperH watchdog driver");
499
MODULE_LICENSE("GPL");
500
 
501
module_init(sh_wdt_init);
502
module_exit(sh_wdt_exit);
503
 

powered by: WebSVN 2.1.0

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