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