kernel: update to kernel version 2.6.32.32
[openwrt.git] / target / linux / rdc / patches-2.6.32 / 110-rdc321x_watchdog_fix.patch
1 --- a/drivers/watchdog/rdc321x_wdt.c
2 +++ b/drivers/watchdog/rdc321x_wdt.c
3 @@ -36,111 +36,99 @@
4 #include <linux/watchdog.h>
5 #include <linux/io.h>
6 #include <linux/uaccess.h>
7 +#include <linux/pci.h>
8 +#include <linux/delay.h>
9 #include <linux/mfd/rdc321x.h>
10
11 -#define RDC_WDT_MASK 0x80000000 /* Mask */
12 +#define RDC321X_WDT_REG 0x00000044
13 +
14 #define RDC_WDT_EN 0x00800000 /* Enable bit */
15 -#define RDC_WDT_WTI 0x00200000 /* Generate CPU reset/NMI/WDT on timeout */
16 -#define RDC_WDT_RST 0x00100000 /* Reset bit */
17 -#define RDC_WDT_WIF 0x00040000 /* WDT IRQ Flag */
18 -#define RDC_WDT_IRT 0x00000100 /* IRQ Routing table */
19 -#define RDC_WDT_CNT 0x00000001 /* WDT count */
20 +#define RDC_WDT_WDTIRQ 0x00400000 /* Create WDT IRQ before CPU reset */
21 +#define RDC_WDT_NMIIRQ 0x00200000 /* Create NMI IRQ before CPU reset */
22 +#define RDC_WDT_RST 0x00100000 /* Reset wdt */
23 +#define RDC_WDT_NIF 0x00080000 /* NMI interrupt occured */
24 +#define RDC_WDT_WIF 0x00040000 /* WDT interrupt occured */
25 +#define RDC_WDT_IRT 0x00000700 /* IRQ Routing table */
26 +#define RDC_WDT_CNT 0x0000007F /* WDT count */
27
28 -#define RDC_CLS_TMR 0x80003844 /* Clear timer */
29 +/* default counter value (2.34 s) */
30 +#define RDC_WDT_DFLT_CNT 0x00000040
31
32 -#define RDC_WDT_INTERVAL (HZ/10+1)
33 +#define RDC_WDT_SETUP (RDC_WDT_EN | RDC_WDT_NMIIRQ | RDC_WDT_RST | RDC_WDT_DFLT_CNT)
34
35 static int ticks = 1000;
36
37 /* some device data */
38
39 static struct {
40 - struct completion stop;
41 - int running;
42 struct timer_list timer;
43 - int queue;
44 - int default_ticks;
45 - unsigned long inuse;
46 - spinlock_t lock;
47 + int seconds_left;
48 + int total_seconds;
49 + bool inuse;
50 + bool running;
51 + bool close_expected;
52 +
53 struct pci_dev *sb_pdev;
54 int base_reg;
55 } rdc321x_wdt_device;
56
57 -/* generic helper functions */
58 +static struct watchdog_info ident = {
59 + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
60 + .identity = "RDC321x WDT",
61 +};
62
63 -static void rdc321x_wdt_trigger(unsigned long unused)
64 +
65 +/* generic helper functions */
66 +static void rdc321x_wdt_timer(unsigned long unused)
67 {
68 - unsigned long flags;
69 - u32 val;
70 + if (!rdc321x_wdt_device.running) {
71 + pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
72 + rdc321x_wdt_device.base_reg, 0);
73 + return;
74 + }
75
76 - if (rdc321x_wdt_device.running)
77 - ticks--;
78 + rdc321x_wdt_device.seconds_left--;
79
80 - /* keep watchdog alive */
81 - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
82 - pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
83 - rdc321x_wdt_device.base_reg, &val);
84 - val |= RDC_WDT_EN;
85 - pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
86 - rdc321x_wdt_device.base_reg, val);
87 - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
88 + if (rdc321x_wdt_device.seconds_left < 1)
89 + return;
90
91 - /* requeue?? */
92 - if (rdc321x_wdt_device.queue && ticks)
93 - mod_timer(&rdc321x_wdt_device.timer,
94 - jiffies + RDC_WDT_INTERVAL);
95 - else {
96 - /* ticks doesn't matter anyway */
97 - complete(&rdc321x_wdt_device.stop);
98 - }
99 + pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
100 + rdc321x_wdt_device.base_reg, RDC_WDT_SETUP);
101
102 + mod_timer(&rdc321x_wdt_device.timer, HZ * 2 + jiffies);
103 }
104
105 static void rdc321x_wdt_reset(void)
106 {
107 - ticks = rdc321x_wdt_device.default_ticks;
108 + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds;
109 }
110
111 static void rdc321x_wdt_start(void)
112 {
113 - unsigned long flags;
114 -
115 - if (!rdc321x_wdt_device.queue) {
116 - rdc321x_wdt_device.queue = 1;
117 -
118 - /* Clear the timer */
119 - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
120 - pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
121 - rdc321x_wdt_device.base_reg, RDC_CLS_TMR);
122 -
123 - /* Enable watchdog and set the timeout to 81.92 us */
124 - pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
125 - rdc321x_wdt_device.base_reg,
126 - RDC_WDT_EN | RDC_WDT_CNT);
127 - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
128 + if (rdc321x_wdt_device.running)
129 + return;
130
131 - mod_timer(&rdc321x_wdt_device.timer,
132 - jiffies + RDC_WDT_INTERVAL);
133 - }
134 + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds;
135 + rdc321x_wdt_device.running = true;
136 + rdc321x_wdt_timer(0);
137
138 - /* if process dies, counter is not decremented */
139 - rdc321x_wdt_device.running++;
140 + return;
141 }
142
143 static int rdc321x_wdt_stop(void)
144 {
145 - if (rdc321x_wdt_device.running)
146 - rdc321x_wdt_device.running = 0;
147 + if (WATCHDOG_NOWAYOUT)
148 + return -ENOSYS;
149
150 - ticks = rdc321x_wdt_device.default_ticks;
151 + rdc321x_wdt_device.running = false;
152
153 - return -EIO;
154 + return 0;
155 }
156
157 /* filesystem operations */
158 static int rdc321x_wdt_open(struct inode *inode, struct file *file)
159 {
160 - if (test_and_set_bit(0, &rdc321x_wdt_device.inuse))
161 + if (xchg(&rdc321x_wdt_device.inuse, true))
162 return -EBUSY;
163
164 return nonseekable_open(inode, file);
165 @@ -148,7 +136,16 @@ static int rdc321x_wdt_open(struct inode
166
167 static int rdc321x_wdt_release(struct inode *inode, struct file *file)
168 {
169 - clear_bit(0, &rdc321x_wdt_device.inuse);
170 + int ret;
171 +
172 + if (rdc321x_wdt_device.close_expected) {
173 + ret = rdc321x_wdt_stop();
174 + if (ret)
175 + return ret;
176 + }
177 +
178 + rdc321x_wdt_device.inuse = false;
179 +
180 return 0;
181 }
182
183 @@ -156,30 +153,29 @@ static long rdc321x_wdt_ioctl(struct fil
184 unsigned long arg)
185 {
186 void __user *argp = (void __user *)arg;
187 - u32 value;
188 - static struct watchdog_info ident = {
189 - .options = WDIOF_CARDRESET,
190 - .identity = "RDC321x WDT",
191 - };
192 - unsigned long flags;
193 + int value;
194
195 switch (cmd) {
196 case WDIOC_KEEPALIVE:
197 rdc321x_wdt_reset();
198 break;
199 - case WDIOC_GETSTATUS:
200 - /* Read the value from the DATA register */
201 - spin_lock_irqsave(&rdc321x_wdt_device.lock, flags);
202 - pci_read_config_dword(rdc321x_wdt_device.sb_pdev,
203 - rdc321x_wdt_device.base_reg, &value);
204 - spin_unlock_irqrestore(&rdc321x_wdt_device.lock, flags);
205 - if (copy_to_user(argp, &value, sizeof(u32)))
206 - return -EFAULT;
207 - break;
208 case WDIOC_GETSUPPORT:
209 if (copy_to_user(argp, &ident, sizeof(ident)))
210 return -EFAULT;
211 break;
212 + case WDIOC_SETTIMEOUT:
213 + if (copy_from_user(&rdc321x_wdt_device.total_seconds, argp, sizeof(int)))
214 + return -EFAULT;
215 + rdc321x_wdt_device.seconds_left = rdc321x_wdt_device.total_seconds;
216 + break;
217 + case WDIOC_GETTIMEOUT:
218 + if (copy_to_user(argp, &rdc321x_wdt_device.total_seconds, sizeof(int)))
219 + return -EFAULT;
220 + break;
221 + case WDIOC_GETTIMELEFT:
222 + if (copy_to_user(argp, &rdc321x_wdt_device.seconds_left, sizeof(int)))
223 + return -EFAULT;
224 + break;
225 case WDIOC_SETOPTIONS:
226 if (copy_from_user(&value, argp, sizeof(int)))
227 return -EFAULT;
228 @@ -194,17 +190,34 @@ static long rdc321x_wdt_ioctl(struct fil
229 }
230 break;
231 default:
232 - return -ENOTTY;
233 + return -EINVAL;
234 }
235 +
236 return 0;
237 }
238
239 static ssize_t rdc321x_wdt_write(struct file *file, const char __user *buf,
240 size_t count, loff_t *ppos)
241 {
242 + size_t i;
243 +
244 if (!count)
245 return -EIO;
246
247 + rdc321x_wdt_device.close_expected = false;
248 +
249 + for (i = 0; i != count; i++) {
250 + char c;
251 +
252 + if (get_user(c, buf + i))
253 + return -EFAULT;
254 +
255 + if (c == 'V') {
256 + rdc321x_wdt_device.close_expected = true;
257 + break;
258 + }
259 + }
260 +
261 rdc321x_wdt_reset();
262
263 return count;
264 @@ -246,27 +259,18 @@ static int __devinit rdc321x_wdt_probe(s
265 rdc321x_wdt_device.sb_pdev = pdata->sb_pdev;
266 rdc321x_wdt_device.base_reg = r->start;
267
268 + rdc321x_wdt_device.running = false;
269 + rdc321x_wdt_device.close_expected = false;
270 + rdc321x_wdt_device.inuse = 0;
271 + setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_timer, 0);
272 + rdc321x_wdt_device.total_seconds = 100;
273 +
274 err = misc_register(&rdc321x_wdt_misc);
275 if (err < 0) {
276 dev_err(&pdev->dev, "misc_register failed\n");
277 return err;
278 }
279
280 - spin_lock_init(&rdc321x_wdt_device.lock);
281 -
282 - /* Reset the watchdog */
283 - pci_write_config_dword(rdc321x_wdt_device.sb_pdev,
284 - rdc321x_wdt_device.base_reg, RDC_WDT_RST);
285 -
286 - init_completion(&rdc321x_wdt_device.stop);
287 - rdc321x_wdt_device.queue = 0;
288 -
289 - clear_bit(0, &rdc321x_wdt_device.inuse);
290 -
291 - setup_timer(&rdc321x_wdt_device.timer, rdc321x_wdt_trigger, 0);
292 -
293 - rdc321x_wdt_device.default_ticks = ticks;
294 -
295 dev_info(&pdev->dev, "watchdog init success\n");
296
297 return 0;
298 @@ -274,10 +278,11 @@ static int __devinit rdc321x_wdt_probe(s
299
300 static int __devexit rdc321x_wdt_remove(struct platform_device *pdev)
301 {
302 - if (rdc321x_wdt_device.queue) {
303 - rdc321x_wdt_device.queue = 0;
304 - wait_for_completion(&rdc321x_wdt_device.stop);
305 - }
306 + if (rdc321x_wdt_device.inuse)
307 + rdc321x_wdt_device.inuse = 0;
308 +
309 + while (timer_pending(&rdc321x_wdt_device.timer))
310 + msleep(100);
311
312 misc_deregister(&rdc321x_wdt_misc);
313
This page took 0.065548 seconds and 5 git commands to generate.