add "svn:keywords" prop
[openwrt/svn-archive/archive.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 +