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