8cb2dbd04b66326bdc2ea5c8cd36c2cb93256d20
[openwrt.git] / openwrt / target / linux / linux-2.4 / patches / soekris / 002-wd1100.patch
1 diff -Nur linux-2.4.29/drivers/char/Config.in linux-2.4.29_geode/drivers/char/Config.in
2 --- linux-2.4.29/drivers/char/Config.in Sun Aug  8 01:26:04 2004
3 +++ linux-2.4.29_geode/drivers/char/Config.in   Tue Feb 15 23:41:54 2005
4 @@ -270,6 +270,7 @@
5        fi
6     fi
7     tristate '  ZF MachZ Watchdog' CONFIG_MACHZ_WDT
8 +   tristate '  Embedded NatSemi SC1x00 Watchdog' CONFIG_WD1100
9     if [ "$CONFIG_SGI_IP22" = "y" ]; then
10        dep_tristate '  Indy/I2 Hardware Watchdog' CONFIG_INDYDOG $CONFIG_SGI_IP22
11     fi
12 diff -Nur linux-2.4.29/drivers/char/Makefile linux-2.4.29_geode/drivers/char/Makefile
13 --- linux-2.4.29/drivers/char/Makefile  Sun Aug  8 01:26:04 2004
14 +++ linux-2.4.29_geode/drivers/char/Makefile    Tue Feb 15 23:41:54 2005
15 @@ -302,6 +302,7 @@
16  obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
17  obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
18  obj-$(CONFIG_IB700_WDT) += ib700wdt.o
19 +obj-$(CONFIG_WD1100) += wd1100.o
20  obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
21  obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
22  obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
23 diff -Nur linux-2.4.29/drivers/char/wd1100.c linux-2.4.29_geode/drivers/char/wd1100.c
24 --- linux-2.4.29/drivers/char/wd1100.c  Thu Jan  1 01:00:00 1970
25 +++ linux-2.4.29_geode/drivers/char/wd1100.c    Tue Feb 15 23:41:54 2005
26 @@ -0,0 +1,391 @@
27 +/*
28 + *   National Semiconductor SC1x00 CPU watchdog driver
29 + *   Copyright (c) Inprimis Technologies 2002
30 + *
31 + *   by Mark Grosberg <markg@inprimis.com>
32 + *   and Rolando Goldman <rolandog@inprimis.com>
33 + *
34 + *   Minor changes by Kianusch Sayah Karadji <kianusch@sk-tech.net>
35 + *   ( Soekris net4801 Support, module-parameter )
36 + *
37 + *   This program is free software; you can redistribute it and/or
38 + *   modify it under the terms of the GNU General Public License
39 + *   as published by the Free Software Foundation; either version
40 + *   2 of the License, or (at your option) any later version.
41 + *   
42 + */
43 +
44 +#include <linux/module.h>
45 +#include <linux/types.h>
46 +#include <linux/kernel.h>
47 +#include <linux/fs.h>
48 +#include <linux/mm.h>
49 +#include <linux/miscdevice.h>
50 +#include <linux/watchdog.h>
51 +#include <linux/spinlock.h>
52 +#include <linux/sysctl.h>
53 +#include <linux/pci.h>
54 +
55 +/* 
56 + * Since the SC1100 is an x86 clone, we don't even bother with 
57 + * allowing other architectures to compile us.
58 + */
59 +#ifndef CONFIG_X86
60 +# error Sorry this driver is only for x86.
61 +#endif
62 +
63 +#include <asm/system.h>
64 +#include <asm/io.h>
65 +#include <asm/uaccess.h>
66 +#include <asm/processor.h>
67 +
68 +/* #define DEBUG_WD1100 */
69 +
70 +static int proc_wd_timeout(ctl_table   *ctl, 
71 +                           int          write,
72 +                           struct file *file,
73 +                           void        *buffer,
74 +                           size_t      *lenp);
75 +static int proc_wd_graceful(ctl_table   *ctl, 
76 +                            int          write,
77 +                            struct file *file,
78 +                            void        *buffer,
79 +                            size_t      *lenp);
80 +                           
81 +/* Register definitions */
82 +
83 +#define SC1100_F5_VENDOR_ID  0x100B
84 +#define SC1100_F5_DEVICE_ID  0x0515   
85 +
86 +#define CPU_WDTO_REG    0x00 /* watchdog time out, 16 bit register */
87 +#define CPU_WDCNFG_REG  0x02 /* watchdog config , 16 bit register */
88 +#define CPU_WDSTS_REG   0x04 /* watchdog status , 8 bit register */
89 +
90 +/* Default timeout: 4 seconds (changeable via sysctl) */
91 +static unsigned int    sysctl_wd_timeout  = 0;
92 +static unsigned int    sysctl_wd_graceful = 0;
93 +
94 +static unsigned int    timeout  = 4;
95 +static unsigned int    graceful = 1;
96 +
97 +MODULE_PARM (timeout,  "i");
98 +MODULE_PARM (graceful, "i");
99 +
100 +static int             in_use    = 0;
101 +static unsigned short  cpu_base;
102 +static spinlock_t      wd_lock;
103 +
104 +/**************************************************************************/
105 +
106 +/* XXX To-do: DEV_WATCHDOG must be in include/linux/sysctl.h */
107 +enum
108 +{ DEV_WATCHDOG = 6 };
109 +
110 +enum
111 +{
112 +   DEV_WD_TIMEOUT   = 1,
113 +   DEV_WD_GRACEFUL  = 2
114 +};
115 +
116 +static struct ctl_table_header *wd_table_header;
117 +
118 +static ctl_table wd_table[] = {
119 +   {
120 +    DEV_WD_TIMEOUT, "timeout",
121 +    &sysctl_wd_timeout, sizeof(int), 0644, NULL, &proc_wd_timeout
122 +   },
123 +   
124 +   {
125 +    DEV_WD_GRACEFUL, "graceful",
126 +    &sysctl_wd_graceful, sizeof(int), 0644, NULL, &proc_wd_graceful
127 +   },
128 +
129 +   {0}
130 +};
131 +
132 +static ctl_table wd_dir_table[] = {
133 +   {DEV_WATCHDOG, "wd", NULL, 0, 0555, wd_table},
134 +   {0}
135 +};
136 +
137 +static ctl_table wd_root_table[] = {
138 +   {CTL_DEV, "dev", NULL, 0, 0555, wd_dir_table},
139 +   {0}
140 +};
141 +
142 +static int proc_wd_timeout(ctl_table   *ctl, 
143 +                           int          write,
144 +                           struct file *file,
145 +                           void        *buffer,
146 +                           size_t      *lenp)
147 +{
148 +  int   rc;
149 +  
150 +  rc = proc_dointvec(ctl, write, file, buffer, lenp);
151 +  if (write && (rc == 0))
152 +  {
153 +     /* Clamp to limits. */
154 +     if (sysctl_wd_timeout < 1) 
155 +       sysctl_wd_timeout = 1;
156 +     else if (sysctl_wd_timeout > 65535)
157 +       sysctl_wd_timeout = 65535;
158 +  }
159 +  
160 +  return (rc);
161 +}
162 +
163 +static int proc_wd_graceful(ctl_table   *ctl, 
164 +                            int          write,
165 +                            struct file *file,
166 +                            void        *buffer,
167 +                            size_t      *lenp)
168 +{
169 +  int   rc;
170 +  
171 +  rc = proc_dointvec(ctl, write, file, buffer, lenp);
172 +  if (write && (rc == 0))
173 +  {
174 +     /* Clamp to true/false. */
175 +     if (sysctl_wd_graceful) 
176 +       sysctl_wd_graceful = 1;
177 +  }
178 +  
179 +  return (rc);
180 +}                           
181 +
182 +/**************************************************************************/
183 +
184 +static __inline__ void reset_wd(void)
185 +{
186 +  outw(sysctl_wd_timeout * 8, cpu_base + CPU_WDTO_REG);
187 +}
188 +
189 +static int reboot_reason(void)
190 +{
191 +   static int    result;
192 +   static int    fetched = 0;
193
194 +   if (!fetched)
195 +   {
196 +     unsigned char sr;
197 +     
198 +     sr = inb(cpu_base + CPU_WDSTS_REG);
199 +     outb(sr | 1, cpu_base + CPU_WDSTS_REG);
200 +   
201 +     fetched = 1;
202 +   }
203 +   
204 +   return (result);
205 +}
206 +
207 +static struct watchdog_info wd_info =
208 +{
209 +   0,   /* Options          */
210 +   0,   /* Firmware version */
211 +   "NSC SC1x00 WD"
212 +};
213 +
214 +static int wd_ioctl(struct inode *inode, 
215 +                    struct file  *file, 
216 +                    unsigned int  cmd,
217 +                    unsigned long arg)
218 +{
219 +   int  i;
220 +   
221 +   switch (cmd)
222 +   {
223 +      default:
224 +        return (-ENOTTY);
225 +        
226 +      case WDIOC_GETSUPPORT:
227 +        i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct watchdog_info));
228 +        if (i)
229 +          return (i);
230 +        else
231 +          return copy_to_user((struct watchdog_info *)arg, 
232 +                              &wd_info, 
233 +                              sizeof(wd_info));
234 +        break;
235 +        
236 +      case WDIOC_KEEPALIVE:
237 +        reset_wd();
238 +        return (0);
239 +        
240 +      case WDIOC_GETBOOTSTATUS:
241 +        i = reboot_reason();
242 +        return (put_user(i, (int *)arg));
243 +        
244 +      case WDIOC_GETSTATUS:
245 +        i = inw(cpu_base + CPU_WDTO_REG) / 8;
246 +        return (put_user(i, (int *)arg));
247 +   }       
248 +}                    
249 +
250 +static int wd_open(struct inode *inode, 
251 +                   struct file  *file)
252 +{
253 +  spin_lock(&wd_lock);
254 +  if (in_use)
255 +  {
256 +    spin_unlock(&wd_lock);
257 +    return (-EBUSY);
258 +  }
259 +  else
260 +    in_use++;
261 +    
262 +  spin_unlock(&wd_lock);
263 +
264 +  MOD_INC_USE_COUNT;
265 +
266 +  /*
267 +   * Configure the chip to do a reset if the timer goes to 0.
268 +   * Set the clock divisor to 4096.
269 +   */
270 +     
271 +  outw(0xfc, cpu_base + CPU_WDCNFG_REG);
272 +  
273 +  /* Start the watchdog: It won't run until we write the TO reg. */
274 +  reset_wd();
275 +
276 +  return (0);
277 +}
278 +
279 +static int wd_release(struct inode *inode,
280 +                       struct file  *file)
281 +{
282 +  spin_lock(&wd_lock);
283 +    
284 +  in_use = 0;
285 +  
286 +  /*
287 +   * If graceful shutdown is not set, then don't bother to stop the
288 +   * watchdog timer. This handles the scenario where the user process
289 +   * that is poking the watchdog gets terminated due to some error
290 +   * (say a SEGV or some VM condition). 
291 +   *
292 +   * In that case, the kernel would happily close the descriptor for
293 +   * us and leave us in a state where we aren't watching the dog...
294 +   *
295 +   * To work around this, the "graceful" sysctl prevents reset of the
296 +   * watchdog on close.
297 +   */
298 +  if (sysctl_wd_graceful)
299 +    outw(0, cpu_base + CPU_WDCNFG_REG);
300 +  
301 +  spin_unlock(&wd_lock);
302 +  MOD_DEC_USE_COUNT;
303 +  
304 +  return (0);
305 +}           
306 +
307 +static ssize_t wd_write(struct file *file,
308 +                        const char  *data,
309 +                        size_t       len,
310 +                        loff_t      *ppos)
311 +{
312 +  /* Device is non-seekable. */
313 +  if (ppos != &file->f_pos)
314 +    return (-ESPIPE);
315 +  
316 +  if (len > 0)
317 +    reset_wd();   
318 +  
319 +  return (len);
320 +}
321 +
322 +static struct file_operations wd_fops=
323 +{
324 +   owner:      THIS_MODULE,
325 +   write:      wd_write,
326 +   ioctl:      wd_ioctl,
327 +   open:       wd_open,
328 +   release:    wd_release,
329 +};
330
331 +static struct miscdevice sc1x00wd_miscdev=
332 +{
333 +   WATCHDOG_MINOR,
334 +   "watchdog",
335 +   &wd_fops
336 +};
337 +
338 +static int __init wd_init(void)
339 +{
340 +   int              ret;
341 +   struct pci_dev  *dev;
342 +   unsigned int     cw;
343 +
344 +   if (timeout < 1)
345 +      timeout = 1;
346 +   else if (timeout > 65535)
347 +      timeout = 65535;
348 +
349 +   if (graceful != 0)
350 +      graceful = 1;
351 +
352 +   sysctl_wd_timeout=timeout;
353 +   sysctl_wd_graceful=graceful;
354 +
355 +   if ((strcmp(boot_cpu_data.x86_vendor_id, "Geode by NSC") != 0)
356 +   ||  ((boot_cpu_data.x86_model  != 4) && boot_cpu_data.x86_model  != 9))
357 +   {
358 +      printk(KERN_WARNING "wd1100.c: This is not an SC1100 processor!\n");
359 +      return (0);
360 +   }
361 +
362 +   /* get the CONFIG BLOCK ADDRESS from scratch pad register */ 
363 +   dev = pci_find_device(SC1100_F5_VENDOR_ID,SC1100_F5_DEVICE_ID,0);
364 +   if (dev == NULL)
365 +   {
366 +     printk(KERN_ERR "wd1100.c: Can not find bridge device.\n");
367 +     return (0);
368 +   }
369 +
370 +   pci_read_config_dword(dev, 0x64, &cw);
371 +   cpu_base = (unsigned short )cw;
372 +  
373 +#ifdef DEBUG_WD1100
374 +   printk("wd1100.c: CPU base = 0x%X\n", (unsigned int )cpu_base);
375 +#endif
376 +
377 +   printk(KERN_INFO "SC1x00 Watchdog driver by Inprimis Technolgies.\n");
378 +   /*
379 +    * We must call reboot_reason() to reset the flag in the WD.
380 +    *
381 +    * Even though it is available as an ioctl(), we call it during
382 +    * module initialization to perform the clear. You can take out
383 +    * the printk(), but don't take out the call to reboot_reason().
384 +    */
385 +   if (reboot_reason())
386 +      printk(KERN_INFO "Last reboot was by watchdog!\n");
387 +    
388 +   spin_lock_init(&wd_lock);
389 +        
390 +   ret = misc_register(&sc1x00wd_miscdev);
391 +   if (ret)
392 +     printk(KERN_ERR "wd1100.c: Can't register device.\n");
393 +   else
394 +   {
395 +     wd_table_header = register_sysctl_table(wd_root_table, 1); 
396 +      if (wd_table_header == NULL)
397 +        printk(KERN_ERR "wd1100.c: Can't register sysctl.\n");
398 +   }
399 +
400 +   return 0;
401 +}   
402 +
403 +static void __exit wd_exit(void)
404 +{
405 +   if (wd_table_header != NULL)
406 +      unregister_sysctl_table(wd_table_header);
407 +   
408 +   misc_deregister(&sc1x00wd_miscdev);
409 +}
410 +
411 +EXPORT_NO_SYMBOLS; 
412 +
413 +module_init(wd_init);
414 +module_exit(wd_exit);
415 +
416 +MODULE_LICENSE("GPL");
417 +