[WATCHDOG] pcwd.c: Port to the new device driver model

Port the pcwd.c driver to the new device driver model.
Since this is an ISA card, I used Rene Herman's new isa bus type.
This made the probing for the card very "easy".

Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
This commit is contained in:
Wim Van Sebroeck 2007-05-05 05:48:23 +00:00
parent 9f7afa6b34
commit 5aa15050a2
1 changed files with 127 additions and 129 deletions

View File

@ -59,10 +59,10 @@
#include <linux/jiffies.h> /* For jiffies stuff */ #include <linux/jiffies.h> /* For jiffies stuff */
#include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ #include <linux/miscdevice.h> /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */
#include <linux/watchdog.h> /* For the watchdog specific items */ #include <linux/watchdog.h> /* For the watchdog specific items */
#include <linux/notifier.h> /* For notifier support */ #include <linux/reboot.h> /* For kernel_power_off() */
#include <linux/reboot.h> /* For reboot_notifier stuff */
#include <linux/init.h> /* For __init/__exit/... */ #include <linux/init.h> /* For __init/__exit/... */
#include <linux/fs.h> /* For file operations */ #include <linux/fs.h> /* For file operations */
#include <linux/isa.h> /* For isa devices */
#include <linux/ioport.h> /* For io-port access */ #include <linux/ioport.h> /* For io-port access */
#include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */ #include <linux/spinlock.h> /* For spin_lock/spin_unlock/... */
@ -70,8 +70,8 @@
#include <asm/io.h> /* For inb/outb/... */ #include <asm/io.h> /* For inb/outb/... */
/* Module and version information */ /* Module and version information */
#define WATCHDOG_VERSION "1.18" #define WATCHDOG_VERSION "1.20"
#define WATCHDOG_DATE "21 Jan 2007" #define WATCHDOG_DATE "18 Feb 2007"
#define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog" #define WATCHDOG_DRIVER_NAME "ISA-PC Watchdog"
#define WATCHDOG_NAME "pcwd" #define WATCHDOG_NAME "pcwd"
#define PFX WATCHDOG_NAME ": " #define PFX WATCHDOG_NAME ": "
@ -88,6 +88,15 @@
#define PCWD_REVISION_A 1 #define PCWD_REVISION_A 1
#define PCWD_REVISION_C 2 #define PCWD_REVISION_C 2
/*
* These are the auto-probe addresses available.
*
* Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350.
* Revision A has an address range of 2 addresses, while Revision C has 4.
*/
#define PCWD_ISA_NR_CARDS 3
static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
/* /*
* These are the defines that describe the control status bits for the * These are the defines that describe the control status bits for the
* PCI-PC Watchdog card. * PCI-PC Watchdog card.
@ -485,7 +494,7 @@ static int pcwd_get_status(int *status)
if (control_status & WD_T110) { if (control_status & WD_T110) {
*status |= WDIOF_OVERHEAT; *status |= WDIOF_OVERHEAT;
if (temp_panic) { if (temp_panic) {
printk (KERN_INFO PFX "Temperature overheat trip!\n"); printk(KERN_INFO PFX "Temperature overheat trip!\n");
kernel_power_off(); kernel_power_off();
/* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
} }
@ -497,7 +506,7 @@ static int pcwd_get_status(int *status)
if (control_status & WD_REVC_TTRP) { if (control_status & WD_REVC_TTRP) {
*status |= WDIOF_OVERHEAT; *status |= WDIOF_OVERHEAT;
if (temp_panic) { if (temp_panic) {
printk (KERN_INFO PFX "Temperature overheat trip!\n"); printk(KERN_INFO PFX "Temperature overheat trip!\n");
kernel_power_off(); kernel_power_off();
/* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */ /* or should we just do a: panic(PFX "Temperature overheat trip!\n"); */
} }
@ -733,20 +742,6 @@ static int pcwd_temp_close(struct inode *inode, struct file *file)
return 0; return 0;
} }
/*
* Notify system
*/
static int pcwd_notify_sys(struct notifier_block *this, unsigned long code, void *unused)
{
if (code==SYS_DOWN || code==SYS_HALT) {
/* Turn the WDT off */
pcwd_stop();
}
return NOTIFY_DONE;
}
/* /*
* Kernel Interfaces * Kernel Interfaces
*/ */
@ -780,10 +775,6 @@ static struct miscdevice temp_miscdev = {
.fops = &pcwd_temp_fops, .fops = &pcwd_temp_fops,
}; };
static struct notifier_block pcwd_notifier = {
.notifier_call = pcwd_notify_sys,
};
/* /*
* Init & exit routines * Init & exit routines
*/ */
@ -803,10 +794,67 @@ static inline int get_revision(void)
return r; return r;
} }
static int __devinit pcwatchdog_init(int base_addr) /*
* The ISA cards have a heartbeat bit in one of the registers, which
* register is card dependent. The heartbeat bit is monitored, and if
* found, is considered proof that a Berkshire card has been found.
* The initial rate is once per second at board start up, then twice
* per second for normal operation.
*/
static int __devinit pcwd_isa_match(struct device *dev, unsigned int id)
{
int base_addr=pcwd_ioports[id];
int port0, last_port0; /* Reg 0, in case it's REV A */
int port1, last_port1; /* Register 1 for REV C cards */
int i;
int retval;
if (debug >= DEBUG)
printk(KERN_DEBUG PFX "pcwd_isa_match id=%d\n",
id);
if (!request_region (base_addr, 4, "PCWD")) {
printk(KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
return 0;
}
retval = 0;
port0 = inb_p(base_addr); /* For REV A boards */
port1 = inb_p(base_addr + 1); /* For REV C boards */
if (port0 != 0xff || port1 != 0xff) {
/* Not an 'ff' from a floating bus, so must be a card! */
for (i = 0; i < 4; ++i) {
msleep(500);
last_port0 = port0;
last_port1 = port1;
port0 = inb_p(base_addr);
port1 = inb_p(base_addr + 1);
/* Has either hearbeat bit changed? */
if ((port0 ^ last_port0) & WD_HRTBT ||
(port1 ^ last_port1) & WD_REVC_HRBT) {
retval = 1;
break;
}
}
}
release_region (base_addr, 4);
return retval;
}
static int __devinit pcwd_isa_probe(struct device *dev, unsigned int id)
{ {
int ret; int ret;
if (debug >= DEBUG)
printk(KERN_DEBUG PFX "pcwd_isa_probe id=%d\n",
id);
cards_found++; cards_found++;
if (cards_found == 1) if (cards_found == 1)
printk(KERN_INFO PFX "v%s Ken Hollis (kenji@bitgate.com)\n", WD_VER); 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)
return -ENODEV; return -ENODEV;
} }
if (base_addr == 0x0000) { if (pcwd_ioports[id] == 0x0000) {
printk(KERN_ERR PFX "No I/O-Address for card detected\n"); printk(KERN_ERR PFX "No I/O-Address for card detected\n");
return -ENODEV; return -ENODEV;
} }
pcwd_private.io_addr = base_addr; pcwd_private.io_addr = pcwd_ioports[id];
spin_lock_init(&pcwd_private.io_lock);
/* Check card's revision */ /* Check card's revision */
pcwd_private.revision = get_revision(); pcwd_private.revision = get_revision();
@ -828,8 +878,8 @@ static int __devinit pcwatchdog_init(int base_addr)
if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) { if (!request_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4, "PCWD")) {
printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", printk(KERN_ERR PFX "I/O address 0x%04x already in use\n",
pcwd_private.io_addr); pcwd_private.io_addr);
pcwd_private.io_addr = 0x0000; ret=-EIO;
return -EIO; goto error_request_region;
} }
/* Initial variables */ /* Initial variables */
@ -865,24 +915,12 @@ static int __devinit pcwatchdog_init(int base_addr)
WATCHDOG_HEARTBEAT); WATCHDOG_HEARTBEAT);
} }
ret = register_reboot_notifier(&pcwd_notifier);
if (ret) {
printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n",
ret);
release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
pcwd_private.io_addr = 0x0000;
return ret;
}
if (pcwd_private.supports_temp) { if (pcwd_private.supports_temp) {
ret = misc_register(&temp_miscdev); ret = misc_register(&temp_miscdev);
if (ret) { if (ret) {
printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
TEMP_MINOR, ret); TEMP_MINOR, ret);
unregister_reboot_notifier(&pcwd_notifier); goto error_misc_register_temp;
release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
pcwd_private.io_addr = 0x0000;
return ret;
} }
} }
@ -890,22 +928,34 @@ static int __devinit pcwatchdog_init(int base_addr)
if (ret) { if (ret) {
printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret); WATCHDOG_MINOR, ret);
if (pcwd_private.supports_temp) goto error_misc_register_watchdog;
misc_deregister(&temp_miscdev);
unregister_reboot_notifier(&pcwd_notifier);
release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
pcwd_private.io_addr = 0x0000;
return ret;
} }
printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout); heartbeat, nowayout);
return 0; return 0;
error_misc_register_watchdog:
if (pcwd_private.supports_temp)
misc_deregister(&temp_miscdev);
error_misc_register_temp:
release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
error_request_region:
pcwd_private.io_addr = 0x0000;
cards_found--;
return ret;
} }
static void __devexit pcwatchdog_exit(void) static int __devexit pcwd_isa_remove(struct device *dev, unsigned int id)
{ {
if (debug >= DEBUG)
printk(KERN_DEBUG PFX "pcwd_isa_remove id=%d\n",
id);
if (!pcwd_private.io_addr)
return 1;
/* Disable the board */ /* Disable the board */
if (!nowayout) if (!nowayout)
pcwd_stop(); pcwd_stop();
@ -914,102 +964,50 @@ static void __devexit pcwatchdog_exit(void)
misc_deregister(&pcwd_miscdev); misc_deregister(&pcwd_miscdev);
if (pcwd_private.supports_temp) if (pcwd_private.supports_temp)
misc_deregister(&temp_miscdev); misc_deregister(&temp_miscdev);
unregister_reboot_notifier(&pcwd_notifier);
release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4); release_region(pcwd_private.io_addr, (pcwd_private.revision == PCWD_REVISION_A) ? 2 : 4);
pcwd_private.io_addr = 0x0000; pcwd_private.io_addr = 0x0000;
cards_found--; cards_found--;
}
/*
* The ISA cards have a heartbeat bit in one of the registers, which
* register is card dependent. The heartbeat bit is monitored, and if
* found, is considered proof that a Berkshire card has been found.
* The initial rate is once per second at board start up, then twice
* per second for normal operation.
*/
static int __init pcwd_checkcard(int base_addr)
{
int port0, last_port0; /* Reg 0, in case it's REV A */
int port1, last_port1; /* Register 1 for REV C cards */
int i;
int retval;
if (!request_region (base_addr, 4, "PCWD")) {
printk (KERN_INFO PFX "Port 0x%04x unavailable\n", base_addr);
return 0;
}
retval = 0;
port0 = inb_p(base_addr); /* For REV A boards */
port1 = inb_p(base_addr + 1); /* For REV C boards */
if (port0 != 0xff || port1 != 0xff) {
/* Not an 'ff' from a floating bus, so must be a card! */
for (i = 0; i < 4; ++i) {
msleep(500);
last_port0 = port0;
last_port1 = port1;
port0 = inb_p(base_addr);
port1 = inb_p(base_addr + 1);
/* Has either hearbeat bit changed? */
if ((port0 ^ last_port0) & WD_HRTBT ||
(port1 ^ last_port1) & WD_REVC_HRBT) {
retval = 1;
break;
}
}
}
release_region (base_addr, 4);
return retval;
}
/*
* These are the auto-probe addresses available.
*
* Revision A only uses ports 0x270 and 0x370. Revision C introduced 0x350.
* Revision A has an address range of 2 addresses, while Revision C has 4.
*/
static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0x000 };
static int __init pcwd_init_module(void)
{
int i, found = 0;
spin_lock_init(&pcwd_private.io_lock);
for (i = 0; pcwd_ioports[i] != 0; i++) {
if (pcwd_checkcard(pcwd_ioports[i])) {
if (!(pcwatchdog_init(pcwd_ioports[i])))
found++;
}
}
if (!found) {
printk (KERN_INFO PFX "No card detected, or port not available\n");
return -ENODEV;
}
return 0; return 0;
} }
static void pcwd_isa_shutdown(struct device *dev, unsigned int id)
{
if (debug >= DEBUG)
printk(KERN_DEBUG PFX "pcwd_isa_shutdown id=%d\n",
id);
pcwd_stop();
}
static struct isa_driver pcwd_isa_driver = {
.match = pcwd_isa_match,
.probe = pcwd_isa_probe,
.remove = __devexit_p(pcwd_isa_remove),
.shutdown = pcwd_isa_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = WATCHDOG_NAME,
},
};
static int __init pcwd_init_module(void)
{
return isa_register_driver(&pcwd_isa_driver, PCWD_ISA_NR_CARDS);
}
static void __exit pcwd_cleanup_module(void) static void __exit pcwd_cleanup_module(void)
{ {
if (pcwd_private.io_addr) isa_unregister_driver(&pcwd_isa_driver);
pcwatchdog_exit();
printk(KERN_INFO PFX "Watchdog Module Unloaded.\n"); printk(KERN_INFO PFX "Watchdog Module Unloaded.\n");
} }
module_init(pcwd_init_module); module_init(pcwd_init_module);
module_exit(pcwd_cleanup_module); module_exit(pcwd_cleanup_module);
MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>"); MODULE_AUTHOR("Ken Hollis <kenji@bitgate.com>, Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver"); MODULE_DESCRIPTION("Berkshire ISA-PC Watchdog driver");
MODULE_VERSION(WATCHDOG_VERSION);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS_MISCDEV(TEMP_MINOR); MODULE_ALIAS_MISCDEV(TEMP_MINOR);