aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/watchdog/pcwd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/watchdog/pcwd.c')
-rw-r--r--drivers/char/watchdog/pcwd.c242
1 files changed, 120 insertions, 122 deletions
diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c
index 6e8b5705b5b7..7b41434fac8c 100644
--- a/drivers/char/watchdog/pcwd.c
+++ b/drivers/char/watchdog/pcwd.c
@@ -59,10 +59,10 @@
59#include <linux/jiffies.h> /* For jiffies stuff */ 59#include <linux/jiffies.h> /* For jiffies stuff */
60#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ 60#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
61#include <linux/watchdog.h> /* For the watchdog specific items */ 61#include <linux/watchdog.h> /* For the watchdog specific items */
62#include <linux/notifier.h> /* For notifier support */ 62#include <linux/reboot.h> /* For kernel_power_off() */
63#include <linux/reboot.h> /* For reboot_notifier stuff */
64#include <linux/init.h> /* For __init/__exit/... */ 63#include <linux/init.h> /* For __init/__exit/... */
65#include <linux/fs.h> /* For file operations */ 64#include <linux/fs.h> /* For file operations */
65#include <linux/isa.h> /* For isa devices */
66#include <linux/ioport.h> /* For io-port access */ 66#include <linux/ioport.h> /* For io-port access */
67#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ 67#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
68 68
@@ -70,8 +70,8 @@
70#include <asm/io.h> /* For inb/outb/... */ 70#include <asm/io.h> /* For inb/outb/... */
71 71
72/* Module and version information */ 72/* Module and version information */
73#define WATCHDOG_VERSION "1.18" 73#define WATCHDOG_VERSION "1.20"
74#define WATCHDOG_DATE "21 Jan 2007" 74#define WATCHDOG_DATE "18 Feb 2007"
75#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" 75#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
76#define WATCHDOG_NAME "pcwd" 76#define WATCHDOG_NAME "pcwd"
77#define PFX WATCHDOG_NAME ": " 77#define PFX WATCHDOG_NAME ": "
@@ -89,6 +89,15 @@
89#define PCWD_REVISION_C 2 89#define PCWD_REVISION_C 2
90 90
91/* 91/*
92 * These are the auto-probe addresses available.
93 *
94 * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350.
95 * Revision A has an address range of 2 addresses, while Revision C has 4.
96 */
97#define PCWD_ISA_NR_CARDS 3
98static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
99
100/*
92 * These are the defines that describe the control status bits for the 101 * These are the defines that describe the control status bits for the
93 * PCI-PC Watchdog card. 102 * PCI-PC Watchdog card.
94*/ 103*/
@@ -485,7 +494,7 @@ static int pcwd_get_status(int *status)
485 if (control_status & WD_T110) { 494 if (control_status & WD_T110) {
486 *status |= WDIOF_OVERHEAT; 495 *status |= WDIOF_OVERHEAT;
487 if (temp_panic) { 496 if (temp_panic) {
488 printk (KERN_INFO PFX "Temperature overheat trip!\n"); 497 printk(KERN_INFO PFX "Temperature overheat trip!\n");
489 kernel_power_off(); 498 kernel_power_off();
490 /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ 499 /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
491 } 500 }
@@ -497,7 +506,7 @@ static int pcwd_get_status(int *status)
497 if (control_status & WD_REVC_TTRP) { 506 if (control_status & WD_REVC_TTRP) {
498 *status |= WDIOF_OVERHEAT; 507 *status |= WDIOF_OVERHEAT;
499 if (temp_panic) { 508 if (temp_panic) {
500 printk (KERN_INFO PFX "Temperature overheat trip!\n"); 509 printk(KERN_INFO PFX "Temperature overheat trip!\n");
501 kernel_power_off(); 510 kernel_power_off();
502 /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ 511 /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
503 } 512 }
@@ -734,20 +743,6 @@ static int pcwd_temp_close(struct inode *inode, struct file *file)
734} 743}
735 744
736/* 745/*
737 * Notify system
738 */
739
740static int pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
741{
742 if (code==SYS_DOWN || code==SYS_HALT) {
743 /* Turn the WDT off */
744 pcwd_stop();
745 }
746
747 return NOTIFY_DONE;
748}
749
750/*
751 * Kernel Interfaces 746 * Kernel Interfaces
752 */ 747 */
753 748
@@ -780,10 +775,6 @@ static struct miscdevice temp_miscdev = {
780 .fops = &pcwd_temp_fops, 775 .fops = &pcwd_temp_fops,
781}; 776};
782 777
783static struct notifier_block pcwd_notifier = {
784 .notifier_call = pcwd_notify_sys,
785};
786
787/* 778/*
788 * Init & exit routines 779 * Init & exit routines
789 */ 780 */
@@ -803,10 +794,67 @@ static inline int get_revision(void)
803 return r; 794 return r;
804} 795}
805 796
806static int __devinit pcwatchdog_init(int base_addr) 797/*
798 * The ISA cards have a heartbeat bit in one of the registers, which
799 * register is card dependent. The heartbeat bit is monitored, and if
800 * found, is considered proof that a Berkshire card has been found.
801 * The initial rate is once per second at board start up, then twice
802 * per second for normal operation.
803 */
804static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
805{
806 int base_addr=pcwd_ioports[id];
807 int port0, last_port0; /* Reg 0, in case it's REV A */
808 int port1, last_port1; /* Register 1 for REV C cards */
809 int i;
810 int retval;
811
812 if (debug >= DEBUG)
813 printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n",
814 id);
815
816 if (!request_region (base_addr, 4, "PCWD")) {
817 printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
818 return 0;
819 }
820
821 retval = 0;
822
823 port0 = inb_p(base_addr); /* For REV A boards */
824 port1 = inb_p(base_addr + 1); /* For REV C boards */
825 if (port0 != 0xff || port1 != 0xff) {
826 /* Not an 'ff' from a floating bus, so must be a card! */
827 for (i = 0; i < 4; ++i) {
828
829 msleep(500);
830
831 last_port0 = port0;
832 last_port1 = port1;
833
834 port0 = inb_p(base_addr);
835 port1 = inb_p(base_addr + 1);
836
837 /* Has either hearbeat bit changed? */
838 if ((port0 ^ last_port0) & WD_HRTBT ||
839 (port1 ^ last_port1) & WD_REVC_HRBT) {
840 retval = 1;
841 break;
842 }
843 }
844 }
845 release_region (base_addr, 4);
846
847 return retval;
848}
849
850static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
807{ 851{
808 int ret; 852 int ret;
809 853
854 if (debug >= DEBUG)
855 printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n",
856 id);
857
810 cards_found++; 858 cards_found++;
811 if (cards_found == 1) 859 if (cards_found == 1)
812 printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); 860 printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER);
@@ -816,11 +864,13 @@ static int __devinit pcwatchdog_init(int base_addr)
816 return -ENODEV; 864 return -ENODEV;
817 } 865 }
818 866
819 if (base_addr == 0x0000) { 867 if (pcwd_ioports[id] == 0x0000) {
820 printk(KERN_ERR PFX "No I/O-Address for card detected\n"); 868 printk(KERN_ERR PFX "No I/O-Address for card detected\n");
821 return -ENODEV; 869 return -ENODEV;
822 } 870 }
823 pcwd_private.io_addr = base_addr; 871 pcwd_private.io_addr = pcwd_ioports[id];
872
873 spin_lock_init(&pcwd_private.io_lock);
824 874
825 /* Check card's revision */ 875 /* Check card's revision */
826 pcwd_private.revision = get_revision(); 876 pcwd_private.revision = get_revision();
@@ -828,8 +878,8 @@ static int __devinit pcwatchdog_init(int base_addr)
828 if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { 878 if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
829 printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", 879 printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
830 pcwd_private.io_addr); 880 pcwd_private.io_addr);
831 pcwd_private.io_addr = 0x0000; 881 ret=-EIO;
832 return -EIO; 882 goto error_request_region;
833 } 883 }
834 884
835 /* Initial variables */ 885 /* Initial variables */
@@ -865,24 +915,12 @@ static int __devinit pcwatchdog_init(int base_addr)
865 WATCHDOG_HEARTBEAT); 915 WATCHDOG_HEARTBEAT);
866 } 916 }
867 917
868 ret = register_reboot_notifier(&pcwd_notifier);
869 if (ret) {
870 printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
871 ret);
872 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
873 pcwd_private.io_addr = 0x0000;
874 return ret;
875 }
876
877 if (pcwd_private.supports_temp) { 918 if (pcwd_private.supports_temp) {
878 ret = misc_register(&temp_miscdev); 919 ret = misc_register(&temp_miscdev);
879 if (ret) { 920 if (ret) {
880 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", 921 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
881 TEMP_MINOR, ret); 922 TEMP_MINOR, ret);
882 unregister_reboot_notifier(&pcwd_notifier); 923 goto error_misc_register_temp;
883 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
884 pcwd_private.io_addr = 0x0000;
885 return ret;
886 } 924 }
887 } 925 }
888 926
@@ -890,22 +928,34 @@ static int __devinit pcwatchdog_init(int base_addr)
890 if (ret) { 928 if (ret) {
891 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", 929 printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
892 WATCHDOG_MINOR, ret); 930 WATCHDOG_MINOR, ret);
893 if (pcwd_private.supports_temp) 931 goto error_misc_register_watchdog;
894 misc_deregister(&temp_miscdev);
895 unregister_reboot_notifier(&pcwd_notifier);
896 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
897 pcwd_private.io_addr = 0x0000;
898 return ret;
899 } 932 }
900 933
901 printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", 934 printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
902 heartbeat, nowayout); 935 heartbeat, nowayout);
903 936
904 return 0; 937 return 0;
938
939error_misc_register_watchdog:
940 if (pcwd_private.supports_temp)
941 misc_deregister(&temp_miscdev);
942error_misc_register_temp:
943 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
944error_request_region:
945 pcwd_private.io_addr = 0x0000;
946 cards_found--;
947 return ret;
905} 948}
906 949
907static void __devexit pcwatchdog_exit(void) 950static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
908{ 951{
952 if (debug >= DEBUG)
953 printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n",
954 id);
955
956 if (!pcwd_private.io_addr)
957 return 1;
958
909 /* Disable the board */ 959 /* Disable the board */
910 if (!nowayout) 960 if (!nowayout)
911 pcwd_stop(); 961 pcwd_stop();
@@ -914,102 +964,50 @@ static void __devexit pcwatchdog_exit(void)
914 misc_deregister(&pcwd_miscdev); 964 misc_deregister(&pcwd_miscdev);
915 if (pcwd_private.supports_temp) 965 if (pcwd_private.supports_temp)
916 misc_deregister(&temp_miscdev); 966 misc_deregister(&temp_miscdev);
917 unregister_reboot_notifier(&pcwd_notifier);
918 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); 967 release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
919 pcwd_private.io_addr = 0x0000; 968 pcwd_private.io_addr = 0x0000;
920 cards_found--; 969 cards_found--;
970
971 return 0;
921} 972}
922 973
923/* 974static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
924 * The ISA cards have a heartbeat bit in one of the registers, which
925 * register is card dependent. The heartbeat bit is monitored, and if
926 * found, is considered proof that a Berkshire card has been found.
927 * The initial rate is once per second at board start up, then twice
928 * per second for normal operation.
929 */
930static int __init pcwd_checkcard(int base_addr)
931{ 975{
932 int port0, last_port0; /* Reg 0, in case it's REV A */ 976 if (debug >= DEBUG)
933 int port1, last_port1; /* Register 1 for REV C cards */ 977 printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n",
934 int i; 978 id);
935 int retval;
936
937 if (!request_region (base_addr, 4, "PCWD")) {
938 printk (KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
939 return 0;
940 }
941
942 retval = 0;
943
944 port0 = inb_p(base_addr); /* For REV A boards */
945 port1 = inb_p(base_addr + 1); /* For REV C boards */
946 if (port0 != 0xff || port1 != 0xff) {
947 /* Not an 'ff' from a floating bus, so must be a card! */
948 for (i = 0; i < 4; ++i) {
949
950 msleep(500);
951
952 last_port0 = port0;
953 last_port1 = port1;
954
955 port0 = inb_p(base_addr);
956 port1 = inb_p(base_addr + 1);
957
958 /* Has either hearbeat bit changed? */
959 if ((port0 ^ last_port0) & WD_HRTBT ||
960 (port1 ^ last_port1) & WD_REVC_HRBT) {
961 retval = 1;
962 break;
963 }
964 }
965 }
966 release_region (base_addr, 4);
967 979
968 return retval; 980 pcwd_stop();
969} 981}
970 982
971/* 983static struct isa_driver pcwd_isa_driver = {
972 * These are the auto-probe addresses available. 984 .match = pcwd_isa_match,
973 * 985 .probe = pcwd_isa_probe,
974 * Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350. 986 .remove = __devexit_p(pcwd_isa_remove),
975 * Revision A has an address range of 2 addresses, while Revision C has 4. 987 .shutdown = pcwd_isa_shutdown,
976 */ 988 .driver = {
977static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; 989 .owner = THIS_MODULE,
990 .name = WATCHDOG_NAME,
991 },
992};
978 993
979static int __init pcwd_init_module(void) 994static int __init pcwd_init_module(void)
980{ 995{
981 int i, found = 0; 996 return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS);
982
983 spin_lock_init(&pcwd_private.io_lock);
984
985 for (i = 0; pcwd_ioports[i] != 0; i++) {
986 if (pcwd_checkcard(pcwd_ioports[i])) {
987 if (!(pcwatchdog_init(pcwd_ioports[i])))
988 found++;
989 }
990 }
991
992 if (!found) {
993 printk (KERN_INFO PFX "No card detected, or port not available\n");
994 return -ENODEV;
995 }
996
997 return 0;
998} 997}
999 998
1000static void __exit pcwd_cleanup_module(void) 999static void __exit pcwd_cleanup_module(void)
1001{ 1000{
1002 if (pcwd_private.io_addr) 1001 isa_unregister_driver(&pcwd_isa_driver);
1003 pcwatchdog_exit();
1004
1005 printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); 1002 printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
1006} 1003}
1007 1004
1008module_init(pcwd_init_module); 1005module_init(pcwd_init_module);
1009module_exit(pcwd_cleanup_module); 1006module_exit(pcwd_cleanup_module);
1010 1007
1011MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>"); 1008MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, Wim Van Sebroeck <wim@iguana.be>");
1012MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); 1009MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
1010MODULE_VERSION(WATCHDOG_VERSION);
1013MODULE_LICENSE("GPL"); 1011MODULE_LICENSE("GPL");
1014MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 1012MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
1015MODULE_ALIAS_MISCDEV(TEMP_MINOR); 1013MODULE_ALIAS_MISCDEV(TEMP_MINOR);