fix csum_ipv6_magic()
[linux-2.6/linux-acpi-2.6/ibm-acpi-2.6.git] / drivers / watchdog / rc32434_wdt.c
blobe96f2ef3784cc35f41a5e7cc32023bd2348c292a
1 /*
2 * IDT Interprise 79RC32434 watchdog driver
4 * Copyright (C) 2006, Ondrej Zajicek <santiago@crfreenet.org>
5 * Copyright (C) 2008, Florian Fainelli <florian@openwrt.org>
7 * based on
8 * SoftDog 0.05: A Software Watchdog Device
10 * (c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version
15 * 2 of the License, or (at your option) any later version.
19 #include <linux/module.h>
20 #include <linux/types.h>
21 #include <linux/kernel.h>
22 #include <linux/fs.h>
23 #include <linux/mm.h>
24 #include <linux/miscdevice.h>
25 #include <linux/watchdog.h>
26 #include <linux/reboot.h>
27 #include <linux/smp_lock.h>
28 #include <linux/init.h>
29 #include <linux/platform_device.h>
30 #include <linux/uaccess.h>
32 #include <asm/bootinfo.h>
33 #include <asm/time.h>
34 #include <asm/mach-rc32434/integ.h>
36 #define VERSION "0.4"
38 static struct {
39 unsigned long inuse;
40 } rc32434_wdt_device;
42 static struct integ __iomem *wdt_reg;
44 static int expect_close;
46 /* Board internal clock speed in Hz,
47 * the watchdog timer ticks at. */
48 extern unsigned int idt_cpu_freq;
50 /* translate wtcompare value to seconds and vice versa */
51 #define WTCOMP2SEC(x) (x / idt_cpu_freq)
52 #define SEC2WTCOMP(x) (x * idt_cpu_freq)
54 /* Use a default timeout of 20s. This should be
55 * safe for CPU clock speeds up to 400MHz, as
56 * ((2 ^ 32) - 1) / (400MHz / 2) = 21s. */
57 #define WATCHDOG_TIMEOUT 20
59 static int timeout = WATCHDOG_TIMEOUT;
61 static int nowayout = WATCHDOG_NOWAYOUT;
62 module_param(nowayout, int, 0);
63 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
64 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
66 /* apply or and nand masks to data read from addr and write back */
67 #define SET_BITS(addr, or, nand) \
68 writel((readl(&addr) | or) & ~nand, &addr)
70 static void rc32434_wdt_start(void)
72 u32 or, nand;
74 /* zero the counter before enabling */
75 writel(0, &wdt_reg->wtcount);
77 /* don't generate a non-maskable interrupt,
78 * do a warm reset instead */
79 nand = 1 << RC32434_ERR_WNE;
80 or = 1 << RC32434_ERR_WRE;
82 /* reset the ERRCS timeout bit in case it's set */
83 nand |= 1 << RC32434_ERR_WTO;
85 SET_BITS(wdt_reg->errcs, or, nand);
87 /* reset WTC timeout bit and enable WDT */
88 nand = 1 << RC32434_WTC_TO;
89 or = 1 << RC32434_WTC_EN;
91 SET_BITS(wdt_reg->wtc, or, nand);
94 static void rc32434_wdt_stop(void)
96 /* Disable WDT */
97 SET_BITS(wdt_reg->wtc, 0, 1 << RC32434_WTC_EN);
100 static int rc32434_wdt_set(int new_timeout)
102 int max_to = WTCOMP2SEC((u32)-1);
104 if (new_timeout < 0 || new_timeout > max_to) {
105 printk(KERN_ERR KBUILD_MODNAME
106 ": timeout value must be between 0 and %d",
107 max_to);
108 return -EINVAL;
110 timeout = new_timeout;
111 writel(SEC2WTCOMP(timeout), &wdt_reg->wtcompare);
113 return 0;
116 static void rc32434_wdt_ping(void)
118 writel(0, &wdt_reg->wtcount);
121 static int rc32434_wdt_open(struct inode *inode, struct file *file)
123 if (test_and_set_bit(0, &rc32434_wdt_device.inuse))
124 return -EBUSY;
126 if (nowayout)
127 __module_get(THIS_MODULE);
129 rc32434_wdt_start();
130 rc32434_wdt_ping();
132 return nonseekable_open(inode, file);
135 static int rc32434_wdt_release(struct inode *inode, struct file *file)
137 if (expect_close == 42) {
138 rc32434_wdt_stop();
139 printk(KERN_INFO KBUILD_MODNAME ": disabling watchdog timer\n");
140 module_put(THIS_MODULE);
141 } else {
142 printk(KERN_CRIT KBUILD_MODNAME
143 ": device closed unexpectedly. WDT will not stop !\n");
144 rc32434_wdt_ping();
146 clear_bit(0, &rc32434_wdt_device.inuse);
147 return 0;
150 static ssize_t rc32434_wdt_write(struct file *file, const char *data,
151 size_t len, loff_t *ppos)
153 if (len) {
154 if (!nowayout) {
155 size_t i;
157 /* In case it was set long ago */
158 expect_close = 0;
160 for (i = 0; i != len; i++) {
161 char c;
162 if (get_user(c, data + i))
163 return -EFAULT;
164 if (c == 'V')
165 expect_close = 42;
168 rc32434_wdt_ping();
169 return len;
171 return 0;
174 static long rc32434_wdt_ioctl(struct file *file, unsigned int cmd,
175 unsigned long arg)
177 void __user *argp = (void __user *)arg;
178 int new_timeout;
179 unsigned int value;
180 static struct watchdog_info ident = {
181 .options = WDIOF_SETTIMEOUT |
182 WDIOF_KEEPALIVEPING |
183 WDIOF_MAGICCLOSE,
184 .identity = "RC32434_WDT Watchdog",
186 switch (cmd) {
187 case WDIOC_KEEPALIVE:
188 rc32434_wdt_ping();
189 break;
190 case WDIOC_GETSTATUS:
191 case WDIOC_GETBOOTSTATUS:
192 value = 0;
193 if (copy_to_user(argp, &value, sizeof(int)))
194 return -EFAULT;
195 break;
196 case WDIOC_GETSUPPORT:
197 if (copy_to_user(argp, &ident, sizeof(ident)))
198 return -EFAULT;
199 break;
200 case WDIOC_SETOPTIONS:
201 if (copy_from_user(&value, argp, sizeof(int)))
202 return -EFAULT;
203 switch (value) {
204 case WDIOS_ENABLECARD:
205 rc32434_wdt_start();
206 break;
207 case WDIOS_DISABLECARD:
208 rc32434_wdt_stop();
209 break;
210 default:
211 return -EINVAL;
213 break;
214 case WDIOC_SETTIMEOUT:
215 if (copy_from_user(&new_timeout, argp, sizeof(int)))
216 return -EFAULT;
217 if (rc32434_wdt_set(new_timeout))
218 return -EINVAL;
219 /* Fall through */
220 case WDIOC_GETTIMEOUT:
221 return copy_to_user(argp, &timeout, sizeof(int));
222 default:
223 return -ENOTTY;
226 return 0;
229 static struct file_operations rc32434_wdt_fops = {
230 .owner = THIS_MODULE,
231 .llseek = no_llseek,
232 .write = rc32434_wdt_write,
233 .unlocked_ioctl = rc32434_wdt_ioctl,
234 .open = rc32434_wdt_open,
235 .release = rc32434_wdt_release,
238 static struct miscdevice rc32434_wdt_miscdev = {
239 .minor = WATCHDOG_MINOR,
240 .name = "watchdog",
241 .fops = &rc32434_wdt_fops,
244 static char banner[] __devinitdata = KERN_INFO KBUILD_MODNAME
245 ": Watchdog Timer version " VERSION ", timer margin: %d sec\n";
247 static int __devinit rc32434_wdt_probe(struct platform_device *pdev)
249 int ret;
250 struct resource *r;
252 r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rb532_wdt_res");
253 if (!r) {
254 printk(KERN_ERR KBUILD_MODNAME
255 "failed to retrieve resources\n");
256 return -ENODEV;
259 wdt_reg = ioremap_nocache(r->start, r->end - r->start);
260 if (!wdt_reg) {
261 printk(KERN_ERR KBUILD_MODNAME
262 "failed to remap I/O resources\n");
263 return -ENXIO;
266 ret = misc_register(&rc32434_wdt_miscdev);
267 if (ret < 0) {
268 printk(KERN_ERR KBUILD_MODNAME
269 "failed to register watchdog device\n");
270 goto unmap;
273 printk(banner, timeout);
275 return 0;
277 unmap:
278 iounmap(wdt_reg);
279 return ret;
282 static int __devexit rc32434_wdt_remove(struct platform_device *pdev)
284 misc_deregister(&rc32434_wdt_miscdev);
285 iounmap(wdt_reg);
286 return 0;
289 static struct platform_driver rc32434_wdt = {
290 .probe = rc32434_wdt_probe,
291 .remove = __devexit_p(rc32434_wdt_remove),
292 .driver = {
293 .name = "rc32434_wdt",
297 static int __init rc32434_wdt_init(void)
299 return platform_driver_register(&rc32434_wdt);
302 static void __exit rc32434_wdt_exit(void)
304 platform_driver_unregister(&rc32434_wdt);
307 module_init(rc32434_wdt_init);
308 module_exit(rc32434_wdt_exit);
310 MODULE_AUTHOR("Ondrej Zajicek <santiago@crfreenet.org>,"
311 "Florian Fainelli <florian@openwrt.org>");
312 MODULE_DESCRIPTION("Driver for the IDT RC32434 SoC watchdog");
313 MODULE_LICENSE("GPL");
314 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);