diff options
Diffstat (limited to 'drivers/char/watchdog/pcwd.c')
-rw-r--r-- | drivers/char/watchdog/pcwd.c | 242 |
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 | ||
98 | static 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 | |||
740 | static 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 | ||
783 | static 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 | ||
806 | static 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 | */ | ||
804 | static 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 | |||
850 | static 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 | |||
939 | error_misc_register_watchdog: | ||
940 | if (pcwd_private.supports_temp) | ||
941 | misc_deregister(&temp_miscdev); | ||
942 | error_misc_register_temp: | ||
943 | release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); | ||
944 | error_request_region: | ||
945 | pcwd_private.io_addr = 0x0000; | ||
946 | cards_found--; | ||
947 | return ret; | ||
905 | } | 948 | } |
906 | 949 | ||
907 | static void __devexit pcwatchdog_exit(void) | 950 | static 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 | /* | 974 | static 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 | */ | ||
930 | static 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 | /* | 983 | static 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 = { |
977 | static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 }; | 989 | .owner = THIS_MODULE, |
990 | .name = WATCHDOG_NAME, | ||
991 | }, | ||
992 | }; | ||
978 | 993 | ||
979 | static int __init pcwd_init_module(void) | 994 | static 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 | ||
1000 | static void __exit pcwd_cleanup_module(void) | 999 | static 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 | ||
1008 | module_init(pcwd_init_module); | 1005 | module_init(pcwd_init_module); |
1009 | module_exit(pcwd_cleanup_module); | 1006 | module_exit(pcwd_cleanup_module); |
1010 | 1007 | ||
1011 | MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>"); | 1008 | MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, Wim Van Sebroeck <wim@iguana.be>"); |
1012 | MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); | 1009 | MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); |
1010 | MODULE_VERSION(WATCHDOG_VERSION); | ||
1013 | MODULE_LICENSE("GPL"); | 1011 | MODULE_LICENSE("GPL"); |
1014 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 1012 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |
1015 | MODULE_ALIAS_MISCDEV(TEMP_MINOR); | 1013 | MODULE_ALIAS_MISCDEV(TEMP_MINOR); |