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

Subversion Repositories test_project

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

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * Blackfin On-Chip Watchdog Driver
3
 *  Supports BF53[123]/BF53[467]/BF54[2489]/BF561
4
 *
5
 * Originally based on softdog.c
6
 * Copyright 2006-2007 Analog Devices Inc.
7
 * Copyright 2006-2007 Michele d'Amico
8
 * Copyright 1996 Alan Cox <alan@redhat.com>
9
 *
10
 * Enter bugs at http://blackfin.uclinux.org/
11
 *
12
 * Licensed under the GPL-2 or later.
13
 */
14
 
15
#include <linux/platform_device.h>
16
#include <linux/module.h>
17
#include <linux/moduleparam.h>
18
#include <linux/types.h>
19
#include <linux/timer.h>
20
#include <linux/miscdevice.h>
21
#include <linux/watchdog.h>
22
#include <linux/fs.h>
23
#include <linux/notifier.h>
24
#include <linux/reboot.h>
25
#include <linux/init.h>
26
#include <linux/interrupt.h>
27
#include <asm/blackfin.h>
28
#include <asm/uaccess.h>
29
 
30
#define stamp(fmt, args...) pr_debug("%s:%i: " fmt "\n", __func__, __LINE__, ## args)
31
#define stampit() stamp("here i am")
32
 
33
#define WATCHDOG_NAME "bfin-wdt"
34
#define PFX WATCHDOG_NAME ": "
35
 
36
/* The BF561 has two watchdogs (one per core), but since Linux
37
 * only runs on core A, we'll just work with that one.
38
 */
39
#ifdef BF561_FAMILY
40
# define bfin_read_WDOG_CTL()    bfin_read_WDOGA_CTL()
41
# define bfin_read_WDOG_CNT()    bfin_read_WDOGA_CNT()
42
# define bfin_read_WDOG_STAT()   bfin_read_WDOGA_STAT()
43
# define bfin_write_WDOG_CTL(x)  bfin_write_WDOGA_CTL(x)
44
# define bfin_write_WDOG_CNT(x)  bfin_write_WDOGA_CNT(x)
45
# define bfin_write_WDOG_STAT(x) bfin_write_WDOGA_STAT(x)
46
#endif
47
 
48
/* Bit in SWRST that indicates boot caused by watchdog */
49
#define SWRST_RESET_WDOG 0x4000
50
 
51
/* Bit in WDOG_CTL that indicates watchdog has expired (WDR0) */
52
#define WDOG_EXPIRED 0x8000
53
 
54
/* Masks for WDEV field in WDOG_CTL register */
55
#define ICTL_RESET   0x0
56
#define ICTL_NMI     0x2
57
#define ICTL_GPI     0x4
58
#define ICTL_NONE    0x6
59
#define ICTL_MASK    0x6
60
 
61
/* Masks for WDEN field in WDOG_CTL register */
62
#define WDEN_MASK    0x0FF0
63
#define WDEN_ENABLE  0x0000
64
#define WDEN_DISABLE 0x0AD0
65
 
66
/* some defaults */
67
#define WATCHDOG_TIMEOUT 20
68
 
69
static unsigned int timeout = WATCHDOG_TIMEOUT;
70
static int nowayout = WATCHDOG_NOWAYOUT;
71
static struct watchdog_info bfin_wdt_info;
72
static unsigned long open_check;
73
static char expect_close;
74
static DEFINE_SPINLOCK(bfin_wdt_spinlock);
75
 
76
/**
77
 *      bfin_wdt_keepalive - Keep the Userspace Watchdog Alive
78
 *
79
 *      The Userspace watchdog got a KeepAlive: schedule the next timeout.
80
 */
81
static int bfin_wdt_keepalive(void)
82
{
83
        stampit();
84
        bfin_write_WDOG_STAT(0);
85
        return 0;
86
}
87
 
88
/**
89
 *      bfin_wdt_stop - Stop the Watchdog
90
 *
91
 *      Stops the on-chip watchdog.
92
 */
93
static int bfin_wdt_stop(void)
94
{
95
        stampit();
96
        bfin_write_WDOG_CTL(WDEN_DISABLE);
97
        return 0;
98
}
99
 
100
/**
101
 *      bfin_wdt_start - Start the Watchdog
102
 *
103
 *      Starts the on-chip watchdog.  Automatically loads WDOG_CNT
104
 *      into WDOG_STAT for us.
105
 */
106
static int bfin_wdt_start(void)
107
{
108
        stampit();
109
        bfin_write_WDOG_CTL(WDEN_ENABLE | ICTL_RESET);
110
        return 0;
111
}
112
 
113
/**
114
 *      bfin_wdt_running - Check Watchdog status
115
 *
116
 *      See if the watchdog is running.
117
 */
118
static int bfin_wdt_running(void)
119
{
120
        stampit();
121
        return ((bfin_read_WDOG_CTL() & WDEN_MASK) != WDEN_DISABLE);
122
}
123
 
124
/**
125
 *      bfin_wdt_set_timeout - Set the Userspace Watchdog timeout
126
 *      @t: new timeout value (in seconds)
127
 *
128
 *      Translate the specified timeout in seconds into System Clock
129
 *      terms which is what the on-chip Watchdog requires.
130
 */
131
static int bfin_wdt_set_timeout(unsigned long t)
132
{
133
        u32 cnt;
134
        unsigned long flags;
135
 
136
        stampit();
137
 
138
        cnt = t * get_sclk();
139
        if (cnt < get_sclk()) {
140
                printk(KERN_WARNING PFX "timeout value is too large\n");
141
                return -EINVAL;
142
        }
143
 
144
        spin_lock_irqsave(&bfin_wdt_spinlock, flags);
145
        {
146
                int run = bfin_wdt_running();
147
                bfin_wdt_stop();
148
                bfin_write_WDOG_CNT(cnt);
149
                if (run) bfin_wdt_start();
150
        }
151
        spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
152
 
153
        timeout = t;
154
 
155
        return 0;
156
}
157
 
158
/**
159
 *      bfin_wdt_open - Open the Device
160
 *      @inode: inode of device
161
 *      @file: file handle of device
162
 *
163
 *      Watchdog device is opened and started.
164
 */
165
static int bfin_wdt_open(struct inode *inode, struct file *file)
166
{
167
        stampit();
168
 
169
        if (test_and_set_bit(0, &open_check))
170
                return -EBUSY;
171
 
172
        if (nowayout)
173
                __module_get(THIS_MODULE);
174
 
175
        bfin_wdt_keepalive();
176
        bfin_wdt_start();
177
 
178
        return nonseekable_open(inode, file);
179
}
180
 
181
/**
182
 *      bfin_wdt_close - Close the Device
183
 *      @inode: inode of device
184
 *      @file: file handle of device
185
 *
186
 *      Watchdog device is closed and stopped.
187
 */
188
static int bfin_wdt_release(struct inode *inode, struct file *file)
189
{
190
        stampit();
191
 
192
        if (expect_close == 42) {
193
                bfin_wdt_stop();
194
        } else {
195
                printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
196
                bfin_wdt_keepalive();
197
        }
198
 
199
        expect_close = 0;
200
        clear_bit(0, &open_check);
201
 
202
        return 0;
203
}
204
 
205
/**
206
 *      bfin_wdt_write - Write to Device
207
 *      @file: file handle of device
208
 *      @buf: buffer to write
209
 *      @count: length of buffer
210
 *      @ppos: offset
211
 *
212
 *      Pings the watchdog on write.
213
 */
214
static ssize_t bfin_wdt_write(struct file *file, const char __user *data,
215
                              size_t len, loff_t *ppos)
216
{
217
        stampit();
218
 
219
        if (len) {
220
                if (!nowayout) {
221
                        size_t i;
222
 
223
                        /* In case it was set long ago */
224
                        expect_close = 0;
225
 
226
                        for (i = 0; i != len; i++) {
227
                                char c;
228
                                if (get_user(c, data + i))
229
                                        return -EFAULT;
230
                                if (c == 'V')
231
                                        expect_close = 42;
232
                        }
233
                }
234
                bfin_wdt_keepalive();
235
        }
236
 
237
        return len;
238
}
239
 
240
/**
241
 *      bfin_wdt_ioctl - Query Device
242
 *      @inode: inode of device
243
 *      @file: file handle of device
244
 *      @cmd: watchdog command
245
 *      @arg: argument
246
 *
247
 *      Query basic information from the device or ping it, as outlined by the
248
 *      watchdog API.
249
 */
250
static int bfin_wdt_ioctl(struct inode *inode, struct file *file,
251
                          unsigned int cmd, unsigned long arg)
252
{
253
        void __user *argp = (void __user *)arg;
254
        int __user *p = argp;
255
 
256
        stampit();
257
 
258
        switch (cmd) {
259
                default:
260
                        return -ENOTTY;
261
 
262
                case WDIOC_GETSUPPORT:
263
                        if (copy_to_user(argp, &bfin_wdt_info, sizeof(bfin_wdt_info)))
264
                                return -EFAULT;
265
                        else
266
                                return 0;
267
 
268
                case WDIOC_GETSTATUS:
269
                case WDIOC_GETBOOTSTATUS:
270
                        return put_user(!!(_bfin_swrst & SWRST_RESET_WDOG), p);
271
 
272
                case WDIOC_KEEPALIVE:
273
                        bfin_wdt_keepalive();
274
                        return 0;
275
 
276
                case WDIOC_SETTIMEOUT: {
277
                        int new_timeout;
278
 
279
                        if (get_user(new_timeout, p))
280
                                return -EFAULT;
281
 
282
                        if (bfin_wdt_set_timeout(new_timeout))
283
                                return -EINVAL;
284
                }
285
                        /* Fall */
286
                case WDIOC_GETTIMEOUT:
287
                        return put_user(timeout, p);
288
 
289
                case WDIOC_SETOPTIONS: {
290
                        unsigned long flags;
291
                        int options, ret = -EINVAL;
292
 
293
                        if (get_user(options, p))
294
                                return -EFAULT;
295
 
296
                        spin_lock_irqsave(&bfin_wdt_spinlock, flags);
297
 
298
                        if (options & WDIOS_DISABLECARD) {
299
                                bfin_wdt_stop();
300
                                ret = 0;
301
                        }
302
 
303
                        if (options & WDIOS_ENABLECARD) {
304
                                bfin_wdt_start();
305
                                ret = 0;
306
                        }
307
 
308
                        spin_unlock_irqrestore(&bfin_wdt_spinlock, flags);
309
 
310
                        return ret;
311
                }
312
        }
313
}
314
 
315
/**
316
 *      bfin_wdt_notify_sys - Notifier Handler
317
 *      @this: notifier block
318
 *      @code: notifier event
319
 *      @unused: unused
320
 *
321
 *      Handles specific events, such as turning off the watchdog during a
322
 *      shutdown event.
323
 */
324
static int bfin_wdt_notify_sys(struct notifier_block *this, unsigned long code,
325
                               void *unused)
326
{
327
        stampit();
328
 
329
        if (code == SYS_DOWN || code == SYS_HALT)
330
                bfin_wdt_stop();
331
 
332
        return NOTIFY_DONE;
333
}
334
 
335
#ifdef CONFIG_PM
336
static int state_before_suspend;
337
 
338
/**
339
 *      bfin_wdt_suspend - suspend the watchdog
340
 *      @pdev: device being suspended
341
 *      @state: requested suspend state
342
 *
343
 *      Remember if the watchdog was running and stop it.
344
 *      TODO: is this even right?  Doesn't seem to be any
345
 *            standard in the watchdog world ...
346
 */
347
static int bfin_wdt_suspend(struct platform_device *pdev, pm_message_t state)
348
{
349
        stampit();
350
 
351
        state_before_suspend = bfin_wdt_running();
352
        bfin_wdt_stop();
353
 
354
        return 0;
355
}
356
 
357
/**
358
 *      bfin_wdt_resume - resume the watchdog
359
 *      @pdev: device being resumed
360
 *
361
 *      If the watchdog was running, turn it back on.
362
 */
363
static int bfin_wdt_resume(struct platform_device *pdev)
364
{
365
        stampit();
366
 
367
        if (state_before_suspend) {
368
                bfin_wdt_set_timeout(timeout);
369
                bfin_wdt_start();
370
        }
371
 
372
        return 0;
373
}
374
#else
375
# define bfin_wdt_suspend NULL
376
# define bfin_wdt_resume NULL
377
#endif
378
 
379
static struct platform_device bfin_wdt_device = {
380
        .name          = WATCHDOG_NAME,
381
        .id            = -1,
382
};
383
 
384
static struct platform_driver bfin_wdt_driver = {
385
        .driver    = {
386
                .name  = WATCHDOG_NAME,
387
                .owner = THIS_MODULE,
388
        },
389
        .suspend   = bfin_wdt_suspend,
390
        .resume    = bfin_wdt_resume,
391
};
392
 
393
static struct file_operations bfin_wdt_fops = {
394
        .owner    = THIS_MODULE,
395
        .llseek   = no_llseek,
396
        .write    = bfin_wdt_write,
397
        .ioctl    = bfin_wdt_ioctl,
398
        .open     = bfin_wdt_open,
399
        .release  = bfin_wdt_release,
400
};
401
 
402
static struct miscdevice bfin_wdt_miscdev = {
403
        .minor    = WATCHDOG_MINOR,
404
        .name     = "watchdog",
405
        .fops     = &bfin_wdt_fops,
406
};
407
 
408
static struct watchdog_info bfin_wdt_info = {
409
        .identity = "Blackfin Watchdog",
410
        .options  = WDIOF_SETTIMEOUT |
411
                    WDIOF_KEEPALIVEPING |
412
                    WDIOF_MAGICCLOSE,
413
};
414
 
415
static struct notifier_block bfin_wdt_notifier = {
416
        .notifier_call = bfin_wdt_notify_sys,
417
};
418
 
419
/**
420
 *      bfin_wdt_init - Initialize module
421
 *
422
 *      Registers the device and notifier handler. Actual device
423
 *      initialization is handled by bfin_wdt_open().
424
 */
425
static int __init bfin_wdt_init(void)
426
{
427
        int ret;
428
 
429
        stampit();
430
 
431
        /* Check that the timeout value is within range */
432
        if (bfin_wdt_set_timeout(timeout))
433
                return -EINVAL;
434
 
435
        /* Since this is an on-chip device and needs no board-specific
436
         * resources, we'll handle all the platform device stuff here.
437
         */
438
        ret = platform_device_register(&bfin_wdt_device);
439
        if (ret)
440
                return ret;
441
 
442
        ret = platform_driver_probe(&bfin_wdt_driver, NULL);
443
        if (ret)
444
                return ret;
445
 
446
        ret = register_reboot_notifier(&bfin_wdt_notifier);
447
        if (ret) {
448
                printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret);
449
                return ret;
450
        }
451
 
452
        ret = misc_register(&bfin_wdt_miscdev);
453
        if (ret) {
454
                printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
455
                       WATCHDOG_MINOR, ret);
456
                unregister_reboot_notifier(&bfin_wdt_notifier);
457
                return ret;
458
        }
459
 
460
        printk(KERN_INFO PFX "initialized: timeout=%d sec (nowayout=%d)\n",
461
               timeout, nowayout);
462
 
463
        return 0;
464
}
465
 
466
/**
467
 *      bfin_wdt_exit - Deinitialize module
468
 *
469
 *      Unregisters the device and notifier handler. Actual device
470
 *      deinitialization is handled by bfin_wdt_close().
471
 */
472
static void __exit bfin_wdt_exit(void)
473
{
474
        misc_deregister(&bfin_wdt_miscdev);
475
        unregister_reboot_notifier(&bfin_wdt_notifier);
476
}
477
 
478
module_init(bfin_wdt_init);
479
module_exit(bfin_wdt_exit);
480
 
481
MODULE_AUTHOR("Michele d'Amico, Mike Frysinger <vapier@gentoo.org>");
482
MODULE_DESCRIPTION("Blackfin Watchdog Device Driver");
483
MODULE_LICENSE("GPL");
484
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
485
 
486
module_param(timeout, uint, 0);
487
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=((2^32)/SCLK), default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
488
 
489
module_param(nowayout, int, 0);
490
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

powered by: WebSVN 2.1.0

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