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