[rdc] use upstream MFD, GPIO patch, put everything in patches.
[openwrt.git] / target / linux / rdc / patches-2.6.30 / 110-rdc321x_watchdog_fix.patch
1 Index: linux-2.6.30.10/drivers/watchdog/rdc321x_wdt.c
2 ===================================================================
3 --- linux-2.6.30.10.orig/drivers/watchdog/rdc321x_wdt.c 2010-04-28 11:11:44.000000000 +0200
4 +++ linux-2.6.30.10/drivers/watchdog/rdc321x_wdt.c      2010-04-28 11:20:51.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 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