/* * Industrial Computer Source PCI-WDT500/501 driver * * (c) Copyright 1996-1997 Alan Cox , * All Rights Reserved. * * 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 Alan Cox nor CymruNet Ltd. admit liability nor provide * warranty for any of this software. This material is provided * "AS-IS" and at no charge. * * (c) Copyright 1995 Alan Cox * * Release 0.10. * * Fixes * Dave Gregorich : Modularisation and minor bugs * Alan Cox : Added the watchdog ioctl() stuff * Alan Cox : Fixed the reboot problem (as noted by * Matt Crocker). * Alan Cox : Added wdt= boot option * Alan Cox : Cleaned up copy/user stuff * Tim Hockin : Added insmod parameters, comment cleanup * Parameterized timeout * JP Nollmann : Added support for PCI wdt501p * Alan Cox : Split ISA and PCI cards into two drivers * Jeff Garzik : PCI cleanups * Tigran Aivazian : Restructured wdtpci_init_one() to handle * failures * Joel Becker : Added WDIOC_GET/SETTIMEOUT * Zwane Mwaikambo : Magic char closing, locking changes, * cleanups * Matt Domsch : nowayout module option */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define WDT_IS_PCI #include "wd501p.h" /* We can only use 1 card due to the /dev/watchdog restriction */ static int dev_count; static unsigned long open_lock; static DEFINE_SPINLOCK(wdtpci_lock); static char expect_close; static resource_size_t io; static int irq; /* Default timeout */ #define WD_TIMO 60 /* Default heartbeat = 60 seconds */ static int heartbeat = WD_TIMO; static int wd_heartbeat; module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0> 8, WDT_COUNT0 + ctr); udelay(8); } /** * wdtpci_start: * * Start the watchdog driver. */ static int wdtpci_start(void) { unsigned long flags; spin_lock_irqsave(&wdtpci_lock, flags); /* * "pet" the watchdog, as Access says. * This resets the clock outputs. */ inb(WDT_DC); /* Disable watchdog */ udelay(8); wdtpci_ctr_mode(2, 0); /* Program CTR2 for Mode 0: Pulse on Terminal Count */ outb(0, WDT_DC); /* Enable watchdog */ udelay(8); inb(WDT_DC); /* Disable watchdog */ udelay(8); outb(0, WDT_CLOCK); /* 2.0833MHz clock */ udelay(8); inb(WDT_BUZZER); /* disable */ udelay(8); inb(WDT_OPTONOTRST); /* disable */ udelay(8); inb(WDT_OPTORST); /* disable */ udelay(8); inb(WDT_PROGOUT); /* disable */ udelay(8); wdtpci_ctr_mode(0, 3); /* Program CTR0 for Mode 3: Square Wave Generator */ wdtpci_ctr_mode(1, 2); /* Program CTR1 for Mode 2: Rate Generator */ wdtpci_ctr_mode(2, 1); /* Program CTR2 for Mode 1: Retriggerable One-Shot */ wdtpci_ctr_load(0, 20833); /* count at 100Hz */ wdtpci_ctr_load(1, wd_heartbeat);/* Heartbeat */ /* DO NOT LOAD CTR2 on PCI card! -- JPN */ outb(0, WDT_DC); /* Enable watchdog */ udelay(8); spin_unlock_irqrestore(&wdtpci_lock, flags); return 0; } /** * wdtpci_stop: * * Stop the watchdog driver. */ static int wdtpci_stop(void) { unsigned long flags; /* Turn the card off */ spin_lock_irqsave(&wdtpci_lock, flags); inb(WDT_DC); /* Disable watchdog */ udelay(8); wdtpci_ctr_load(2, 0); /* 0 length reset pulses now */ spin_unlock_irqrestore(&wdtpci_lock, flags); return 0; } /** * wdtpci_ping: * * Reload counter one with the watchdog heartbeat. We don't bother * reloading the cascade counter. */ static int wdtpci_ping(void) { unsigned long flags; spin_lock_irqsave(&wdtpci_lock, flags); /
ChangeLog:
	Started by Ingo Molnar <mingo@redhat.com>
	Update by Max Krasnyansky <maxk@qualcomm.com>

SMP IRQ affinity

/proc/irq/IRQ#/smp_affinity and /proc/irq/IRQ#/smp_affinity_list specify
which target CPUs are permitted for a given IRQ source.  It's a bitmask
(smp_affinity) or cpu list (smp_affinity_list) of allowed CPUs.  It's not
allowed to turn off all CPUs, and if an IRQ controller does not support
IRQ affinity then the value will not change from the default of all cpus.

/proc/irq/default_smp_affinity specifies default affinity mask that applies
to all non-active IRQs. Once IRQ is allocated/activated its affinity bitmask
will be set to the default mask. It can then be changed as described above.
Default mask is 0xffffffff.

Here is an example of restricting IRQ44 (eth1) to CPU0-3 then restricting
it to CPU4-7 (this is an 8-CPU SMP box):

[root@moon 44]# cd /proc/irq/44
[root@moon 44]# cat smp_affinity
ffffffff

[root@moon 44]# echo 0f > smp_affinity
[root@moon 44]# cat smp_affinity
0000000f
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
...
--- hell ping statistics ---
6029 packets transmitted, 6027 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.1/0.4 ms
[root@moon 44]# cat /proc/interrupts | grep 'CPU\|44:'
           CPU0       CPU1       CPU2       CPU3      CPU4       CPU5        CPU6       CPU7
 44:       1068       1785       1785       1783         0          0           0         0    IO-APIC-level  eth1

As can be seen from the line above IRQ44 was delivered only to the first four
processors (0-3).
Now lets restrict that IRQ to CPU(4-7).

[root@moon 44]# echo f0 > smp_affinity
[root@moon 44]# cat smp_affinity
000000f0
[root@moon 44]# ping -f h
PING hell (195.4.7.3): 56 data bytes
..
--- hell ping statistics ---
2779 packets transmitted, 2777 packets received, 0% packet loss
round-trip min/avg/max = 0.1/0.5/585.4 ms
[root@moon 44]# cat /proc/interrupts |  'CPU\|44:'
           CPU0       CPU1       CPU2       CPU3      CPU4       CPU5        CPU6       CPU7
 44:       1068       1785       1785       1783      1784       1069        1070       1069   IO-APIC-level  eth1

This time around IRQ44 was delivered only to the last four processors.
i.e counters for the CPU0-3 did not change.

Here is an example of limiting that same irq (44) to cpus 1024 to 1031:

[root@moon 44]# echo 1024-1031 > smp_affinity_list
[root@moon 44]# cat smp_affinity_list
1024-1031

Note that to do this with a bitmask would require 32 bitmasks of zero
to follow the pertinent one.
t or worse yet during the following fsck. This would suck, in fact * trust me - if it happens it does suck. */ static int wdtpci_notify_sys(struct notifier_block *this, unsigned long code, void *unused) { if (code == SYS_DOWN || code == SYS_HALT) wdtpci_stop(); return NOTIFY_DONE; } /* * Kernel Interfaces */ static const struct file_operations wdtpci_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = wdtpci_write, .unlocked_ioctl = wdtpci_ioctl, .open = wdtpci_open, .release = wdtpci_release, }; static struct miscdevice wdtpci_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wdtpci_fops, }; static const struct file_operations wdtpci_temp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = wdtpci_temp_read, .open = wdtpci_temp_open, .release = wdtpci_temp_release, }; static struct miscdevice temp_miscdev = { .minor = TEMP_MINOR, .name = "temperature", .fops = &wdtpci_temp_fops, }; /* * The WDT card needs to learn about soft shutdowns in order to * turn the timebomb registers off. */ static struct notifier_block wdtpci_notifier = { .notifier_call = wdtpci_notify_sys, }; static int wdtpci_init_one(struct pci_dev *dev, const struct pci_device_id *ent) { int ret = -EIO; dev_count++; if (dev_count > 1) { pr_err("This driver only supports one device\n"); return -ENODEV; } if (type != 500 && type != 501) { pr_err("unknown card type '%d'\n", type); return -ENODEV; } if (pci_enable_device(dev)) { pr_err("Not possible to enable PCI Device\n"); return -ENODEV; } if (pci_resource_start(dev, 2) == 0x0000) { pr_err("No I/O-Address for card detected\n"); ret = -ENODEV; goto out_pci; } if (pci_request_region(dev, 2, "wdt_pci")) { pr_err("I/O address 0x%llx already in use\n", (unsigned long long)pci_resource_start(dev, 2)); goto out_pci; } irq = dev->irq; io = pci_resource_start(dev, 2); if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED, "wdt_pci", &wdtpci_miscdev)) { pr_err("IRQ %d is not free\n", irq); goto out_reg; } pr_info("PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n", (unsigned long long)io, irq); /* Check that the heartbeat value is within its range; if not reset to the default */ if (wdtpci_set_heartbeat(heartbeat)) { wdtpci_set_heartbeat(WD_TIMO); pr_info("heartbeat value must be 0 < heartbeat < 65536, using %d\n", WD_TIMO); } ret = register_reboot_notifier(&wdtpci_notifier); if (ret) { pr_err("cannot register reboot notifier (err=%d)\n", ret); goto out_irq; } if (type == 501) { ret = misc_register(&temp_miscdev); if (ret) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", TEMP_MINOR, ret); goto out_rbt; } } ret = misc_register(&wdtpci_miscdev); if (ret) { pr_err("cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); goto out_misc; } pr_info("initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); if (type == 501) pr_info("Fan Tachometer is %s\n", tachometer ? "Enabled" : "Disabled"); ret = 0; out: return ret; out_misc: if (type == 501) misc_deregister(&temp_miscdev); out_rbt: unregister_reboot_notifier(&wdtpci_notifier); out_irq: free_irq(irq, &wdtpci_miscdev); out_reg: pci_release_region(dev, 2); out_pci: pci_disable_device(dev); goto out; } static void wdtpci_remove_one(struct pci_dev *pdev) { /* here we assume only one device will ever have * been picked up and registered by probe function */ misc_deregister(&wdtpci_miscdev); if (type == 501) misc_deregister(&temp_miscdev); unregister_reboot_notifier(&wdtpci_notifier); free_irq(irq, &wdtpci_miscdev); pci_release_region(pdev, 2); pci_disable_device(pdev); dev_count--; } static const struct pci_device_id wdtpci_pci_tbl[] = { { .vendor = PCI_VENDOR_ID_ACCESSIO, .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, { 0, }, /* terminate list */ }; MODULE_DEVICE_TABLE(pci, wdtpci_pci_tbl); static struct pci_driver wdtpci_driver = { .name = "wdt_pci", .id_table = wdtpci_pci_tbl, .probe = wdtpci_init_one, .remove = wdtpci_remove_one, }; module_pci_driver(wdtpci_driver); MODULE_AUTHOR("JP Nollmann, Alan Cox"); MODULE_DESCRIPTION("Driver for the ICS PCI-WDT500/501 watchdog cards"); MODULE_LICENSE("GPL");