diff -ruN linux-2.4.19-pre7/Documentation/pcwd-watchdog.txt watchdog-tree/Documentation/pcwd-watchdog.txt --- linux-2.4.19-pre7/Documentation/pcwd-watchdog.txt Tue Apr 16 13:17:49 2002 +++ watchdog-tree/Documentation/pcwd-watchdog.txt Wed Dec 31 19:00:00 1969 @@ -1,134 +0,0 @@ - Berkshire Products PC Watchdog Card - Support for ISA Cards Revision A and C - Documentation and Driver by Ken Hollis - - The PC Watchdog is a card that offers the same type of functionality that - the WDT card does, only it doesn't require an IRQ to run. Furthermore, - the Revision C card allows you to monitor any IO Port to automatically - trigger the card into being reset. This way you can make the card - monitor hard drive status, or anything else you need. - - The Watchdog Driver has one basic role: to talk to the card and send - signals to it so it doesn't reset your computer ... at least during - normal operation. - - The Watchdog Driver will automatically find your watchdog card, and will - attach a running driver for use with that card. After the watchdog - drivers have initialized, you can then talk to the card using the PC - Watchdog program, available from http://ftp.bitgate.com/pcwd/. - - I suggest putting a "watchdog -d" before the beginning of an fsck, and - a "watchdog -e -t 1" immediately after the end of an fsck. (Remember - to run the program with an "&" to run it in the background!) - - If you want to write a program to be compatible with the PC Watchdog - driver, simply do the following: - --- Snippet of code -- -/* - * Watchdog Driver Test Program - */ - -#include -#include -#include -#include -#include -#include -#include - -int fd; - -/* - * This function simply sends an IOCTL to the driver, which in turn ticks - * the PC Watchdog card to reset its internal timer so it doesn't trigger - * a computer reset. - */ -void keep_alive(void) -{ - int dummy; - - ioctl(fd, WDIOC_KEEPALIVE, &dummy); -} - -/* - * The main program. Run the program with "-d" to disable the card, - * or "-e" to enable the card. - */ -int main(int argc, char *argv[]) -{ - fd = open("/dev/watchdog", O_WRONLY); - - if (fd == -1) { - fprintf(stderr, "Watchdog device not enabled.\n"); - fflush(stderr); - exit(-1); - } - - if (argc > 1) { - if (!strncasecmp(argv[1], "-d", 2)) { - ioctl(fd, WDIOC_SETOPTIONS, WDIOS_DISABLECARD); - fprintf(stderr, "Watchdog card disabled.\n"); - fflush(stderr); - exit(0); - } else if (!strncasecmp(argv[1], "-e", 2)) { - ioctl(fd, WDIOC_SETOPTIONS, WDIOS_ENABLECARD); - fprintf(stderr, "Watchdog card enabled.\n"); - fflush(stderr); - exit(0); - } else { - fprintf(stderr, "-d to disable, -e to enable.\n"); - fprintf(stderr, "run by itself to tick the card.\n"); - fflush(stderr); - exit(0); - } - } else { - fprintf(stderr, "Watchdog Ticking Away!\n"); - fflush(stderr); - } - - while(1) { - keep_alive(); - sleep(1); - } -} --- End snippet -- - - Other IOCTL functions include: - - WDIOC_GETSUPPORT - This returns the support of the card itself. This - returns in structure "PCWDS" which returns: - options = WDIOS_TEMPPANIC - (This card supports temperature) - firmware_version = xxxx - (Firmware version of the card) - - WDIOC_GETSTATUS - This returns the status of the card, with the bits of - WDIOF_* bitwise-anded into the value. (The comments - are in linux/pcwd.h) - - WDIOC_GETBOOTSTATUS - This returns the status of the card that was reported - at bootup. - - WDIOC_GETTEMP - This returns the temperature of the card. (You can also - read /dev/watchdog, which gives a temperature update - every second.) - - WDIOC_SETOPTIONS - This lets you set the options of the card. You can either - enable or disable the card this way. - - WDIOC_KEEPALIVE - This pings the card to tell it not to reset your computer. - - And that's all she wrote! - - -- Ken Hollis - (kenji@bitgate.com) - -(This documentation may be out of date. Check - http://ftp.bitgate.com/pcwd/ for the absolute latest additions.) diff -ruN linux-2.4.19-pre7/Documentation/watchdog/api.txt watchdog-tree/Documentation/watchdog/api.txt --- linux-2.4.19-pre7/Documentation/watchdog/api.txt Wed Dec 31 19:00:00 1969 +++ watchdog-tree/Documentation/watchdog/api.txt Sat Apr 20 17:47:24 2002 @@ -0,0 +1,148 @@ +LINUX WATCHDOG DRIVER API + +Some parts of this were copied from watchdog-api.txt which is +Copyright 2002 Christer Weingel , which also +copied some parts from the sbc60xxwdt driver which is +Copyright 2000 Jakob Oestergaard + +So all told, this document is: +Copyright 2002 Rob Radez , +Copyright 2002 Christer Weingel , +Copyright 2000 Jakob Oestergaard +Thanks to Joel.Becker@oracle.com for his WDIOC_SETTIMEOUT changes. + +This document describes the state of the Linux 2.4.19-pre7 and +2.4.19-pre7-ac2 kernels. + +Introduction: + +A Watchdog Timer (WDT) is a hardware circuit that can reset the +computer system in case of a software fault. You probably knew that +already. + +Usually a userspace daemon will notify the kernel watchdog driver via the +/dev/watchdog special device file that userspace is still alive, at +regular intervals. When such a notification occurs, the driver will +usually tell the hardware watchdog that everything is in order, and +that the watchdog should wait for yet another little while to reset +the system. If userspace fails (RAM error, kernel bug, whatever), the +notifications cease to occur, and the hardware watchdog will reset the +system (causing a reboot) after the timeout occurs. + +API: +Watchdog device drivers are nice and simple. Once they're recognized by +the driver, a watchdogs interaction with userspace is through file +operations. Most drivers will only use open, close, write, and ioctl, +although some might also use read if they include temperature support. + +Watchdog drivers should register as a misc device with minor +WATCHDOG_MINOR (130). + +On open, the driver should start the watchdog device. + +On write, the driver should ping the device. However, if the character +'V' is written, then expect the device file to be released and shutdown +cleanly unless CONFIG_WATCHDOG_NOWAYOUT is defined. + +On release, depending on the situation, the device might or might not be +shutdown cleanly. There is a config option, CONFIG_WATCHDOG_NOWAYOUT, that +specifies if on release the watchdog should be disabled so that the system +will not reboot. At the moment, NOWAYOUT is a compile time option that +becomes hard-coded into the driver, but it is moving towards becoming the +default that can be changed by module or kernel command-line options. +Presently, if CONFIG_WATCHDOG_NOWAYOUT is defined, then if the device file +is released, the watchdog will continue running and will reboot when it does +not receive a ping. If NOWAYOUT is not defined, most drivers will exit +cleanly, disabling the watchdog. However, this is where the 'expects to be +closed' comes in. If the driver implements that, it will check a variable +set in the write function to see if the release was expected. If the +release is expected, the driver will disable the watchdog on release. If +the release is not expected, the driver will not disable the watchdog. + +Read is optional, allowing drivers to display any non-standardized data +that does not fit an ioctl. + +On ioctl, there are 5 ioctls that must be implemented. They are: +WDIOC_GETSUPPORT +WDIOC_KEEPALIVE +WDIOC_GETSTATUS +WDIOC_GETBOOTSTATUS +WDIOC_GETTIMEOUT + +There are also 2 optional ioctls: +WDIOC_SETOPTIONS +WDIOC_SETTIMEOUT +(WDIOC_GETTEMP only applies to a temperature device file) + +Required ioctls: +WDIOC_GETSUPPORT: + This ioctl copies a struct watchdog_info to userspace. + From : + +struct watchdog_info { + __u32 options; /* Options the card/driver supports */ + __u32 firmware_version; /* Firmware version of the card */ + __u8 identity[32]; /* Identity of the board */ +}; + + identity is just a string containing the name of the watchdog. + firmware_version is the firmware version if available. If the + version is not available, set it to 0. + options specifies what the watchdog (and driver) supports. From + again: + +#define WDIOF_OVERHEAT 0x0001 /* Reset due to CPU overheat */ +#define WDIOF_FANFAULT 0x0002 /* Fan failed */ +#define WDIOF_EXTERN1 0x0004 /* External relay 1 */ +#define WDIOF_EXTERN2 0x0008 /* External relay 2 */ +#define WDIOF_POWERUNDER 0x0010 /* Power bad/power fault */ +#define WDIOF_CARDRESET 0x0020 /* Card previously reset the CPU */ +#define WDIOF_POWEROVER 0x0040 /* Power over voltage */ +#define WDIOF_SETTIMEOUT 0x0080 /* Set timeout (in seconds) */ +#define WDIOF_KEEPALIVEPING 0x8000 /* Keep alive ping reply */ + + These flags are also used in... + +WDIOC_GETSTATUS and WDIOC_GETBOOTSTATUS: + Both of these ioctls return the status of the watchdog; + WDIOC_GETSTATUS gets the current status and + WDIOC_GETBOOTSTATUS gets the status at either kernel startup or + module load. If there is nothing to report, then just copy out 0. + +WDIOC_KEEPALIVE: + This ioctl sends a ping. This is in addition to writing to the + device file. + +Optional ioctls: +WDIOC_SETOPTIONS: + If the watchdog supports options (other than setting the timeout), + this is how they get set. Currently only the PC Watchdog driver + (pcwd.c) supports this for enabling and disabling the watchdog, + and setting the 'panic on temperature' option. At this point + in time, only three options are supported (): + +#define WDIOS_DISABLECARD 0x0001 /* Turn off the watchdog timer */ +#define WDIOS_ENABLECARD 0x0002 /* Turn on the watchdog timer */ +#define WDIOS_TEMPPANIC 0x0004 /* Kernel panic on temperature trip */ + + Note: If both DISABLECARD and ENABLECARD are set, the driver + should first disable and then enable the card. + +WDIOC_SETTIMEOUT: + If the watchdog has a configurable timeout, then this is how and + where it gets set (other than module load or kernel command line). + The passed argument is the new timeout in seconds. Watchdogs + with a granularity that does not allow the new timeout must set + the timeout to the next greater allowable timeout. The watchdog + must never fire in less than the passed timeout. After setting + the new timeout, the ioctl copies the actual timeout in seconds + back to userspace. This allows userspace to know what the + timeout really is. + +WDIOC_GETTIMEOUT: + This ioctl returns the current timeout in seconds. Return 0 if + timeout value is unknown. + +Note: +On module load, the watchdog should not be started up. +On module unload, the watchdog should be disabled if possible. diff -ruN linux-2.4.19-pre7/Documentation/watchdog/howtowrite.txt watchdog-tree/Documentation/watchdog/howtowrite.txt --- linux-2.4.19-pre7/Documentation/watchdog/howtowrite.txt Wed Dec 31 19:00:00 1969 +++ watchdog-tree/Documentation/watchdog/howtowrite.txt Fri Apr 19 09:20:05 2002 @@ -0,0 +1,78 @@ +LINUX WATCHDOG DRIVER HOWTO + +First off, this document is Copyright 2002 Rob Radez () + +So, you want to write a watchdog driver. Congratulations. It's really +pretty simple. The first thing to do is make sure you follow the API. +You're halfway there already. If you find this too newbie-ish, that's +because it's written by a newbie for other newbies, since people find +watchdog drivers so simple. + +So I'm assuming you know what device you want to support, how to access +it, what it supports, and how to set its options. + +Step one is in module_init since it runs for both module load and kernel +startup. Recognize the card, request whatever regions, and then register +as a misc device. Most drivers will only need to register four file +operations for the watchdog device: open, write, ioctl, and release. +Read isn't necessary but can be useful for temperature devices. That's +another doc though. Make sure to register a reboot notifier so that the +watchdog can be disabled on system shutdown/restart. + +module_exit should unregister, disable the watchdog if possible, and in +general clean up after the module. + +Next, in the open file operation, the watchdog should be started and you +probably give it its first ping. Unless your device supports otherwise, +make sure only one process can open the device file at a time. If you +don't make sure only one process can open it, make sure that your driver +can handle having multiple processes open it :-). + +In the write file operation, the device should be pinged. If +CONFIG_WATCHDOG_NOWAYOUT has not been defined, then you should also +check to see if userspace wrote the character 'V' to the device file. if +so, the driver should expect to be closed after that write, so if the +release function gets called after the write, the driver should be shutdown +cleanly, disabling the watchdog. Make sure that the driver only expects a +shutdown after the first write of a 'V'. If userspace writes a 'V', then +writes something else, then releases the device file, that should be an +unexpected shutdown. + +In the release file operation, the device should check if the release is +expected and if so, disable the watchdog. If the release is unexpected, +just exit out and let mayhem ensue. This handles CONFIG_WATCHDOG_NOWAYOUT +nicely, in my opinion, since if NOWAYOUT is defined, then the shutdown is +never expected and the watchdog doesn't get disabled on release. + +Next up is the fun of ioctl. Re-read the watchdog API. Really. Then +follow it. If the watchdog supports setting the timeout, make sure to +check any values that get copied from userspace to make sure that they +are valid for the device. + +Finally, make sure to edit Documentation/wd-status.txt and add info +about the card you just wrote a driver for. + +So good job, you now have a watchdog driver. + +Notes: + The *_expect_close variable typically only needs to be one byte. + Since it is static and global, it is automatically initialized + to 0. So, if the code changing the variable is ifndef'd out + (say, checking NOWAYOUT), then the variable is never non-zero + and everyone is happy. + + The following is blatantly ripped off from the original + sbc60xxwdt.c: + Why `V' ? Well, `V' is the character in ASCII for the value 86, + and we all know that 86 is _the_ most random number in the universe. + Therefore it is the letter that has the slightest chance of occuring + by chance, when the system becomes corrupted. + + Why is the expect_close variable set to and checked for the value + '42'? Well, 42 is the meaning of life, the universe, and everything. + Plus, it helps protect against single bit memory corruption. + + Please don't just throw around magical constants in your code. It + makes it much easier to maintain and update three years after you + have stopped working on the driver if the methods of accessing the + device are easy to understand. diff -ruN linux-2.4.19-pre7/Documentation/watchdog/status.txt watchdog-tree/Documentation/watchdog/status.txt --- linux-2.4.19-pre7/Documentation/watchdog/status.txt Wed Dec 31 19:00:00 1969 +++ watchdog-tree/Documentation/watchdog/status.txt Sat Apr 20 19:35:55 2002 @@ -0,0 +1,151 @@ +LINUX WATCHDOG DRIVER STATUS + +Implementations in the current drivers in the kernel tree: + +Here I have tried to summarize what the different drivers support and +where they do strange things compared to the other drivers. + +Current as of: 2.4.19-pre7 and 2.4.19-pre7-ac2 + +acquirewdt.c -- Acquire Single Board Computer + + No idea what the default timeout is. + Supports only KEEPALIVEPING. + GETSTATUS and GETBOOTSTATUS return 0. + +advantechwdt.c -- Advantech Single Board Computer + + Timeout defaults to 60 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +alim7101_wdt.c -- ALi M7101 PMU Computer + + Device timeout is 1.6 seconds, but the driver emulates a + timeout of 30 seconds. This will become changeable. + Supports KEEPALIVEPING (but could also support SETTIMEOUT). + GETSTATUS and GETBOOTSTATUS return 0. + +eurotechwdt.c -- Eurotech CPU-1220/1410 + + Timeout defaults to 60 seconds, but is changeable. + Has a module parameter "ev", a string that controlls what + should happen on a timeout. If the string contains "int", + then nothing happens, anything else that causes a reboot. + Supports KEEPALIVEPING, CARDRESET, and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +i810-tco.c -- Intel 810 chipset + + Timeout defaults to 30 seconds, but is changeable in steps + of .6 seconds (Change this to just seconds). + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +ib700wdt.c -- IB700 Single Board Computer + + Timeout defaults to 30 seconds, but is changeable. The + timeout values are limited by the device though. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +indydog.c -- Hardware Watchdog Device for SGI IP22 + + Timeout is hardcoded in hardware to 60 seconds. + Support KEEPALIVEPING. + GETSTATUS and GETBOOTSTATUS return 0. + +machzwd.c -- MachZ ZF-Logic + + Device is hardcoded to timeout in 2s and then 7.2ms. + Driver emulates a 30 second timeout, but is changeable. + Has a module parameter "action" that controls what happens + when the timeout runs out which can be 0 = RESET (default), + 1 = SMI, 2 = NMI, 3 = SCI. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +mixcomwd.c -- MixCom Watchdog + + Timeout appears to be somewhere around 5 seconds, but who + knows. + Supports KEEPALIVEPING. + GETSTATUS and GETBOOTSTATUS return 0. + This driver badly needs a rewrite and some comments. + +pcwd.c -- Berkshire PC Watchdog + + Hardcoded timeout of 1.5 seconds but hardware supports setting + different timeouts. + Supports KEEPALIVEPING, OVERHEAT, and CARDRESET. + GETSTATUS and GETBOOTSTATUS return the card's status. + SETOPTIONS can be used to enable and disable the card + and to ask the driver to panic if the system overheats. + +sbc60xxwdt.c -- 60xx Single Board Computer + + Device has a hardcoded 1s timeout. + Driver emulates a 30 second timeout, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +sc1200wdt.c -- National Semiconductor PC87307/PC97307 (ala SC1200) + + Timeout defaults to 60 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +sc520_wdt.c -- AMD Elan SC520 Processor + + Device timeout is from about .5ms to 32s, defaults to .25ms. + Driver emulates a timeout of 30 seconds, but is changeable + changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +shwdt.c -- SuperH 3/4 integrated watchdog + + Device timeout is from 41usecs to ~5ms, defaults to 5ms. + Driver emulates a timeout of 30 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +softdog.c -- Software watchdog + + Timeout defaults to 60 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +w83877f_wdt.c -- W83877F Computer + + Device timeout is 1.6s. + Driver emulates a timeout of 30 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +wafer5823wdt.c -- ICP Wafer 5823 Single Board Computer + + Timeout defaults to 60 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +wdt.c -- ICS WDT500/501 ISA and +wdt_pci.c -- ICS WDT500/501 PCI + + Timeout defaults to 60 seconds, but is changeable. + GETSUPPORT returns with bits set depending on the actual + card. The WDT501 supports a lot of external monitoring, the + WDT500 much less. + GETSTATUS and GETBOOTSTATUS return the card's status. + +wdt285.c -- Footbridge watchdog + + Timeout defaults to 60 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. + +wdt977.c -- Netwinder W83977AF chip + + Timeout defaults to 180 seconds, but is changeable. + Supports KEEPALIVEPING and SETTIMEOUT. + GETSTATUS and GETBOOTSTATUS return 0. diff -ruN linux-2.4.19-pre7/Documentation/watchdog-api.txt watchdog-tree/Documentation/watchdog-api.txt --- linux-2.4.19-pre7/Documentation/watchdog-api.txt Tue Apr 16 13:17:49 2002 +++ watchdog-tree/Documentation/watchdog-api.txt Wed Dec 31 19:00:00 1969 @@ -1,390 +0,0 @@ -The Linux Watchdog driver API. - -Copyright 2002 Christer Weingel - -Some parts of this document are copied verbatim from the sbc60xxwdt -driver which is (c) Copyright 2000 Jakob Oestergaard - -This document describes the state of the Linux 2.4.18 kernel. - -Introduction: - -A Watchdog Timer (WDT) is a hardware circuit that can reset the -computer system in case of a software fault. You probably knew that -already. - -Usually a userspace daemon will notify the kernel watchdog driver via the -/dev/watchdog special device file that userspace is still alive, at -regular intervals. When such a notification occurs, the driver will -usually tell the hardware watchdog that everything is in order, and -that the watchdog should wait for yet another little while to reset -the system. If userspace fails (RAM error, kernel bug, whatever), the -notifications cease to occur, and the hardware watchdog will reset the -system (causing a reboot) after the timeout occurs. - -The Linux watchdog API is a rather AD hoc construction and different -drivers implement different, and sometimes incompatible, parts of it. -This file is an attempt to document the existing usage and allow -future driver writers to use it as a reference. - -The simplest API: - -All drivers support the basic mode of operation, where the watchdog -activates as soon as /dev/watchdog is opened and will reboot unless -the watchdog is pinged within a certain time, this time is called the -timeout or margin. The simplest way to ping the watchdog is to write -some data to the device. So a very simple watchdog daemon would look -like this: - -int main(int argc, const char *argv[]) { - int fd=open("/dev/watchdog",O_WRONLY); - if (fd==-1) { - perror("watchdog"); - exit(1); - } - while(1) { - write(fd, "\0", 1); - sleep(10); - } -} - -A more advanced driver could for example check that a HTTP server is -still responding before doing the write call to ping the watchdog. - -When the device is closed, the watchdog is disabled. This is not -always such a good idea, since if there is a bug in the watchdog -daemon and it crashes the system will not reboot. Because of this, -some of the drivers support the configuration option "Disable watchdog -shutdown on close", CONFIG_WATCHDOG_NOWAYOUT. If it is set to Y when -compiling the kernel, there is no way of disabling the watchdog once -it has been started. So, if the watchdog dameon crashes, the system -will reboot after the timeout has passed. - -Some other drivers will not disable the watchdog, unless a specific -magic character 'V' has been sent /dev/watchdog just before closing -the file. If the userspace daemon closes the file without sending -this special character, the driver will assume that the daemon (and -userspace in general) died, and will stop pinging the watchdog without -disabling it first. This will then cause a reboot. - -The ioctl API: - -All conforming drivers also support an ioctl API. - -Pinging the watchdog using an ioctl: - -All drivers that have an ioctl interface support at least one ioctl, -KEEPALIVE. This ioctl does exactly the same thing as a write to the -watchdog device, so the main loop in the above program could be -replaced with: - - while (1) { - ioctl(fd, WDIOC_KEEPALIVE, 0); - sleep(10); - } - -the argument to the ioctl is ignored. - -Setting and getting the timeout: - -For some drivers it is possible to modify the watchdog timeout on the -fly with the SETTIMEOUT ioctl, those drivers have the WDIOF_SETTIMEOUT -flag set in their option field. The argument is an integer -representing the timeout in seconds. The driver returns the real -timeout used in the same variable, and this timeout might differ from -the requested one due to limitation of the hardware. - - int timeout = 45; - ioctl(fd, WDIOC_SETTIMEOUT, &timeout); - printf("The timeout was set to %d seconds\n", timeout); - -This example might actually print "The timeout was set to 60 seconds" -if the device has a granularity of minutes for its timeout. - -Starting with the Linux 2.4.18 kernel, it is possible to query the -current timeout using the GETTIMEOUT ioctl. - - ioctl(fd, WDIOC_GETTIMEOUT, &timeout); - printf("The timeout was is %d seconds\n", timeout); - -Envinronmental monitoring: - -All watchdog drivers are required return more information about the system, -some do temperature, fan and power level monitoring, some can tell you -the reason for the last reboot of the system. The GETSUPPORT ioctl is -available to ask what the device can do: - - struct watchdog_info ident; - ioctl(fd, WDIOC_GETSUPPORT, &ident); - -the fields returned in the ident struct are: - - identity a string identifying the watchdog driver - firmware_version the firmware version of the card if available - options a flags describing what the device supports - -the options field can have the following bits set, and describes what -kind of information that the GET_STATUS and GET_BOOT_STATUS ioctls can -return. [FIXME -- Is this correct?] - - WDIOF_OVERHEAT Reset due to CPU overheat - -The machine was last rebooted by the watchdog because the thermal limit was -exceeded - - WDIOF_FANFAULT Fan failed - -A system fan monitored by the watchdog card has failed - - WDIOF_EXTERN1 External relay 1 - -External monitoring relay/source 1 was triggered. Controllers intended for -real world applications include external monitoring pins that will trigger -a reset. - - WDIOF_EXTERN2 External relay 2 - -External monitoring relay/source 2 was triggered - - WDIOF_POWERUNDER Power bad/power fault - -The machine is showing an undervoltage status - - WDIOF_CARDRESET Card previously reset the CPU - -The last reboot was caused by the watchdog card - - WDIOF_POWEROVER Power over voltage - -The machine is showing an overvoltage status. Note that if one level is -under and one over both bits will be set - this may seem odd but makes -sense. - - WDIOF_KEEPALIVEPING Keep alive ping reply - -The watchdog saw a keepalive ping since it was last queried. - - WDIOF_SETTIMEOUT Can set/get the timeout - - -For those drivers that return any bits set in the option field, the -GETSTATUS and GETBOOTSTATUS ioctls can be used to ask for the current -status, and the status at the last reboot, respectively. - - int flags; - ioctl(fd, WDIOC_GETSTATUS, &flags); - - or - - ioctl(fd, WDIOC_GETBOOTSTATUS, &flags); - -Note that not all devices support these two calls, and some only -support the GETBOOTSTATUS call. - -Some drivers can measure the temperature using the GETTEMP ioctl. The -returned value is the temperature in degrees farenheit. - - int temperature; - ioctl(fd, WDIOC_GETTEMP, &temperature); - -Finally the SETOPTIONS ioctl can be used to control some aspects of -the cards operation; right now the pcwd driver is the only one -supporting thiss ioctl. - - int options = 0; - ioctl(fd, WDIOC_SETOPTIONS, options); - -The following options are available: - - WDIOS_DISABLECARD Turn off the watchdog timer - WDIOS_ENABLECARD Turn on the watchdog timer - WDIOS_TEMPPANIC Kernel panic on temperature trip - -[FIXME -- better explanations] - -Implementations in the current drivers in the kernel tree: - -Here I have tried to summarize what the different drivers support and -where they do strange things compared to the other drivers. - -acquirewdt.c -- Acquire Single Board Computer - - This driver has a hardcoded timeout of 1 minute - - Supports CONFIG_WATCHDOG_NOWAYOUT - - GETSUPPORT returns KEEPALIVEPING. GETSTATUS will return 1 if - the device is open, 0 if not. [FIXME -- isn't this rather - silly? To be able to use the ioctl, the device must be open - and so GETSTATUS will always return 1]. - -advantechwdt.c -- Advantech Single Board Computer - - Timeout that defaults to 60 seconds, supports SETTIMEOUT. - - Supports CONFIG_WATCHDOG_NOWAYOUT - - GETSUPPORT returns WDIOF_KEEPALIVEPING and WDIOF_SETTIMEOUT. - The GETSTATUS call returns if the device is open or not. - [FIXME -- silliness again?] - -eurotechwdt.c -- Eurotech CPU-1220/1410 - - The timeout can be set using the SETTIMEOUT ioctl and defaults - to 60 seconds. - - Also has a module parameter "ev", event type which controls - what should happen on a timeout, the string "int" or anything - else that causes a reboot. [FIXME -- better description] - - Supports CONFIG_WATCHDOG_NOWAYOUT - - GETSUPPORT returns CARDRESET and WDIOF_SETTIMEOUT but - GETSTATUS is not supported and GETBOOTSTATUS just returns 0. - -i810-tco.c -- Intel 810 chipset - - Also has support for a lot of other i8x0 stuff, but the - watchdog is one of the things. - - The timeout is set using the module parameter "i810_margin", - which is in steps of 0.6 seconds where 2 - - Custom Linux Driver And Program Development - - -The following watchdog drivers are currently implemented: - - ICS WDT501-P - ICS WDT501-P (no fan tachometer) - ICS WDT500-P - Software Only - SA1100 Internal Watchdog - Berkshire Products PC Watchdog Revision A & C (by Ken Hollis) - - -All six interfaces provide /dev/watchdog, which when open must be written -to within a timeout or the machine will reboot. Each write delays the reboot -time another timeout. In the case of the software watchdog the ability to -reboot will depend on the state of the machines and interrupts. The hardware -boards physically pull the machine down off their own onboard timers and -will reboot from almost anything. - -A second temperature monitoring interface is available on the WDT501P cards -and some Berkshire cards. This provides /dev/temperature. This is the machine -internal temperature in degrees Fahrenheit. Each read returns a single byte -giving the temperature. - -The third interface logs kernel messages on additional alert events. - -Both software and hardware watchdog drivers are available in the standard -kernel. If you are using the software watchdog, you probably also want -to use "panic=60" as a boot argument as well. - -The wdt card cannot be safely probed for. Instead you need to pass -wdt=ioaddr,irq as a boot parameter - eg "wdt=0x240,11". - -The SA1100 watchdog module can be configured with the "sa1100_margin" -commandline argument which specifies timeout value in seconds. - -The i810 TCO watchdog modules can be configured with the "i810_margin" -commandline argument which specifies the counter initial value. The counter -is decremented every 0.6 seconds and default to 50 (30 seconds). Values can -range between 3 and 63. - -The i810 TCO watchdog driver also implements the WDIOC_GETSTATUS and -WDIOC_GETBOOTSTATUS ioctl()s. WDIOC_GETSTATUS returns the actual counter value -and WDIOC_GETBOOTSTATUS returns the value of TCO2 Status Register (see Intel's -documentation for the 82801AA and 82801AB datasheet). - -Features --------- - WDT501P WDT500P Software Berkshire i810 TCO SA1100WD -Reboot Timer X X X X X X -External Reboot X X o o o X -I/O Port Monitor o o o X o o -Temperature X o o X o o -Fan Speed X o o o o o -Power Under X o o o o o -Power Over X o o o o o -Overheat X o o o o o - -The external event interfaces on the WDT boards are not currently supported. -Minor numbers are however allocated for it. - - -Example Watchdog Driver ------------------------ - -#include -#include -#include - -int main(int argc, const char *argv[]) -{ - int fd=open("/dev/watchdog",O_WRONLY); - if(fd==-1) - { - perror("watchdog"); - exit(1); - } - while(1) - { - write(fd,"\0",1); - fsync(fd); - sleep(10); - } -} - - -Contact Information - -People keep asking about the WDT watchdog timer hardware: The phone contacts -for Industrial Computer Source are: - -Industrial Computer Source -http://www.indcompsrc.com -ICS Advent, San Diego -6260 Sequence Dr. -San Diego, CA 92121-4371 -Phone (858) 677-0877 -FAX: (858) 677-0895 -> -ICS Advent Europe, UK -Oving Road -Chichester, -West Sussex, -PO19 4ET, UK -Phone: 00.44.1243.533900 - - -and please mention Linux when enquiring. - -For full information about the PCWD cards see the pcwd-watchdog.txt document. diff -ruN linux-2.4.19-pre7/drivers/char/acquirewdt.c watchdog-tree/drivers/char/acquirewdt.c --- linux-2.4.19-pre7/drivers/char/acquirewdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/acquirewdt.c Sat Apr 20 18:40:47 2002 @@ -21,47 +21,68 @@ #include #include -#include #include #include #include -#include #include #include -#include #include -#include #include #include #include #include #include #include -#include -#include -static int acq_is_open; -static spinlock_t acq_lock; +static unsigned long acq_is_open; +static char acq_expect_close; /* * You must set these - there is no sane way to probe for this board. */ -#define WDT_STOP 0x43 -#define WDT_START 0x443 - -#define WD_TIMO (100*60) /* 1 minute */ - +static int wdt_stop = 0x43; +static int wdt_start = 0x443; /* * Kernel methods. */ + +#ifndef MODULE + +static int __init acq_setup(char *str) +{ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + if(ints[0] > 0){ + wdt_stop = ints[1]; + if(ints[0] > 1) + wdt_start = ints[2]; + } + + return 1; +} + +__setup("acqwdt=", acq_setup); + +#endif /* !MODULE */ + +MODULE_PARM(wdt_stop, "i"); +MODULE_PARM_DESC(wdt_stop, "Acquire WDT 'stop' io port (default 0x43)"); +MODULE_PARM(wdt_start, "i"); +MODULE_PARM_DESC(wdt_start, "Acquire WDT 'start' io port (default 0x443)"); static void acq_ping(void) { /* Write a watchdog value */ - inb_p(WDT_START); + inb_p(wdt_start); +} + +static void acq_disable(void) +{ + inb_p(wdt_stop); } static ssize_t acq_write(struct file *file, const char *buf, size_t count, loff_t *ppos) @@ -72,25 +93,30 @@ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + acq_expect_close = 0; + + for(i = 0; i != count; i++) + { + if(buf[i] == 'V') + acq_expect_close = 42; + } +#endif acq_ping(); - return 1; } - return 0; -} - -static ssize_t acq_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - return -EINVAL; + return count; } - - static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static struct watchdog_info ident= { - WDIOF_KEEPALIVEPING, 1, "Acquire WDT" + options: WDIOF_KEEPALIVEPING, + firmware_version: 0, + identity: "Acquire WDT" }; switch(cmd) @@ -101,14 +127,38 @@ break; case WDIOC_GETSTATUS: - if (copy_to_user((int *)arg, &acq_is_open, sizeof(int))) - return -EFAULT; - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: acq_ping(); break; + case WDIOC_GETTIMEOUT: + return put_user(0, (int *)arg); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + { + acq_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) + { + acq_ping(); + retval = 0; + } + + return retval; + } + default: return -ENOTTY; } @@ -117,41 +167,27 @@ static int acq_open(struct inode *inode, struct file *file) { - switch(MINOR(inode->i_rdev)) - { - case WATCHDOG_MINOR: - spin_lock(&acq_lock); - if(acq_is_open) - { - spin_unlock(&acq_lock); - return -EBUSY; - } - /* - * Activate - */ - - acq_is_open=1; - inb_p(WDT_START); - spin_unlock(&acq_lock); - return 0; - default: - return -ENODEV; - } + if(test_and_set_bit(0, &acq_is_open)) + return -EBUSY; + /* + * Activate + */ + + acq_ping(); /* A ping suffices to activate the watchdog */ + return 0; } static int acq_close(struct inode *inode, struct file *file) { - lock_kernel(); - if(MINOR(inode->i_rdev)==WATCHDOG_MINOR) + if(acq_expect_close == 42) { - spin_lock(&acq_lock); -#ifndef CONFIG_WATCHDOG_NOWAYOUT - inb_p(WDT_STOP); -#endif - acq_is_open=0; - spin_unlock(&acq_lock); + acq_disable(); + } else { + printk(KERN_CRIT "acquirewdt: Unexpected close, not stopping the watchdog!\n"); + acq_ping(); } - unlock_kernel(); + clear_bit(0, &acq_is_open); + acq_expect_close = 0; return 0; } @@ -165,7 +201,7 @@ if(code==SYS_DOWN || code==SYS_HALT) { /* Turn the card off */ - inb_p(WDT_STOP); + acq_disable(); } return NOTIFY_DONE; } @@ -177,7 +213,7 @@ static struct file_operations acq_fops = { owner: THIS_MODULE, - read: acq_read, + llseek: no_llseek, write: acq_write, ioctl: acq_ioctl, open: acq_open, @@ -186,9 +222,9 @@ static struct miscdevice acq_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &acq_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &acq_fops, }; @@ -206,13 +242,13 @@ static int __init acq_init(void) { - printk("WDT driver for Acquire single board computer initialising.\n"); + printk(KERN_INFO "WDT driver for Acquire single board computer initialising.\n"); - spin_lock_init(&acq_lock); if (misc_register(&acq_miscdev)) return -ENODEV; - request_region(WDT_STOP, 1, "Acquire WDT"); - request_region(WDT_START, 1, "Acquire WDT"); + if(wdt_stop != wdt_start) + request_region(wdt_stop, 1, "Acquire WDT"); + request_region(wdt_start, 1, "Acquire WDT"); register_reboot_notifier(&acq_notifier); return 0; } @@ -221,12 +257,15 @@ { misc_deregister(&acq_miscdev); unregister_reboot_notifier(&acq_notifier); - release_region(WDT_STOP,1); - release_region(WDT_START,1); + if(wdt_stop != wdt_start) + release_region(wdt_stop,1); + release_region(wdt_start,1); } module_init(acq_init); module_exit(acq_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Unkown"); +MODULE_DESCRIPTION("Acquire Single Board Computer Watchdog Timer driver"); EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/advantechwdt.c watchdog-tree/drivers/char/advantechwdt.c --- linux-2.4.19-pre7/drivers/char/advantechwdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/advantechwdt.c Sat Apr 20 18:40:56 2002 @@ -24,14 +24,11 @@ #include #include -#include #include #include #include -#include #include #include -#include #include #include #include @@ -40,41 +37,67 @@ #include #include #include -#include -#include -static int advwdt_is_open; -static spinlock_t advwdt_lock; +static unsigned long advwdt_is_open; +static char adv_expect_close; /* * You must set these - there is no sane way to probe for this board. * * To enable or restart, write the timeout value in seconds (1 to 63) - * to I/O port WDT_START. To disable, read I/O port WDT_STOP. + * to I/O port wdt_start. To disable, read I/O port wdt_stop. * Both are 0x443 for most boards (tested on a PCA-6276VE-00B1), but * check your manual (at least the PCA-6159 seems to be different - - * the manual says WDT_STOP is 0x43, not 0x443). + * the manual says wdt_stop is 0x43, not 0x443). * (0x43 is also a write-only control register for the 8254 timer!) - * - * TODO: module parameters to set the I/O port addresses and NOWAYOUT - * option at load time. */ -#define WDT_STOP 0x443 -#define WDT_START 0x443 +static int wdt_stop = 0x443; +static int wdt_start = 0x443; -#define WD_TIMO 60 /* 1 minute */ -static int wd_margin = WD_TIMO; +static int wd_margin = 60; /* 60 sec default timeout */ /* * Kernel methods. */ - + +#ifndef MODULE + +static int __init adv_setup(char *str) +{ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if(ints[0] > 0){ + wdt_stop = ints[1]; + if(ints[0] > 1) + wdt_start = ints[2]; + } + + return 1; +} + +__setup("advwdt=", adv_setup); + +#endif /* !MODULE */ + +MODULE_PARM(wdt_stop, "i"); +MODULE_PARM_DESC(wdt_stop, "Advantech WDT 'stop' io port (default 0x443)"); +MODULE_PARM(wdt_start, "i"); +MODULE_PARM_DESC(wdt_start, "Advantech WDT 'start' io port (default 0x443)"); + static void advwdt_ping(void) { /* Write a watchdog value */ - outb_p(wd_margin, WDT_START); + outb_p(wd_margin, wdt_start); +} + +static void +advwdt_disable(void) +{ + inb_p(wdt_stop); } static ssize_t @@ -85,16 +108,19 @@ return -ESPIPE; if (count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + adv_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + adv_expect_close = 42; + } +#endif advwdt_ping(); - return 1; } - return 0; -} - -static ssize_t -advwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - return -EINVAL; + return count; } static int @@ -103,7 +129,9 @@ { int new_margin; static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 1, "Advantech WDT" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "Advantech WDT" }; switch (cmd) { @@ -113,9 +141,8 @@ break; case WDIOC_GETSTATUS: - if (copy_to_user((int *)arg, &advwdt_is_open, sizeof(int))) - return -EFAULT; - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: advwdt_ping(); @@ -132,7 +159,26 @@ case WDIOC_GETTIMEOUT: return put_user(wd_margin, (int *)arg); - break; + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + advwdt_disable(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + advwdt_ping(); + retval = 0; + } + + return retval; + } default: return -ENOTTY; @@ -143,39 +189,27 @@ static int advwdt_open(struct inode *inode, struct file *file) { - switch (MINOR(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&advwdt_lock); - if (advwdt_is_open) { - spin_unlock(&advwdt_lock); - return -EBUSY; - } - /* - * Activate - */ - - advwdt_is_open = 1; - advwdt_ping(); - spin_unlock(&advwdt_lock); - return 0; - default: - return -ENODEV; - } + if (test_and_set_bit(0, &advwdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + advwdt_ping(); + return 0; } static int advwdt_close(struct inode *inode, struct file *file) { - lock_kernel(); - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { - spin_lock(&advwdt_lock); -#ifndef CONFIG_WATCHDOG_NOWAYOUT - inb_p(WDT_STOP); -#endif - advwdt_is_open = 0; - spin_unlock(&advwdt_lock); + if (adv_expect_close == 42) { + advwdt_disable(); + } else { + printk(KERN_CRIT "advancetechwdt: Unexpected close, not stopping watchdog!\n"); + advwdt_ping(); } - unlock_kernel(); + clear_bit(0, &advwdt_is_open); + adv_expect_close = 0; return 0; } @@ -189,7 +223,7 @@ { if (code == SYS_DOWN || code == SYS_HALT) { /* Turn the WDT off */ - inb_p(WDT_STOP); + advwdt_disable(); } return NOTIFY_DONE; } @@ -200,7 +234,7 @@ static struct file_operations advwdt_fops = { owner: THIS_MODULE, - read: advwdt_read, + llseek: no_llseek, write: advwdt_write, ioctl: advwdt_ioctl, open: advwdt_open, @@ -208,9 +242,9 @@ }; static struct miscdevice advwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &advwdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &advwdt_fops, }; /* @@ -227,14 +261,12 @@ static int __init advwdt_init(void) { - printk("WDT driver for Advantech single board computer initialising.\n"); + printk(KERN_INFO "WDT driver for Advantech single board computer initialising.\n"); - spin_lock_init(&advwdt_lock); misc_register(&advwdt_miscdev); -#if WDT_START != WDT_STOP - request_region(WDT_STOP, 1, "Advantech WDT"); -#endif - request_region(WDT_START, 1, "Advantech WDT"); + if(wdt_stop != wdt_start) + request_region(wdt_stop, 1, "Advantech WDT"); + request_region(wdt_start, 1, "Advantech WDT"); register_reboot_notifier(&advwdt_notifier); return 0; } @@ -244,16 +276,18 @@ { misc_deregister(&advwdt_miscdev); unregister_reboot_notifier(&advwdt_notifier); -#if WDT_START != WDT_STOP - release_region(WDT_STOP,1); -#endif - release_region(WDT_START,1); + if(wdt_stop != wdt_start) + release_region(wdt_stop,1); + release_region(wdt_start,1); } module_init(advwdt_init); module_exit(advwdt_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marek Michalkiewicz "); +MODULE_DESCRIPTION("Advantech Single Board Computer WDT driver"); +EXPORT_NO_SYMBOLS; /* end of advantechwdt.c */ diff -ruN linux-2.4.19-pre7/drivers/char/alim7101_wdt.c watchdog-tree/drivers/char/alim7101_wdt.c --- linux-2.4.19-pre7/drivers/char/alim7101_wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/alim7101_wdt.c Sat Apr 20 18:41:10 2002 @@ -7,21 +7,6 @@ * * (c)2002 Steve Hill * - * Theory of operation: - * A Watchdog Timer (WDT) is a hardware circuit that can - * reset the computer system in case of a software fault. - * You probably knew that already. - * - * Usually a userspace daemon will notify the kernel WDT driver - * via the /proc/watchdog special device file that userspace is - * still alive, at regular intervals. When such a notification - * occurs, the driver will usually tell the hardware watchdog - * that everything is in order, and that the watchdog should wait - * for yet another little while to reset the system. - * If userspace fails (RAM error, kernel bug, whatever), the - * notifications cease to occur, and the hardware watchdog will - * reset the system (causing a reboot) after the timeout occurs. - * * This WDT driver is different from most other Linux WDT * drivers in that the driver will ping the watchdog by itself, * because this particular WDT has a very short timeout (1.6 @@ -30,18 +15,14 @@ */ #include -#include #include #include #include #include -#include #include #include -#include #include #include -#include #include #include #include @@ -69,14 +50,13 @@ * Here we require the userspace daemon to send us a heartbeat * char to /dev/watchdog every 30 seconds. */ - -#define WDT_HEARTBEAT (HZ * 30) +static int wdt_heartbeat = 30; /* this times HZ gives number of seconds between userspace heartbeat pings */ static void wdt_timer_ping(unsigned long); static struct timer_list timer; static unsigned long next_heartbeat; -static unsigned long wdt_is_open; -static int wdt_expect_close; +static unsigned long aliwdt_is_open; +static char wdt_expect_close; static struct pci_dev *alim7101_pmu; /* @@ -97,7 +77,7 @@ pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp & ~ALI_WDT_ARM)); pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, (tmp | ALI_WDT_ARM)); } else { - printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + printk(KERN_WARNING OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); } /* Re-set the timer interval */ timer.expires = jiffies + WDT_INTERVAL; @@ -121,7 +101,7 @@ static void wdt_startup(void) { - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (wdt_heartbeat * HZ); /* We must enable before we kick off the timer in case the timer occurs as we ping it */ @@ -133,7 +113,7 @@ add_timer(&timer); - printk(OUR_NAME ": Watchdog timer is now enabled.\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now enabled.\n"); } static void wdt_turnoff(void) @@ -141,7 +121,7 @@ /* Stop the timer */ del_timer_sync(&timer); wdt_change(WDT_DISABLE); - printk(OUR_NAME ": Watchdog timer is now disabled...\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now disabled...\n"); } /* @@ -157,6 +137,7 @@ /* See if we got the magic character */ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT size_t ofs; /* note: just in case someone wrote the magic character @@ -166,25 +147,18 @@ /* now scan */ for(ofs = 0; ofs != count; ofs++) if(buf[ofs] == 'V') - wdt_expect_close = 1; - + wdt_expect_close = 42; +#endif /* someone wrote to us, we should restart timer */ - next_heartbeat = jiffies + WDT_HEARTBEAT; - return 1; + next_heartbeat = jiffies + (wdt_heartbeat * HZ); }; - return 0; -} - -static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos) -{ - /* No can do */ - return -EINVAL; + return count; } static int fop_open(struct inode * inode, struct file * file) { /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) + if(test_and_set_bit(0, &aliwdt_is_open)) return -EBUSY; /* Good, fire up the show */ wdt_startup(); @@ -193,26 +167,24 @@ static int fop_close(struct inode * inode, struct file * file) { -#ifdef CONFIG_WDT_NOWAYOUT - if(wdt_expect_close) + if(wdt_expect_close == 42) wdt_turnoff(); else { - printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); + printk(KERN_WARNING OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); } -#else - wdt_turnoff(); -#endif - clear_bit(0, &wdt_is_open); + clear_bit(0, &aliwdt_is_open); + wdt_expect_close = 0; return 0; } static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_heartbeat; static struct watchdog_info ident= { - 0, - 1, - "ALiM7101" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "ALiM7101" }; switch(cmd) @@ -220,8 +192,43 @@ case WDIOC_GETSUPPORT: return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_KEEPALIVE: - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (wdt_heartbeat * HZ); return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, (int *)arg)) + return -EFAULT; + if (new_heartbeat < 1 || new_heartbeat > 3600) /* arbitrary one hour limit */ + return -EINVAL; + wdt_heartbeat = new_heartbeat; + next_heartbeat = jiffies + (wdt_heartbeat * HZ); + /* Fall through */ + case WDIOC_GETTIMEOUT: + return put_user(wdt_heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + { + wdt_turnoff(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) + { + wdt_startup(); + retval = 0; + } + + return retval; + } + default: return -ENOTTY; } @@ -230,7 +237,6 @@ static struct file_operations wdt_fops = { owner: THIS_MODULE, llseek: no_llseek, - read: fop_read, write: fop_write, open: fop_open, release: fop_close, @@ -238,9 +244,9 @@ }; static struct miscdevice wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &wdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, }; /* @@ -257,7 +263,7 @@ * reboot with no heartbeat */ wdt_change(WDT_ENABLE); - printk(OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n"); + printk(KERN_WARNING OUR_NAME ": Watchdog timer is now enabled with no heartbeat - should reboot in ~1 second.\n"); }; return NOTIFY_DONE; } @@ -332,4 +338,5 @@ EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Steve Hill"); +MODULE_DESCRIPTION("ALi M7101 PMU Computer Watchdog Timer driver"); MODULE_LICENSE("GPL"); diff -ruN linux-2.4.19-pre7/drivers/char/eurotechwdt.c watchdog-tree/drivers/char/eurotechwdt.c --- linux-2.4.19-pre7/drivers/char/eurotechwdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/eurotechwdt.c Sat Apr 20 18:41:24 2002 @@ -24,15 +24,11 @@ #include #include -#include #include #include #include -#include -#include #include #include -#include #include #include #include @@ -41,12 +37,10 @@ #include #include #include -#include -#include -static int eurwdt_is_open; +static unsigned long eurwdt_is_open; static int eurwdt_timeout; -static spinlock_t eurwdt_lock; +static char eur_expect_close; /* * You must set these - there is no sane way to probe for this board. @@ -100,7 +94,7 @@ return 1; } -__setup("wdt=", eurwdt_setup); +__setup("eurwdt=", eurwdt_setup); #endif /* !MODULE */ @@ -211,11 +205,20 @@ return -ESPIPE; if (count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + eur_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + eur_expect_close = 42; + } +#endif eurwdt_ping(); /* the default timeout */ - return 1; } - return 0; + return count; } /** @@ -233,8 +236,8 @@ unsigned int cmd, unsigned long arg) { static struct watchdog_info ident = { - options : WDIOF_CARDRESET | WDIOF_SETTIMEOUT, - firmware_version : 1, + options : WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version : 0, identity : "WDT Eurotech CPU-1220/1410" }; @@ -248,6 +251,7 @@ return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: case WDIOC_GETBOOTSTATUS: return put_user(0, (int *) arg); @@ -269,6 +273,27 @@ case WDIOC_GETTIMEOUT: return put_user(eurwdt_timeout, (int *)arg); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + eurwdt_disable_timer(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + eurwdt_activate_timer(); + eurwdt_ping(); + retval = 0; + } + + return retval; + } } } @@ -283,32 +308,15 @@ static int eurwdt_open(struct inode *inode, struct file *file) { - switch (MINOR(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&eurwdt_lock); - if (eurwdt_is_open) { - spin_unlock(&eurwdt_lock); - return -EBUSY; - } - - eurwdt_is_open = 1; - eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */ - - /* Activate the WDT */ - eurwdt_activate_timer(); - - spin_unlock(&eurwdt_lock); + if (test_and_set_bit(0, &eurwdt_is_open)) + return -EBUSY; - MOD_INC_USE_COUNT; + eurwdt_timeout = WDT_TIMEOUT; /* initial timeout */ - return 0; + /* Activate the WDT */ + eurwdt_activate_timer(); - case TEMP_MINOR: - return 0; - - default: - return -ENODEV; - } + return 0; } /** @@ -325,14 +333,14 @@ static int eurwdt_release(struct inode *inode, struct file *file) { - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT + if (eur_expect_close == 42) { eurwdt_disable_timer(); -#endif - eurwdt_is_open = 0; - - MOD_DEC_USE_COUNT; + } else { + printk(KERN_CRIT "eurwdt: Unexpected close, not stopping watchdog!\n"); + eurwdt_ping(); } + clear_bit(0, &eurwdt_is_open); + eur_expect_close = 0; return 0; } @@ -376,9 +384,9 @@ static struct miscdevice eurwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &eurwdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &eurwdt_fops, }; /* @@ -457,8 +465,6 @@ printk(KERN_INFO "Eurotech WDT driver 0.01 at %X (Interrupt %d)" " - timeout event: %s\n", io, irq, (!strcmp("int", ev) ? "int" : "reboot")); - - spin_lock_init(&eurwdt_lock); out: return ret; diff -ruN linux-2.4.19-pre7/drivers/char/i810-tco.c watchdog-tree/drivers/char/i810-tco.c --- linux-2.4.19-pre7/drivers/char/i810-tco.c Tue Apr 16 13:18:26 2002 +++ watchdog-tree/drivers/char/i810-tco.c Sat Apr 20 18:41:33 2002 @@ -1,5 +1,5 @@ /* - * i810-tco 0.03: TCO timer driver for i8xx chipsets + * i810-tco 0.04: TCO timer driver for i8xx chipsets * * (c) Copyright 2000 kernel concepts , All Rights Reserved. * http://www.kernelconcepts.de @@ -29,10 +29,15 @@ * 20000710 Nils Faerber * Initial Version 0.01 * 20000728 Nils Faerber - * 0.02 Fix for SMI_EN->TCO_EN bit, some cleanups + * 0.02 Fix for SMI_EN->TCO_EN bit, some cleanups * 20020224 Joel Becker, Wim Van Sebroeck * 0.03 Support for 82801CA(M) chipset, timer margin needs to be > 3, * add support for WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT. + * 20020412 Rob Radez + * 0.04 Fix possible timer_alive race, add expect close support, clean up + * ioctls (WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, and + * WDIOC_SETOPTIONS), made i810tco_getdevice __init, made + * boot_status non-global but didn't remove it */ #include @@ -55,7 +60,7 @@ #define TIMER_MARGIN 50 /* steps of 0.6sec, 3, All Rights Reserved. * http://www.kernelconcepts.de diff -ruN linux-2.4.19-pre7/drivers/char/ib700wdt.c watchdog-tree/drivers/char/ib700wdt.c --- linux-2.4.19-pre7/drivers/char/ib700wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/ib700wdt.c Sat Apr 20 18:41:41 2002 @@ -29,14 +29,11 @@ #include #include -#include #include #include #include -#include #include #include -#include #include #include #include @@ -45,11 +42,8 @@ #include #include #include -#include -#include -static int ibwdt_is_open; -static spinlock_t ibwdt_lock; +static unsigned long ibwdt_is_open; /* * @@ -106,24 +100,56 @@ 0, /* 0xF */ }; -#define WDT_STOP 0x441 -#define WDT_START 0x443 +static int wdt_stop = 0x441; +static int wdt_start = 0x443; /* Default timeout */ #define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ static int wd_margin = WD_TIMO; +static char ib_expect_close; /* * Kernel methods. */ +#ifndef MODULE +static int __init ib_setup(char *str) +{ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if(ints[0] > 0){ + wdt_stop = ints[1]; + if(ints[0] > 1) + wdt_start = ints[2]; + } + + return 1; +} + +__setup("ibwdt=", ib_setup); + +#endif /* !MODULE */ + +MODULE_PARM(wdt_stop, "i"); +MODULE_PARM_DESC(wdt_stop, "IB700 WDT 'stop' io port (default 0x441)"); +MODULE_PARM(wdt_start, "i"); +MODULE_PARM_DESC(wdt_start, "IB700 WDT 'start' io port (default 0x443)"); + static void ibwdt_ping(void) { /* Write a watchdog value */ - outb_p(wd_times[wd_margin], WDT_START); + outb_p(wd_times[wd_margin], wdt_start); +} + +static void +ibwdt_stop(void) +{ + outb_p(wd_times[wd_margin], wdt_stop); } static ssize_t @@ -134,16 +160,19 @@ return -ESPIPE; if (count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + ib_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + ib_expect_close = 42; + } +#endif ibwdt_ping(); - return 1; } - return 0; -} - -static ssize_t -ibwdt_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - return -EINVAL; + return count; } static int @@ -153,7 +182,9 @@ int i, new_margin; static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 1, "IB700 WDT" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "IB700 WDT" }; switch (cmd) { @@ -163,9 +194,8 @@ break; case WDIOC_GETSTATUS: - if (copy_to_user((int *)arg, &ibwdt_is_open, sizeof(int))) - return -EFAULT; - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: ibwdt_ping(); @@ -187,6 +217,26 @@ return put_user(wd_times[wd_margin], (int *)arg); break; + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + ibwdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + ibwdt_ping(); + retval = 0; + } + + return retval; + } + default: return -ENOTTY; } @@ -196,39 +246,27 @@ static int ibwdt_open(struct inode *inode, struct file *file) { - switch (MINOR(inode->i_rdev)) { - case WATCHDOG_MINOR: - spin_lock(&ibwdt_lock); - if (ibwdt_is_open) { - spin_unlock(&ibwdt_lock); - return -EBUSY; - } - /* - * Activate - */ - - ibwdt_is_open = 1; - ibwdt_ping(); - spin_unlock(&ibwdt_lock); - return 0; - default: - return -ENODEV; - } + if (test_and_set_bit(0, &ibwdt_is_open)) + return -EBUSY; + /* + * Activate + */ + + ibwdt_ping(); + return 0; } static int ibwdt_close(struct inode *inode, struct file *file) { - lock_kernel(); - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { - spin_lock(&ibwdt_lock); -#ifndef CONFIG_WATCHDOG_NOWAYOUT - outb_p(wd_times[wd_margin], WDT_STOP); -#endif - ibwdt_is_open = 0; - spin_unlock(&ibwdt_lock); + if (ib_expect_close == 42) { + ibwdt_stop(); + } else { + printk(KERN_CRIT "ibwdt: Unexpected close, not stopping watchdog!\n"); + ibwdt_ping(); } - unlock_kernel(); + clear_bit(0, &ibwdt_is_open); + ib_expect_close = 0; return 0; } @@ -242,7 +280,7 @@ { if (code == SYS_DOWN || code == SYS_HALT) { /* Turn the WDT off */ - outb_p(wd_times[wd_margin], WDT_STOP); + ibwdt_stop(); } return NOTIFY_DONE; } @@ -253,7 +291,7 @@ static struct file_operations ibwdt_fops = { owner: THIS_MODULE, - read: ibwdt_read, + llseek: no_llseek, write: ibwdt_write, ioctl: ibwdt_ioctl, open: ibwdt_open, @@ -261,9 +299,9 @@ }; static struct miscdevice ibwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &ibwdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &ibwdt_fops, }; /* @@ -280,14 +318,12 @@ static int __init ibwdt_init(void) { - printk("WDT driver for IB700 single board computer initialising.\n"); + printk(KERN_INFO "WDT driver for IB700 single board computer initialising.\n"); - spin_lock_init(&ibwdt_lock); misc_register(&ibwdt_miscdev); -#if WDT_START != WDT_STOP - request_region(WDT_STOP, 1, "IB700 WDT"); -#endif - request_region(WDT_START, 1, "IB700 WDT"); + if(wdt_stop != wdt_start) + request_region(wdt_stop, 1, "IB700 WDT"); + request_region(wdt_start, 1, "IB700 WDT"); register_reboot_notifier(&ibwdt_notifier); return 0; } @@ -297,10 +333,9 @@ { misc_deregister(&ibwdt_miscdev); unregister_reboot_notifier(&ibwdt_notifier); -#if WDT_START != WDT_STOP - release_region(WDT_STOP,1); -#endif - release_region(WDT_START,1); + if(wdt_stop != wdt_start) + release_region(wdt_stop,1); + release_region(wdt_start,1); } module_init(ibwdt_init); @@ -309,5 +344,6 @@ MODULE_AUTHOR("Charles Howes "); MODULE_DESCRIPTION("IB700 SBC watchdog driver"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; /* end of ib700wdt.c */ diff -ruN linux-2.4.19-pre7/drivers/char/indydog.c watchdog-tree/drivers/char/indydog.c --- linux-2.4.19-pre7/drivers/char/indydog.c Tue Apr 16 13:18:20 2002 +++ watchdog-tree/drivers/char/indydog.c Sat Apr 20 18:41:49 2002 @@ -19,12 +19,12 @@ #include #include #include -#include #include #include #include static unsigned long indydog_alive; +static char indy_expect_close; static struct sgimc_misc_ctrl *mcmisc_regs; static void indydog_ping() @@ -32,6 +32,13 @@ mcmisc_regs->watchdogt = 0; } +static void indydog_shutdown() +{ + u32 mc_ctrl0 = mcmisc_regs->cpuctrl0; + mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; + mcmisc_regs->cpuctrl0 = mc_ctrl0; + printk(KERN_INFO "indydog: Stopped watchdog timer.\n"); +} /* * Allow only one person to hold it open @@ -43,9 +50,7 @@ if( test_and_set_bit(0,&indydog_alive) ) return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif + /* * Activate timer */ @@ -55,7 +60,7 @@ mcmisc_regs->cpuctrl0 = mc_ctrl0; indydog_ping(); - printk("Started watchdog timer.\n"); + printk(KERN_INFO "indydog: Started watchdog timer.\n"); return 0; } @@ -63,17 +68,14 @@ { /* * Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT */ -#ifndef CONFIG_WATCHDOG_NOWAYOUT - { - u32 mc_ctrl0 = mcmisc_regs->cpuctrl0; - mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG; - mcmisc_regs->cpuctrl0 = mc_ctrl0; - printk("Stopped watchdog timer.\n"); + if(indy_expect_close == 42){ + indydog_shutdown(); + } else { + printk(KERN_WARNING "indydog: Unexpected close, not stopping watchdog!\n"); } -#endif clear_bit(0,&indydog_alive); + indy_expect_close = 0; return 0; } @@ -87,17 +89,28 @@ * Refresh the timer. */ if(len) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + indy_expect_close = 0; + + for(i = 0; i != len; i++){ + if(data[i] == 'V') + indy_expect_close = 42; + } +#endif indydog_ping(); - return 1; } - return 0; + return len; } static int indydog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { static struct watchdog_info ident = { - identity: "Hardware Watchdog for SGI IP22", + options: WDIOF_KEEPALIVEPING, + firmware_version: 0, + identity: "SGI IP22 WDT", }; switch (cmd) { default: @@ -112,6 +125,8 @@ case WDIOC_KEEPALIVE: indydog_ping(); return 0; + case WDIOC_GETTIMEOUT: + return put_user(60,(int *)arg); } } @@ -148,8 +163,12 @@ static void __exit watchdog_exit(void) { misc_deregister(&indydog_miscdev); + indydog_shutdown(); } module_init(watchdog_init); module_exit(watchdog_exit); +MODULE_AUTHOR("Guido Guenther "); +MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22"); MODULE_LICENSE("GPL"); +EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/machzwd.c watchdog-tree/drivers/char/machzwd.c --- linux-2.4.19-pre7/drivers/char/machzwd.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/machzwd.c Sat Apr 20 19:32:46 2002 @@ -25,21 +25,18 @@ * the system when the counter reaches zero. * */ +/* TODO clean up and try to understand what's happening -robr */ #include #include -#include #include #include #include #include -#include #include #include -#include #include #include -#include #include #include #include @@ -96,7 +93,7 @@ return inb(DATA_B); } - +EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Fernando Fuganti "); MODULE_DESCRIPTION("MachZ ZF-Logic Watchdog driver"); MODULE_LICENSE("GPL"); @@ -107,7 +104,7 @@ static struct watchdog_info zf_info = { options: WDIOF_KEEPALIVEPING, - firmware_version: 1, + firmware_version: 0, identity: "ZF-Logic watchdog" }; @@ -120,18 +117,14 @@ * 3 = GEN_SCI * defaults to GEN_RESET (0) */ -static int action = 0; -static int zf_action = GEN_RESET; -static int zf_is_open = 0; -static int zf_expect_close = 0; -static spinlock_t zf_lock; +static int action; +static int zf_action; /* static's are initialized to 0, which is the same as GEN_RESET in this case, which is what the original code did. */ +static unsigned long zf_is_open; +static char zf_expect_close; static spinlock_t zf_port_lock; static struct timer_list zf_timer; -static unsigned long next_heartbeat = 0; - - -/* timeout for user land heart beat (10 seconds) */ -#define ZF_USER_TIMEO (HZ*10) +static unsigned long next_heartbeat; +static int zf_user_timeout = 30; /* multiply by HZ to get seconds til user timeout */ /* timeout for hardware watchdog (~500ms) */ #define ZF_HW_TIMEO (HZ/2) @@ -243,7 +236,7 @@ zf_set_timer(ZF_CTIMEOUT, WD1); /* user land ping */ - next_heartbeat = jiffies + ZF_USER_TIMEO; + next_heartbeat = jiffies + (zf_user_timeout * HZ); /* start the timer for internal ping */ zf_timer.expires = jiffies + ZF_HW_TIMEO; @@ -319,8 +312,8 @@ /* now scan */ for(ofs = 0; ofs != count; ofs++){ if(buf[ofs] == 'V'){ - zf_expect_close = 1; - dprintk("zf_expect_close 1\n"); + zf_expect_close = 42; + dprintk("zf_expect_close 42\n"); } } #endif @@ -328,7 +321,7 @@ * Well, anyhow someone wrote to us, * we should return that favour */ - next_heartbeat = jiffies + ZF_USER_TIMEO; + next_heartbeat = jiffies + (zf_user_timeout * HZ); dprintk("user ping at %ld\n", jiffies); return 1; @@ -337,38 +330,58 @@ return 0; } -static ssize_t zf_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - return -EINVAL; -} - - - static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int ret; + int new_timeout; switch(cmd){ case WDIOC_GETSUPPORT: - ret = copy_to_user((struct watchdog_info *)arg, - &zf_info, sizeof(zf_info)); - if(ret) + if(copy_to_user((struct watchdog_info *)arg, + &zf_info, sizeof(zf_info))) return -EFAULT; break; case WDIOC_GETSTATUS: - ret = copy_to_user((int *)arg, &zf_is_open, - sizeof(int)); - if(ret) - return -EFAULT; - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: - zf_ping(0); + next_heartbeat = jiffies + (zf_user_timeout * HZ); break; + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if(get_user(options, (int *)arg)) + return -EFAULT; + + if(options & WDIOS_DISABLECARD){ + zf_timer_off(); + retval = 0; + } + + if(options & WDIOS_ENABLECARD){ + zf_timer_on(); + retval = 0; + } + + return retval; + } + + case WDIOC_SETTIMEOUT: + if(get_user(new_timeout, (int *)arg)) + return -EFAULT; + if(new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ + return -EINVAL; + zf_user_timeout = new_timeout; + next_heartbeat = jiffies + (zf_user_timeout * HZ); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + return put_user(zf_user_timeout, (int *)arg); + default: return -ENOTTY; } @@ -378,47 +391,26 @@ static int zf_open(struct inode *inode, struct file *file) { - switch(MINOR(inode->i_rdev)){ - case WATCHDOG_MINOR: - spin_lock(&zf_lock); - if(zf_is_open){ - spin_unlock(&zf_lock); - return -EBUSY; - } + if(test_and_set_bit(0, &zf_is_open)) + return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif - zf_is_open = 1; - - spin_unlock(&zf_lock); + zf_timer_on(); - zf_timer_on(); - - return 0; - default: - return -ENODEV; - } + return 0; } static int zf_close(struct inode *inode, struct file *file) { - if(MINOR(inode->i_rdev) == WATCHDOG_MINOR){ + if(zf_expect_close == 42){ + zf_timer_off(); + } else { + del_timer(&zf_timer); + printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n"); + } - if(zf_expect_close){ - zf_timer_off(); - } else { - del_timer(&zf_timer); - printk(KERN_ERR PFX ": device file closed unexpectedly. Will not stop the WDT!\n"); - } - - spin_lock(&zf_lock); - zf_is_open = 0; - spin_unlock(&zf_lock); + clear_bit(0, &zf_is_open); - zf_expect_close = 0; - } - + zf_expect_close = 0; return 0; } @@ -441,7 +433,7 @@ static struct file_operations zf_fops = { owner: THIS_MODULE, - read: zf_read, + llseek: no_llseek, write: zf_write, ioctl: zf_ioctl, open: zf_open, @@ -449,9 +441,9 @@ }; static struct miscdevice zf_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &zf_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &zf_fops, }; @@ -492,7 +484,6 @@ zf_show_action(action); - spin_lock_init(&zf_lock); spin_lock_init(&zf_port_lock); ret = misc_register(&zf_miscdev); diff -ruN linux-2.4.19-pre7/drivers/char/mixcomwd.c watchdog-tree/drivers/char/mixcomwd.c --- linux-2.4.19-pre7/drivers/char/mixcomwd.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/mixcomwd.c Sat Apr 20 18:38:38 2002 @@ -28,9 +28,19 @@ * Version 0.4 (99/11/15): * - support for one more type board * + * Version 0.5 (2002/04/12): + * - make mixcomwd_opened unsigned, + * removed lock_kernel/unlock_kernel from mixcomwd_release, + * modified ioctl a bit to conform to API + */ +/* This device cannot be shutdown. So the decision to make is whether if + * NOWAYOUT is unset, to lock the module into the kernel and setup a timer + * to constantly ping the damn thing. If it's done here, it should be + * done in the other non-shutdownable drivers too -robr + * TODO figure this crap out and if locking it in, needs expect close */ -#define VERSION "0.4" +#define VERSION "0.5" #include #include @@ -43,26 +53,25 @@ #include #include #include -#include #include #include +#if 0 +#define MIXCOM_WATCHDOG_OFFSET 0xc10 static int mixcomwd_ioports[] = { 0x180, 0x280, 0x380, 0x000 }; +/* so the below array equals this one plus the offset */ +#define FLASHCOM_WATCHDOG_OFFSET 0x4 +#endif + +static int mixcomwd_ioports[] = { 0xd90, 0xe90, 0xf90, 0x000 }; -#define MIXCOM_WATCHDOG_OFFSET 0xc10 #define MIXCOM_ID 0x11 -#define FLASHCOM_WATCHDOG_OFFSET 0x4 #define FLASHCOM_ID 0x18 -static long mixcomwd_opened; /* long req'd for setbit --RR */ +static unsigned long mixcomwd_opened; /* unsigned long req'd for setbit --RR */ static int watchdog_port; -#ifndef CONFIG_WATCHDOG_NOWAYOUT -static int mixcomwd_timer_alive; -static struct timer_list mixcomwd_timer; -#endif - static void mixcomwd_ping(void) { outb_p(55,watchdog_port); @@ -70,6 +79,9 @@ } #ifndef CONFIG_WATCHDOG_NOWAYOUT +static unsigned long mixcomwd_timer_alive; +static struct timer_list mixcomwd_timer; + static void mixcomwd_timerfun(unsigned long d) { mixcomwd_ping(); @@ -90,10 +102,8 @@ mixcomwd_ping(); #ifndef CONFIG_WATCHDOG_NOWAYOUT - if(mixcomwd_timer_alive) { + if(test_and_clear_bit(0, &mixcomwd_timer_alive)) del_timer(&mixcomwd_timer); - mixcomwd_timer_alive=0; - } #endif return 0; } @@ -101,23 +111,20 @@ static int mixcomwd_release(struct inode *inode, struct file *file) { - lock_kernel(); #ifndef CONFIG_WATCHDOG_NOWAYOUT if(mixcomwd_timer_alive) { printk(KERN_ERR "mixcomwd: release called while internal timer alive"); - unlock_kernel(); return -EBUSY; } init_timer(&mixcomwd_timer); mixcomwd_timer.expires=jiffies + 5 * HZ; mixcomwd_timer.function=mixcomwd_timerfun; mixcomwd_timer.data=0; - mixcomwd_timer_alive=1; + set_bit(0, &mixcomwd_timer_alive); add_timer(&mixcomwd_timer); #endif clear_bit(0,&mixcomwd_opened); - unlock_kernel(); return 0; } @@ -139,22 +146,17 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int status; static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "MixCOM watchdog" + options: WDIOF_KEEPALIVEPING, + firmware_version: 0, + identity: "MixCOM watchdog" }; switch(cmd) { case WDIOC_GETSTATUS: - status=mixcomwd_opened; -#ifndef CONFIG_WATCHDOG_NOWAYOUT - status|=mixcomwd_timer_alive; -#endif - if (copy_to_user((int *)arg, &status, sizeof(int))) { - return -EFAULT; - } - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) { @@ -164,6 +166,8 @@ case WDIOC_KEEPALIVE: mixcomwd_ping(); break; + case WDIOC_GETTIMEOUT: + return put_user(0, (int *)arg); default: return -ENOTTY; } @@ -181,21 +185,21 @@ static struct miscdevice mixcomwd_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &mixcomwd_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &mixcomwd_fops, }; static int __init mixcomwd_checkcard(int port) { int id; - if(check_region(port+MIXCOM_WATCHDOG_OFFSET,1)) { + if(request_region(port,1,"MixCOM Watchdog")) return 0; - } - id=inb_p(port + MIXCOM_WATCHDOG_OFFSET) & 0x3f; + id=inb_p(port) & 0x3f; if(id!=MIXCOM_ID) { + release_region(port, 1); return 0; } return 1; @@ -205,12 +209,12 @@ { int id; - if(check_region(port + FLASHCOM_WATCHDOG_OFFSET,1)) { + if(request_region(port,1,"MixCOM Watchdog")) return 0; - } - id=inb_p(port + FLASHCOM_WATCHDOG_OFFSET); + id=inb_p(port); if(id!=FLASHCOM_ID) { + release_region(port, 1); return 0; } return 1; @@ -225,25 +229,23 @@ for (i = 0; !found && mixcomwd_ioports[i] != 0; i++) { if (mixcomwd_checkcard(mixcomwd_ioports[i])) { found = 1; - watchdog_port = mixcomwd_ioports[i] + MIXCOM_WATCHDOG_OFFSET; + watchdog_port = mixcomwd_ioports[i]; } } /* The FlashCOM card can be set up at 0x300 -> 0x378, in 0x8 jumps */ - for (i = 0x300; !found && i < 0x380; i+=0x8) { + for (i = 0x304; !found && i < 0x380; i+=0x8) { if (flashcom_checkcard(i)) { found = 1; - watchdog_port = i + FLASHCOM_WATCHDOG_OFFSET; + watchdog_port = i; } } if (!found) { - printk("mixcomwd: No card detected, or port not available.\n"); + printk(KERN_INFO "mixcomwd: No card detected, or port not available.\n"); return -ENODEV; } - request_region(watchdog_port,1,"MixCOM watchdog"); - ret = misc_register(&mixcomwd_miscdev); if (ret) return ret; @@ -256,11 +258,10 @@ static void __exit mixcomwd_exit(void) { #ifndef CONFIG_WATCHDOG_NOWAYOUT - if(mixcomwd_timer_alive) { + if(test_and_clear_bit(0, &mixcomwd_timer_alive)) { printk(KERN_WARNING "mixcomwd: I quit now, hardware will" " probably reboot!\n"); del_timer(&mixcomwd_timer); - mixcomwd_timer_alive=0; } #endif release_region(watchdog_port,1); @@ -271,4 +272,6 @@ module_exit(mixcomwd_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Gergely Madarasz "); +MODULE_DESCRIPTION("MixCom Watchdog driver"); EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/pcwd.c watchdog-tree/drivers/char/pcwd.c --- linux-2.4.19-pre7/drivers/char/pcwd.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/pcwd.c Sat Apr 20 18:42:58 2002 @@ -40,57 +40,69 @@ * fairly useless proc entry. * 990610 removed said useless proc code for the merge * 000403 Removed last traces of proc code. - * 020210 Backported 2.5 open_allowed changes, and got rid of a useless - * variable + * 011214 Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * 020210 Backported 2.5 open_allowed changes, and got rid of a useless + * variable + * Added timeout module option to override default + * 020306 Support the PCI version [Lindsay Harris ] + * 020412 Added expect close support and WDIOC_GETTIMEOUT #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include -#include #include #include -#include -#include -#include #include #include -/* - * These are the auto-probe addresses available. - * - * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. - * Revision A has an address range of 2 addresses, while Revision C has 3. - */ -static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; - -#define WD_VER "1.10 (06/05/99)" +#include +#include +#include + +#define WD_VER "1.13 (03/06/2002)" + +/* Stuff for the PCI version */ +#ifndef PCI_VENDOR_ID_QUICKLOGIC +#define PCI_VENDOR_ID_QUICKLOGIC 0x11e3 +#endif +#ifndef PCI_DEVICE_ID_BERKSHIRE +#define PCI_DEVICE_ID_BERKSHIRE 0x5030 +#endif /* - * It should be noted that PCWD_REVISION_B was removed because A and B + * It should be noted that PCWD_REV_B was removed because A and B * are essentially the same types of card, with the exception that B * has temperature reporting. Since I didn't receive a Rev.B card, * the Rev.B card is not supported. (It's a good thing too, as they * are no longer in production.) */ -#define PCWD_REVISION_A 1 -#define PCWD_REVISION_C 2 +#define PCWD_REV_A 0 +#define PCWD_REV_C 1 +#define PCWD_REV_PCI 2 + +static int timeout_val; +static int timeout = 2; +static char pcwd_expect_close; + +MODULE_PARM (timeout, "i"); +MODULE_PARM_DESC (timeout, "Watchdog probe timeout in seconds (default=2)"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif -#define WD_TIMEOUT 3 /* 1 1/2 seconds for a timeout */ +MODULE_PARM (nowayout, "i"); +MODULE_PARM_DESC (nowayout, + "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); /* * These are the defines for the PC Watchdog card, revision A. @@ -101,333 +113,329 @@ #define WD_RLY2 0x08 /* External relay triggered */ #define WD_SRLY2 0x80 /* Software external relay triggered */ -static int current_readport, revision, temp_panic; -static atomic_t open_allowed = ATOMIC_INIT(1); -static int initial_status, supports_temp, mode_debug; -static spinlock_t io_lock; - /* - * PCWD_CHECKCARD - * - * This routine checks the "current_readport" to see if the card lies there. - * If it does, it returns accordingly. + * Differences between cards regarding how they perform some operations + * are handled by an array of structs, with per card functions for the + * incompatible operations. It's all defined here. */ -static int __init pcwd_checkcard(void) -{ - int card_dat, prev_card_dat, found = 0, count = 0, done = 0; - /* As suggested by Alan Cox - this is a safety measure. */ - if (check_region(current_readport, 4)) { - printk("pcwd: Port 0x%x unavailable.\n", current_readport); - return 0; - } +/* ENABLE/DISABLE the card */ +typedef int (*fn_enable) (int); /* Enable/disable card */ - card_dat = 0x00; - prev_card_dat = 0x00; +static int pcwd_enable_card (int enable); /* Actually works */ +static int pcwd_enable_nop (int enable); /* NOP - REV A cannot */ - prev_card_dat = inb(current_readport); - if (prev_card_dat == 0xFF) - return 0; +/* Obtain firmware version, if possible */ +#define PCWD_FIRMWARE_BSZ 16 /* Version buffer size */ +typedef void (*fn_firmware) (char *bp); + +static void pcwd_firmware_ver_none (char *bp); /* REV A can't do it */ +static void pcwd_firmware_ver_revc (char *bp); /* REV C boards can */ +static void pcwd_firmware_ver_pci (char *bp); /* PCI boards can too */ + +/* Tickle the watchdog timer */ +typedef void (*fn_tickle) (void); + +static void pcwd_tickle_reva (void); /* Rev A only */ +static void pcwd_tickle (void); /* Rev C, PCI */ + +/* Determine reboot and temperature status */ +typedef int (*fn_status) (int reset_boot); + +static int pcwd_get_stat_reva (int reset_boot); +static int pcwd_get_stat (int reset_boot); + +/* Per card type specifications */ +typedef struct { + fn_tickle wd_tickle; /* Reset the watchdog */ + fn_enable enable_card; /* Enable/disable card, if possible */ + fn_firmware firmware_ver; /* Get firmware version, if possible */ + fn_status wd_status; /* Card reset and/or over temp */ + int io_size; /* I/O space used */ + const char *name; /* Nice name to display */ +} PCWD_CARD_INFO; - while(count < WD_TIMEOUT) { - - /* Read the raw card data from the port, and strip off the - first 4 bits */ - - card_dat = inb_p(current_readport); - card_dat &= 0x000F; - - /* Sleep 1/2 second (or 500000 microseconds :) */ - - mdelay(500); - done = 0; +/* Per card information, indexed by card version ID */ +static PCWD_CARD_INFO pcwd_card_info[] = { + { + pcwd_tickle_reva, + pcwd_enable_nop, + pcwd_firmware_ver_none, + pcwd_get_stat_reva, + 2, + "Berkshire Products PC Watchdog (REV A)", + }, + { + pcwd_tickle, + pcwd_enable_card, + pcwd_firmware_ver_revc, + pcwd_get_stat, + 4, + "Berkshire Products PC Watchdog (REV C)", + }, + { + pcwd_tickle, + pcwd_enable_card, + pcwd_firmware_ver_pci, + pcwd_get_stat, + 8, + "Berkshire Products PC Watchdog (PCI)", + }, +}; - /* If there's a heart beat in both instances, then this means we - found our card. This also means that either the card was - previously reset, or the computer was power-cycled. */ +/* Overall driver information, including per card pointer */ +static struct { + PCWD_CARD_INFO *card_info; /* Points to one of the above */ + atomic_t open_allowed; /* Watchdog is single open */ + int flags; /* Defined below */ + int boot_status; /* Card status at boot time */ + int io_addr; /* Card's base address */ +} pcwd_info = { +NULL, ATOMIC_INIT (1), 0, 0, 0}; + +/* Bits allocated in flags above. */ +#define PCWD_HAS_TEMP 0x0001 /* Set when thermometer available */ +#define PCWD_PCI_REG 0x0002 /* Set if PCI register code worked */ +#define PCWD_TEMP_PANIC 0x0004 /* Panic when over temperature */ - if ((card_dat & WD_HRTBT) && (prev_card_dat & WD_HRTBT) && - (!done)) { - found = 1; - done = 1; - break; - } +static spinlock_t io_lock; - /* If the card data is exactly the same as the previous card data, - it's safe to assume that we should check again. The manual says - that the heart beat will change every second (or the bit will - toggle), and this can be used to see if the card is there. If - the card was powered up with a cold boot, then the card will - not start blinking until 2.5 minutes after a reboot, so this - bit will stay at 1. */ - - if ((card_dat == prev_card_dat) && (!done)) { - count++; - done = 1; - } +/* D E T E R M I N E C A R D S T A T U S F U N C T I O N S */ +/* Rev A cards return status information from the base register, + * which is used for the temperature in other cards. */ + +static int +pcwd_get_stat_reva (int reset_boot) +{ + int retval; + int status; + + spin_lock (&io_lock); + status = inb_p (pcwd_info.io_addr); + spin_unlock (&io_lock); + + /* Transform the card register to the ioctl bits we use internally */ + retval = 0; + if (status & WD_WDRST) + retval |= WDIOF_CARDRESET; + if (status & WD_T110) + retval |= WDIOF_OVERHEAT; - /* If the card data is toggling any bits, this means that the heart - beat was detected, or something else about the card is set. */ + return retval; +} - if ((card_dat != prev_card_dat) && (!done)) { - done = 1; - found = 1; - break; - } +/* + * Rev C and PCI cards return card status in the base address + 1 register. + * And use different bits to indicate a card initiated reset, and + * an over-temperature condition. And the reboot status can be reset. + */ - /* Otherwise something else strange happened. */ +static int +pcwd_get_stat (int reset_boot) +{ + int retval; + int status; - if (!done) - count++; + spin_lock (&io_lock); + status = inb_p (pcwd_info.io_addr + 1); + if (reset_boot) { + /* NOTE: the REV C card clears the "card caused reboot" + * flag when writing ANY value to this port. However, + * the PCI card requires writing a 1 to bit 0. */ + outb_p (0x01, pcwd_info.io_addr + 1); } + spin_unlock (&io_lock); - return((found) ? 1 : 0); -} - -void pcwd_showprevstate(void) -{ - int card_status = 0x0000; + retval = 0; + if (status & 0x01) + retval |= WDIOF_CARDRESET; + if (status & 0x04) + retval |= WDIOF_OVERHEAT; - if (revision == PCWD_REVISION_A) - initial_status = card_status = inb(current_readport); - else { - initial_status = card_status = inb(current_readport + 1); - outb_p(0x00, current_readport + 1); /* clear reset status */ - } + return retval; +} - if (revision == PCWD_REVISION_A) { - if (card_status & WD_WDRST) - printk("pcwd: Previous reboot was caused by the card.\n"); +/* W A T C H D O G T I M E R R E S E T F U N C T I O N S */ +/* Rev A cards are reset by setting a specific bit in register 1. */ - if (card_status & WD_T110) { - printk("pcwd: Card senses a CPU Overheat. Panicking!\n"); - panic("pcwd: CPU Overheat.\n"); - } +static void +pcwd_tickle_reva (void) +{ + int wdrst_stat; - if ((!(card_status & WD_WDRST)) && - (!(card_status & WD_T110))) - printk("pcwd: Cold boot sense.\n"); - } else { - if (card_status & 0x01) - printk("pcwd: Previous reboot was caused by the card.\n"); + spin_lock (&io_lock); + wdrst_stat = inb_p (pcwd_info.io_addr); + wdrst_stat = (wdrst_stat & 0x0F) | WD_WDRST; - if (card_status & 0x04) { - printk("pcwd: Card senses a CPU Overheat. Panicking!\n"); - panic("pcwd: CPU Overheat.\n"); - } + outb_p (wdrst_stat, pcwd_info.io_addr + 1); + spin_unlock (&io_lock); - if ((!(card_status & 0x01)) && - (!(card_status & 0x04))) - printk("pcwd: Cold boot sense.\n"); - } + return; } -static void pcwd_send_heartbeat(void) -{ - int wdrst_stat; - - wdrst_stat = inb_p(current_readport); - wdrst_stat &= 0x0F; +/* Other cards are reset by writing anything to the base register. */ - wdrst_stat |= WD_WDRST; +static void +pcwd_tickle (void) +{ + spin_lock (&io_lock); + outb_p (0x42, pcwd_info.io_addr); + spin_unlock (&io_lock); - if (revision == PCWD_REVISION_A) - outb_p(wdrst_stat, current_readport + 1); - else - outb_p(wdrst_stat, current_readport); + return; } -static int pcwd_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int +pcwd_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { - int cdat, rv; - static struct watchdog_info ident= - { - WDIOF_OVERHEAT|WDIOF_CARDRESET, - 1, - "PCWD" - }; + int rv; + int retval; - switch(cmd) { - default: - return -ENOTTY; + static struct watchdog_info ident = { + options: WDIOF_OVERHEAT | WDIOF_CARDRESET, + firmware_version: 1, + identity: "PCWD" + }; + switch (cmd) { case WDIOC_GETSUPPORT: - if(copy_to_user((void*)arg, &ident, sizeof(ident))) - return -EFAULT; - return 0; + rv = copy_to_user ((void *) arg, &ident, sizeof (ident)); + return rv ? -EFAULT : 0; case WDIOC_GETSTATUS: - spin_lock(&io_lock); - if (revision == PCWD_REVISION_A) - cdat = inb(current_readport); - else - cdat = inb(current_readport + 1 ); - spin_unlock(&io_lock); - rv = 0; - - if (revision == PCWD_REVISION_A) - { - if (cdat & WD_WDRST) - rv |= WDIOF_CARDRESET; - - if (cdat & WD_T110) - { - rv |= WDIOF_OVERHEAT; - - if (temp_panic) - panic("pcwd: Temperature overheat trip!\n"); - } - } - else - { - if (cdat & 0x01) - rv |= WDIOF_CARDRESET; - - if (cdat & 0x04) - { - rv |= WDIOF_OVERHEAT; + rv = pcwd_info.card_info->wd_status (0); - if (temp_panic) - panic("pcwd: Temperature overheat trip!\n"); - } + if (rv & WDIOF_OVERHEAT) { + if (pcwd_info.flags & PCWD_TEMP_PANIC) + panic ("pcwd: Temperature overheat trip!\n"); } - if(put_user(rv, (int *) arg)) + if (put_user (rv, (int *) arg)) return -EFAULT; return 0; case WDIOC_GETBOOTSTATUS: - rv = 0; - - if (revision == PCWD_REVISION_A) - { - if (initial_status & WD_WDRST) - rv |= WDIOF_CARDRESET; + rv = pcwd_info.boot_status; - if (initial_status & WD_T110) - rv |= WDIOF_OVERHEAT; - } - else - { - if (initial_status & 0x01) - rv |= WDIOF_CARDRESET; - - if (initial_status & 0x04) - rv |= WDIOF_OVERHEAT; - } - - if(put_user(rv, (int *) arg)) + if (put_user (rv, (int *) arg)) return -EFAULT; return 0; case WDIOC_GETTEMP: - rv = 0; - if ((supports_temp) && (mode_debug == 0)) - { - spin_lock(&io_lock); - rv = inb(current_readport); - spin_unlock(&io_lock); - if(put_user(rv, (int*) arg)) - return -EFAULT; - } else if(put_user(rv, (int*) arg)) - return -EFAULT; + if (pcwd_info.flags & PCWD_HAS_TEMP) { + spin_lock (&io_lock); + rv = inb_p (pcwd_info.io_addr); + spin_unlock (&io_lock); + } + if (put_user (rv, (int *) arg)) + return -EFAULT; return 0; case WDIOC_SETOPTIONS: - if (revision == PCWD_REVISION_C) - { - if(copy_from_user(&rv, (int*) arg, sizeof(int))) - return -EFAULT; - - if (rv & WDIOS_DISABLECARD) - { - spin_lock(&io_lock); - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); - cdat = inb_p(current_readport + 2); - spin_unlock(&io_lock); - if ((cdat & 0x10) == 0) - { - printk("pcwd: Could not disable card.\n"); - return -EIO; - } + if (copy_from_user (&rv, (int *) arg, sizeof (int))) + return -EFAULT; - return 0; - } + retval = -EINVAL; - if (rv & WDIOS_ENABLECARD) - { - spin_lock(&io_lock); - outb_p(0x00, current_readport + 3); - cdat = inb_p(current_readport + 2); - spin_unlock(&io_lock); - if (cdat & 0x10) - { - printk("pcwd: Could not enable card.\n"); - return -EIO; - } - return 0; + if (rv & WDIOS_DISABLECARD) { + if (!pcwd_info.card_info->enable_card (0)) { + printk (KERN_EMERG + "pcwd: Could not disable card\n"); + return -EIO; } - if (rv & WDIOS_TEMPPANIC) - { - temp_panic = 1; + retval = 0; + } + + if (rv & WDIOS_ENABLECARD) { + if (!pcwd_info.card_info->enable_card (1)) { + printk (KERN_EMERG + "pcwd: Could not enable card\n"); + return -EIO; } + retval = 0; + } + + if (rv & WDIOS_TEMPPANIC) { + pcwd_info.flags |= PCWD_TEMP_PANIC; + + retval = 0; } - return -EINVAL; - + + return retval; + case WDIOC_KEEPALIVE: - pcwd_send_heartbeat(); + pcwd_info.card_info->wd_tickle (); return 0; + + case WDIOC_GETTIMEOUT: + return put_user(0, (int *)arg); + + default: + return -ENOTTY; } return 0; } -static ssize_t pcwd_write(struct file *file, const char *buf, size_t len, - loff_t *ppos) +/* Write: only for the watchdog device (thermometer is read-only). */ + +static ssize_t +pcwd_write (struct file *file, const char *buf, size_t len, loff_t * ppos) { /* Can't seek (pwrite) on this device */ if (ppos != &file->f_pos) return -ESPIPE; - if (len) - { - pcwd_send_heartbeat(); - return 1; + if (len) { + if (!nowayout) { + size_t i; + + pcwd_expect_close = 0; + + for (i = 0; i != len; i++) { + if (buf[i] == 'V') + pcwd_expect_close = 42; + } + } + + pcwd_info.card_info->wd_tickle (); } - return 0; + return len; } -static int pcwd_open(struct inode *ino, struct file *filep) +static int +pcwd_open (struct inode *ino, struct file *filep) { - switch (MINOR(ino->i_rdev)) - { - case WATCHDOG_MINOR: - if (!atomic_dec_and_test(&open_allowed)){ - atomic_inc(&open_allowed); - return -EBUSY; - } - MOD_INC_USE_COUNT; - /* Enable the port */ - if (revision == PCWD_REVISION_C) - { - spin_lock(&io_lock); - outb_p(0x00, current_readport + 3); - spin_unlock(&io_lock); - } - return(0); - case TEMP_MINOR: - return(0); - default: - return (-ENODEV); - } + switch (MINOR (ino->i_rdev)) { + case WATCHDOG_MINOR: + if (!atomic_dec_and_test (&pcwd_info.open_allowed)) { + atomic_inc (&pcwd_info.open_allowed); + return -EBUSY; + } + + /* Enable the card */ + pcwd_info.card_info->enable_card (1); + pcwd_info.card_info->wd_tickle (); + + return 0; + + case TEMP_MINOR: + if (pcwd_info.flags & PCWD_HAS_TEMP) { + return 0; + } + return -ENODEV; + + default: + return -ENODEV; + } } -static ssize_t pcwd_read(struct file *file, char *buf, size_t count, - loff_t *ppos) +/* Read: applies only to the thermometer (watchdog is write only). */ +static ssize_t +pcwd_read (struct file *file, char *buf, size_t count, loff_t * ppos) { unsigned short c; unsigned char cp; @@ -435,230 +443,507 @@ /* Can't seek (pread) on this device */ if (ppos != &file->f_pos) return -ESPIPE; - switch(MINOR(file->f_dentry->d_inode->i_rdev)) - { - case TEMP_MINOR: - /* - * Convert metric to Fahrenheit, since this was - * the decided 'standard' for this return value. - */ - - c = inb(current_readport); - cp = (c * 9 / 5) + 32; - if(copy_to_user(buf, &cp, 1)) - return -EFAULT; - return 1; - default: - return -EINVAL; + + /* + * Convert celsius to fahrenheit, since this was + * the decided 'standard' for this return value. + */ + + spin_lock (&io_lock); + c = inb_p (pcwd_info.io_addr); + spin_unlock (&io_lock); + + cp = (c * 9 / 5) + 32; + if (copy_to_user (buf, &cp, 1)) + return -EFAULT; + + return 1; +} + +static int +pcwd_close (struct inode *ino, struct file *filep) +{ + switch (MINOR (ino->i_rdev)) { + case WATCHDOG_MINOR: + if (!nowayout && pcwd_expect_close == 42) + pcwd_info.card_info->enable_card (0); + + atomic_inc (&pcwd_info.open_allowed); + pcwd_expect_close = 0; + break; + + case TEMP_MINOR: + break; + } + return 0; +} + +/* + * System is shutting down, so disable the card. Otherwise the timeout + * may expire during shutdown. Of course, this means a hang during + * shutdown will not be reset, but somebody is probably nearby and will + * notice. The alternative is to have the shutdown aborted when the + * watchdog expires and hits reset. + */ + +static int +pcwd_notify_sys (struct notifier_block *this, unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) { + /* + * If initialisation is still in progress, the device pointer + * may not be valid, so check, just to make sure. + */ + + if (pcwd_info.card_info) + pcwd_info.card_info->enable_card (0); } + + return NOTIFY_DONE; } -static int pcwd_close(struct inode *ino, struct file *filep) +/* C A R D E N A B L E / D I S A B L E F U N C T I O N S */ +/* Enable/disable the card, not REV A. The two writes are required by card */ + +static int +pcwd_enable_card (int enable) { - if (MINOR(ino->i_rdev)==WATCHDOG_MINOR) - { -#ifndef CONFIG_WATCHDOG_NOWAYOUT - /* Disable the board */ - if (revision == PCWD_REVISION_C) { - spin_lock(&io_lock); - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); - spin_unlock(&io_lock); - } -#endif - atomic_inc(&open_allowed); + int stat_reg; + + spin_lock (&io_lock); + if (enable) { + outb_p (0x00, pcwd_info.io_addr + 3); + } else { + outb_p (0xA5, pcwd_info.io_addr + 3); + outb_p (0xA5, pcwd_info.io_addr + 3); } + stat_reg = inb_p (pcwd_info.io_addr + 2); + spin_unlock (&io_lock); + + stat_reg &= 0x10; /* "disabled when set" bit */ + if (enable) { + stat_reg ^= 0x10; + } + + return stat_reg; +} + +static int +pcwd_enable_nop (int enable) +{ return 0; } -static inline void get_support(void) +static void __init +set_card_type (int is_pci) { - if (inb(current_readport) != 0xF0) - supports_temp = 1; + + if (is_pci) { + pcwd_info.card_info = &pcwd_card_info[PCWD_REV_PCI]; + pcwd_info.flags |= PCWD_PCI_REG; + } else { + pcwd_info.card_info = &pcwd_card_info[PCWD_REV_C]; + + /* REV A cards use only 2 io ports; test + * presumes a floating bus reads as 0xff. */ + if ((inb (pcwd_info.io_addr + 2) == 0xFF) || + (inb (pcwd_info.io_addr + 3) == 0xFF)) { + pcwd_info.card_info = &pcwd_card_info[PCWD_REV_A]; + } + } + + return; } -static inline int get_revision(void) +/* G E T F I R M W A R E V E R S I O N F U N C T I O N S */ +/* REV A can't do it */ +static void __init +pcwd_firmware_ver_none (char *bp) { - int r = PCWD_REVISION_C; - - spin_lock(&io_lock); - if ((inb(current_readport + 2) == 0xFF) || - (inb(current_readport + 3) == 0xFF)) - r=PCWD_REVISION_A; - spin_unlock(&io_lock); + strncpy (bp, "", PCWD_FIRMWARE_BSZ); - return r; + return; } -static int __init send_command(int cmd) +/* PCI boards can too */ +static void __init +pcwd_firmware_ver_pci (char *bp) { - int i; + int count; + + /* Write the 'Get Firmware Version' command to port 6 and wait */ + outb (0x08, pcwd_info.io_addr + 6); + + /* Card sets bit 0x40 (WRSP) bit in port 2. Can take 10ms! */ + for (count = 0; count < 15; ++count) { + mdelay (1); /* Board responds slowly */ - outb_p(cmd, current_readport + 2); - mdelay(1); + if (inb (pcwd_info.io_addr + 2) & 0x40) { + /* Board says data now valid */ - i = inb(current_readport); - i = inb(current_readport); + snprintf (bp, PCWD_FIRMWARE_BSZ, "%u.%u", + inb (pcwd_info.io_addr + 5), + inb (pcwd_info.io_addr + 4)); + + return; + } + } + strncpy (bp, "", PCWD_FIRMWARE_BSZ); - return(i); + return; } -static inline char *get_firmware(void) +/* + * REV C boards read diagnostic (including firmware version) data + * from the register 0. To do this, the card is put into diagnostic + * mode, then the command is submitted and data read from register 0. + * NOTE: the onboard processor writes 4 bits at a time to the register, + * so it's necessary to wait for the data to stabilise before + * accepting it. + */ + +static int __init +send_command (int cmd) { - int i, found = 0, count = 0, one, ten, hund, minor; - char *ret; + int ii; - ret = kmalloc(6, GFP_KERNEL); - if(ret == NULL) - return NULL; + int reg0, last_reg0; /* Double read for stabilising */ - while((count < 3) && (!found)) { - outb_p(0x80, current_readport + 2); - i = inb(current_readport); + outb (cmd, pcwd_info.io_addr + 2); + /* + * The following delay need only be 200 microseconds according + * to the spec I have. But my card seems slower, as waiting + * 250 microseconds returns valid data, but NOT from this + * command. The 1000 value may be excessive, but is reliable. + */ + mdelay (1); - if (i == 0x00) - found = 1; - else if (i == 0xF3) - outb_p(0x00, current_readport + 2); + reg0 = inb (pcwd_info.io_addr); + for (ii = 0; ii < 25; ++ii) { + last_reg0 = reg0; + reg0 = inb (pcwd_info.io_addr); + + if (reg0 == last_reg0) + break; /* Data is stable */ + + udelay (250); + } - udelay(400L); + return reg0; +} + +/* REV C board function to retrieve firmware version */ +static void __init +pcwd_firmware_ver_revc (char *bp) +{ + int i, found = 0, count = 0; + + /* Set the card into debug mode to find firmware version */ + outb_p (0x00, pcwd_info.io_addr + 2); /* Spec says to do this */ + udelay (500); + + while ((count < 3) && (!found)) { + i = send_command (0x80); + + if (i == 0x00) { + found = 1; + break; + } else if (i == 0xF3) { + /* Card does not like what we've done to it */ + outb_p (0x00, pcwd_info.io_addr + 2); + udelay (1200); /* Spec says wait 1ms */ + outb_p (0x00, pcwd_info.io_addr + 2); + udelay (500); + } count++; } if (found) { - mode_debug = 1; - one = send_command(0x81); - ten = send_command(0x82); - hund = send_command(0x83); - minor = send_command(0x84); - sprintf(ret, "%c.%c%c%c", one, ten, hund, minor); - } - else - sprintf(ret, "ERROR"); + *bp++ = send_command (0x81); + *bp++ = '.'; + *bp++ = send_command (0x82); + *bp++ = send_command (0x83); + *bp++ = send_command (0x84); + *bp++ = '\0'; + + /* Out of debug mode */ + outb (0x00, pcwd_info.io_addr + 2); + } else + strncpy (bp, "", PCWD_FIRMWARE_BSZ); - return(ret); + return; } -static void debug_off(void) +/* Initialisation function called ONLY from the PCI layer. */ + +static int __init +pcwd_init_one (struct pci_dev *dev, const struct pci_device_id *ent) { - outb_p(0x00, current_readport + 2); - mode_debug = 0; + static int devices = 0; + + ++devices; + if (devices > 1) { + printk (KERN_ERR "pcwd: Driver supports only ONE device\n"); + + return -ENODEV; + } + + pcwd_info.io_addr = pci_resource_start (dev, 0); + + if (pcwd_info.io_addr == 0 || pci_enable_device (dev)) + return -ENODEV; + + return 0; } +static struct pci_device_id pcwd_pci_tbl[] __initdata = { + {PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_BERKSHIRE, + PCI_ANY_ID, PCI_ANY_ID,}, + {0}, /* End of list */ +}; + +MODULE_DEVICE_TABLE (pci, pcwd_pci_tbl); + +static struct pci_driver pcwd_driver = { + name:"pcwd", + id_table:pcwd_pci_tbl, + probe:pcwd_init_one, +}; + static struct file_operations pcwd_fops = { - owner: THIS_MODULE, - read: pcwd_read, - write: pcwd_write, - ioctl: pcwd_ioctl, - open: pcwd_open, - release: pcwd_close, + owner:THIS_MODULE, + write:pcwd_write, + ioctl:pcwd_ioctl, + open:pcwd_open, + release:pcwd_close, }; static struct miscdevice pcwd_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &pcwd_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &pcwd_fops, +}; + +static struct file_operations pcwd_temp_fops = { + owner:THIS_MODULE, + read:pcwd_read, + open:pcwd_open, + release:pcwd_close, }; static struct miscdevice temp_miscdev = { - TEMP_MINOR, - "temperature", - &pcwd_fops + minor: TEMP_MINOR, + name: "temperature", + fops: &pcwd_temp_fops, }; - -static int __init pcwatchdog_init(void) + +/* Need to know about shutdown to kill the timer - may reset during shutdown! */ + static struct notifier_block pcwd_notifier = { - int i, found = 0; - spin_lock_init(&io_lock); - - revision = PCWD_REVISION_A; - - printk("pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); - - /* Initial variables */ - supports_temp = 0; - mode_debug = 0; - temp_panic = 0; - initial_status = 0x0000; - -#ifndef PCWD_BLIND - for (i = 0; pcwd_ioports[i] != 0; i++) { - current_readport = pcwd_ioports[i]; + pcwd_notify_sys, + NULL, + 0, +}; - if (pcwd_checkcard()) { - found = 1; - break; +/* + * The ISA cards have a heartbeat bit in one of the registers, which + * register is card dependent. The heartbeat bit is monitored, and if + * found, is considered proof that a Berkshire card has been found. + * The initial rate is once per second at board start up, then twice + * per second for normal operation. + */ +static int __init +check_isa_card (int base_addr) +{ + int reg0, last_reg0; /* Reg 0, in case it's REV A */ + int reg1, last_reg1; /* Register 1 for REV C cards */ + int ii; + int retval; + + /* As suggested by Alan Cox - this is a safety measure. */ + if (!request_region (base_addr, 4, "pcwd-isa")) { + printk (KERN_INFO "pcwd: Port 0x%x unavailable\n", base_addr); + return 0; + } + + retval = 0; + + reg0 = inb_p (base_addr); /* For REV A boards */ + reg1 = inb (base_addr + 1); /* For REV C boards */ + if (reg0 != 0xff || reg1 != 0xff) { + /* Not an 'ff' from a floating bus, so must be a card! */ + for (ii = 0; ii < timeout_val; ++ii) { + + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (HZ / 2); + + last_reg0 = reg0; + last_reg1 = reg1; + + reg0 = inb_p (base_addr); + reg1 = inb (base_addr + 1); + + /* Has either hearbeat bit changed? */ + if ((reg0 ^ last_reg0) & WD_HRTBT || + (reg1 ^ last_reg1) & 0x02) { + + retval = 1; + break; + } } } + release_region (base_addr, 4); - if (!found) { - printk("pcwd: No card detected, or port not available.\n"); - return(-EIO); + return retval; +} + +static int __init +pcwd_card_init (void) +{ + int retval; + char fvbuf[PCWD_FIRMWARE_BSZ]; + + pcwd_info.card_info->firmware_ver (fvbuf); + + printk (KERN_INFO "pcwd: %s at port 0x%03x (Firmware: %s)\n", + pcwd_info.card_info->name, pcwd_info.io_addr, fvbuf); + + /* Returns 0xf0 in temperature register if no thermometer */ + if (inb (pcwd_info.io_addr) != 0xF0) { + pcwd_info.flags |= PCWD_HAS_TEMP; + printk (KERN_INFO "pcwd: Temperature option detected\n"); } -#endif -#ifdef PCWD_BLIND - current_readport = PCWD_BLIND; -#endif + if (nowayout) + printk (KERN_INFO + "pcwd: Watchdog cannot be stopped once started\n"); + + /* Record the power up status of "card did reset" and/or temp trip */ + pcwd_info.boot_status = pcwd_info.card_info->wd_status (1); - get_support(); - revision = get_revision(); + if (pcwd_info.boot_status & WDIOF_CARDRESET) + printk (KERN_INFO + "pcwd: Previous reboot was caused by the card\n"); - if (revision == PCWD_REVISION_A) - printk("pcwd: PC Watchdog (REV.A) detected at port 0x%03x\n", current_readport); - else if (revision == PCWD_REVISION_C) - printk("pcwd: PC Watchdog (REV.C) detected at port 0x%03x (Firmware version: %s)\n", - current_readport, get_firmware()); - else { - /* Should NEVER happen, unless get_revision() fails. */ - printk("pcwd: Unable to get revision.\n"); - return -1; + if (pcwd_info.boot_status & WDIOF_OVERHEAT) { + printk (KERN_EMERG + "pcwd: Card senses a CPU Overheat. Panicking!\n"); + panic ("pcwd: CPU Overheat\n"); } - if (supports_temp) - printk("pcwd: Temperature Option Detected.\n"); + if (pcwd_info.boot_status == 0) + printk (KERN_INFO "pcwd: Cold boot sense\n"); - debug_off(); + pcwd_info.card_info->enable_card (0); - pcwd_showprevstate(); + retval = 0; + if (!request_region (pcwd_info.io_addr, + pcwd_info.card_info->io_size, + pcwd_info.card_info->name)) { + printk (KERN_ERR "pcwd: I/0 %d is not free\n", + pcwd_info.io_addr); - /* Disable the board */ - if (revision == PCWD_REVISION_C) { - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); + return retval; } - if (revision == PCWD_REVISION_A) - request_region(current_readport, 2, "PCWD Rev.A (Berkshire)"); - else - request_region(current_readport, 4, "PCWD Rev.C (Berkshire)"); + retval = misc_register (&pcwd_miscdev); + if (retval) { + release_region (pcwd_info.io_addr, + pcwd_info.card_info->io_size); + printk (KERN_ERR "pcwd: can't misc_register on minor %d\n", + WATCHDOG_MINOR); + return retval; + } - misc_register(&pcwd_miscdev); + if (pcwd_info.flags & PCWD_HAS_TEMP) { + if (misc_register (&temp_miscdev)) { + printk (KERN_ERR + "pwcd: can't misc_register thermometer - disabling it\n"); + pcwd_info.flags &= ~PCWD_HAS_TEMP; + } + } - if (supports_temp) - misc_register(&temp_miscdev); + retval = register_reboot_notifier (&pcwd_notifier); + if (retval) { + if (pcwd_info.flags & PCWD_HAS_TEMP) + misc_deregister (&temp_miscdev); + misc_deregister (&pcwd_miscdev); + release_region (pcwd_info.io_addr, + pcwd_info.card_info->io_size); + } - return 0; + return retval; } -static void __exit pcwatchdog_exit(void) +static int __init +pcwatchdog_init (void) { - misc_deregister(&pcwd_miscdev); - /* Disable the board */ - if (revision == PCWD_REVISION_C) { - outb_p(0xA5, current_readport + 3); - outb_p(0xA5, current_readport + 3); + int i, found = 0; + /* + * ISA card auto-probe addresses available. Last one is only + * available on REV C cards. + */ + static int pcwd_ioports[] = { 0x270, 0x350, 0x370 }; +#define PCWD_NUM_ADDR (sizeof(pcwd_ioports)/sizeof(pcwd_ioports[0])) + + timeout_val = timeout * 2; + + spin_lock_init (&io_lock); + + printk (KERN_INFO "pcwd: v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); + + if (pci_register_driver (&pcwd_driver) > 0) { + found = 1; + set_card_type (1); /* Set to PCI card model */ + } else { + /* No PCI entry, try the ISA addresses. */ + for (i = 0; i < PCWD_NUM_ADDR; i++) { + + if (check_isa_card (pcwd_ioports[i])) { + found = 1; + + pcwd_info.io_addr = pcwd_ioports[i]; + + set_card_type (0); + break; + } + } + } + + if (!found) { + printk (KERN_INFO + "pcwd: No card detected, or port not available\n"); + return -EIO; } - if (supports_temp) - misc_deregister(&temp_miscdev); - release_region(current_readport, (revision == PCWD_REVISION_A) ? 2 : 4); + return pcwd_card_init (); } -module_init(pcwatchdog_init); -module_exit(pcwatchdog_exit); +static void __exit +pcwatchdog_exit (void) +{ + unregister_reboot_notifier (&pcwd_notifier); + misc_deregister (&pcwd_miscdev); + + if (!nowayout) + pcwd_info.card_info->enable_card (0); + + if (pcwd_info.flags & PCWD_HAS_TEMP) + misc_deregister (&temp_miscdev); + + release_region (pcwd_info.io_addr, pcwd_info.card_info->io_size); + + if (pcwd_info.flags & PCWD_PCI_REG) + pci_unregister_driver (&pcwd_driver); + + return; +} -MODULE_LICENSE("GPL"); +module_init (pcwatchdog_init); +module_exit (pcwatchdog_exit); +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Ken Hollis (khollis@bitgate.com)"); +MODULE_DESCRIPTION ("PC Watchdog Driver"); EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/sbc60xxwdt.c watchdog-tree/drivers/char/sbc60xxwdt.c --- linux-2.4.19-pre7/drivers/char/sbc60xxwdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/sbc60xxwdt.c Sat Apr 20 19:33:32 2002 @@ -17,57 +17,35 @@ * 12/4 - 2000 [Initial revision] * 25/4 - 2000 Added /dev/watchdog support * 09/5 - 2001 [smj@oro.net] fixed fop_write to "return 1" on success + * 12/4 - 2002 [rob@osinvestor.com] eliminate fop_read + * fix possible wdt_is_open race + * add CONFIG_WATCHDOG_NOWAYOUT support + * remove lock_kernel/unlock_kernel pairs + * added KERN_* to printk's + * got rid of extraneous comments + * made the emulated heartbeat runtime configurable + * changed watchdog_info to correctly reflect what the driver offers + * added WDIOC_GETSTATUS, WDIOC_GETBOOTSTATUS, WDIOC_SETTIMEOUT, WDIOC_GETTIMEOUT, and WDIOC_SETOPTIONS ioctls * * - * Theory of operation: - * A Watchdog Timer (WDT) is a hardware circuit that can - * reset the computer system in case of a software fault. - * You probably knew that already. - * - * Usually a userspace daemon will notify the kernel WDT driver - * via the /proc/watchdog special device file that userspace is - * still alive, at regular intervals. When such a notification - * occurs, the driver will usually tell the hardware watchdog - * that everything is in order, and that the watchdog should wait - * for yet another little while to reset the system. - * If userspace fails (RAM error, kernel bug, whatever), the - * notifications cease to occur, and the hardware watchdog will - * reset the system (causing a reboot) after the timeout occurs. - * * This WDT driver is different from the other Linux WDT - * drivers in several ways: + * drivers in the following ways: * *) The driver will ping the watchdog by itself, because this * particular WDT has a very short timeout (one second) and it * would be insane to count on any userspace daemon always * getting scheduled within that time frame. - * *) This driver expects the userspace daemon to send a specific - * character code ('V') to /dev/watchdog before closing the - * /dev/watchdog file. If the userspace daemon closes the file - * without sending this special character, the driver will assume - * that the daemon (and userspace in general) died, and will - * stop pinging the WDT without disabling it first. This will - * cause a reboot. - * - * Why `V' ? Well, `V' is the character in ASCII for the value 86, - * and we all know that 86 is _the_ most random number in the universe. - * Therefore it is the letter that has the slightest chance of occuring - * by chance, when the system becomes corrupted. * */ #include -#include #include #include #include #include -#include #include #include -#include #include #include -#include #include #include #include @@ -81,8 +59,8 @@ * You must set these - The driver cannot probe for the settings */ -#define WDT_STOP 0x45 -#define WDT_START 0x443 +static int wdt_stop = 0x45; +static int wdt_start = 0x443; /* * The 60xx board can use watchdog timeout values from one second @@ -95,19 +73,47 @@ /* * We must not require too good response from the userspace daemon. * Here we require the userspace daemon to send us a heartbeat - * char to /dev/watchdog every 10 seconds. + * char to /dev/watchdog every 30 seconds. * If the daemon pulses us every 5 seconds, we can still afford * a 5 second scheduling delay on the (high priority) daemon. That * should be sufficient for a box under any load. */ -#define WDT_HEARTBEAT (HZ * 10) +static int sbc_heartbeat = 30; /* multiply by HZ to get seconds to wait for a ping */ static void wdt_timer_ping(unsigned long); static struct timer_list timer; static unsigned long next_heartbeat; -static int wdt_is_open; -static int wdt_expect_close; +static unsigned long wdt_is_open; +static char wdt_expect_close; + +/* + * Setup routines + */ + +#ifndef MODULE +static int __init sbc_setup(char *str) +{ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if(ints[0] > 0){ + wdt_stop = ints[1]; + if(ints[0] > 1) + wdt_start = ints[2]; + } + + return 1; +} + +__setup("sbcwdt=", sbc_setup); +#endif /* !MODULE */ + +MODULE_PARM(wdt_stop, "i"); +MODULE_PARM_DESC(wdt_stop, "SBC60xx WDT 'stop' io port (default 0x45)"); +MODULE_PARM(wdt_start, "i"); +MODULE_PARM_DESC(wdt_start, "SBC60xx WDT 'start' io port (default 0x443)"); /* * Whack the dog @@ -120,13 +126,13 @@ */ if(time_before(jiffies, next_heartbeat)) { - /* Ping the WDT by reading from WDT_START */ - inb_p(WDT_START); + /* Ping the WDT by reading from wdt_start */ + inb_p(wdt_start); /* Re-set the timer interval */ timer.expires = jiffies + WDT_INTERVAL; add_timer(&timer); } else { - printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + printk(KERN_WARNING OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); } } @@ -136,20 +142,20 @@ static void wdt_startup(void) { - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sbc_heartbeat * HZ); /* Start the timer */ timer.expires = jiffies + WDT_INTERVAL; add_timer(&timer); - printk(OUR_NAME ": Watchdog timer is now enabled.\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now enabled.\n"); } static void wdt_turnoff(void) { /* Stop the timer */ del_timer(&timer); - inb_p(WDT_STOP); - printk(OUR_NAME ": Watchdog timer is now disabled...\n"); + inb_p(wdt_stop); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now disabled...\n"); } @@ -166,6 +172,7 @@ /* See if we got the magic character */ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT size_t ofs; /* note: just in case someone wrote the magic character @@ -175,68 +182,49 @@ /* now scan */ for(ofs = 0; ofs != count; ofs++) { - char c; - if(get_user(c, buf+ofs)) - return -EFAULT; - if(c == 'V') - wdt_expect_close = 1; + if(buf[ofs] == 'V') + wdt_expect_close = 42; } +#endif /* Well, anyhow someone wrote to us, we should return that favour */ - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sbc_heartbeat * HZ); return 1; } return 0; } -static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos) -{ - /* No can do */ - return -EINVAL; -} - static int fop_open(struct inode * inode, struct file * file) { - switch(MINOR(inode->i_rdev)) - { - case WATCHDOG_MINOR: - /* Just in case we're already talking to someone... */ - if(wdt_is_open) - return -EBUSY; - /* Good, fire up the show */ - wdt_is_open = 1; - wdt_startup(); - return 0; - - default: - return -ENODEV; - } + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + return 0; } static int fop_close(struct inode * inode, struct file * file) { - lock_kernel(); - if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) - { - if(wdt_expect_close) - wdt_turnoff(); - else { - del_timer(&timer); - printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); - } + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + del_timer(&timer); + printk(KERN_WARNING OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); } - wdt_is_open = 0; - unlock_kernel(); + clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; return 0; } static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_heartbeat; static struct watchdog_info ident= { - 0, - 1, - "SB60xx" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "SB60xx" }; switch(cmd) @@ -246,15 +234,46 @@ case WDIOC_GETSUPPORT: return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_KEEPALIVE: - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sbc_heartbeat * HZ); return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_SETTIMEOUT: + if(get_user(new_heartbeat, (int *)arg)) + return -EFAULT; + if(new_heartbeat < 1 || new_heartbeat > 3600) /* arbitrary upper limit */ + return -EINVAL; + sbc_heartbeat = new_heartbeat; + next_heartbeat = jiffies + (sbc_heartbeat * HZ); + /* Fall through */ + case WDIOC_GETTIMEOUT: + return put_user(sbc_heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if(get_user(options, (int *)arg)) + return -EFAULT; + + if(options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } } } static struct file_operations wdt_fops = { owner: THIS_MODULE, llseek: no_llseek, - read: fop_read, write: fop_write, open: fop_open, release: fop_close, @@ -262,9 +281,9 @@ }; static struct miscdevice wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &wdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, }; /* @@ -299,17 +318,17 @@ misc_deregister(&wdt_miscdev); unregister_reboot_notifier(&wdt_notifier); - release_region(WDT_START,1); - release_region(WDT_STOP,1); + release_region(wdt_start,1); + release_region(wdt_stop,1); } static int __init sbc60xxwdt_init(void) { int rc = -EBUSY; - if (!request_region(WDT_STOP, 1, "SBC 60XX WDT")) + if (!request_region(wdt_stop, 1, "SBC 60XX WDT")) goto err_out; - if (!request_region(WDT_START, 1, "SBC 60XX WDT")) + if (!request_region(wdt_start, 1, "SBC 60XX WDT")) goto err_out_region1; init_timer(&timer); @@ -331,9 +350,9 @@ err_out_miscdev: misc_deregister(&wdt_miscdev); err_out_region2: - release_region(WDT_START,1); + release_region(wdt_start,1); err_out_region1: - release_region(WDT_STOP,1); + release_region(wdt_stop, 1); err_out: return rc; } @@ -342,4 +361,6 @@ module_exit(sbc60xxwdt_unload); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jakob Oestergaard "); +MODULE_DESCRIPTION("60xx Single Board Computer Watchdog Timer driver"); EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/sc1200wdt.c watchdog-tree/drivers/char/sc1200wdt.c --- linux-2.4.19-pre7/drivers/char/sc1200wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/sc1200wdt.c Sat Apr 20 18:43:14 2002 @@ -18,11 +18,13 @@ * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik and Alan Cox. * 20020222 Zwane Mwaikambo Added probing. * 20020225 Zwane Mwaikambo Added ISAPNP support. + * 20020412 Rob Radez Broke out start/stop functions + * Return proper status instead of temperature warning + * Add WDIOC_GETBOOTSTATUS and WDIOC_SETOPTIONS ioctls + * Fix CONFIG_WATCHDOG_NOWAYOUT */ - #include #include -#include #include #include #include @@ -55,10 +57,6 @@ #define WDCF 0x06 /* Watchdog config register */ #define WDST 0x07 /* Watchdog status register */ -/* WDO Status */ -#define WDO_ENABLED 0x00 -#define WDO_DISABLED 0x01 - /* WDCF bitfields - which devices assert WDO */ #define KBC_IRQ 0x01 /* Keyboard Controller */ #define MSE_IRQ 0x02 /* Mouse */ @@ -71,12 +69,15 @@ static int io = -1; static int io_len = 2; /* for non plug and play */ struct semaphore open_sem; -static int expect_close = 0; +static char expect_close; spinlock_t sc1200wdt_lock; /* io port access serialisation */ #if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE static int isapnp = 1; static struct pci_dev *wdt_dev; + +MODULE_PARM(isapnp, "i"); +MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); #endif MODULE_PARM(io, "i"); @@ -84,10 +85,6 @@ MODULE_PARM(timeout, "i"); MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1"); -#if defined CONFIG_ISAPNP || defined CONFIG_ISAPNP_MODULE -MODULE_PARM(isapnp, "i"); -MODULE_PARM_DESC(isapnp, "When set to 0 driver ISA PnP support will be disabled"); -#endif /* Read from Data Register */ @@ -110,39 +107,49 @@ } -/* This returns the status of the WDO signal, inactive high. - * returns WDO_ENABLED or WDO_DISABLED - */ +static void sc1200wdt_start(void) +{ + unsigned char reg; + + sc1200wdt_read_data(WDCF, ®); + /* assert WDO when any of the following interrupts are triggered too */ + reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); + sc1200wdt_write_data(WDCF, reg); + /* set the timeout and get the ball rolling */ + sc1200wdt_write_data(WDTO, timeout); +} + + +static void sc1200wdt_stop(void) +{ + sc1200wdt_write_data(WDTO, 0); +} + + +/* This returns the status of the WDO signal, inactive high. */ static inline int sc1200wdt_status(void) { unsigned char ret; sc1200wdt_read_data(WDST, &ret); - return (ret & 0x01); /* bits 1 - 7 are undefined */ + /* If the bit is inactive, the watchdog is enabled, so return + * KEEPALIVEPING which is a bit of a kludge because there's nothing + * else for enabled/disabled status + */ + return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING; /* bits 1 - 7 are undefined */ } static int sc1200wdt_open(struct inode *inode, struct file *file) { - unsigned char reg; - /* allow one at a time */ if (down_trylock(&open_sem)) return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif - if (timeout > MAX_TIMEOUT) timeout = MAX_TIMEOUT; - sc1200wdt_read_data(WDCF, ®); - /* assert WDO when any of the following interrupts are triggered too */ - reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ); - sc1200wdt_write_data(WDCF, reg); - /* set the timeout and get the ball rolling */ - sc1200wdt_write_data(WDTO, timeout); + sc1200wdt_start(); printk(KERN_INFO PFX "Watchdog enabled, timeout = %d min(s)", timeout); return 0; @@ -153,7 +160,8 @@ { int new_timeout; static struct watchdog_info ident = { - options: WDIOF_SETTIMEOUT, + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, identity: "PC87307/PC97307" }; @@ -168,7 +176,10 @@ case WDIOC_GETSTATUS: return put_user(sc1200wdt_status(), (int *)arg); - + + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_KEEPALIVE: sc1200wdt_write_data(WDTO, timeout); return 0; @@ -188,22 +199,41 @@ case WDIOC_GETTIMEOUT: return put_user(timeout * 60, (int *)arg); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + sc1200wdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + sc1200wdt_start(); + retval = 0; + } + + return retval; + } } } static int sc1200wdt_release(struct inode *inode, struct file *file) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT - if (expect_close) { - sc1200wdt_write_data(WDTO, 0); + if (expect_close == 42) { + sc1200wdt_stop(); printk(KERN_INFO PFX "Watchdog disabled\n"); } else { sc1200wdt_write_data(WDTO, timeout); printk(KERN_CRIT PFX "Unexpected close!, timeout = %d min(s)\n", timeout); } -#endif up(&open_sem); + expect_close = 0; return 0; } @@ -223,7 +253,7 @@ for (i = 0; i != len; i++) { if (data[i] == 'V') - expect_close = 1; + expect_close = 42; } #endif sc1200wdt_write_data(WDTO, timeout); @@ -237,7 +267,7 @@ static int sc1200wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) - sc1200wdt_write_data(WDTO, 0); + sc1200wdt_stop(); return NOTIFY_DONE; } @@ -261,7 +291,7 @@ { minor: WATCHDOG_MINOR, name: "watchdog", - fops: &sc1200wdt_fops + fops: &sc1200wdt_fops, }; diff -ruN linux-2.4.19-pre7/drivers/char/sc520_wdt.c watchdog-tree/drivers/char/sc520_wdt.c --- linux-2.4.19-pre7/drivers/char/sc520_wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/sc520_wdt.c Sat Apr 20 18:43:22 2002 @@ -24,20 +24,15 @@ * - Used ioremap/writew/readw * - Added NOWAYOUT support * - * Theory of operation: - * A Watchdog Timer (WDT) is a hardware circuit that can - * reset the computer system in case of a software fault. - * You probably knew that already. - * - * Usually a userspace daemon will notify the kernel WDT driver - * via the /proc/watchdog special device file that userspace is - * still alive, at regular intervals. When such a notification - * occurs, the driver will usually tell the hardware watchdog - * that everything is in order, and that the watchdog should wait - * for yet another little while to reset the system. - * If userspace fails (RAM error, kernel bug, whatever), the - * notifications cease to occur, and the hardware watchdog will - * reset the system (causing a reboot) after the timeout occurs. + * 4/12 - 2002 Changes by Rob Radez + * - Change comments + * - Make emulated timeout runtime configurable + * - Eliminate fop_llseek + * - Change CONFIG_WATCHDOG_NOWAYOUT semantics + * - Report proper capabilities in watchdog_info + * - Add WDIOC_{GETSTATUS, GETBOOTSTATUS, SETTIMEOUT, GETTIMEOUT, + * SETOPTIONS} ioctls + * - Add KERN_* tags to printks * * This WDT driver is different from most other Linux WDT * drivers in that the driver will ping the watchdog by itself, @@ -49,18 +44,14 @@ */ #include -#include #include #include #include #include -#include #include #include -#include #include #include -#include #include #include #include @@ -70,7 +61,7 @@ /* * The SC520 can timeout anywhere from 492us to 32.21s. - * If we reset the watchdog every ~250ms we should be safe. + * If we reset the watchdog every ~250us we should be safe. */ #define WDT_INTERVAL (HZ/4+1) @@ -81,7 +72,7 @@ * char to /dev/watchdog every 30 seconds. */ -#define WDT_HEARTBEAT (HZ * 30) +static int sc520_heartbeat = 30; /* multiply by HZ to get the timeout before the next heartbeat */ /* * AMD Elan SC520 timeout value is 492us times a power of 2 (0-7) @@ -108,7 +99,7 @@ static struct timer_list timer; static unsigned long next_heartbeat; static unsigned long wdt_is_open; -static int wdt_expect_close; +static char wdt_expect_close; static spinlock_t wdt_spinlock; /* @@ -132,7 +123,7 @@ timer.expires = jiffies + WDT_INTERVAL; add_timer(&timer); } else { - printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + printk(KERN_WARNING OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); } } @@ -160,24 +151,22 @@ static void wdt_startup(void) { - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sc520_heartbeat * HZ); /* Start the timer */ timer.expires = jiffies + WDT_INTERVAL; add_timer(&timer); wdt_config(WDT_ENB | WDT_WRST_ENB | TIMEOUT_EXPONENT); - printk(OUR_NAME ": Watchdog timer is now enabled.\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now enabled.\n"); } static void wdt_turnoff(void) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT /* Stop the timer */ del_timer(&timer); wdt_config(0); - printk(OUR_NAME ": Watchdog timer is now disabled...\n"); -#endif + printk(KERN_INFO OUR_NAME ": Watchdog timer is now disabled...\n"); } @@ -194,6 +183,7 @@ /* See if we got the magic character */ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT size_t ofs; /* note: just in case someone wrote the magic character @@ -203,10 +193,10 @@ /* now scan */ for(ofs = 0; ofs != count; ofs++) if(buf[ofs] == 'V') - wdt_expect_close = 1; - + wdt_expect_close = 42; +#endif /* Well, anyhow someone wrote to us, we should return that favour */ - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sc520_heartbeat * HZ); return 1; } return 0; @@ -214,51 +204,36 @@ static int fop_open(struct inode * inode, struct file * file) { - switch(MINOR(inode->i_rdev)) - { - case WATCHDOG_MINOR: - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) - return -EBUSY; - /* Good, fire up the show */ - wdt_startup(); -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif - return 0; - default: - return -ENODEV; - } + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + return 0; } static int fop_close(struct inode * inode, struct file * file) { - if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) - { - if(wdt_expect_close) - wdt_turnoff(); - else { - del_timer(&timer); - printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); - } + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + del_timer(&timer); + printk(KERN_WARNING OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; return 0; } -static long long fop_llseek(struct file *file, long long offset, int origin) -{ - return -ESPIPE; -} - static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_heartbeat; static struct watchdog_info ident= { - 0, - 1, - "SC520" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "SC520" }; switch(cmd) @@ -268,14 +243,45 @@ case WDIOC_GETSUPPORT: return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_KEEPALIVE: - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (sc520_heartbeat * HZ); return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_SETTIMEOUT: + if(get_user(new_heartbeat, (int *)arg)) + return -EFAULT; + if(new_heartbeat < 1 || new_heartbeat > 3600) /* arbitrary upper limit */ + return -EINVAL; + sc520_heartbeat = new_heartbeat; + next_heartbeat = jiffies + (sc520_heartbeat * HZ); + case WDIOC_GETTIMEOUT: + return put_user(sc520_heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if(get_user(options, (int *)arg)) + return -EFAULT; + + if(options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } } } static struct file_operations wdt_fops = { owner: THIS_MODULE, - llseek: fop_llseek, + llseek: no_llseek, write: fop_write, open: fop_open, release: fop_close, @@ -283,9 +289,9 @@ }; static struct miscdevice wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &wdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, }; /* @@ -343,13 +349,13 @@ /* get the Base Address Register */ cbar = inl_p(0xfffc); - printk(OUR_NAME ": CBAR: 0x%08lx\n", cbar); + printk(KERN_DEBUG OUR_NAME ": CBAR: 0x%08lx\n", cbar); /* check if MMCR aliasing bit is set */ if (cbar & 0x80000000) { - printk(OUR_NAME ": MMCR Aliasing enabled.\n"); + printk(KERN_INFO OUR_NAME ": MMCR Aliasing enabled.\n"); wdtmrctl = (__u16 *)(cbar & 0x3fffffff); } else { - printk(OUR_NAME "!!! WARNING !!!\n" + printk(KERN_WARNING OUR_NAME "!!! WARNING !!!\n" "\t MMCR Aliasing found NOT enabled!\n" "\t Using default value of: %p\n" "\t This has not been tested!\n" diff -ruN linux-2.4.19-pre7/drivers/char/shwdt.c watchdog-tree/drivers/char/shwdt.c --- linux-2.4.19-pre7/drivers/char/shwdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/shwdt.c Sat Apr 20 18:43:33 2002 @@ -9,6 +9,12 @@ * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. + * + * 14-Dec-2001 Matt Domsch + * Added nowayout module option to override CONFIG_WATCHDOG_NOWAYOUT + * 19-Apr-2002 Rob Radez + * Added expect close support, made emulated timeout runtime changeable + * general cleanups, add some ioctls */ #include #include @@ -19,7 +25,6 @@ #include #include #include -#include #include #include @@ -47,18 +52,60 @@ #define WTCSR_CKS1 0x02 #define WTCSR_CKS0 0x01 -#define WTCSR_CKS 0x07 -#define WTCSR_CKS_1 0x00 -#define WTCSR_CKS_4 0x01 -#define WTCSR_CKS_16 0x02 -#define WTCSR_CKS_32 0x03 -#define WTCSR_CKS_64 0x04 -#define WTCSR_CKS_256 0x05 -#define WTCSR_CKS_1024 0x06 +/* + * CKS0-2 supports a number of clock division ratios. At the time the watchdog + * is enabled, it defaults to a 41 usec overflow period .. we overload this to + * something a little more reasonable, and really can't deal with anything + * lower than WTCSR_CKS_1024, else we drop back into the usec range. + * + * Clock Division Ratio Overflow Period + * -------------------------------------------- + * 1/32 (initial value) 41 usecs + * 1/64 82 usecs + * 1/128 164 usecs + * 1/256 328 usecs + * 1/512 656 usecs + * 1/1024 1.31 msecs + * 1/2048 2.62 msecs + * 1/4096 5.25 msecs + */ +#define WTCSR_CKS_32 0x00 +#define WTCSR_CKS_64 0x01 +#define WTCSR_CKS_128 0x02 +#define WTCSR_CKS_256 0x03 +#define WTCSR_CKS_512 0x04 +#define WTCSR_CKS_1024 0x05 +#define WTCSR_CKS_2048 0x06 #define WTCSR_CKS_4096 0x07 -static int sh_is_open = 0; +/* + * Default clock division ratio is 5.25 msecs. Overload this at module load + * time. Any value not in the msec range will default to a timeout of one + * jiffy, which exceeds the usec overflow periods. + */ +static int clock_division_ratio = WTCSR_CKS_4096; + +#define msecs_to_jiffies(msecs) (jiffies + ((HZ * msecs + 999) / 1000)) +#define next_ping_period(cks) msecs_to_jiffies(cks - 4) + +static unsigned long sh_is_open; static struct watchdog_info sh_wdt_info; +static char shwdt_expect_close; + +static struct timer_list timer; +static unsigned long next_heartbeat; +static int sh_heartbeat = 30; + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +static int nowayout = 1; +#else +static int nowayout = 0; +#endif + +MODULE_PARM(nowayout,"i"); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)"); +MODULE_PARM(clock_division_ratio, "i"); +MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7."); /** * sh_wdt_write_cnt - Write to Counter @@ -93,6 +140,10 @@ */ static void sh_wdt_start(void) { + timer.expires = next_ping_period(clock_division_ratio); + next_heartbeat = jiffies + (sh_heartbeat * HZ); + add_timer(&timer); + sh_wdt_write_csr(WTCSR_WT | WTCSR_CKS_4096); sh_wdt_write_cnt(0); sh_wdt_write_csr((ctrl_inb(WTCSR) | WTCSR_TME)); @@ -105,6 +156,8 @@ */ static void sh_wdt_stop(void) { + del_timer(&timer); + sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_TME)); } @@ -117,8 +170,13 @@ */ static void sh_wdt_ping(unsigned long data) { - sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_IOVF)); - sh_wdt_write_cnt(0); + if (time_before(jiffies, next_heartbeat)) { + sh_wdt_write_csr((ctrl_inb(WTCSR) & ~WTCSR_IOVF)); + sh_wdt_write_cnt(0); + + timer.expires = next_ping_period(clock_division_ratio); + add_timer(&timer); + } } /** @@ -131,19 +189,10 @@ */ static int sh_wdt_open(struct inode *inode, struct file *file) { - switch (MINOR(inode->i_rdev)) { - case WATCHDOG_MINOR: - if (sh_is_open) { - return -EBUSY; - } + if (test_and_set_bit(0, &sh_is_open)) + return -EBUSY; - sh_is_open = 1; - sh_wdt_start(); - - return 0; - default: - return -ENODEV; - } + sh_wdt_start(); return 0; } @@ -158,37 +207,19 @@ */ static int sh_wdt_close(struct inode *inode, struct file *file) { - lock_kernel(); - - if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT + if (!nowayout && shwdt_expect_close == 42) { sh_wdt_stop(); -#endif - sh_is_open = 0; + } else { + printk(KERN_CRIT "shwdt: Unexpected close, not stopping watchdog!\n"); + next_heartbeat = jiffies + (sh_heartbeat * HZ); } + clear_bit(0, &sh_is_open); + shwdt_expect_close = 0; - unlock_kernel(); - return 0; } /** - * sh_wdt_read - Read from Device - * - * @file: file handle of device - * @buf: buffer to write to - * @count: length of buffer - * @ppos: offset - * - * Unsupported. - */ -static ssize_t sh_wdt_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/** * sh_wdt_write - Write to Device * * @file: file handle of device @@ -206,11 +237,18 @@ return -ESPIPE; if (count) { - sh_wdt_ping(0); - return 1; + size_t i; + + shwdt_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + shwdt_expect_close = 42; + } + next_heartbeat = jiffies + (sh_heartbeat * HZ); } - return 0; + return count; } /** @@ -227,6 +265,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_timeout; + switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, @@ -237,17 +277,41 @@ break; case WDIOC_GETSTATUS: - if (copy_to_user((int *)arg, - &sh_is_open, - sizeof(int))) { + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_KEEPALIVE: + next_heartbeat = jiffies + (sh_heartbeat * HZ); + + break; + case WDIOC_SETTIMEOUT: + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + if (new_timeout < 1 || new_timeout > 3600) /* arbitrary upper limit */ + return -EINVAL; + sh_heartbeat = new_timeout; + next_heartbeat = jiffies + (sh_heartbeat * HZ); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(sh_heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + sh_wdt_stop(); + retval = 0; } - break; - case WDIOC_KEEPALIVE: - sh_wdt_ping(0); - - break; + if (options & WDIOS_ENABLECARD) { + sh_wdt_start(); + retval = 0; + } + + return retval; + } default: return -ENOTTY; } @@ -277,7 +341,7 @@ static struct file_operations sh_wdt_fops = { owner: THIS_MODULE, - read: sh_wdt_read, + llseek: no_llseek, write: sh_wdt_write, ioctl: sh_wdt_ioctl, open: sh_wdt_open, @@ -285,9 +349,9 @@ }; static struct watchdog_info sh_wdt_info = { - WDIOF_KEEPALIVEPING, - 1, - "SH WDT", + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "SH WDT", }; static struct notifier_block sh_wdt_notifier = { @@ -297,9 +361,9 @@ }; static struct miscdevice sh_wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &sh_wdt_fops, + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &sh_wdt_fops, }; /** @@ -335,6 +399,10 @@ misc_deregister(&sh_wdt_miscdev); return -EINVAL; } + + init_timer(&timer); + timer.function = sh_wdt_ping; + timer.data = 0; return 0; } diff -ruN linux-2.4.19-pre7/drivers/char/softdog.c watchdog-tree/drivers/char/softdog.c --- linux-2.4.19-pre7/drivers/char/softdog.c Tue Apr 16 13:18:20 2002 +++ watchdog-tree/drivers/char/softdog.c Sat Apr 20 18:43:42 2002 @@ -27,10 +27,16 @@ * 19980911 Alan Cox * Made SMP safe for 2.3.x * - * 20011127 Joel Becker (jlbec@evilplan.org> + * 20011127 Joel Becker * Added soft_noboot; Allows testing the softdog trigger without * requiring a recompile. * Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT. + * + * 20020412 Rob Radez + * Add expect close support + * Change CONFIG_WATCHDOG_NOWAYOUT semantics + * Also the fact that all watchdogs use WATCHDOG_MINOR suggests that it + * is no longer necessary to worry about switching ;-). */ #include @@ -42,22 +48,24 @@ #include #include #include -#include #include #include -#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ - -static int soft_margin = TIMER_MARGIN; /* in seconds */ +static int soft_margin = 60; /* Default is 60 seconds */ +static char soft_expect_close; #ifdef ONLY_TESTING -static int soft_noboot = 1; +static char soft_noboot = 42; #else -static int soft_noboot = 0; +static char soft_noboot; #endif /* ONLY_TESTING */ MODULE_PARM(soft_margin,"i"); +MODULE_PARM_DESC(soft_margin, "Softdog timeout in seconds (default 60)"); MODULE_PARM(soft_noboot,"i"); +MODULE_PARM_DESC(soft_noboot, "Softdog action, set to 42 to ignore reboots, 0 to reboot (default depends on ONLY_TESTING)"); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("Software Watchdog Device"); /* * Our timer @@ -77,13 +85,13 @@ static void watchdog_fire(unsigned long data) { - if (soft_noboot) + if (soft_noboot == 42) printk(KERN_CRIT "SOFTDOG: Triggered - Reboot ignored.\n"); else { printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n"); machine_restart(NULL); - printk("SOFTDOG: Reboot didn't ?????\n"); + printk(KERN_CRIT "SOFTDOG: Reboot didn't ?????\n"); } } @@ -95,9 +103,6 @@ { if(test_and_set_bit(0, &timer_alive)) return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif /* * Activate timer */ @@ -109,12 +114,14 @@ { /* * Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT */ -#ifndef CONFIG_WATCHDOG_NOWAYOUT - del_timer(&watchdog_ticktock); -#endif + if(soft_expect_close == 42) { + del_timer(&watchdog_ticktock); + } else { + printk(KERN_CRIT "softdog: Unexpected close, not stopping watchdog!\n"); + } clear_bit(0, &timer_alive); + soft_expect_close = 0; return 0; } @@ -128,10 +135,19 @@ * Refresh the timer. */ if(len) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + soft_expect_close = 0; + + for(i = 0; i != len; i++) { + if(data[i] == 'V') + soft_expect_close = 42; + } +#endif mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); - return 1; } - return 0; + return len; } static int softdog_ioctl(struct inode *inode, struct file *file, @@ -139,9 +155,9 @@ { int new_margin; static struct watchdog_info ident = { - WDIOF_SETTIMEOUT, - 0, - "Software Watchdog" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "Software Watchdog" }; switch (cmd) { default: @@ -166,6 +182,25 @@ /* Fall */ case WDIOC_GETTIMEOUT: return put_user(soft_margin, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + del_timer(&watchdog_ticktock); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ)); + retval = 0; + } + + return retval; + } } } @@ -206,3 +241,5 @@ module_init(watchdog_init); module_exit(watchdog_exit); + +EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/w83877f_wdt.c watchdog-tree/drivers/char/w83877f_wdt.c --- linux-2.4.19-pre7/drivers/char/w83877f_wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/w83877f_wdt.c Sat Apr 20 18:43:50 2002 @@ -17,22 +17,13 @@ * * 4/19 - 2001 [Initial revision] * 9/27 - 2001 Added spinlocking - * - * - * Theory of operation: - * A Watchdog Timer (WDT) is a hardware circuit that can - * reset the computer system in case of a software fault. - * You probably knew that already. - * - * Usually a userspace daemon will notify the kernel WDT driver - * via the /proc/watchdog special device file that userspace is - * still alive, at regular intervals. When such a notification - * occurs, the driver will usually tell the hardware watchdog - * that everything is in order, and that the watchdog should wait - * for yet another little while to reset the system. - * If userspace fails (RAM error, kernel bug, whatever), the - * notifications cease to occur, and the hardware watchdog will - * reset the system (causing a reboot) after the timeout occurs. + * 4/12 - 2002 [rob@osinvestor.com] Eliminate extra comments + * Made emulated timeout runtime configurable + * Eliminate fop_read + * Eliminate extra spin_unlock + * Report proper capabilities in watchdog_info + * Added a bunch of ioctls + * Added KERN_* tags to printks * * This WDT driver is different from most other Linux WDT * drivers in that the driver will ping the watchdog by itself, @@ -42,18 +33,14 @@ */ #include -#include #include #include #include #include -#include #include #include -#include #include #include -#include #include #include #include @@ -84,13 +71,13 @@ * char to /dev/watchdog every 30 seconds. */ -#define WDT_HEARTBEAT (HZ * 30) +static int w8_heartbeat = 30; /* multiply by HZ to get the jiffies to wait for the userspace ping */ static void wdt_timer_ping(unsigned long); static struct timer_list timer; static unsigned long next_heartbeat; static unsigned long wdt_is_open; -static int wdt_expect_close; +static char wdt_expect_close; static spinlock_t wdt_spinlock; /* @@ -117,7 +104,7 @@ spin_unlock(&wdt_spinlock); } else { - printk(OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); + printk(KERN_WARNING OUR_NAME ": Heartbeat lost! Will not ping the watchdog\n"); } } @@ -149,7 +136,7 @@ static void wdt_startup(void) { - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (w8_heartbeat * HZ); /* Start the timer */ timer.expires = jiffies + WDT_INTERVAL; @@ -157,7 +144,7 @@ wdt_change(WDT_ENABLE); - printk(OUR_NAME ": Watchdog timer is now enabled.\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now enabled.\n"); } static void wdt_turnoff(void) @@ -167,7 +154,7 @@ wdt_change(WDT_DISABLE); - printk(OUR_NAME ": Watchdog timer is now disabled...\n"); + printk(KERN_INFO OUR_NAME ": Watchdog timer is now disabled...\n"); } @@ -184,6 +171,7 @@ /* See if we got the magic character */ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT size_t ofs; /* note: just in case someone wrote the magic character @@ -193,62 +181,46 @@ /* now scan */ for(ofs = 0; ofs != count; ofs++) if(buf[ofs] == 'V') - wdt_expect_close = 1; - + wdt_expect_close = 42; +#endif /* someone wrote to us, we should restart timer */ - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (w8_heartbeat * HZ); return 1; }; return 0; } -static ssize_t fop_read(struct file * file, char * buf, size_t count, loff_t * ppos) -{ - /* No can do */ - return -EINVAL; -} - static int fop_open(struct inode * inode, struct file * file) { - switch(MINOR(inode->i_rdev)) - { - case WATCHDOG_MINOR: - /* Just in case we're already talking to someone... */ - if(test_and_set_bit(0, &wdt_is_open)) { - spin_unlock(&fop_spinlock); - return -EBUSY; - } - /* Good, fire up the show */ - wdt_startup(); - return 0; - - default: - return -ENODEV; - } + /* Just in case we're already talking to someone... */ + if(test_and_set_bit(0, &wdt_is_open)) + return -EBUSY; + /* Good, fire up the show */ + wdt_startup(); + return 0; } static int fop_close(struct inode * inode, struct file * file) { - if(MINOR(inode->i_rdev) == WATCHDOG_MINOR) - { - if(wdt_expect_close) - wdt_turnoff(); - else { - del_timer(&timer); - printk(OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); - } + if(wdt_expect_close == 42) + wdt_turnoff(); + else { + del_timer(&timer); + printk(KERN_WARNING OUR_NAME ": device file closed unexpectedly. Will not stop the WDT!\n"); } clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; return 0; } static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_heartbeat; static struct watchdog_info ident= { - 0, - 1, - "W83877F" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "W83877F" }; switch(cmd) @@ -258,15 +230,46 @@ case WDIOC_GETSUPPORT: return copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))?-EFAULT:0; case WDIOC_KEEPALIVE: - next_heartbeat = jiffies + WDT_HEARTBEAT; + next_heartbeat = jiffies + (w8_heartbeat * HZ); return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_SETTIMEOUT: + if(get_user(new_heartbeat, (int *)arg)) + return -EFAULT; + if(new_heartbeat < 1 || new_heartbeat > 3600) /* arbitrary upper limit */ + return -EINVAL; + w8_heartbeat = new_heartbeat; + next_heartbeat = jiffies + (w8_heartbeat * HZ); + /* Fall through */ + case WDIOC_GETTIMEOUT: + return put_user(w8_heartbeat, (int *)arg); + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if(get_user(options, (int *)arg)) + return -EFAULT; + + if(options & WDIOS_DISABLECARD) { + wdt_turnoff(); + retval = 0; + } + + if(options & WDIOS_ENABLECARD) { + wdt_startup(); + retval = 0; + } + + return retval; + } } } static struct file_operations wdt_fops = { owner: THIS_MODULE, llseek: no_llseek, - read: fop_read, write: fop_write, open: fop_open, release: fop_close, @@ -274,9 +277,9 @@ }; static struct miscdevice wdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &wdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, }; /* @@ -320,7 +323,6 @@ int rc = -EBUSY; spin_lock_init(&wdt_spinlock); - spin_lock_init(&fop_spinlock); if (!request_region(ENABLE_W83877F_PORT, 2, "W83877F WDT")) goto err_out; diff -ruN linux-2.4.19-pre7/drivers/char/wafer5823wdt.c watchdog-tree/drivers/char/wafer5823wdt.c --- linux-2.4.19-pre7/drivers/char/wafer5823wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/wafer5823wdt.c Sat Apr 20 18:43:59 2002 @@ -40,42 +40,67 @@ static unsigned long wafwdt_is_open; static spinlock_t wafwdt_lock; +static char waf_expect_close; /* * You must set these - there is no sane way to probe for this board. * * To enable, write the timeout value in seconds (1 to 255) to I/O - * port WDT_START, then read the port to start the watchdog. To pat - * the dog, read port WDT_STOP to stop the timer, then read WDT_START + * port wdt_start, then read the port to start the watchdog. To pat + * the dog, read port wdt_stop to stop the timer, then read wdt_start * to restart it again. */ -#define WDT_START 0x443 -#define WDT_STOP 0x843 +static int wdt_stop = 0x843; +static int wdt_start = 0x443; -#define WD_TIMO 60 /* 1 minute */ +#ifndef MODULE +static int __init waf_setup(char *str) +{ + int ints[4]; + + str = get_options(str, ARRAY_SIZE(ints), ints); + + if(ints[0] > 0){ + wdt_stop = ints[1]; + if(ints[0] > 1) + wdt_start = ints[2]; + } + + return 1; +} + +__setup("wafwdt=", waf_setup); +#endif /* !MODULE */ + +MODULE_PARM(wdt_stop, "i"); +MODULE_PARM_DESC(wdt_stop, "Wafer 5823 WDT 'stop' io port (default 0x843)"); +MODULE_PARM(wdt_start, "i"); +MODULE_PARM_DESC(wdt_start, "Wafer 5823 WDT 'start' io port (default 0x443)"); + +static unsigned char wd_timo = 60; static void wafwdt_ping(void) { /* pat watchdog */ spin_lock(&wafwdt_lock); - inb_p(WDT_STOP); - inb_p(WDT_START); + inb_p(wdt_stop); + inb_p(wdt_start); spin_unlock(&wafwdt_lock); } static void wafwdt_start(void) { /* start up watchdog */ - outb_p(WD_TIMO, WDT_START); - inb_p(WDT_START); + outb_p(wd_timo, wdt_start); + inb_p(wdt_start); } static void wafwdt_stop(void) { /* stop watchdog */ - inb_p(WDT_STOP); + inb_p(wdt_stop); } static ssize_t wafwdt_write(struct file *file, const char *buf, size_t count, loff_t * ppos) @@ -85,6 +110,16 @@ return -ESPIPE; if (count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + waf_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + waf_expect_close = 42; + } +#endif wafwdt_ping(); return 1; } @@ -94,10 +129,12 @@ static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + int new_timo; static struct watchdog_info ident = { - WDIOF_KEEPALIVEPING, 1, "Wafer 5823 WDT" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 1, + identity: "Wafer 5823 WDT" }; - int one=1; switch (cmd) { case WDIOC_GETSUPPORT: @@ -107,14 +144,44 @@ break; case WDIOC_GETSTATUS: - if (copy_to_user((int *) arg, &one, sizeof (int))) - return -EFAULT; - break; + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); case WDIOC_KEEPALIVE: wafwdt_ping(); break; + case WDIOC_SETTIMEOUT: + if (get_user(new_timo, (int *)arg)) + return -EFAULT; + if (new_timo < 1 || new_timo > 255) + return -EINVAL; + wd_timo = new_timo; + wafwdt_ping(); + /* Fall through */ + case WDIOC_GETTIMEOUT: + return put_user((int)wd_timo, (int *)arg); + + case WDIOC_SETOPTIONS: + { + int options, retval = -EINVAL; + + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) { + wafwdt_stop(); + retval = 0; + } + + if (options & WDIOS_ENABLECARD) { + wafwdt_start(); + retval = 0; + } + + return retval; + } + default: return -ENOTTY; } @@ -133,9 +200,12 @@ wafwdt_close(struct inode *inode, struct file *file) { clear_bit(0, &wafwdt_is_open); -#ifndef CONFIG_WATCHDOG_NOWAYOUT - wafwdt_stop(); -#endif + if (waf_expect_close == 42) { + wafwdt_stop(); + } else { + printk(KERN_CRIT "wafer5823: Unexpected close, not stopping watchdog!\n"); + } + waf_expect_close = 0; return 0; } @@ -165,9 +235,9 @@ }; static struct miscdevice wafwdt_miscdev = { - WATCHDOG_MINOR, - "watchdog", - &wafwdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wafwdt_fops, }; /* @@ -186,18 +256,18 @@ printk(KERN_INFO "WDT driver for Wafer 5823 single board computer initialising.\n"); spin_lock_init(&wafwdt_lock); - if(!request_region(WDT_STOP, 1, "Wafer 5823 WDT")) + if(!request_region(wdt_stop, 1, "Wafer 5823 WDT")) goto error; - if(!request_region(WDT_START, 1, "Wafer 5823 WDT")) + if(!request_region(wdt_start, 1, "Wafer 5823 WDT")) goto error2; if(misc_register(&wafwdt_miscdev)<0) goto error3; register_reboot_notifier(&wafwdt_notifier); return 0; error3: - release_region(WDT_START, 1); + release_region(wdt_start, 1); error2: - release_region(WDT_STOP, 1); + release_region(wdt_stop, 1); error: return -ENODEV; } @@ -206,14 +276,15 @@ { misc_deregister(&wafwdt_miscdev); unregister_reboot_notifier(&wafwdt_notifier); - release_region(WDT_STOP, 1); - release_region(WDT_START, 1); + release_region(wdt_stop, 1); + release_region(wdt_start, 1); } module_init(wafwdt_init); module_exit(wafwdt_exit); MODULE_AUTHOR("Justin Cormack"); +MODULE_DESCRIPTION("ICP Wafer 5823 Single Board Computer WDT driver"); MODULE_LICENSE("GPL"); EXPORT_NO_SYMBOLS; diff -ruN linux-2.4.19-pre7/drivers/char/wdt.c watchdog-tree/drivers/char/wdt.c --- linux-2.4.19-pre7/drivers/char/wdt.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/wdt.c Sat Apr 20 18:44:17 2002 @@ -28,20 +28,18 @@ * Parameterized timeout * Tigran Aivazian : Restructured wdt_init() to handle failures * Joel Becker : Added WDIOC_GET/SETTIMEOUT + * Rob Radez : Change CONFIG_WATCHDOG_NOWAYOUT semantics + * Add expect close support */ #include #include -#include #include #include #include -#include -#include #include #include #include "wd501p.h" -#include #include #include #include @@ -65,6 +63,7 @@ #define WD_TIMO (100*60) /* 1 minute */ static int wd_margin = WD_TIMO; +static char wdt_expect_close; #ifndef MODULE @@ -243,6 +242,16 @@ if(count) { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + wdt_expect_close = 0; + + for (i = 0; i != count; i++) { + if (buf[i] == 'V') + wdt_expect_close = 42; + } +#endif wdt_ping(); return 1; } @@ -302,11 +311,11 @@ static struct watchdog_info ident= { - WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER - |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT - |WDIOF_SETTIMEOUT, - 1, - "WDT500/501" + options: WDIOF_OVERHEAT|WDIOF_POWERUNDER + |WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2 + |WDIOF_FANFAULT|WDIOF_SETTIMEOUT, + firmware_version: 1, + identity: "WDT500/501" }; ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */ @@ -361,7 +370,6 @@ * Activate */ - wdt_is_open=1; inb_p(WDT_DC); /* Disable */ wdt_ctr_mode(0,3); wdt_ctr_mode(1,2); @@ -394,11 +402,15 @@ { if(MINOR(inode->i_rdev)==WATCHDOG_MINOR) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT - inb_p(WDT_DC); /* Disable counters */ - wdt_ctr_load(2,0); /* 0 length reset pulses now */ -#endif + if (wdt_expect_close == 42) { + inb_p(WDT_DC); /* Disable counters */ + wdt_ctr_load(2,0); /* 0 length reset pulses now */ + } else { + printk(KERN_CRIT "wdt: Unexpected close, not stopping watchdog!\n"); + wdt_ping(); + } clear_bit(0, &wdt_is_open); + wdt_expect_close = 0; } return 0; } @@ -444,9 +456,9 @@ static struct miscdevice wdt_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &wdt_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt_fops, }; #ifdef CONFIG_WDT_501 diff -ruN linux-2.4.19-pre7/drivers/char/wdt285.c watchdog-tree/drivers/char/wdt285.c --- linux-2.4.19-pre7/drivers/char/wdt285.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/wdt285.c Sat Apr 20 18:11:59 2002 @@ -14,6 +14,11 @@ * 2 of the License, or (at your option) any later version. * */ +/* + * From what I've seen of this driver, it is impossible to stop it once + * it has started, thus, it does not support CONFIG_WATCHDOG_NOWAYOUT + * nor expect close. -robr + */ #include #include @@ -25,7 +30,6 @@ #include #include #include -#include #include #include @@ -33,19 +37,17 @@ #include #include -/* - * Define this to stop the watchdog actually rebooting the machine. - */ -#undef ONLY_TESTING - -#define TIMER_MARGIN 60 /* (secs) Default is 1 minute */ - #define FCLK (50*1000*1000) /* 50MHz */ -static int soft_margin = TIMER_MARGIN; /* in seconds */ -static int timer_alive; +static int soft_margin = 60; /* default is 60 seconds */ +static unsigned long timer_alive; #ifdef ONLY_TESTING +static char 285_noboot = 42; +#else +static char 285_noboot; /* initialized to 0 */ +#endif + /* * If the timer expires.. */ @@ -56,7 +58,6 @@ *CSR_TIMER4_CNTL = 0; *CSR_TIMER4_CLR = 0; } -#endif static void watchdog_ping(void) { @@ -72,7 +73,7 @@ static int watchdog_open(struct inode *inode, struct file *file) { - if(timer_alive) + if(test_and_set_bit(0, &timer_alive)) return -EBUSY; /* * Ahead watchdog factor ten, Mr Sulu @@ -81,28 +82,20 @@ watchdog_ping(); *CSR_TIMER4_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_AUTORELOAD | TIMER_CNTL_DIV256; -#ifdef ONLY_TESTING - request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL); -#else - *CSR_SA110_CNTL |= 1 << 13; - MOD_INC_USE_COUNT; -#endif - timer_alive = 1; + if(285_noboot == 42) { + request_irq(IRQ_TIMER4, watchdog_fire, 0, "watchdog", NULL); + } else { + *CSR_SA110_CNTL |= 1 << 13; + } return 0; } static int watchdog_release(struct inode *inode, struct file *file) { -#ifdef ONLY_TESTING - lock_kernel(); - free_irq(IRQ_TIMER4, NULL); - timer_alive = 0; - unlock_kernel(); -#else - /* - * It's irreversible! - */ -#endif + if(285_noboot == 42) { + free_irq(IRQ_TIMER4, NULL); + clear_bit(0, &timer_alive); + } return 0; } @@ -126,12 +119,12 @@ static int watchdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { - int i, new_margin; + int new_margin; static struct watchdog_info ident= { - WDIOF_SETTIMEOUT, - 0, - "Footbridge Watchdog" + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "Footbridge Watchdog" }; switch(cmd) { @@ -151,7 +144,7 @@ if (get_user(new_margin, (int *)arg)) return -EFAULT; /* Arbitrary, can't find the card's limits */ - if ((new_marg < 0) || (new_margin > 60)) + if ((new_margin < 0) || (new_margin > 60)) return -EINVAL; soft_margin = new_margin; watchdog_ping(); @@ -172,9 +165,9 @@ static struct miscdevice watchdog_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &watchdog_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &watchdog_fops, }; static int __init footbridge_watchdog_init(void) @@ -183,10 +176,10 @@ return -ENODEV; misc_register(&watchdog_miscdev); - printk("Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n", + printk(KERN_INFO "Footbridge Watchdog Timer: 0.01, timer margin: %d sec\n", soft_margin); if (machine_is_cats()) - printk("Warning: Watchdog reset may not work on this machine.\n"); + printk(KERN_INFO "Warning: Watchdog reset may not work on this machine.\n"); return 0; } @@ -203,6 +196,8 @@ MODULE_PARM(soft_margin,"i"); MODULE_PARM_DESC(soft_margin,"Watchdog timeout in seconds"); +MODULE_PARM(285_noboot, "i"); +MODULE_PARM_DESC(285_noboot, "wdt285 action, set to 42 to ignore reboots, 0 to reboot (defaults depends on ONLY_TESTING)"); module_init(footbridge_watchdog_init); module_exit(footbridge_watchdog_exit); diff -ruN linux-2.4.19-pre7/drivers/char/wdt977.c watchdog-tree/drivers/char/wdt977.c --- linux-2.4.19-pre7/drivers/char/wdt977.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/wdt977.c Sat Apr 20 18:44:33 2002 @@ -20,17 +20,15 @@ #include #include #include -#include #include #include #include -#define WATCHDOG_MINOR 130 - -static int timeout = 3; -static int timer_alive; -static int testmode; +static int timeout = 3; +static unsigned long timer_alive; +static int testmode; +static char wdt977_expect_close; /* * Allow only one person to hold it open @@ -38,12 +36,8 @@ static int wdt977_open(struct inode *inode, struct file *file) { - if(timer_alive) + if(test_and_set_bit(0, &timer_alive)) return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif - timer_alive++; //max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog. if (timeout>255) @@ -89,51 +83,72 @@ { /* * Shut off the timer. - * Lock it in if it's a module and we defined ...NOWAYOUT */ -#ifndef CONFIG_WATCHDOG_NOWAYOUT - lock_kernel(); + if(wdt977_expect_close == 42){ + // unlock the SuperIO chip + outb(0x87,0x370); + outb(0x87,0x370); + + //select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 + //F3 is reset to its default state + //F4 can clear the TIMEOUT'ed state (bit 0) - back to default + //We can not use GP17 as a PowerLed, as we use its usage as a RedLed - // unlock the SuperIO chip - outb(0x87,0x370); - outb(0x87,0x370); - - //select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 - //F3 is reset to its default state - //F4 can clear the TIMEOUT'ed state (bit 0) - back to default - //We can not use GP17 as a PowerLed, as we use its usage as a RedLed - - outb(0x07,0x370); - outb(0x08,0x371); - outb(0xF2,0x370); - outb(0xFF,0x371); - outb(0xF3,0x370); - outb(0x00,0x371); - outb(0xF4,0x370); - outb(0x00,0x371); - outb(0xF2,0x370); - outb(0x00,0x371); - - //at last select device Aux1 (dev=7) and set GP16 as a watchdog output - outb(0x07,0x370); - outb(0x07,0x371); - outb(0xE6,0x370); - outb(0x08,0x371); - - // lock the SuperIO chip - outb(0xAA,0x370); + outb(0x07,0x370); + outb(0x08,0x371); + outb(0xF2,0x370); + outb(0xFF,0x371); + outb(0xF3,0x370); + outb(0x00,0x371); + outb(0xF4,0x370); + outb(0x00,0x371); + outb(0xF2,0x370); + outb(0x00,0x371); - timer_alive=0; - unlock_kernel(); + //at last select device Aux1 (dev=7) and set GP16 as a watchdog output + outb(0x07,0x370); + outb(0x07,0x371); + outb(0xE6,0x370); + outb(0x08,0x371); + + // lock the SuperIO chip + outb(0xAA,0x370); + } else { + printk(KERN_CRIT "wdt977: Unexpected close, not closing watchdog!\n"); + } + clear_bit(0, &timer_alive); + wdt977_expect_close = 0; printk(KERN_INFO "Watchdog: shutdown.\n"); -#endif return 0; } static ssize_t wdt977_write(struct file *file, const char *data, size_t len, loff_t *ppos) { + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + if(len) + { +#ifndef CONFIG_WATCHDOG_NOWAYOUT + size_t i; + + wdt977_expect_close = 0; + + for (i = 0; i != len; i++) { + if (data[i] == 'V') + wdt977_expect_close = 42; + } +#endif + wdt977_ping(); + return 1; + } + return 0; +} + +static int wdt977_ping(void) +{ //max timeout value = 255 minutes (0xFF). Write 0 to disable WatchDog. if (timeout>255) timeout = 255; @@ -162,22 +177,60 @@ // lock the SuperIO chip outb(0xAA,0x370); - return 1; + return 0; +} + +static int wdt977_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int new_timeout; + static struct watchdog_info ident= + { + options: WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + firmware_version: 0, + identity: "Netwinder W83977AF" + }; + + switch(cmd) + { + default: + return -ENOTTY; + case WDIOC_GETSUPPORT: + if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident))) + return -EFAULT; + return 0; + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + case WDIOC_KEEPALIVE: + wdt977_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if(get_user(new_timeout, (int *)arg)) + return -EFAULT; + if(new_timeout < 0 || new_timeout > 255) + return -EINVAL; + timeout = new_timeout + wdt977_ping(); + /* Fall through */ + case WDIOC_GETTIMEOT: + return put_user(timeout, (int *)arg); + } } static struct file_operations wdt977_fops= { owner: THIS_MODULE, write: wdt977_write, + ioctl: wdt977_ioctl, open: wdt977_open, release: wdt977_release, }; static struct miscdevice wdt977_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &wdt977_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdt977_fops, }; static int __init nwwatchdog_init(void) @@ -201,3 +254,5 @@ module_exit(nwwatchdog_exit); MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Woody Suwalski "); +MODULE_DESCRIPTION("Watchdog Device for Netwinder W83977AF chip"); diff -ruN linux-2.4.19-pre7/drivers/char/wdt_pci.c watchdog-tree/drivers/char/wdt_pci.c --- linux-2.4.19-pre7/drivers/char/wdt_pci.c Tue Apr 16 13:18:17 2002 +++ watchdog-tree/drivers/char/wdt_pci.c Sat Apr 20 18:44:45 2002 @@ -32,20 +32,19 @@ * Tigran Aivazian : Restructured wdtpci_init_one() to handle failures * Joel Becker : Added WDIOC_GET/SETTIMEOUT * Zwane Mwaikambo : Magic char closing, locking changes, cleanups + * Rob Radez : Change CONFIG_WATCHDOG_NOWAYOUT semantics + * Fixed some comments */ #include #include -#include #include #include #include -#include #include #include #define WDT_IS_PCI #include "wd501p.h" -#include #include #include #include @@ -76,7 +75,7 @@ static struct semaphore open_sem; static spinlock_t wdtpci_lock; -static int expect_close = 0; +static char expect_close; static int io; static int irq; @@ -238,7 +237,7 @@ for (i = 0; i != count; i++) { if (buf[i] == 'V') - expect_close = 1; + expect_close = 42; } #endif wdtpci_ping(); @@ -299,11 +298,11 @@ int new_margin; static struct watchdog_info ident= { - WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER - |WDIOF_EXTERN1|WDIOF_EXTERN2|WDIOF_FANFAULT - |WDIOF_SETTIMEOUT, - 1, - "WDT500/501PCI" + options: WDIOF_OVERHEAT|WDIOF_POWERUNDER + |WDIOF_POWEROVER|WDIOF_EXTERN1|WDIOF_EXTERN2 + |WDIOF_FANFAULT |WDIOF_SETTIMEOUT, + firmware_version: 1, + identity: "WDT500/501PCI" }; ident.options&=WDT_OPTION_MASK; /* Mask down to the card we have */ @@ -358,9 +357,6 @@ if (down_trylock(&open_sem)) return -EBUSY; -#ifdef CONFIG_WATCHDOG_NOWAYOUT - MOD_INC_USE_COUNT; -#endif /* * Activate */ @@ -400,7 +396,7 @@ } /** - * wdtpci_close: + * wdtpci_release: * @inode: inode to board * @file: file handle to board * @@ -415,19 +411,18 @@ { if (MINOR(inode->i_rdev)==WATCHDOG_MINOR) { -#ifndef CONFIG_WATCHDOG_NOWAYOUT unsigned long flags; - if (expect_close) { + if (expect_close == 42) { spin_lock_irqsave(&wdtpci_lock, flags); inb_p(WDT_DC); /* Disable counters */ wdtpci_ctr_load(2,0); /* 0 length reset pulses now */ spin_unlock_irqrestore(&wdtpci_lock, flags); } else { - printk(KERN_CRIT PFX "Unexpected close, not stopping timer!"); + printk(KERN_CRIT PFX "Unexpected close, not stopping timer!\n"); wdtpci_ping(); } -#endif up(&open_sem); + expect_close = 0; } return 0; } @@ -476,17 +471,17 @@ static struct miscdevice wdtpci_miscdev= { - WATCHDOG_MINOR, - "watchdog", - &wdtpci_fops + minor: WATCHDOG_MINOR, + name: "watchdog", + fops: &wdtpci_fops, }; #ifdef CONFIG_WDT_501 static struct miscdevice temp_miscdev= { - TEMP_MINOR, - "temperature", - &wdtpci_fops + minor: TEMP_MINOR, + name: "temperature", + fops: &wdtpci_fops, }; #endif @@ -521,7 +516,7 @@ irq = dev->irq; io = pci_resource_start (dev, 2); - printk ("WDT501-P(PCI-WDG-CSM) driver 0.07 at %X " + printk (KERN_INFO "WDT501-P(PCI-WDG-CSM) driver 0.08 at %X " "(Interrupt %d)\n", io, irq); if (pci_enable_device (dev)) diff -ruN linux-2.4.19-pre7/drivers/sbus/char/cpwatchdog.c watchdog-tree/drivers/sbus/char/cpwatchdog.c --- linux-2.4.19-pre7/drivers/sbus/char/cpwatchdog.c Tue Apr 16 13:18:06 2002 +++ watchdog-tree/drivers/sbus/char/cpwatchdog.c Sat Apr 13 07:04:48 2002 @@ -179,7 +179,6 @@ static int wd1_timeout = 0; static int wd2_timeout = 0; -#ifdef MODULE EXPORT_NO_SYMBOLS; MODULE_PARM (wd0_timeout, "i"); @@ -196,7 +195,6 @@ MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE ("watchdog"); -#endif /* ifdef MODULE */ /* Forward declarations of internal methods */ @@ -327,13 +325,11 @@ wd_dev.initialized = 1; } - MOD_INC_USE_COUNT; return(0); } static int wd_release(struct inode *inode, struct file *file) { - MOD_DEC_USE_COUNT; return 0; } @@ -343,9 +339,9 @@ int setopt = 0; struct wd_timer* pTimer = (struct wd_timer*)file->private_data; struct watchdog_info info = { - 0, - 0, - "Altera EPF8820ATC144-4" + options: WDIOF_KEEPALIVEPING, + firmware_version: 0, + identity: "Altera EPF8820ATC144-4" }; if(NULL == pTimer) { @@ -367,6 +363,10 @@ if (put_user(0, (int *) arg)) return -EFAULT; break; + case WDIOC_GETTIMEOUT: + if (put_user(wd_opt_timeout(), (int *) arg)) + return -EFAULT; + break; case WDIOC_KEEPALIVE: wd_pingtimer(pTimer); break; @@ -599,7 +599,7 @@ /* Timer device initialization helper. * Returns 0 on success, other on failure */ -static int wd_inittimer(int whichdog) +static int __init wd_inittimer(int whichdog) { struct miscdevice *whichmisc; volatile struct wd_timer_regblk *whichregs; diff -ruN linux-2.4.19-pre7/drivers/sbus/char/riowatchdog.c watchdog-tree/drivers/sbus/char/riowatchdog.c --- linux-2.4.19-pre7/drivers/sbus/char/riowatchdog.c Tue Apr 16 13:18:06 2002 +++ watchdog-tree/drivers/sbus/char/riowatchdog.c Mon Apr 8 18:55:17 2002 @@ -194,17 +194,11 @@ return 0; } -static ssize_t riowd_read(struct file *file, char *buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - static struct file_operations riowd_fops = { owner: THIS_MODULE, ioctl: riowd_ioctl, open: riowd_open, write: riowd_write, - read: riowd_read, release: riowd_release, };