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