0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0032
0033 #include <linux/module.h> /* For module specific items */
0034 #include <linux/moduleparam.h> /* For new moduleparam's */
0035 #include <linux/types.h> /* For standard types (like size_t) */
0036 #include <linux/errno.h> /* For the -ENODEV/... values */
0037 #include <linux/kernel.h> /* For printk/panic/... */
0038 #include <linux/delay.h> /* For mdelay function */
0039 #include <linux/miscdevice.h> /* For struct miscdevice */
0040 #include <linux/watchdog.h> /* For the watchdog specific items */
0041 #include <linux/notifier.h> /* For notifier support */
0042 #include <linux/reboot.h> /* For reboot_notifier stuff */
0043 #include <linux/init.h> /* For __init/__exit/... */
0044 #include <linux/fs.h> /* For file operations */
0045 #include <linux/pci.h> /* For pci functions */
0046 #include <linux/ioport.h> /* For io-port access */
0047 #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
0048 #include <linux/uaccess.h> /* For copy_to_user/put_user/... */
0049 #include <linux/io.h> /* For inb/outb/... */
0050
0051
0052 #define WATCHDOG_VERSION "1.03"
0053 #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog"
0054 #define WATCHDOG_NAME "pcwd_pci"
0055 #define DRIVER_VERSION WATCHDOG_DRIVER_NAME " driver, v" WATCHDOG_VERSION
0056
0057
0058 #ifndef PCI_VENDOR_ID_QUICKLOGIC
0059 #define PCI_VENDOR_ID_QUICKLOGIC 0x11e3
0060 #endif
0061
0062 #ifndef PCI_DEVICE_ID_WATCHDOG_PCIPCWD
0063 #define PCI_DEVICE_ID_WATCHDOG_PCIPCWD 0x5030
0064 #endif
0065
0066
0067
0068
0069
0070
0071 #define WD_PCI_WTRP 0x01
0072 #define WD_PCI_HRBT 0x02
0073 #define WD_PCI_TTRP 0x04
0074 #define WD_PCI_RL2A 0x08
0075 #define WD_PCI_RL1A 0x10
0076 #define WD_PCI_R2DS 0x40
0077
0078 #define WD_PCI_RLY2 0x80
0079
0080 #define WD_PCI_WDIS 0x10
0081 #define WD_PCI_ENTP 0x20
0082 #define WD_PCI_WRSP 0x40
0083 #define WD_PCI_PCMD 0x80
0084
0085
0086
0087 #define PCI_COMMAND_TIMEOUT 150
0088
0089
0090 #define CMD_GET_STATUS 0x04
0091 #define CMD_GET_FIRMWARE_VERSION 0x08
0092 #define CMD_READ_WATCHDOG_TIMEOUT 0x18
0093 #define CMD_WRITE_WATCHDOG_TIMEOUT 0x19
0094 #define CMD_GET_CLEAR_RESET_COUNT 0x84
0095
0096
0097 static const int heartbeat_tbl[] = {
0098 5,
0099 10,
0100 30,
0101 60,
0102 300,
0103 600,
0104 1800,
0105 3600,
0106 };
0107
0108
0109 static int cards_found;
0110
0111
0112 static int temp_panic;
0113 static unsigned long is_active;
0114 static char expect_release;
0115
0116 static struct {
0117
0118 int supports_temp;
0119
0120 int boot_status;
0121
0122 unsigned long io_addr;
0123
0124 spinlock_t io_lock;
0125
0126 struct pci_dev *pdev;
0127 } pcipcwd_private;
0128
0129
0130 #define QUIET 0
0131 #define VERBOSE 1
0132 #define DEBUG 2
0133 static int debug = QUIET;
0134 module_param(debug, int, 0);
0135 MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)");
0136
0137 #define WATCHDOG_HEARTBEAT 0
0138
0139 static int heartbeat = WATCHDOG_HEARTBEAT;
0140 module_param(heartbeat, int, 0);
0141 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. "
0142 "(0<heartbeat<65536 or 0=delay-time from dip-switches, default="
0143 __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
0144
0145 static bool nowayout = WATCHDOG_NOWAYOUT;
0146 module_param(nowayout, bool, 0);
0147 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0148 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0149
0150
0151
0152
0153
0154 static int send_command(int cmd, int *msb, int *lsb)
0155 {
0156 int got_response, count;
0157
0158 if (debug >= DEBUG)
0159 pr_debug("sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n",
0160 cmd, *msb, *lsb);
0161
0162 spin_lock(&pcipcwd_private.io_lock);
0163
0164
0165
0166
0167
0168
0169 outb_p(*lsb, pcipcwd_private.io_addr + 4);
0170 outb_p(*msb, pcipcwd_private.io_addr + 5);
0171 outb_p(cmd, pcipcwd_private.io_addr + 6);
0172
0173
0174
0175
0176 got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
0177 for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response);
0178 count++) {
0179 mdelay(1);
0180 got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP;
0181 }
0182
0183 if (debug >= DEBUG) {
0184 if (got_response) {
0185 pr_debug("time to process command was: %d ms\n",
0186 count);
0187 } else {
0188 pr_debug("card did not respond on command!\n");
0189 }
0190 }
0191
0192 if (got_response) {
0193
0194 *lsb = inb_p(pcipcwd_private.io_addr + 4);
0195 *msb = inb_p(pcipcwd_private.io_addr + 5);
0196
0197
0198 inb_p(pcipcwd_private.io_addr + 6);
0199
0200 if (debug >= DEBUG)
0201 pr_debug("received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n",
0202 cmd, *msb, *lsb);
0203 }
0204
0205 spin_unlock(&pcipcwd_private.io_lock);
0206
0207 return got_response;
0208 }
0209
0210 static inline void pcipcwd_check_temperature_support(void)
0211 {
0212 if (inb_p(pcipcwd_private.io_addr) != 0xF0)
0213 pcipcwd_private.supports_temp = 1;
0214 }
0215
0216 static int pcipcwd_get_option_switches(void)
0217 {
0218 int option_switches;
0219
0220 option_switches = inb_p(pcipcwd_private.io_addr + 3);
0221 return option_switches;
0222 }
0223
0224 static void pcipcwd_show_card_info(void)
0225 {
0226 int got_fw_rev, fw_rev_major, fw_rev_minor;
0227 char fw_ver_str[20];
0228 int option_switches;
0229
0230 got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major,
0231 &fw_rev_minor);
0232 if (got_fw_rev)
0233 sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor);
0234 else
0235 sprintf(fw_ver_str, "<card no answer>");
0236
0237
0238 option_switches = pcipcwd_get_option_switches();
0239
0240 pr_info("Found card at port 0x%04x (Firmware: %s) %s temp option\n",
0241 (int) pcipcwd_private.io_addr, fw_ver_str,
0242 (pcipcwd_private.supports_temp ? "with" : "without"));
0243
0244 pr_info("Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n",
0245 option_switches,
0246 ((option_switches & 0x10) ? "ON" : "OFF"),
0247 ((option_switches & 0x08) ? "ON" : "OFF"));
0248
0249 if (pcipcwd_private.boot_status & WDIOF_CARDRESET)
0250 pr_info("Previous reset was caused by the Watchdog card\n");
0251
0252 if (pcipcwd_private.boot_status & WDIOF_OVERHEAT)
0253 pr_info("Card sensed a CPU Overheat\n");
0254
0255 if (pcipcwd_private.boot_status == 0)
0256 pr_info("No previous trip detected - Cold boot or reset\n");
0257 }
0258
0259 static int pcipcwd_start(void)
0260 {
0261 int stat_reg;
0262
0263 spin_lock(&pcipcwd_private.io_lock);
0264 outb_p(0x00, pcipcwd_private.io_addr + 3);
0265 udelay(1000);
0266
0267 stat_reg = inb_p(pcipcwd_private.io_addr + 2);
0268 spin_unlock(&pcipcwd_private.io_lock);
0269
0270 if (stat_reg & WD_PCI_WDIS) {
0271 pr_err("Card timer not enabled\n");
0272 return -1;
0273 }
0274
0275 if (debug >= VERBOSE)
0276 pr_debug("Watchdog started\n");
0277
0278 return 0;
0279 }
0280
0281 static int pcipcwd_stop(void)
0282 {
0283 int stat_reg;
0284
0285 spin_lock(&pcipcwd_private.io_lock);
0286 outb_p(0xA5, pcipcwd_private.io_addr + 3);
0287 udelay(1000);
0288
0289 outb_p(0xA5, pcipcwd_private.io_addr + 3);
0290 udelay(1000);
0291
0292 stat_reg = inb_p(pcipcwd_private.io_addr + 2);
0293 spin_unlock(&pcipcwd_private.io_lock);
0294
0295 if (!(stat_reg & WD_PCI_WDIS)) {
0296 pr_err("Card did not acknowledge disable attempt\n");
0297 return -1;
0298 }
0299
0300 if (debug >= VERBOSE)
0301 pr_debug("Watchdog stopped\n");
0302
0303 return 0;
0304 }
0305
0306 static int pcipcwd_keepalive(void)
0307 {
0308
0309 spin_lock(&pcipcwd_private.io_lock);
0310 outb_p(0x42, pcipcwd_private.io_addr);
0311 spin_unlock(&pcipcwd_private.io_lock);
0312
0313 if (debug >= DEBUG)
0314 pr_debug("Watchdog keepalive signal send\n");
0315
0316 return 0;
0317 }
0318
0319 static int pcipcwd_set_heartbeat(int t)
0320 {
0321 int t_msb = t / 256;
0322 int t_lsb = t % 256;
0323
0324 if ((t < 0x0001) || (t > 0xFFFF))
0325 return -EINVAL;
0326
0327
0328 send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb);
0329
0330 heartbeat = t;
0331 if (debug >= VERBOSE)
0332 pr_debug("New heartbeat: %d\n", heartbeat);
0333
0334 return 0;
0335 }
0336
0337 static int pcipcwd_get_status(int *status)
0338 {
0339 int control_status;
0340
0341 *status = 0;
0342 control_status = inb_p(pcipcwd_private.io_addr + 1);
0343 if (control_status & WD_PCI_WTRP)
0344 *status |= WDIOF_CARDRESET;
0345 if (control_status & WD_PCI_TTRP) {
0346 *status |= WDIOF_OVERHEAT;
0347 if (temp_panic)
0348 panic(KBUILD_MODNAME ": Temperature overheat trip!\n");
0349 }
0350
0351 if (debug >= DEBUG)
0352 pr_debug("Control Status #1: 0x%02x\n", control_status);
0353
0354 return 0;
0355 }
0356
0357 static int pcipcwd_clear_status(void)
0358 {
0359 int control_status;
0360 int msb;
0361 int reset_counter;
0362
0363 if (debug >= VERBOSE)
0364 pr_info("clearing watchdog trip status & LED\n");
0365
0366 control_status = inb_p(pcipcwd_private.io_addr + 1);
0367
0368 if (debug >= DEBUG) {
0369 pr_debug("status was: 0x%02x\n", control_status);
0370 pr_debug("sending: 0x%02x\n",
0371 (control_status & WD_PCI_R2DS) | WD_PCI_WTRP);
0372 }
0373
0374
0375 outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP,
0376 pcipcwd_private.io_addr + 1);
0377
0378
0379 msb = 0;
0380 reset_counter = 0xff;
0381 send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter);
0382
0383 if (debug >= DEBUG) {
0384 pr_debug("reset count was: 0x%02x\n", reset_counter);
0385 }
0386
0387 return 0;
0388 }
0389
0390 static int pcipcwd_get_temperature(int *temperature)
0391 {
0392 *temperature = 0;
0393 if (!pcipcwd_private.supports_temp)
0394 return -ENODEV;
0395
0396 spin_lock(&pcipcwd_private.io_lock);
0397 *temperature = inb_p(pcipcwd_private.io_addr);
0398 spin_unlock(&pcipcwd_private.io_lock);
0399
0400
0401
0402
0403
0404 *temperature = (*temperature * 9 / 5) + 32;
0405
0406 if (debug >= DEBUG) {
0407 pr_debug("temperature is: %d F\n", *temperature);
0408 }
0409
0410 return 0;
0411 }
0412
0413 static int pcipcwd_get_timeleft(int *time_left)
0414 {
0415 int msb;
0416 int lsb;
0417
0418
0419
0420 send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
0421
0422 *time_left = (msb << 8) + lsb;
0423
0424 if (debug >= VERBOSE)
0425 pr_debug("Time left before next reboot: %d\n", *time_left);
0426
0427 return 0;
0428 }
0429
0430
0431
0432
0433
0434 static ssize_t pcipcwd_write(struct file *file, const char __user *data,
0435 size_t len, loff_t *ppos)
0436 {
0437
0438 if (len) {
0439 if (!nowayout) {
0440 size_t i;
0441
0442
0443
0444 expect_release = 0;
0445
0446
0447
0448 for (i = 0; i != len; i++) {
0449 char c;
0450 if (get_user(c, data + i))
0451 return -EFAULT;
0452 if (c == 'V')
0453 expect_release = 42;
0454 }
0455 }
0456
0457
0458 pcipcwd_keepalive();
0459 }
0460 return len;
0461 }
0462
0463 static long pcipcwd_ioctl(struct file *file, unsigned int cmd,
0464 unsigned long arg)
0465 {
0466 void __user *argp = (void __user *)arg;
0467 int __user *p = argp;
0468 static const struct watchdog_info ident = {
0469 .options = WDIOF_OVERHEAT |
0470 WDIOF_CARDRESET |
0471 WDIOF_KEEPALIVEPING |
0472 WDIOF_SETTIMEOUT |
0473 WDIOF_MAGICCLOSE,
0474 .firmware_version = 1,
0475 .identity = WATCHDOG_DRIVER_NAME,
0476 };
0477
0478 switch (cmd) {
0479 case WDIOC_GETSUPPORT:
0480 return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
0481
0482 case WDIOC_GETSTATUS:
0483 {
0484 int status;
0485 pcipcwd_get_status(&status);
0486 return put_user(status, p);
0487 }
0488
0489 case WDIOC_GETBOOTSTATUS:
0490 return put_user(pcipcwd_private.boot_status, p);
0491
0492 case WDIOC_GETTEMP:
0493 {
0494 int temperature;
0495
0496 if (pcipcwd_get_temperature(&temperature))
0497 return -EFAULT;
0498
0499 return put_user(temperature, p);
0500 }
0501
0502 case WDIOC_SETOPTIONS:
0503 {
0504 int new_options, retval = -EINVAL;
0505
0506 if (get_user(new_options, p))
0507 return -EFAULT;
0508
0509 if (new_options & WDIOS_DISABLECARD) {
0510 if (pcipcwd_stop())
0511 return -EIO;
0512 retval = 0;
0513 }
0514
0515 if (new_options & WDIOS_ENABLECARD) {
0516 if (pcipcwd_start())
0517 return -EIO;
0518 retval = 0;
0519 }
0520
0521 if (new_options & WDIOS_TEMPPANIC) {
0522 temp_panic = 1;
0523 retval = 0;
0524 }
0525
0526 return retval;
0527 }
0528
0529 case WDIOC_KEEPALIVE:
0530 pcipcwd_keepalive();
0531 return 0;
0532
0533 case WDIOC_SETTIMEOUT:
0534 {
0535 int new_heartbeat;
0536
0537 if (get_user(new_heartbeat, p))
0538 return -EFAULT;
0539
0540 if (pcipcwd_set_heartbeat(new_heartbeat))
0541 return -EINVAL;
0542
0543 pcipcwd_keepalive();
0544 }
0545 fallthrough;
0546
0547 case WDIOC_GETTIMEOUT:
0548 return put_user(heartbeat, p);
0549
0550 case WDIOC_GETTIMELEFT:
0551 {
0552 int time_left;
0553
0554 if (pcipcwd_get_timeleft(&time_left))
0555 return -EFAULT;
0556
0557 return put_user(time_left, p);
0558 }
0559
0560 default:
0561 return -ENOTTY;
0562 }
0563 }
0564
0565 static int pcipcwd_open(struct inode *inode, struct file *file)
0566 {
0567
0568 if (test_and_set_bit(0, &is_active)) {
0569 if (debug >= VERBOSE)
0570 pr_err("Attempt to open already opened device\n");
0571 return -EBUSY;
0572 }
0573
0574
0575 pcipcwd_start();
0576 pcipcwd_keepalive();
0577 return stream_open(inode, file);
0578 }
0579
0580 static int pcipcwd_release(struct inode *inode, struct file *file)
0581 {
0582
0583
0584
0585 if (expect_release == 42) {
0586 pcipcwd_stop();
0587 } else {
0588 pr_crit("Unexpected close, not stopping watchdog!\n");
0589 pcipcwd_keepalive();
0590 }
0591 expect_release = 0;
0592 clear_bit(0, &is_active);
0593 return 0;
0594 }
0595
0596
0597
0598
0599
0600 static ssize_t pcipcwd_temp_read(struct file *file, char __user *data,
0601 size_t len, loff_t *ppos)
0602 {
0603 int temperature;
0604
0605 if (pcipcwd_get_temperature(&temperature))
0606 return -EFAULT;
0607
0608 if (copy_to_user(data, &temperature, 1))
0609 return -EFAULT;
0610
0611 return 1;
0612 }
0613
0614 static int pcipcwd_temp_open(struct inode *inode, struct file *file)
0615 {
0616 if (!pcipcwd_private.supports_temp)
0617 return -ENODEV;
0618
0619 return stream_open(inode, file);
0620 }
0621
0622 static int pcipcwd_temp_release(struct inode *inode, struct file *file)
0623 {
0624 return 0;
0625 }
0626
0627
0628
0629
0630
0631 static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code,
0632 void *unused)
0633 {
0634 if (code == SYS_DOWN || code == SYS_HALT)
0635 pcipcwd_stop();
0636
0637 return NOTIFY_DONE;
0638 }
0639
0640
0641
0642
0643
0644 static const struct file_operations pcipcwd_fops = {
0645 .owner = THIS_MODULE,
0646 .llseek = no_llseek,
0647 .write = pcipcwd_write,
0648 .unlocked_ioctl = pcipcwd_ioctl,
0649 .compat_ioctl = compat_ptr_ioctl,
0650 .open = pcipcwd_open,
0651 .release = pcipcwd_release,
0652 };
0653
0654 static struct miscdevice pcipcwd_miscdev = {
0655 .minor = WATCHDOG_MINOR,
0656 .name = "watchdog",
0657 .fops = &pcipcwd_fops,
0658 };
0659
0660 static const struct file_operations pcipcwd_temp_fops = {
0661 .owner = THIS_MODULE,
0662 .llseek = no_llseek,
0663 .read = pcipcwd_temp_read,
0664 .open = pcipcwd_temp_open,
0665 .release = pcipcwd_temp_release,
0666 };
0667
0668 static struct miscdevice pcipcwd_temp_miscdev = {
0669 .minor = TEMP_MINOR,
0670 .name = "temperature",
0671 .fops = &pcipcwd_temp_fops,
0672 };
0673
0674 static struct notifier_block pcipcwd_notifier = {
0675 .notifier_call = pcipcwd_notify_sys,
0676 };
0677
0678
0679
0680
0681
0682 static int pcipcwd_card_init(struct pci_dev *pdev,
0683 const struct pci_device_id *ent)
0684 {
0685 int ret = -EIO;
0686
0687 cards_found++;
0688 if (cards_found == 1)
0689 pr_info("%s\n", DRIVER_VERSION);
0690
0691 if (cards_found > 1) {
0692 pr_err("This driver only supports 1 device\n");
0693 return -ENODEV;
0694 }
0695
0696 if (pci_enable_device(pdev)) {
0697 pr_err("Not possible to enable PCI Device\n");
0698 return -ENODEV;
0699 }
0700
0701 if (pci_resource_start(pdev, 0) == 0x0000) {
0702 pr_err("No I/O-Address for card detected\n");
0703 ret = -ENODEV;
0704 goto err_out_disable_device;
0705 }
0706
0707 spin_lock_init(&pcipcwd_private.io_lock);
0708 pcipcwd_private.pdev = pdev;
0709 pcipcwd_private.io_addr = pci_resource_start(pdev, 0);
0710
0711 if (pci_request_regions(pdev, WATCHDOG_NAME)) {
0712 pr_err("I/O address 0x%04x already in use\n",
0713 (int) pcipcwd_private.io_addr);
0714 ret = -EIO;
0715 goto err_out_disable_device;
0716 }
0717
0718
0719 pcipcwd_get_status(&pcipcwd_private.boot_status);
0720
0721
0722 pcipcwd_clear_status();
0723
0724
0725 pcipcwd_stop();
0726
0727
0728 pcipcwd_check_temperature_support();
0729
0730
0731 pcipcwd_show_card_info();
0732
0733
0734 if (heartbeat == 0)
0735 heartbeat =
0736 heartbeat_tbl[(pcipcwd_get_option_switches() & 0x07)];
0737
0738
0739
0740 if (pcipcwd_set_heartbeat(heartbeat)) {
0741 pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT);
0742 pr_info("heartbeat value must be 0<heartbeat<65536, using %d\n",
0743 WATCHDOG_HEARTBEAT);
0744 }
0745
0746 ret = register_reboot_notifier(&pcipcwd_notifier);
0747 if (ret != 0) {
0748 pr_err("cannot register reboot notifier (err=%d)\n", ret);
0749 goto err_out_release_region;
0750 }
0751
0752 if (pcipcwd_private.supports_temp) {
0753 ret = misc_register(&pcipcwd_temp_miscdev);
0754 if (ret != 0) {
0755 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0756 TEMP_MINOR, ret);
0757 goto err_out_unregister_reboot;
0758 }
0759 }
0760
0761 ret = misc_register(&pcipcwd_miscdev);
0762 if (ret != 0) {
0763 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
0764 WATCHDOG_MINOR, ret);
0765 goto err_out_misc_deregister;
0766 }
0767
0768 pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n",
0769 heartbeat, nowayout);
0770
0771 return 0;
0772
0773 err_out_misc_deregister:
0774 if (pcipcwd_private.supports_temp)
0775 misc_deregister(&pcipcwd_temp_miscdev);
0776 err_out_unregister_reboot:
0777 unregister_reboot_notifier(&pcipcwd_notifier);
0778 err_out_release_region:
0779 pci_release_regions(pdev);
0780 err_out_disable_device:
0781 pci_disable_device(pdev);
0782 return ret;
0783 }
0784
0785 static void pcipcwd_card_exit(struct pci_dev *pdev)
0786 {
0787
0788 if (!nowayout)
0789 pcipcwd_stop();
0790
0791
0792 misc_deregister(&pcipcwd_miscdev);
0793 if (pcipcwd_private.supports_temp)
0794 misc_deregister(&pcipcwd_temp_miscdev);
0795 unregister_reboot_notifier(&pcipcwd_notifier);
0796 pci_release_regions(pdev);
0797 pci_disable_device(pdev);
0798 cards_found--;
0799 }
0800
0801 static const struct pci_device_id pcipcwd_pci_tbl[] = {
0802 { PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD,
0803 PCI_ANY_ID, PCI_ANY_ID, },
0804 { 0 },
0805 };
0806 MODULE_DEVICE_TABLE(pci, pcipcwd_pci_tbl);
0807
0808 static struct pci_driver pcipcwd_driver = {
0809 .name = WATCHDOG_NAME,
0810 .id_table = pcipcwd_pci_tbl,
0811 .probe = pcipcwd_card_init,
0812 .remove = pcipcwd_card_exit,
0813 };
0814
0815 module_pci_driver(pcipcwd_driver);
0816
0817 MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
0818 MODULE_DESCRIPTION("Berkshire PCI-PC Watchdog driver");
0819 MODULE_LICENSE("GPL");