[rdc] fix link checking with switches, only port1 of switches would report link up
[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  
64 -static void rdc321x_wdt_trigger(unsigned long unused)
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