code style scripts/checkpatch.pl (linux-3.9-rc1) formatting
[linux-2.6.34.14-moxart.git] / drivers / watchdog / moxart_wdt.c
blob9a356ce1d0677310a0086b7cc365bc9558b6c85c
1 /* MOXART Watchdog driver (based on MOXA sources)
2 * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
3 * This program is free software; you can redistribute it and/or modify it
4 * under the terms of the GNU General Public License as published by the
5 * Free Software Foundation; either version 2 of the License,
6 * or (at your option) any later version. */
8 #include <linux/module.h>
9 #include <linux/proc_fs.h>
10 #include <linux/string.h>
11 #include <linux/watchdog.h>
12 #include <linux/reboot.h>
13 #include <linux/miscdevice.h>
14 #include <linux/poll.h>
15 #include <linux/timer.h>
16 #include <linux/init.h>
17 #include <linux/interrupt.h>
18 #include <linux/uaccess.h>
19 #include <linux/io.h>
21 #include <mach/hardware.h>
23 #define MOXA_WATCHDOG_MINOR WATCHDOG_MINOR
25 /* 30 seconds */
26 #define DEFAULT_WATCHDOG_TIME (30UL*1000UL)
28 /* 50 msec */
29 #define WATCHDOG_MIN_TIME 50UL
31 /* 60 seconds */
32 #define WATCHDOG_MAX_TIME (60UL*1000UL)
34 /* 500 msec, for watchdog timer polling */
35 #define WATCHDOG_TOL_TIME (500UL)
37 /* 1 seconds, for watchdog timer count */
38 #define WATCHDOG_TOL_COUNT_TIME (1000UL)
40 #define WATCHDOG_COUNTER(x) ((APB_CLK/1000UL)*(x))
41 #define WATCHDOG_JIFFIES(x) ((((x)+WATCHDOG_TOL_TIME)*HZ)/1000UL)
43 /* enable watch dog and set time (unint msec) */
44 #define IOCTL_WATCHDOG_ENABLE 1
46 /* disable watch dog, kernle do it */
47 #define IOCTL_WATCHDOG_DISABLE 2
49 /* get now setting about mode and time */
50 #define IOCTL_WATCHDOG_GET_SETTING 3
52 /* to ack watch dog */
53 #define IOCTL_WATCHDOG_ACK 4
55 struct wdt_set_struct {
56 int mode;
57 unsigned long time;
60 static int opencounts;
61 static int wdt_user_enabled;
62 static unsigned long wdt_time = DEFAULT_WATCHDOG_TIME;
63 static struct timer_list wdt_timer;
64 static struct work_struct rebootqueue;
66 static void wdt_enable(void)
68 __raw_writel(WATCHDOG_COUNTER(wdt_time + WATCHDOG_TOL_COUNT_TIME),
69 IO_ADDRESS(MOXART_WATCHDOG_BASE) + 4);
70 __raw_writel(0x5ab9, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 8);
71 __raw_writel(0x03, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 12);
74 static void wdt_disable(void)
76 __raw_writel(0, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 12);
79 static int wdt_ioctl(struct inode *inode, struct file *file,
80 unsigned int cmd, unsigned long arg)
82 unsigned long time;
83 struct wdt_set_struct nowset;
84 unsigned long flags;
86 switch (cmd) {
87 case IOCTL_WATCHDOG_ENABLE:
88 if (copy_from_user(&time, (void *) arg, sizeof(time)))
89 return -EFAULT;
90 if (time < WATCHDOG_MIN_TIME || time > WATCHDOG_MAX_TIME)
91 return -EINVAL;
92 local_irq_save(flags);
93 if (wdt_user_enabled) {
94 wdt_disable();
95 del_timer(&wdt_timer);
97 wdt_time = time;
98 wdt_user_enabled = 1;
99 wdt_timer.expires = jiffies + WATCHDOG_JIFFIES(wdt_time);
100 add_timer(&wdt_timer);
101 wdt_enable();
102 local_irq_restore(flags);
103 break;
104 case IOCTL_WATCHDOG_DISABLE:
105 local_irq_save(flags);
106 if (wdt_user_enabled) {
107 wdt_disable();
108 del_timer(&wdt_timer);
109 wdt_user_enabled = 0;
111 local_irq_restore(flags);
112 break;
113 case IOCTL_WATCHDOG_GET_SETTING:
114 nowset.mode = wdt_user_enabled;
115 nowset.time = wdt_time;
116 if (copy_to_user((void *) arg, &nowset, sizeof(nowset)))
117 return -EFAULT;
118 break;
119 case IOCTL_WATCHDOG_ACK:
120 local_irq_save(flags);
121 if (wdt_user_enabled) {
122 wdt_disable();
123 del_timer(&wdt_timer);
124 wdt_timer.expires = jiffies +
125 WATCHDOG_JIFFIES(wdt_time);
126 add_timer(&wdt_timer);
127 wdt_enable();
129 local_irq_restore(flags);
130 break;
131 default:
132 return -EINVAL;
134 return 0;
137 static int wdt_open(struct inode *inode, struct file *file)
139 unsigned long flags;
141 dbg_printk(KERN_INFO "MOXART watchdog: wdt_open\n");
142 if (MINOR(inode->i_rdev) != MOXA_WATCHDOG_MINOR)
143 return -ENODEV;
144 local_irq_save(flags);
145 opencounts++;
146 local_irq_restore(flags);
147 return 0;
150 static int wdt_release(struct inode *inode, struct file *file)
152 unsigned long flags;
154 local_irq_save(flags);
155 dbg_printk(KERN_INFO "MOXART watchdog: wdt_release\n");
156 opencounts--;
157 if (opencounts <= 0) {
158 if (wdt_user_enabled) {
159 wdt_disable();
160 del_timer(&wdt_timer);
161 wdt_user_enabled = 0;
163 opencounts = 0;
165 local_irq_restore(flags);
166 return 0;
169 static void wdt_reboot(struct work_struct *work)
171 char *argv[2], *envp[5];
173 dbg_printk(KERN_INFO "MOXART watchdog: wdt_reboot\n");
175 /* With this define set, watchdog reset can be verified.
176 * At boot: 10 second wdt_timer calls wdt_poll and after another
177 * 10 seconds a reset is forced. However, in this driver, a user
178 * mode reboot is preferred (but hardware watchdog should still work)
179 * (unless lockup happens after wdt_disable()) */
180 /* #define DEBUG_TEST_WATCHDOG */
181 #ifdef DEBUG_TEST_WATCHDOG
183 __raw_writel(APB_CLK*10, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 4);
184 __raw_writel(0x5ab9, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 8);
185 __raw_writel(0x03, IO_ADDRESS(MOXART_WATCHDOG_BASE) + 12);
187 #endif
189 if (in_interrupt())
190 return;
191 /* if (!current->fs->root) return; */
192 argv[0] = "/bin/reboot";
193 argv[1] = 0;
194 envp[0] = "HOME=/";
195 envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
196 envp[2] = 0;
197 call_usermodehelper(argv[0], argv, envp, 0);
200 static void wdt_poll(unsigned long ignore)
202 unsigned long flags;
204 local_irq_save(flags);
205 wdt_disable();
206 #ifdef DEBUG_TEST_WATCHDOG
207 wdt_enable();
208 #endif
209 del_timer(&wdt_timer);
210 pr_info("MOXART watchdog: Now reboot the system.\n");
211 schedule_work(&rebootqueue);
212 local_irq_restore(flags);
215 static int wdt_proc_output(char *buf)
217 char *p;
219 p = buf;
220 p += sprintf(p, "user enable\t: %d\nack time\t: %d msec\n",
221 wdt_user_enabled, (int) wdt_time);
222 return p - buf;
225 static int wdt_read_proc(char *page, char **start, off_t off,
226 int count, int *eof, void *data)
228 int len = wdt_proc_output(page);
230 if (len <= off + count)
231 *eof = 1;
232 *start = page + off;
233 len -= off;
234 if (len > count)
235 len = count;
236 if (len < 0)
237 len = 0;
238 return len;
241 static const struct file_operations moxart_wdt_fops = {
242 .owner = THIS_MODULE,
243 .llseek = no_llseek,
244 .ioctl = wdt_ioctl,
245 .open = wdt_open,
246 .release = wdt_release,
249 static struct miscdevice wdt_dev = {
250 MOXA_WATCHDOG_MINOR,
251 "moxart_watchdog",
252 &moxart_wdt_fops
255 static void __exit moxart_wdt_exit(void)
257 unsigned long flags;
259 local_irq_save(flags);
260 if (wdt_user_enabled) {
261 wdt_disable();
262 del_timer(&wdt_timer);
263 wdt_user_enabled = 0;
264 opencounts = 0;
266 local_irq_restore(flags);
267 misc_deregister(&wdt_dev);
270 static int __init moxart_wdt_init(void)
272 if (misc_register(&wdt_dev)) {
273 pr_info("MOXART watchdog: misc_register: failed to register device.\n");
274 goto moxart_wdt_init_err;
277 opencounts = 0;
278 wdt_user_enabled = 0;
280 INIT_WORK(&rebootqueue, wdt_reboot);
281 init_timer(&wdt_timer);
282 wdt_timer.function = wdt_poll;
284 #ifdef DEBUG_TEST_WATCHDOG
285 wdt_time = 10*1000;
286 wdt_user_enabled = 1;
287 wdt_timer.expires = jiffies + WATCHDOG_JIFFIES(wdt_time);
288 add_timer(&wdt_timer);
289 wdt_enable();
290 #endif
292 create_proc_read_entry("driver/moxart_wdt", 0, 0, wdt_read_proc, NULL);
294 pr_info("MOXART watchdog driver\n");
296 return 0;
298 moxart_wdt_init_err:
299 misc_deregister(&wdt_dev);
300 return -ENOMEM;
303 module_init(moxart_wdt_init);
304 module_exit(moxart_wdt_exit);
306 MODULE_DESCRIPTION("MOXART watchdog driver");
307 MODULE_LICENSE("GPL");