Blame view
drivers/watchdog/pcwd_pci.c
20.8 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 |
/* * Berkshire PCI-PC Watchdog Card Driver * |
39e3a0556 [WATCHDOG] pcwd_p... |
4 |
* (c) Copyright 2003-2007 Wim Van Sebroeck <wim@iguana.be>. |
1da177e4c Linux-2.6.12-rc2 |
5 6 7 8 |
* * Based on source code of the following authors: * Ken Hollis <kenji@bitgate.com>, * Lindsay Harris <lindsay@bluegum.com>, |
29fa0586d [PATCH] Switch al... |
9 |
* Alan Cox <alan@lxorguk.ukuu.org.uk>, |
1da177e4c Linux-2.6.12-rc2 |
10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
* Matt Domsch <Matt_Domsch@dell.com>, * Rob Radez <rob@osinvestor.com> * * This program is free software; you can redistribute it and/or * modify it 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. * * Neither Wim Van Sebroeck nor Iguana vzw. admit liability nor * provide warranty for any of this software. This material is * provided "AS-IS" and at no charge. */ /* |
58b519f3e [WATCHDOG] add WD... |
24 |
* A bells and whistles driver is available from: |
b3faed637 [WATCHDOG] pcwd_p... |
25 26 |
* http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/ * |
143a2e54b [WATCHDOG] More c... |
27 28 |
* More info available at * http://www.berkprod.com/ or http://www.pcwatchdog.com/ |
1da177e4c Linux-2.6.12-rc2 |
29 30 31 32 33 |
*/ /* * Includes, defines, variables, module parameters, ... */ |
c315b7e84 [WATCHDOG] pcwd_p... |
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
#include <linux/module.h> /* For module specific items */ #include <linux/moduleparam.h> /* For new moduleparam's */ #include <linux/types.h> /* For standard types (like size_t) */ #include <linux/errno.h> /* For the -ENODEV/... values */ #include <linux/kernel.h> /* For printk/panic/... */ #include <linux/delay.h> /* For mdelay function */ #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ #include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/notifier.h> /* For notifier support */ #include <linux/reboot.h> /* For reboot_notifier stuff */ #include <linux/init.h> /* For __init/__exit/... */ #include <linux/fs.h> /* For file operations */ #include <linux/pci.h> /* For pci functions */ #include <linux/ioport.h> /* For io-port access */ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ |
089ab0791 [WATCHDOG] Clean-... |
49 50 |
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */ #include <linux/io.h> /* For inb/outb/... */ |
1da177e4c Linux-2.6.12-rc2 |
51 52 |
/* Module and version information */ |
39e3a0556 [WATCHDOG] pcwd_p... |
53 |
#define WATCHDOG_VERSION "1.03" |
1da177e4c Linux-2.6.12-rc2 |
54 55 56 |
#define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" #define WATCHDOG_NAME "pcwd_pci" #define PFX WATCHDOG_NAME ": " |
143a2e54b [WATCHDOG] More c... |
57 58 |
#define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION " " |
1da177e4c Linux-2.6.12-rc2 |
59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
/* Stuff for the PCI ID's */ #ifndef PCI_VENDOR_ID_QUICKLOGIC #define PCI_VENDOR_ID_QUICKLOGIC 0x11e3 #endif #ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD #define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030 #endif /* * These are the defines that describe the control status bits for the * PCI-PC Watchdog card. */ |
a0800f6da [WATCHDOG] pcwd_p... |
73 74 75 76 77 78 |
/* Port 1 : Control Status #1 */ #define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ #define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ #define WD_PCI_TTRP 0x04 /* Temperature Trip status */ #define WD_PCI_RL2A 0x08 /* Relay 2 Active */ #define WD_PCI_RL1A 0x10 /* Relay 1 Active */ |
143a2e54b [WATCHDOG] More c... |
79 80 |
#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip / reset */ |
a0800f6da [WATCHDOG] pcwd_p... |
81 82 83 84 85 86 |
#define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */ /* Port 2 : Control Status #2 */ #define WD_PCI_WDIS 0x10 /* Watchdog Disable */ #define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */ #define WD_PCI_WRSP 0x40 /* Watchdog wrote response */ #define WD_PCI_PCMD 0x80 /* PC has sent command */ |
1da177e4c Linux-2.6.12-rc2 |
87 88 89 90 91 92 |
/* according to documentation max. time to process a command for the pci * watchdog card is 100 ms, so we give it 150 ms to do it's job */ #define PCI_COMMAND_TIMEOUT 150 /* Watchdog's internal commands */ |
a0800f6da [WATCHDOG] pcwd_p... |
93 94 95 96 97 |
#define CMD_GET_STATUS 0x04 #define CMD_GET_FIRMWARE_VERSION 0x08 #define CMD_READ_WATCHDOG_TIMEOUT 0x18 #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 #define CMD_GET_CLEAR_RESET_COUNT 0x84 |
1da177e4c Linux-2.6.12-rc2 |
98 |
|
39e3a0556 [WATCHDOG] pcwd_p... |
99 |
/* Watchdog's Dip Switch heartbeat values */ |
7944d3a5a [WATCHDOG] more c... |
100 |
static const int heartbeat_tbl[] = { |
39e3a0556 [WATCHDOG] pcwd_p... |
101 102 103 104 105 106 107 108 109 |
5, /* OFF-OFF-OFF = 5 Sec */ 10, /* OFF-OFF-ON = 10 Sec */ 30, /* OFF-ON-OFF = 30 Sec */ 60, /* OFF-ON-ON = 1 Min */ 300, /* ON-OFF-OFF = 5 Min */ 600, /* ON-OFF-ON = 10 Min */ 1800, /* ON-ON-OFF = 30 Min */ 3600, /* ON-ON-ON = 1 hour */ }; |
1da177e4c Linux-2.6.12-rc2 |
110 111 112 113 114 115 116 |
/* We can only use 1 card due to the /dev/watchdog restriction */ static int cards_found; /* internal variables */ static int temp_panic; static unsigned long is_active; static char expect_release; |
143a2e54b [WATCHDOG] More c... |
117 118 119 120 121 122 123 124 125 126 127 128 |
/* this is private data for each PCI-PC watchdog card */ static struct { /* Wether or not the card has a temperature device */ int supports_temp; /* The card's boot status */ int boot_status; /* The cards I/O address */ unsigned long io_addr; /* the lock for io operations */ spinlock_t io_lock; /* the PCI-device */ struct pci_dev *pdev; |
1da177e4c Linux-2.6.12-rc2 |
129 130 131 |
} pcipcwd_private; /* module parameters */ |
195331d7c [WATCHDOG] pcwd_p... |
132 133 134 135 136 137 |
#define QUIET 0 /* Default */ #define VERBOSE 1 /* Verbose */ #define DEBUG 2 /* print fancy stuff too */ static int debug = QUIET; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); |
143a2e54b [WATCHDOG] More c... |
138 139 |
#define WATCHDOG_HEARTBEAT 0 /* default heartbeat = delay-time from dip-switches */ |
1da177e4c Linux-2.6.12-rc2 |
140 141 |
static int heartbeat = WATCHDOG_HEARTBEAT; module_param(heartbeat, int, 0); |
143a2e54b [WATCHDOG] More c... |
142 143 144 |
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. " "(0<heartbeat<65536 or 0=delay-time from dip-switches, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")"); |
1da177e4c Linux-2.6.12-rc2 |
145 |
|
4bfdf3783 [PATCH] consolida... |
146 |
static int nowayout = WATCHDOG_NOWAYOUT; |
1da177e4c Linux-2.6.12-rc2 |
147 |
module_param(nowayout, int, 0); |
143a2e54b [WATCHDOG] More c... |
148 149 |
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); |
1da177e4c Linux-2.6.12-rc2 |
150 151 152 153 154 155 156 157 |
/* * Internal functions */ static int send_command(int cmd, int *msb, int *lsb) { int got_response, count; |
195331d7c [WATCHDOG] pcwd_p... |
158 |
if (debug >= DEBUG) |
143a2e54b [WATCHDOG] More c... |
159 160 161 |
printk(KERN_DEBUG PFX "sending following data " "cmd=0x%02x msb=0x%02x lsb=0x%02x ", cmd, *msb, *lsb); |
195331d7c [WATCHDOG] pcwd_p... |
162 |
|
1da177e4c Linux-2.6.12-rc2 |
163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
spin_lock(&pcipcwd_private.io_lock); /* If a command requires data it should be written first. * Data for commands with 8 bits of data should be written to port 4. * Commands with 16 bits of data, should be written as LSB to port 4 * and MSB to port 5. * After the required data has been written then write the command to * port 6. */ outb_p(*lsb, pcipcwd_private.io_addr + 4); outb_p(*msb, pcipcwd_private.io_addr + 5); outb_p(cmd, pcipcwd_private.io_addr + 6); /* wait till the pci card processed the command, signaled by * the WRSP bit in port 2 and give it a max. timeout of * PCI_COMMAND_TIMEOUT to process */ |
a0800f6da [WATCHDOG] pcwd_p... |
177 |
got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; |
143a2e54b [WATCHDOG] More c... |
178 179 |
for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { |
1da177e4c Linux-2.6.12-rc2 |
180 |
mdelay(1); |
a0800f6da [WATCHDOG] pcwd_p... |
181 |
got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; |
1da177e4c Linux-2.6.12-rc2 |
182 |
} |
195331d7c [WATCHDOG] pcwd_p... |
183 184 |
if (debug >= DEBUG) { if (got_response) { |
143a2e54b [WATCHDOG] More c... |
185 186 187 |
printk(KERN_DEBUG PFX "time to process command was: %d ms ", |
195331d7c [WATCHDOG] pcwd_p... |
188 189 |
count); } else { |
143a2e54b [WATCHDOG] More c... |
190 191 192 |
printk(KERN_DEBUG PFX "card did not respond on command! "); |
195331d7c [WATCHDOG] pcwd_p... |
193 194 |
} } |
1da177e4c Linux-2.6.12-rc2 |
195 196 197 198 199 200 201 |
if (got_response) { /* read back response */ *lsb = inb_p(pcipcwd_private.io_addr + 4); *msb = inb_p(pcipcwd_private.io_addr + 5); /* clear WRSP bit */ inb_p(pcipcwd_private.io_addr + 6); |
195331d7c [WATCHDOG] pcwd_p... |
202 203 |
if (debug >= DEBUG) |
143a2e54b [WATCHDOG] More c... |
204 205 206 |
printk(KERN_DEBUG PFX "received following data for " "cmd=0x%02x: msb=0x%02x lsb=0x%02x ", |
195331d7c [WATCHDOG] pcwd_p... |
207 |
cmd, *msb, *lsb); |
1da177e4c Linux-2.6.12-rc2 |
208 |
} |
195331d7c [WATCHDOG] pcwd_p... |
209 |
|
1da177e4c Linux-2.6.12-rc2 |
210 211 212 213 |
spin_unlock(&pcipcwd_private.io_lock); return got_response; } |
a0800f6da [WATCHDOG] pcwd_p... |
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
static inline void pcipcwd_check_temperature_support(void) { if (inb_p(pcipcwd_private.io_addr) != 0xF0) pcipcwd_private.supports_temp = 1; } static int pcipcwd_get_option_switches(void) { int option_switches; option_switches = inb_p(pcipcwd_private.io_addr + 3); return option_switches; } static void pcipcwd_show_card_info(void) { int got_fw_rev, fw_rev_major, fw_rev_minor; char fw_ver_str[20]; /* The cards firmware version */ int option_switches; |
143a2e54b [WATCHDOG] More c... |
233 234 |
got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); |
7944d3a5a [WATCHDOG] more c... |
235 |
if (got_fw_rev) |
a0800f6da [WATCHDOG] pcwd_p... |
236 |
sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); |
7944d3a5a [WATCHDOG] more c... |
237 |
else |
a0800f6da [WATCHDOG] pcwd_p... |
238 |
sprintf(fw_ver_str, "<card no answer>"); |
a0800f6da [WATCHDOG] pcwd_p... |
239 240 241 |
/* Get switch settings */ option_switches = pcipcwd_get_option_switches(); |
143a2e54b [WATCHDOG] More c... |
242 243 244 |
printk(KERN_INFO PFX "Found card at port " "0x%04x (Firmware: %s) %s temp option ", |
a0800f6da [WATCHDOG] pcwd_p... |
245 246 |
(int) pcipcwd_private.io_addr, fw_ver_str, (pcipcwd_private.supports_temp ? "with" : "without")); |
143a2e54b [WATCHDOG] More c... |
247 248 249 |
printk(KERN_INFO PFX "Option switches (0x%02x): " "Temperature Reset Enable=%s, Power On Delay=%s ", |
a0800f6da [WATCHDOG] pcwd_p... |
250 251 252 253 254 |
option_switches, ((option_switches & 0x10) ? "ON" : "OFF"), ((option_switches & 0x08) ? "ON" : "OFF")); if (pcipcwd_private.boot_status & WDIOF_CARDRESET) |
143a2e54b [WATCHDOG] More c... |
255 256 257 |
printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card "); |
a0800f6da [WATCHDOG] pcwd_p... |
258 259 260 261 262 263 |
if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) printk(KERN_INFO PFX "Card sensed a CPU Overheat "); if (pcipcwd_private.boot_status == 0) |
143a2e54b [WATCHDOG] More c... |
264 265 266 |
printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset "); |
a0800f6da [WATCHDOG] pcwd_p... |
267 |
} |
1da177e4c Linux-2.6.12-rc2 |
268 269 270 271 272 273 274 275 276 277 |
static int pcipcwd_start(void) { int stat_reg; spin_lock(&pcipcwd_private.io_lock); outb_p(0x00, pcipcwd_private.io_addr + 3); udelay(1000); stat_reg = inb_p(pcipcwd_private.io_addr + 2); spin_unlock(&pcipcwd_private.io_lock); |
a0800f6da [WATCHDOG] pcwd_p... |
278 |
if (stat_reg & WD_PCI_WDIS) { |
1da177e4c Linux-2.6.12-rc2 |
279 280 281 282 |
printk(KERN_ERR PFX "Card timer not enabled "); return -1; } |
195331d7c [WATCHDOG] pcwd_p... |
283 284 285 |
if (debug >= VERBOSE) printk(KERN_DEBUG PFX "Watchdog started "); |
1da177e4c Linux-2.6.12-rc2 |
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
return 0; } static int pcipcwd_stop(void) { int stat_reg; spin_lock(&pcipcwd_private.io_lock); outb_p(0xA5, pcipcwd_private.io_addr + 3); udelay(1000); outb_p(0xA5, pcipcwd_private.io_addr + 3); udelay(1000); stat_reg = inb_p(pcipcwd_private.io_addr + 2); spin_unlock(&pcipcwd_private.io_lock); |
a0800f6da [WATCHDOG] pcwd_p... |
302 |
if (!(stat_reg & WD_PCI_WDIS)) { |
143a2e54b [WATCHDOG] More c... |
303 304 305 |
printk(KERN_ERR PFX "Card did not acknowledge disable attempt "); |
1da177e4c Linux-2.6.12-rc2 |
306 307 |
return -1; } |
195331d7c [WATCHDOG] pcwd_p... |
308 309 310 |
if (debug >= VERBOSE) printk(KERN_DEBUG PFX "Watchdog stopped "); |
1da177e4c Linux-2.6.12-rc2 |
311 312 313 314 315 316 |
return 0; } static int pcipcwd_keepalive(void) { /* Re-trigger watchdog by writing to port 0 */ |
045798b56 [WATCHDOG] pcwd_p... |
317 |
spin_lock(&pcipcwd_private.io_lock); |
a0800f6da [WATCHDOG] pcwd_p... |
318 |
outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */ |
045798b56 [WATCHDOG] pcwd_p... |
319 |
spin_unlock(&pcipcwd_private.io_lock); |
195331d7c [WATCHDOG] pcwd_p... |
320 321 322 323 |
if (debug >= DEBUG) printk(KERN_DEBUG PFX "Watchdog keepalive signal send "); |
1da177e4c Linux-2.6.12-rc2 |
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
return 0; } static int pcipcwd_set_heartbeat(int t) { int t_msb = t / 256; int t_lsb = t % 256; if ((t < 0x0001) || (t > 0xFFFF)) return -EINVAL; /* Write new heartbeat to watchdog */ send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb); heartbeat = t; |
195331d7c [WATCHDOG] pcwd_p... |
339 340 341 342 |
if (debug >= VERBOSE) printk(KERN_DEBUG PFX "New heartbeat: %d ", heartbeat); |
1da177e4c Linux-2.6.12-rc2 |
343 344 345 346 347 |
return 0; } static int pcipcwd_get_status(int *status) { |
a0800f6da [WATCHDOG] pcwd_p... |
348 |
int control_status; |
1da177e4c Linux-2.6.12-rc2 |
349 |
|
7944d3a5a [WATCHDOG] more c... |
350 |
*status = 0; |
a0800f6da [WATCHDOG] pcwd_p... |
351 352 |
control_status = inb_p(pcipcwd_private.io_addr + 1); if (control_status & WD_PCI_WTRP) |
1da177e4c Linux-2.6.12-rc2 |
353 |
*status |= WDIOF_CARDRESET; |
a0800f6da [WATCHDOG] pcwd_p... |
354 |
if (control_status & WD_PCI_TTRP) { |
1da177e4c Linux-2.6.12-rc2 |
355 356 357 358 359 |
*status |= WDIOF_OVERHEAT; if (temp_panic) panic(PFX "Temperature overheat trip! "); } |
195331d7c [WATCHDOG] pcwd_p... |
360 361 362 363 |
if (debug >= DEBUG) printk(KERN_DEBUG PFX "Control Status #1: 0x%02x ", control_status); |
1da177e4c Linux-2.6.12-rc2 |
364 365 366 367 368 |
return 0; } static int pcipcwd_clear_status(void) { |
a0800f6da [WATCHDOG] pcwd_p... |
369 370 371 |
int control_status; int msb; int reset_counter; |
195331d7c [WATCHDOG] pcwd_p... |
372 373 374 |
if (debug >= VERBOSE) printk(KERN_INFO PFX "clearing watchdog trip status & LED "); |
a0800f6da [WATCHDOG] pcwd_p... |
375 |
control_status = inb_p(pcipcwd_private.io_addr + 1); |
195331d7c [WATCHDOG] pcwd_p... |
376 377 378 379 380 381 382 |
if (debug >= DEBUG) { printk(KERN_DEBUG PFX "status was: 0x%02x ", control_status); printk(KERN_DEBUG PFX "sending: 0x%02x ", (control_status & WD_PCI_R2DS) | WD_PCI_WTRP); } |
a0800f6da [WATCHDOG] pcwd_p... |
383 |
/* clear trip status & LED and keep mode of relay 2 */ |
143a2e54b [WATCHDOG] More c... |
384 385 |
outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP, pcipcwd_private.io_addr + 1); |
a0800f6da [WATCHDOG] pcwd_p... |
386 387 |
/* clear reset counter */ |
7944d3a5a [WATCHDOG] more c... |
388 389 |
msb = 0; reset_counter = 0xff; |
a0800f6da [WATCHDOG] pcwd_p... |
390 |
send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter); |
195331d7c [WATCHDOG] pcwd_p... |
391 392 393 394 395 |
if (debug >= DEBUG) { printk(KERN_DEBUG PFX "reset count was: 0x%02x ", reset_counter); } |
1da177e4c Linux-2.6.12-rc2 |
396 397 398 399 400 401 402 403 |
return 0; } static int pcipcwd_get_temperature(int *temperature) { *temperature = 0; if (!pcipcwd_private.supports_temp) return -ENODEV; |
045798b56 [WATCHDOG] pcwd_p... |
404 |
spin_lock(&pcipcwd_private.io_lock); |
a0800f6da [WATCHDOG] pcwd_p... |
405 |
*temperature = inb_p(pcipcwd_private.io_addr); |
045798b56 [WATCHDOG] pcwd_p... |
406 |
spin_unlock(&pcipcwd_private.io_lock); |
a0800f6da [WATCHDOG] pcwd_p... |
407 |
|
1da177e4c Linux-2.6.12-rc2 |
408 409 410 411 |
/* * Convert celsius to fahrenheit, since this was * the decided 'standard' for this return value. */ |
a0800f6da [WATCHDOG] pcwd_p... |
412 |
*temperature = (*temperature * 9 / 5) + 32; |
1da177e4c Linux-2.6.12-rc2 |
413 |
|
195331d7c [WATCHDOG] pcwd_p... |
414 415 416 417 418 |
if (debug >= DEBUG) { printk(KERN_DEBUG PFX "temperature is: %d F ", *temperature); } |
1da177e4c Linux-2.6.12-rc2 |
419 420 |
return 0; } |
58b519f3e [WATCHDOG] add WD... |
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
static int pcipcwd_get_timeleft(int *time_left) { int msb; int lsb; /* Read the time that's left before rebooting */ /* Note: if the board is not yet armed then we will read 0xFFFF */ send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); *time_left = (msb << 8) + lsb; if (debug >= VERBOSE) printk(KERN_DEBUG PFX "Time left before next reboot: %d ", *time_left); return 0; } |
1da177e4c Linux-2.6.12-rc2 |
439 440 441 442 443 |
/* * /dev/watchdog handling */ static ssize_t pcipcwd_write(struct file *file, const char __user *data, |
a0800f6da [WATCHDOG] pcwd_p... |
444 |
size_t len, loff_t *ppos) |
1da177e4c Linux-2.6.12-rc2 |
445 446 447 448 449 450 451 452 453 |
{ /* See if we got the magic character 'V' and reload the timer */ if (len) { if (!nowayout) { size_t i; /* note: just in case someone wrote the magic character * five months ago... */ expect_release = 0; |
143a2e54b [WATCHDOG] More c... |
454 455 |
/* scan to see whether or not we got the * magic character */ |
1da177e4c Linux-2.6.12-rc2 |
456 457 |
for (i = 0; i != len; i++) { char c; |
7944d3a5a [WATCHDOG] more c... |
458 |
if (get_user(c, data + i)) |
1da177e4c Linux-2.6.12-rc2 |
459 460 461 462 463 464 465 466 467 468 469 |
return -EFAULT; if (c == 'V') expect_release = 42; } } /* someone wrote to us, we should reload the timer */ pcipcwd_keepalive(); } return len; } |
c94885205 [WATCHDOG] pcwd: ... |
470 471 |
static long pcipcwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
472 473 474 |
{ void __user *argp = (void __user *)arg; int __user *p = argp; |
42747d712 [WATCHDOG] watchd... |
475 |
static const struct watchdog_info ident = { |
1da177e4c Linux-2.6.12-rc2 |
476 477 478 479 480 481 482 483 484 485 |
.options = WDIOF_OVERHEAT | WDIOF_CARDRESET | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, .firmware_version = 1, .identity = WATCHDOG_DRIVER_NAME, }; switch (cmd) { |
5eb82498e [WATCHDOG] Coding... |
486 |
case WDIOC_GETSUPPORT: |
7944d3a5a [WATCHDOG] more c... |
487 |
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; |
5eb82498e [WATCHDOG] Coding... |
488 489 490 491 492 493 494 |
case WDIOC_GETSTATUS: { int status; pcipcwd_get_status(&status); return put_user(status, p); } |
1da177e4c Linux-2.6.12-rc2 |
495 |
|
5eb82498e [WATCHDOG] Coding... |
496 497 |
case WDIOC_GETBOOTSTATUS: return put_user(pcipcwd_private.boot_status, p); |
1da177e4c Linux-2.6.12-rc2 |
498 |
|
5eb82498e [WATCHDOG] Coding... |
499 500 501 |
case WDIOC_GETTEMP: { int temperature; |
1da177e4c Linux-2.6.12-rc2 |
502 |
|
5eb82498e [WATCHDOG] Coding... |
503 504 |
if (pcipcwd_get_temperature(&temperature)) return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
505 |
|
5eb82498e [WATCHDOG] Coding... |
506 507 |
return put_user(temperature, p); } |
1da177e4c Linux-2.6.12-rc2 |
508 |
|
5eb82498e [WATCHDOG] Coding... |
509 510 511 |
case WDIOC_SETOPTIONS: { int new_options, retval = -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
512 |
|
7944d3a5a [WATCHDOG] more c... |
513 |
if (get_user(new_options, p)) |
5eb82498e [WATCHDOG] Coding... |
514 |
return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
515 |
|
5eb82498e [WATCHDOG] Coding... |
516 517 518 519 520 |
if (new_options & WDIOS_DISABLECARD) { if (pcipcwd_stop()) return -EIO; retval = 0; } |
1da177e4c Linux-2.6.12-rc2 |
521 |
|
5eb82498e [WATCHDOG] Coding... |
522 523 524 525 526 |
if (new_options & WDIOS_ENABLECARD) { if (pcipcwd_start()) return -EIO; retval = 0; } |
1da177e4c Linux-2.6.12-rc2 |
527 |
|
5eb82498e [WATCHDOG] Coding... |
528 529 530 |
if (new_options & WDIOS_TEMPPANIC) { temp_panic = 1; retval = 0; |
1da177e4c Linux-2.6.12-rc2 |
531 |
} |
5eb82498e [WATCHDOG] Coding... |
532 533 |
return retval; } |
1da177e4c Linux-2.6.12-rc2 |
534 |
|
0c06090c9 [WATCHDOG] Coding... |
535 536 537 |
case WDIOC_KEEPALIVE: pcipcwd_keepalive(); return 0; |
5eb82498e [WATCHDOG] Coding... |
538 539 540 |
case WDIOC_SETTIMEOUT: { int new_heartbeat; |
1da177e4c Linux-2.6.12-rc2 |
541 |
|
5eb82498e [WATCHDOG] Coding... |
542 543 |
if (get_user(new_heartbeat, p)) return -EFAULT; |
1da177e4c Linux-2.6.12-rc2 |
544 |
|
5eb82498e [WATCHDOG] Coding... |
545 |
if (pcipcwd_set_heartbeat(new_heartbeat)) |
143a2e54b [WATCHDOG] More c... |
546 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
547 |
|
5eb82498e [WATCHDOG] Coding... |
548 549 550 |
pcipcwd_keepalive(); /* Fall */ } |
1da177e4c Linux-2.6.12-rc2 |
551 |
|
5eb82498e [WATCHDOG] Coding... |
552 553 |
case WDIOC_GETTIMEOUT: return put_user(heartbeat, p); |
58b519f3e [WATCHDOG] add WD... |
554 |
|
5eb82498e [WATCHDOG] Coding... |
555 556 557 |
case WDIOC_GETTIMELEFT: { int time_left; |
58b519f3e [WATCHDOG] add WD... |
558 |
|
5eb82498e [WATCHDOG] Coding... |
559 560 561 562 563 |
if (pcipcwd_get_timeleft(&time_left)) return -EFAULT; return put_user(time_left, p); } |
58b519f3e [WATCHDOG] add WD... |
564 |
|
5eb82498e [WATCHDOG] Coding... |
565 566 |
default: return -ENOTTY; |
1da177e4c Linux-2.6.12-rc2 |
567 568 569 570 571 572 |
} } static int pcipcwd_open(struct inode *inode, struct file *file) { /* /dev/watchdog can only be opened once */ |
a0800f6da [WATCHDOG] pcwd_p... |
573 |
if (test_and_set_bit(0, &is_active)) { |
195331d7c [WATCHDOG] pcwd_p... |
574 |
if (debug >= VERBOSE) |
143a2e54b [WATCHDOG] More c... |
575 576 577 |
printk(KERN_ERR PFX "Attempt to open already opened device. "); |
1da177e4c Linux-2.6.12-rc2 |
578 |
return -EBUSY; |
a0800f6da [WATCHDOG] pcwd_p... |
579 |
} |
1da177e4c Linux-2.6.12-rc2 |
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
/* Activate */ pcipcwd_start(); pcipcwd_keepalive(); return nonseekable_open(inode, file); } static int pcipcwd_release(struct inode *inode, struct file *file) { /* * Shut off the timer. */ if (expect_release == 42) { pcipcwd_stop(); } else { |
143a2e54b [WATCHDOG] More c... |
595 596 597 |
printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog! "); |
1da177e4c Linux-2.6.12-rc2 |
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
pcipcwd_keepalive(); } expect_release = 0; clear_bit(0, &is_active); return 0; } /* * /dev/temperature handling */ static ssize_t pcipcwd_temp_read(struct file *file, char __user *data, size_t len, loff_t *ppos) { int temperature; if (pcipcwd_get_temperature(&temperature)) return -EFAULT; |
7944d3a5a [WATCHDOG] more c... |
616 |
if (copy_to_user(data, &temperature, 1)) |
1da177e4c Linux-2.6.12-rc2 |
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
return -EFAULT; return 1; } static int pcipcwd_temp_open(struct inode *inode, struct file *file) { if (!pcipcwd_private.supports_temp) return -ENODEV; return nonseekable_open(inode, file); } static int pcipcwd_temp_release(struct inode *inode, struct file *file) { return 0; } /* * Notify system */ |
143a2e54b [WATCHDOG] More c... |
638 639 |
static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused) |
1da177e4c Linux-2.6.12-rc2 |
640 |
{ |
7944d3a5a [WATCHDOG] more c... |
641 642 |
if (code == SYS_DOWN || code == SYS_HALT) pcipcwd_stop(); /* Turn the WDT off */ |
1da177e4c Linux-2.6.12-rc2 |
643 644 645 646 647 648 649 |
return NOTIFY_DONE; } /* * Kernel Interfaces */ |
62322d255 [PATCH] make more... |
650 |
static const struct file_operations pcipcwd_fops = { |
1da177e4c Linux-2.6.12-rc2 |
651 652 653 |
.owner = THIS_MODULE, .llseek = no_llseek, .write = pcipcwd_write, |
c94885205 [WATCHDOG] pcwd: ... |
654 |
.unlocked_ioctl = pcipcwd_ioctl, |
1da177e4c Linux-2.6.12-rc2 |
655 656 657 658 659 660 661 662 663 |
.open = pcipcwd_open, .release = pcipcwd_release, }; static struct miscdevice pcipcwd_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &pcipcwd_fops, }; |
62322d255 [PATCH] make more... |
664 |
static const struct file_operations pcipcwd_temp_fops = { |
1da177e4c Linux-2.6.12-rc2 |
665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 |
.owner = THIS_MODULE, .llseek = no_llseek, .read = pcipcwd_temp_read, .open = pcipcwd_temp_open, .release = pcipcwd_temp_release, }; static struct miscdevice pcipcwd_temp_miscdev = { .minor = TEMP_MINOR, .name = "temperature", .fops = &pcipcwd_temp_fops, }; static struct notifier_block pcipcwd_notifier = { .notifier_call = pcipcwd_notify_sys, }; /* * Init & exit routines */ |
1da177e4c Linux-2.6.12-rc2 |
685 686 687 688 |
static int __devinit pcipcwd_card_init(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = -EIO; |
1da177e4c Linux-2.6.12-rc2 |
689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 |
cards_found++; if (cards_found == 1) printk(KERN_INFO PFX DRIVER_VERSION); if (cards_found > 1) { printk(KERN_ERR PFX "This driver only supports 1 device "); return -ENODEV; } if (pci_enable_device(pdev)) { printk(KERN_ERR PFX "Not possible to enable PCI Device "); return -ENODEV; } if (pci_resource_start(pdev, 0) == 0x0000) { printk(KERN_ERR PFX "No I/O-Address for card detected "); ret = -ENODEV; goto err_out_disable_device; } pcipcwd_private.pdev = pdev; pcipcwd_private.io_addr = pci_resource_start(pdev, 0); if (pci_request_regions(pdev, WATCHDOG_NAME)) { printk(KERN_ERR PFX "I/O address 0x%04x already in use ", (int) pcipcwd_private.io_addr); ret = -EIO; goto err_out_disable_device; } /* get the boot_status */ pcipcwd_get_status(&pcipcwd_private.boot_status); /* clear the "card caused reboot" flag */ pcipcwd_clear_status(); /* disable card */ pcipcwd_stop(); /* Check whether or not the card supports the temperature device */ |
a0800f6da [WATCHDOG] pcwd_p... |
734 |
pcipcwd_check_temperature_support(); |
1da177e4c Linux-2.6.12-rc2 |
735 |
|
a0800f6da [WATCHDOG] pcwd_p... |
736 737 |
/* Show info about the card itself */ pcipcwd_show_card_info(); |
1da177e4c Linux-2.6.12-rc2 |
738 |
|
39e3a0556 [WATCHDOG] pcwd_p... |
739 740 |
/* If heartbeat = 0 then we use the heartbeat from the dip-switches */ if (heartbeat == 0) |
143a2e54b [WATCHDOG] More c... |
741 742 |
heartbeat = heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)]; |
39e3a0556 [WATCHDOG] pcwd_p... |
743 |
|
143a2e54b [WATCHDOG] More c... |
744 745 |
/* Check that the heartbeat value is within it's range ; * if not reset to the default */ |
1da177e4c Linux-2.6.12-rc2 |
746 747 |
if (pcipcwd_set_heartbeat(heartbeat)) { pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT); |
143a2e54b [WATCHDOG] More c... |
748 749 750 |
printk(KERN_INFO PFX "heartbeat value must be 0<heartbeat<65536, using %d ", |
1da177e4c Linux-2.6.12-rc2 |
751 752 753 754 755 |
WATCHDOG_HEARTBEAT); } ret = register_reboot_notifier(&pcipcwd_notifier); if (ret != 0) { |
143a2e54b [WATCHDOG] More c... |
756 757 758 |
printk(KERN_ERR PFX "cannot register reboot notifier (err=%d) ", ret); |
1da177e4c Linux-2.6.12-rc2 |
759 760 761 762 763 764 |
goto err_out_release_region; } if (pcipcwd_private.supports_temp) { ret = misc_register(&pcipcwd_temp_miscdev); if (ret != 0) { |
143a2e54b [WATCHDOG] More c... |
765 766 767 |
printk(KERN_ERR PFX "cannot register miscdev on " "minor=%d (err=%d) ", TEMP_MINOR, ret); |
1da177e4c Linux-2.6.12-rc2 |
768 769 770 771 772 773 |
goto err_out_unregister_reboot; } } ret = misc_register(&pcipcwd_miscdev); if (ret != 0) { |
143a2e54b [WATCHDOG] More c... |
774 775 776 |
printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d) ", |
1da177e4c Linux-2.6.12-rc2 |
777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 |
WATCHDOG_MINOR, ret); goto err_out_misc_deregister; } printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d) ", heartbeat, nowayout); return 0; err_out_misc_deregister: if (pcipcwd_private.supports_temp) misc_deregister(&pcipcwd_temp_miscdev); err_out_unregister_reboot: unregister_reboot_notifier(&pcipcwd_notifier); err_out_release_region: pci_release_regions(pdev); err_out_disable_device: pci_disable_device(pdev); return ret; } static void __devexit pcipcwd_card_exit(struct pci_dev *pdev) { /* Stop the timer before we leave */ if (!nowayout) pcipcwd_stop(); /* Deregister */ misc_deregister(&pcipcwd_miscdev); if (pcipcwd_private.supports_temp) misc_deregister(&pcipcwd_temp_miscdev); unregister_reboot_notifier(&pcipcwd_notifier); pci_release_regions(pdev); pci_disable_device(pdev); cards_found--; } |
4562f5394 watchdog: convert... |
814 |
static DEFINE_PCI_DEVICE_TABLE(pcipcwd_pci_tbl) = { |
1da177e4c Linux-2.6.12-rc2 |
815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
{ PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD, PCI_ANY_ID, PCI_ANY_ID, }, { 0 }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, pcipcwd_pci_tbl); static struct pci_driver pcipcwd_driver = { .name = WATCHDOG_NAME, .id_table = pcipcwd_pci_tbl, .probe = pcipcwd_card_init, .remove = __devexit_p(pcipcwd_card_exit), }; static int __init pcipcwd_init_module(void) { |
a0800f6da [WATCHDOG] pcwd_p... |
830 |
spin_lock_init(&pcipcwd_private.io_lock); |
1da177e4c Linux-2.6.12-rc2 |
831 832 833 834 835 836 837 |
return pci_register_driver(&pcipcwd_driver); } static void __exit pcipcwd_cleanup_module(void) { pci_unregister_driver(&pcipcwd_driver); |
045798b56 [WATCHDOG] pcwd_p... |
838 839 840 |
printk(KERN_INFO PFX "Watchdog Module Unloaded. "); |
1da177e4c Linux-2.6.12-rc2 |
841 842 843 844 845 846 847 848 849 850 |
} module_init(pcipcwd_init_module); module_exit(pcipcwd_cleanup_module); MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS_MISCDEV(TEMP_MINOR); |