Merge branch 'master'
[linux-2.6] / drivers / char / watchdog / mv64x60_wdt.c
1 /*
2  * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface
3  *
4  * Author: James Chapman <jchapman@katalix.com>
5  *
6  * Platform-specific setup code should configure the dog to generate
7  * interrupt or reset as required.  This code only enables/disables
8  * and services the watchdog.
9  *
10  * Derived from mpc8xx_wdt.c, with the following copyright.
11  * 
12  * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
13  * the terms of the GNU General Public License version 2. This program
14  * is licensed "as is" without any warranty of any kind, whether express
15  * or implied.
16  */
17
18 #include <linux/config.h>
19 #include <linux/fs.h>
20 #include <linux/init.h>
21 #include <linux/kernel.h>
22 #include <linux/miscdevice.h>
23 #include <linux/module.h>
24 #include <linux/watchdog.h>
25 #include <linux/platform_device.h>
26
27 #include <asm/mv64x60.h>
28 #include <asm/uaccess.h>
29 #include <asm/io.h>
30
31 /* MV64x60 WDC (config) register access definitions */
32 #define MV64x60_WDC_CTL1_MASK   (3 << 24)
33 #define MV64x60_WDC_CTL1(val)   ((val & 3) << 24)
34 #define MV64x60_WDC_CTL2_MASK   (3 << 26)
35 #define MV64x60_WDC_CTL2(val)   ((val & 3) << 26)
36
37 /* Flags bits */
38 #define MV64x60_WDOG_FLAG_OPENED        0
39 #define MV64x60_WDOG_FLAG_ENABLED       1
40
41 static unsigned long wdt_flags;
42 static int wdt_status;
43 static void __iomem *mv64x60_regs;
44 static int mv64x60_wdt_timeout;
45
46 static void mv64x60_wdt_reg_write(u32 val)
47 {
48         /* Allow write only to CTL1 / CTL2 fields, retaining values in
49          * other fields.
50          */
51         u32 data = readl(mv64x60_regs + MV64x60_WDT_WDC);
52         data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK);
53         data |= val;
54         writel(data, mv64x60_regs + MV64x60_WDT_WDC);
55 }
56
57 static void mv64x60_wdt_service(void)
58 {
59         /* Write 01 followed by 10 to CTL2 */
60         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01));
61         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02));
62 }
63
64 static void mv64x60_wdt_handler_disable(void)
65 {
66         if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
67                 /* Write 01 followed by 10 to CTL1 */
68                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
69                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
70                 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
71         }
72 }
73
74 static void mv64x60_wdt_handler_enable(void)
75 {
76         if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
77                 /* Write 01 followed by 10 to CTL1 */
78                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
79                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
80                 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
81         }
82 }
83
84 static int mv64x60_wdt_open(struct inode *inode, struct file *file)
85 {
86         if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
87                 return -EBUSY;
88
89         mv64x60_wdt_service();
90         mv64x60_wdt_handler_enable();
91
92         nonseekable_open(inode, file);
93
94         return 0;
95 }
96
97 static int mv64x60_wdt_release(struct inode *inode, struct file *file)
98 {
99         mv64x60_wdt_service();
100
101 #if !defined(CONFIG_WATCHDOG_NOWAYOUT)
102         mv64x60_wdt_handler_disable();
103 #endif
104
105         clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
106
107         return 0;
108 }
109
110 static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
111                                  size_t len, loff_t * ppos)
112 {
113         if (len)
114                 mv64x60_wdt_service();
115
116         return len;
117 }
118
119 static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,
120                              unsigned int cmd, unsigned long arg)
121 {
122         int timeout;
123         void __user *argp = (void __user *)arg;
124         static struct watchdog_info info = {
125                 .options = WDIOF_KEEPALIVEPING,
126                 .firmware_version = 0,
127                 .identity = "MV64x60 watchdog",
128         };
129
130         switch (cmd) {
131         case WDIOC_GETSUPPORT:
132                 if (copy_to_user(argp, &info, sizeof(info)))
133                         return -EFAULT;
134                 break;
135
136         case WDIOC_GETSTATUS:
137         case WDIOC_GETBOOTSTATUS:
138                 if (put_user(wdt_status, (int __user *)argp))
139                         return -EFAULT;
140                 wdt_status &= ~WDIOF_KEEPALIVEPING;
141                 break;
142
143         case WDIOC_GETTEMP:
144                 return -EOPNOTSUPP;
145
146         case WDIOC_SETOPTIONS:
147                 return -EOPNOTSUPP;
148
149         case WDIOC_KEEPALIVE:
150                 mv64x60_wdt_service();
151                 wdt_status |= WDIOF_KEEPALIVEPING;
152                 break;
153
154         case WDIOC_SETTIMEOUT:
155                 return -EOPNOTSUPP;
156
157         case WDIOC_GETTIMEOUT:
158                 timeout = mv64x60_wdt_timeout * HZ;
159                 if (put_user(timeout, (int __user *)argp))
160                         return -EFAULT;
161                 break;
162
163         default:
164                 return -ENOIOCTLCMD;
165         }
166
167         return 0;
168 }
169
170 static struct file_operations mv64x60_wdt_fops = {
171         .owner = THIS_MODULE,
172         .llseek = no_llseek,
173         .write = mv64x60_wdt_write,
174         .ioctl = mv64x60_wdt_ioctl,
175         .open = mv64x60_wdt_open,
176         .release = mv64x60_wdt_release,
177 };
178
179 static struct miscdevice mv64x60_wdt_miscdev = {
180         .minor = WATCHDOG_MINOR,
181         .name = "watchdog",
182         .fops = &mv64x60_wdt_fops,
183 };
184
185 static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
186 {
187         struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
188         int bus_clk = 133;
189
190         mv64x60_wdt_timeout = 10;
191         if (pdata) {
192                 mv64x60_wdt_timeout = pdata->timeout;
193                 bus_clk = pdata->bus_clk;
194         }
195
196         mv64x60_regs = mv64x60_get_bridge_vbase();
197
198         writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
199                mv64x60_regs + MV64x60_WDT_WDC);
200
201         return misc_register(&mv64x60_wdt_miscdev);
202 }
203
204 static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
205 {
206         misc_deregister(&mv64x60_wdt_miscdev);
207
208         mv64x60_wdt_service();
209         mv64x60_wdt_handler_disable();
210
211         return 0;
212 }
213
214 static struct platform_driver mv64x60_wdt_driver = {
215         .probe = mv64x60_wdt_probe,
216         .remove = __devexit_p(mv64x60_wdt_remove),
217         .driver = {
218                 .owner = THIS_MODULE,
219                 .name = MV64x60_WDT_NAME,
220         },
221 };
222
223 static struct platform_device *mv64x60_wdt_dev;
224
225 static int __init mv64x60_wdt_init(void)
226 {
227         int ret;
228
229         printk(KERN_INFO "MV64x60 watchdog driver\n");
230
231         mv64x60_wdt_dev = platform_device_register_simple(MV64x60_WDT_NAME,
232                                                           -1, NULL, 0);
233         if (IS_ERR(mv64x60_wdt_dev)) {
234                 ret = PTR_ERR(mv64x60_wdt_dev);
235                 goto out;
236         }
237
238         ret = platform_driver_register(&mv64x60_wdt_driver);
239       out:
240         return ret;
241 }
242
243 static void __exit mv64x60_wdt_exit(void)
244 {
245         platform_driver_unregister(&mv64x60_wdt_driver);
246         platform_device_unregister(mv64x60_wdt_dev);
247 }
248
249 module_init(mv64x60_wdt_init);
250 module_exit(mv64x60_wdt_exit);
251
252 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
253 MODULE_DESCRIPTION("MV64x60 watchdog driver");
254 MODULE_LICENSE("GPL");
255 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);