]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/watchdog/mpc83xx_wdt.c
[WATCHDOG] mpc83xx_wdt: convert to the OF platform driver
[linux-2.6-omap-h63xx.git] / drivers / watchdog / mpc83xx_wdt.c
1 /*
2  * mpc83xx_wdt.c - MPC83xx watchdog userspace interface
3  *
4  * Authors: Dave Updegraff <dave@cray.org>
5  *          Kumar Gala <galak@kernel.crashing.org>
6  *              Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
7  *                              ..and from sc520_wdt
8  *
9  * Note: it appears that you can only actually ENABLE or DISABLE the thing
10  * once after POR. Once enabled, you cannot disable, and vice versa.
11  *
12  * This program is free software; you can redistribute  it and/or modify it
13  * under  the terms of  the GNU General  Public License as published by the
14  * Free Software Foundation;  either version 2 of the  License, or (at your
15  * option) any later version.
16  */
17
18 #include <linux/fs.h>
19 #include <linux/init.h>
20 #include <linux/kernel.h>
21 #include <linux/miscdevice.h>
22 #include <linux/of_platform.h>
23 #include <linux/module.h>
24 #include <linux/watchdog.h>
25 #include <linux/io.h>
26 #include <linux/uaccess.h>
27 #include <sysdev/fsl_soc.h>
28
29 struct mpc83xx_wdt {
30         __be32 res0;
31         __be32 swcrr; /* System watchdog control register */
32 #define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
33 #define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
34 #define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
35 #define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
36         __be32 swcnr; /* System watchdog count register */
37         u8 res1[2];
38         __be16 swsrr; /* System watchdog service register */
39         u8 res2[0xF0];
40 };
41
42 static struct mpc83xx_wdt __iomem *wd_base;
43
44 static u16 timeout = 0xffff;
45 module_param(timeout, ushort, 0);
46 MODULE_PARM_DESC(timeout,
47         "Watchdog timeout in ticks. (0<timeout<65536, default=65535");
48
49 static int reset = 1;
50 module_param(reset, bool, 0);
51 MODULE_PARM_DESC(reset,
52         "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
53
54 /*
55  * We always prescale, but if someone really doesn't want to they can set this
56  * to 0
57  */
58 static int prescale = 1;
59 static unsigned int timeout_sec;
60
61 static unsigned long wdt_is_open;
62 static DEFINE_SPINLOCK(wdt_spinlock);
63
64 static void mpc83xx_wdt_keepalive(void)
65 {
66         /* Ping the WDT */
67         spin_lock(&wdt_spinlock);
68         out_be16(&wd_base->swsrr, 0x556c);
69         out_be16(&wd_base->swsrr, 0xaa39);
70         spin_unlock(&wdt_spinlock);
71 }
72
73 static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf,
74                                  size_t count, loff_t *ppos)
75 {
76         if (count)
77                 mpc83xx_wdt_keepalive();
78         return count;
79 }
80
81 static int mpc83xx_wdt_open(struct inode *inode, struct file *file)
82 {
83         u32 tmp = SWCRR_SWEN;
84         if (test_and_set_bit(0, &wdt_is_open))
85                 return -EBUSY;
86
87         /* Once we start the watchdog we can't stop it */
88         __module_get(THIS_MODULE);
89
90         /* Good, fire up the show */
91         if (prescale)
92                 tmp |= SWCRR_SWPR;
93         if (reset)
94                 tmp |= SWCRR_SWRI;
95
96         tmp |= timeout << 16;
97
98         out_be32(&wd_base->swcrr, tmp);
99
100         return nonseekable_open(inode, file);
101 }
102
103 static int mpc83xx_wdt_release(struct inode *inode, struct file *file)
104 {
105         printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n");
106         mpc83xx_wdt_keepalive();
107         clear_bit(0, &wdt_is_open);
108         return 0;
109 }
110
111 static long mpc83xx_wdt_ioctl(struct file *file, unsigned int cmd,
112                                                         unsigned long arg)
113 {
114         void __user *argp = (void __user *)arg;
115         int __user *p = argp;
116         static struct watchdog_info ident = {
117                 .options = WDIOF_KEEPALIVEPING,
118                 .firmware_version = 1,
119                 .identity = "MPC83xx",
120         };
121
122         switch (cmd) {
123         case WDIOC_GETSUPPORT:
124                 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
125         case WDIOC_GETSTATUS:
126         case WDIOC_GETBOOTSTATUS:
127                 return put_user(0, p);
128         case WDIOC_KEEPALIVE:
129                 mpc83xx_wdt_keepalive();
130                 return 0;
131         case WDIOC_GETTIMEOUT:
132                 return put_user(timeout_sec, p);
133         default:
134                 return -ENOTTY;
135         }
136 }
137
138 static const struct file_operations mpc83xx_wdt_fops = {
139         .owner          = THIS_MODULE,
140         .llseek         = no_llseek,
141         .write          = mpc83xx_wdt_write,
142         .unlocked_ioctl = mpc83xx_wdt_ioctl,
143         .open           = mpc83xx_wdt_open,
144         .release        = mpc83xx_wdt_release,
145 };
146
147 static struct miscdevice mpc83xx_wdt_miscdev = {
148         .minor  = WATCHDOG_MINOR,
149         .name   = "watchdog",
150         .fops   = &mpc83xx_wdt_fops,
151 };
152
153 static int __devinit mpc83xx_wdt_probe(struct of_device *ofdev,
154                                        const struct of_device_id *match)
155 {
156         int ret;
157         u32 freq = fsl_get_sys_freq();
158
159         if (!freq || freq == -1)
160                 return -EINVAL;
161
162         wd_base = of_iomap(ofdev->node, 0);
163         if (!wd_base)
164                 return -ENOMEM;
165
166         ret = misc_register(&mpc83xx_wdt_miscdev);
167         if (ret) {
168                 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
169                         WATCHDOG_MINOR, ret);
170                 goto err_unmap;
171         }
172
173         /* Calculate the timeout in seconds */
174         if (prescale)
175                 timeout_sec = (timeout * 0x10000) / freq;
176         else
177                 timeout_sec = timeout / freq;
178
179         pr_info("WDT driver for MPC83xx initialized. mode:%s timeout=%d "
180                 "(%d seconds)\n", reset ? "reset" : "interrupt", timeout,
181                 timeout_sec);
182         return 0;
183 err_unmap:
184         iounmap(wd_base);
185         return ret;
186 }
187
188 static int __devexit mpc83xx_wdt_remove(struct of_device *ofdev)
189 {
190         misc_deregister(&mpc83xx_wdt_miscdev);
191         iounmap(wd_base);
192
193         return 0;
194 }
195
196 static const struct of_device_id mpc83xx_wdt_match[] = {
197         {
198                 .compatible = "mpc83xx_wdt",
199         },
200         {},
201 };
202 MODULE_DEVICE_TABLE(of, mpc83xx_wdt_match);
203
204 static struct of_platform_driver mpc83xx_wdt_driver = {
205         .match_table    = mpc83xx_wdt_match,
206         .probe          = mpc83xx_wdt_probe,
207         .remove         = __devexit_p(mpc83xx_wdt_remove),
208         .driver         = {
209                 .name   = "mpc83xx_wdt",
210                 .owner  = THIS_MODULE,
211         },
212 };
213
214 static int __init mpc83xx_wdt_init(void)
215 {
216         return of_register_platform_driver(&mpc83xx_wdt_driver);
217 }
218
219 static void __exit mpc83xx_wdt_exit(void)
220 {
221         of_unregister_platform_driver(&mpc83xx_wdt_driver);
222 }
223
224 module_init(mpc83xx_wdt_init);
225 module_exit(mpc83xx_wdt_exit);
226
227 MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
228 MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor");
229 MODULE_LICENSE("GPL");
230 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);