OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * avila-wdt.c |
||
3 | * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name> |
||
4 | * |
||
5 | * based on: |
||
6 | * drivers/char/watchdog/ixp4xx_wdt.c |
||
7 | * |
||
8 | * Watchdog driver for Intel IXP4xx network processors |
||
9 | * |
||
10 | * Author: Deepak Saxena <dsaxena@plexity.net> |
||
11 | * |
||
12 | * Copyright 2004 (c) MontaVista, Software, Inc. |
||
13 | * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> |
||
14 | * |
||
15 | * This file is licensed under the terms of the GNU General Public |
||
16 | * License version 2. This program is licensed "as is" without any |
||
17 | * warranty of any kind, whether express or implied. |
||
18 | */ |
||
19 | |||
20 | #include <linux/module.h> |
||
21 | #include <linux/moduleparam.h> |
||
22 | #include <linux/types.h> |
||
23 | #include <linux/kernel.h> |
||
24 | #include <linux/jiffies.h> |
||
25 | #include <linux/timer.h> |
||
26 | #include <linux/fs.h> |
||
27 | #include <linux/miscdevice.h> |
||
28 | #include <linux/watchdog.h> |
||
29 | #include <linux/init.h> |
||
30 | #include <linux/bitops.h> |
||
31 | #include <linux/uaccess.h> |
||
32 | #include <mach/hardware.h> |
||
33 | |||
34 | static int nowayout = WATCHDOG_NOWAYOUT; |
||
35 | static int heartbeat = 20; /* (secs) Default is 20 seconds */ |
||
36 | static unsigned long wdt_status; |
||
37 | static atomic_t wdt_counter; |
||
38 | struct timer_list wdt_timer; |
||
39 | |||
40 | #define WDT_IN_USE 0 |
||
41 | #define WDT_OK_TO_CLOSE 1 |
||
42 | #define WDT_RUNNING 2 |
||
43 | |||
44 | static void wdt_refresh(unsigned long data) |
||
45 | { |
||
46 | if (test_bit(WDT_RUNNING, &wdt_status)) { |
||
47 | if (atomic_dec_and_test(&wdt_counter)) { |
||
48 | printk(KERN_WARNING "Avila watchdog expired, expect a reboot soon!\n"); |
||
49 | clear_bit(WDT_RUNNING, &wdt_status); |
||
50 | return; |
||
51 | } |
||
52 | } |
||
53 | |||
54 | /* strobe to the watchdog */ |
||
55 | gpio_line_set(14, IXP4XX_GPIO_HIGH); |
||
56 | gpio_line_set(14, IXP4XX_GPIO_LOW); |
||
57 | |||
58 | mod_timer(&wdt_timer, jiffies + msecs_to_jiffies(500)); |
||
59 | } |
||
60 | |||
61 | static void wdt_enable(void) |
||
62 | { |
||
63 | atomic_set(&wdt_counter, heartbeat * 2); |
||
64 | |||
65 | /* Disable clock generator output on GPIO 14/15 */ |
||
66 | *IXP4XX_GPIO_GPCLKR &= ~(1 << 8); |
||
67 | |||
68 | /* activate GPIO 14 out */ |
||
69 | gpio_line_config(14, IXP4XX_GPIO_OUT); |
||
70 | gpio_line_set(14, IXP4XX_GPIO_LOW); |
||
71 | |||
72 | if (!test_bit(WDT_RUNNING, &wdt_status)) |
||
73 | wdt_refresh(0); |
||
74 | set_bit(WDT_RUNNING, &wdt_status); |
||
75 | } |
||
76 | |||
77 | static void wdt_disable(void) |
||
78 | { |
||
79 | /* Re-enable clock generator output on GPIO 14/15 */ |
||
80 | *IXP4XX_GPIO_GPCLKR |= (1 << 8); |
||
81 | } |
||
82 | |||
83 | static int avila_wdt_open(struct inode *inode, struct file *file) |
||
84 | { |
||
85 | if (test_and_set_bit(WDT_IN_USE, &wdt_status)) |
||
86 | return -EBUSY; |
||
87 | |||
88 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); |
||
89 | wdt_enable(); |
||
90 | return nonseekable_open(inode, file); |
||
91 | } |
||
92 | |||
93 | static ssize_t |
||
94 | avila_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) |
||
95 | { |
||
96 | if (len) { |
||
97 | if (!nowayout) { |
||
98 | size_t i; |
||
99 | |||
100 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); |
||
101 | |||
102 | for (i = 0; i != len; i++) { |
||
103 | char c; |
||
104 | |||
105 | if (get_user(c, data + i)) |
||
106 | return -EFAULT; |
||
107 | if (c == 'V') |
||
108 | set_bit(WDT_OK_TO_CLOSE, &wdt_status); |
||
109 | } |
||
110 | } |
||
111 | wdt_enable(); |
||
112 | } |
||
113 | return len; |
||
114 | } |
||
115 | |||
116 | static struct watchdog_info ident = { |
||
117 | .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE | |
||
118 | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, |
||
119 | .identity = "Avila Watchdog", |
||
120 | }; |
||
121 | |||
122 | |||
123 | static long avila_wdt_ioctl(struct file *file, unsigned int cmd, |
||
124 | unsigned long arg) |
||
125 | { |
||
126 | int ret = -ENOTTY; |
||
127 | int time; |
||
128 | |||
129 | switch (cmd) { |
||
130 | case WDIOC_GETSUPPORT: |
||
131 | ret = copy_to_user((struct watchdog_info *)arg, &ident, |
||
132 | sizeof(ident)) ? -EFAULT : 0; |
||
133 | break; |
||
134 | |||
135 | case WDIOC_GETSTATUS: |
||
136 | ret = put_user(0, (int *)arg); |
||
137 | break; |
||
138 | |||
139 | case WDIOC_KEEPALIVE: |
||
140 | wdt_enable(); |
||
141 | ret = 0; |
||
142 | break; |
||
143 | |||
144 | case WDIOC_SETTIMEOUT: |
||
145 | ret = get_user(time, (int *)arg); |
||
146 | if (ret) |
||
147 | break; |
||
148 | |||
149 | if (time <= 0 || time > 60) { |
||
150 | ret = -EINVAL; |
||
151 | break; |
||
152 | } |
||
153 | |||
154 | heartbeat = time; |
||
155 | wdt_enable(); |
||
156 | /* Fall through */ |
||
157 | |||
158 | case WDIOC_GETTIMEOUT: |
||
159 | ret = put_user(heartbeat, (int *)arg); |
||
160 | break; |
||
161 | } |
||
162 | return ret; |
||
163 | } |
||
164 | |||
165 | static int avila_wdt_release(struct inode *inode, struct file *file) |
||
166 | { |
||
167 | if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) |
||
168 | wdt_disable(); |
||
169 | else |
||
170 | printk(KERN_CRIT "WATCHDOG: Device closed unexpectedly - " |
||
171 | "timer will not stop\n"); |
||
172 | clear_bit(WDT_IN_USE, &wdt_status); |
||
173 | clear_bit(WDT_OK_TO_CLOSE, &wdt_status); |
||
174 | |||
175 | return 0; |
||
176 | } |
||
177 | |||
178 | |||
179 | static const struct file_operations avila_wdt_fops = { |
||
180 | .owner = THIS_MODULE, |
||
181 | .llseek = no_llseek, |
||
182 | .write = avila_wdt_write, |
||
183 | .unlocked_ioctl = avila_wdt_ioctl, |
||
184 | .open = avila_wdt_open, |
||
185 | .release = avila_wdt_release, |
||
186 | }; |
||
187 | |||
188 | static struct miscdevice avila_wdt_miscdev = { |
||
189 | .minor = WATCHDOG_MINOR + 1, |
||
190 | .name = "avila_watchdog", |
||
191 | .fops = &avila_wdt_fops, |
||
192 | }; |
||
193 | |||
194 | static int __init avila_wdt_init(void) |
||
195 | { |
||
196 | int ret; |
||
197 | |||
198 | init_timer(&wdt_timer); |
||
199 | wdt_timer.expires = 0; |
||
200 | wdt_timer.data = 0; |
||
201 | wdt_timer.function = wdt_refresh; |
||
202 | ret = misc_register(&avila_wdt_miscdev); |
||
203 | if (ret == 0) |
||
204 | printk(KERN_INFO "Avila Watchdog Timer: heartbeat %d sec\n", |
||
205 | heartbeat); |
||
206 | return ret; |
||
207 | } |
||
208 | |||
209 | static void __exit avila_wdt_exit(void) |
||
210 | { |
||
211 | misc_deregister(&avila_wdt_miscdev); |
||
212 | del_timer(&wdt_timer); |
||
213 | wdt_disable(); |
||
214 | } |
||
215 | |||
216 | |||
217 | module_init(avila_wdt_init); |
||
218 | module_exit(avila_wdt_exit); |
||
219 | |||
220 | MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>"); |
||
221 | MODULE_DESCRIPTION("Gateworks Avila Hardware Watchdog"); |
||
222 | |||
223 | module_param(heartbeat, int, 0); |
||
224 | MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 20s)"); |
||
225 | |||
226 | module_param(nowayout, int, 0); |
||
227 | MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); |
||
228 | |||
229 | MODULE_LICENSE("GPL"); |
||
230 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
||
231 |