X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fchar%2Fwatchdog%2Fpcwd_pci.c;h=f4872c87106393786bf1d5be663877a8bbe61fca;hb=97bf2856c6014879bd04983a3e9dfcdac1e7fe85;hp=e24694c3ece31b11e113c42c4e72aa2b30a5ec30;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c index e24694c3e..f4872c871 100644 --- a/drivers/char/watchdog/pcwd_pci.c +++ b/drivers/char/watchdog/pcwd_pci.c @@ -1,7 +1,7 @@ /* * Berkshire PCI-PC Watchdog Card Driver * - * (c) Copyright 2003 Wim Van Sebroeck . + * (c) Copyright 2003-2005 Wim Van Sebroeck . * * Based on source code of the following authors: * Ken Hollis , @@ -21,7 +21,9 @@ */ /* - * A bells and whistles driver is available from http://www.pcwd.de/ + * A bells and whistles driver is available from: + * http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/ + * * More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/ */ @@ -29,27 +31,28 @@ * Includes, defines, variables, module parameters, ... */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include +#include /* For module specific items */ +#include /* For new moduleparam's */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For mdelay function */ +#include /* For MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR) */ +#include /* For the watchdog specific items */ +#include /* For notifier support */ +#include /* For reboot_notifier stuff */ +#include /* For __init/__exit/... */ +#include /* For file operations */ +#include /* For pci functions */ +#include /* For io-port access */ +#include /* For spin_lock/spin_unlock/... */ + +#include /* For copy_to_user/put_user/... */ +#include /* For inb/outb/... */ /* Module and version information */ -#define WATCHDOG_VERSION "1.00" -#define WATCHDOG_DATE "13/03/2004" +#define WATCHDOG_VERSION "1.02" +#define WATCHDOG_DATE "03 Sep 2005" #define WATCHDOG_DRIVER_NAME "PCI-PC Watchdog" #define WATCHDOG_NAME "pcwd_pci" #define PFX WATCHDOG_NAME ": " @@ -68,19 +71,30 @@ * These are the defines that describe the control status bits for the * PCI-PC Watchdog card. */ -#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ -#define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ -#define WD_PCI_TTRP 0x04 /* Temperature Trip status */ +/* Port 1 : Control Status #1 */ +#define WD_PCI_WTRP 0x01 /* Watchdog Trip status */ +#define WD_PCI_HRBT 0x02 /* Watchdog Heartbeat */ +#define WD_PCI_TTRP 0x04 /* Temperature Trip status */ +#define WD_PCI_RL2A 0x08 /* Relay 2 Active */ +#define WD_PCI_RL1A 0x10 /* Relay 1 Active */ +#define WD_PCI_R2DS 0x40 /* Relay 2 Disable Temperature-trip/reset */ +#define WD_PCI_RLY2 0x80 /* Activate Relay 2 on the board */ +/* Port 2 : Control Status #2 */ +#define WD_PCI_WDIS 0x10 /* Watchdog Disable */ +#define WD_PCI_ENTP 0x20 /* Enable Temperature Trip Reset */ +#define WD_PCI_WRSP 0x40 /* Watchdog wrote response */ +#define WD_PCI_PCMD 0x80 /* PC has sent command */ /* according to documentation max. time to process a command for the pci - watchdog card is 100 ms, so we give it 150 ms to do it's job */ + * watchdog card is 100 ms, so we give it 150 ms to do it's job */ #define PCI_COMMAND_TIMEOUT 150 /* Watchdog's internal commands */ -#define CMD_GET_STATUS 0x04 -#define CMD_GET_FIRMWARE_VERSION 0x08 -#define CMD_READ_WATCHDOG_TIMEOUT 0x18 -#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 +#define CMD_GET_STATUS 0x04 +#define CMD_GET_FIRMWARE_VERSION 0x08 +#define CMD_READ_WATCHDOG_TIMEOUT 0x18 +#define CMD_WRITE_WATCHDOG_TIMEOUT 0x19 +#define CMD_GET_CLEAR_RESET_COUNT 0x84 /* We can only use 1 card due to the /dev/watchdog restriction */ static int cards_found; @@ -89,26 +103,28 @@ static int cards_found; static int temp_panic; static unsigned long is_active; static char expect_release; -static struct { - int supports_temp; /* Wether or not the card has a temperature device */ - int boot_status; /* The card's boot status */ - unsigned long io_addr; /* The cards I/O address */ - spinlock_t io_lock; - struct pci_dev *pdev; +static struct { /* this is private data for each PCI-PC watchdog card */ + int supports_temp; /* Wether or not the card has a temperature device */ + int boot_status; /* The card's boot status */ + unsigned long io_addr; /* The cards I/O address */ + spinlock_t io_lock; /* the lock for io operations */ + struct pci_dev *pdev; /* the PCI-device */ } pcipcwd_private; /* module parameters */ +#define QUIET 0 /* Default */ +#define VERBOSE 1 /* Verbose */ +#define DEBUG 2 /* print fancy stuff too */ +static int debug = QUIET; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level: 0=Quiet, 1=Verbose, 2=Debug (default=0)"); + #define WATCHDOG_HEARTBEAT 2 /* 2 sec default heartbeat */ static int heartbeat = WATCHDOG_HEARTBEAT; module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (0= DEBUG) + printk(KERN_DEBUG PFX "sending following data cmd=0x%02x msb=0x%02x lsb=0x%02x\n", + cmd, *msb, *lsb); + spin_lock(&pcipcwd_private.io_lock); /* If a command requires data it should be written first. * Data for commands with 8 bits of data should be written to port 4. @@ -134,10 +154,19 @@ static int send_command(int cmd, int *msb, int *lsb) /* wait till the pci card processed the command, signaled by * the WRSP bit in port 2 and give it a max. timeout of * PCI_COMMAND_TIMEOUT to process */ - got_response = inb_p(pcipcwd_private.io_addr + 2) & 0x40; + got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; for (count = 0; (count < PCI_COMMAND_TIMEOUT) && (!got_response); count++) { mdelay(1); - got_response = inb_p(pcipcwd_private.io_addr + 2) & 0x40; + got_response = inb_p(pcipcwd_private.io_addr + 2) & WD_PCI_WRSP; + } + + if (debug >= DEBUG) { + if (got_response) { + printk(KERN_DEBUG PFX "time to process command was: %d ms\n", + count); + } else { + printk(KERN_DEBUG PFX "card did not respond on command!\n"); + } } if (got_response) { @@ -147,12 +176,66 @@ static int send_command(int cmd, int *msb, int *lsb) /* clear WRSP bit */ inb_p(pcipcwd_private.io_addr + 6); + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "received following data for cmd=0x%02x: msb=0x%02x lsb=0x%02x\n", + cmd, *msb, *lsb); } + spin_unlock(&pcipcwd_private.io_lock); return got_response; } +static inline void pcipcwd_check_temperature_support(void) +{ + if (inb_p(pcipcwd_private.io_addr) != 0xF0) + pcipcwd_private.supports_temp = 1; +} + +static int pcipcwd_get_option_switches(void) +{ + int option_switches; + + option_switches = inb_p(pcipcwd_private.io_addr + 3); + return option_switches; +} + +static void pcipcwd_show_card_info(void) +{ + int got_fw_rev, fw_rev_major, fw_rev_minor; + char fw_ver_str[20]; /* The cards firmware version */ + int option_switches; + + got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); + if (got_fw_rev) { + sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); + } else { + sprintf(fw_ver_str, ""); + } + + /* Get switch settings */ + option_switches = pcipcwd_get_option_switches(); + + printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", + (int) pcipcwd_private.io_addr, fw_ver_str, + (pcipcwd_private.supports_temp ? "with" : "without")); + + printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", + option_switches, + ((option_switches & 0x10) ? "ON" : "OFF"), + ((option_switches & 0x08) ? "ON" : "OFF")); + + if (pcipcwd_private.boot_status & WDIOF_CARDRESET) + printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); + + if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) + printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); + + if (pcipcwd_private.boot_status == 0) + printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); +} + static int pcipcwd_start(void) { int stat_reg; @@ -164,11 +247,14 @@ static int pcipcwd_start(void) stat_reg = inb_p(pcipcwd_private.io_addr + 2); spin_unlock(&pcipcwd_private.io_lock); - if (stat_reg & 0x10) { + if (stat_reg & WD_PCI_WDIS) { printk(KERN_ERR PFX "Card timer not enabled\n"); return -1; } + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog started\n"); + return 0; } @@ -186,18 +272,25 @@ static int pcipcwd_stop(void) stat_reg = inb_p(pcipcwd_private.io_addr + 2); spin_unlock(&pcipcwd_private.io_lock); - if (!(stat_reg & 0x10)) { + if (!(stat_reg & WD_PCI_WDIS)) { printk(KERN_ERR PFX "Card did not acknowledge disable attempt\n"); return -1; } + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Watchdog stopped\n"); + return 0; } static int pcipcwd_keepalive(void) { /* Re-trigger watchdog by writing to port 0 */ - outb_p(0x42, pcipcwd_private.io_addr); + outb_p(0x42, pcipcwd_private.io_addr); /* send out any data */ + + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Watchdog keepalive signal send\n"); + return 0; } @@ -213,29 +306,64 @@ static int pcipcwd_set_heartbeat(int t) send_command(CMD_WRITE_WATCHDOG_TIMEOUT, &t_msb, &t_lsb); heartbeat = t; + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "New heartbeat: %d\n", + heartbeat); + return 0; } static int pcipcwd_get_status(int *status) { - int new_status; + int control_status; *status=0; - new_status = inb_p(pcipcwd_private.io_addr + 1); - if (new_status & WD_PCI_WTRP) + control_status = inb_p(pcipcwd_private.io_addr + 1); + if (control_status & WD_PCI_WTRP) *status |= WDIOF_CARDRESET; - if (new_status & WD_PCI_TTRP) { + if (control_status & WD_PCI_TTRP) { *status |= WDIOF_OVERHEAT; if (temp_panic) panic(PFX "Temperature overheat trip!\n"); } + if (debug >= DEBUG) + printk(KERN_DEBUG PFX "Control Status #1: 0x%02x\n", + control_status); + return 0; } static int pcipcwd_clear_status(void) { - outb_p(0x01, pcipcwd_private.io_addr + 1); + int control_status; + int msb; + int reset_counter; + + if (debug >= VERBOSE) + printk(KERN_INFO PFX "clearing watchdog trip status & LED\n"); + + control_status = inb_p(pcipcwd_private.io_addr + 1); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "status was: 0x%02x\n", control_status); + printk(KERN_DEBUG PFX "sending: 0x%02x\n", + (control_status & WD_PCI_R2DS) | WD_PCI_WTRP); + } + + /* clear trip status & LED and keep mode of relay 2 */ + outb_p((control_status & WD_PCI_R2DS) | WD_PCI_WTRP, pcipcwd_private.io_addr + 1); + + /* clear reset counter */ + msb=0; + reset_counter=0xff; + send_command(CMD_GET_CLEAR_RESET_COUNT, &msb, &reset_counter); + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "reset count was: 0x%02x\n", + reset_counter); + } + return 0; } @@ -245,11 +373,36 @@ static int pcipcwd_get_temperature(int *temperature) if (!pcipcwd_private.supports_temp) return -ENODEV; + *temperature = inb_p(pcipcwd_private.io_addr); + /* * Convert celsius to fahrenheit, since this was * the decided 'standard' for this return value. */ - *temperature = ((inb_p(pcipcwd_private.io_addr)) * 9 / 5) + 32; + *temperature = (*temperature * 9 / 5) + 32; + + if (debug >= DEBUG) { + printk(KERN_DEBUG PFX "temperature is: %d F\n", + *temperature); + } + + return 0; +} + +static int pcipcwd_get_timeleft(int *time_left) +{ + int msb; + int lsb; + + /* Read the time that's left before rebooting */ + /* Note: if the board is not yet armed then we will read 0xFFFF */ + send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb); + + *time_left = (msb << 8) + lsb; + + if (debug >= VERBOSE) + printk(KERN_DEBUG PFX "Time left before next reboot: %d\n", + *time_left); return 0; } @@ -258,13 +411,9 @@ static int pcipcwd_get_temperature(int *temperature) * /dev/watchdog handling */ -static ssize_t pcipcwd_write(struct file *file, const char *data, - size_t len, loff_t *ppos) +static ssize_t pcipcwd_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) { - /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; - /* See if we got the magic character 'V' and reload the timer */ if (len) { if (!nowayout) { @@ -293,6 +442,8 @@ static ssize_t pcipcwd_write(struct file *file, const char *data, static int pcipcwd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + void __user *argp = (void __user *)arg; + int __user *p = argp; static struct watchdog_info ident = { .options = WDIOF_OVERHEAT | WDIOF_CARDRESET | @@ -305,7 +456,7 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, switch (cmd) { case WDIOC_GETSUPPORT: - return copy_to_user((struct watchdog_info *) arg, &ident, + return copy_to_user(argp, &ident, sizeof (ident)) ? -EFAULT : 0; case WDIOC_GETSTATUS: @@ -314,11 +465,11 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, pcipcwd_get_status(&status); - return put_user(status, (int *) arg); + return put_user(status, p); } case WDIOC_GETBOOTSTATUS: - return put_user(pcipcwd_private.boot_status, (int *) arg); + return put_user(pcipcwd_private.boot_status, p); case WDIOC_GETTEMP: { @@ -327,7 +478,7 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, if (pcipcwd_get_temperature(&temperature)) return -EFAULT; - return put_user(temperature, (int *) arg); + return put_user(temperature, p); } case WDIOC_KEEPALIVE: @@ -338,16 +489,18 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, { int new_options, retval = -EINVAL; - if (get_user (new_options, (int *) arg)) + if (get_user (new_options, p)) return -EFAULT; if (new_options & WDIOS_DISABLECARD) { - pcipcwd_stop(); + if (pcipcwd_stop()) + return -EIO; retval = 0; } if (new_options & WDIOS_ENABLECARD) { - pcipcwd_start(); + if (pcipcwd_start()) + return -EIO; retval = 0; } @@ -363,7 +516,7 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, { int new_heartbeat; - if (get_user(new_heartbeat, (int *) arg)) + if (get_user(new_heartbeat, p)) return -EFAULT; if (pcipcwd_set_heartbeat(new_heartbeat)) @@ -374,23 +527,36 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file, } case WDIOC_GETTIMEOUT: - return put_user(heartbeat, (int *)arg); + return put_user(heartbeat, p); + + case WDIOC_GETTIMELEFT: + { + int time_left; + + if (pcipcwd_get_timeleft(&time_left)) + return -EFAULT; + + return put_user(time_left, p); + } default: - return -ENOIOCTLCMD; + return -ENOTTY; } } static int pcipcwd_open(struct inode *inode, struct file *file) { /* /dev/watchdog can only be opened once */ - if (test_and_set_bit(0, &is_active)) + if (test_and_set_bit(0, &is_active)) { + if (debug >= VERBOSE) + printk(KERN_ERR PFX "Attempt to open already opened device.\n"); return -EBUSY; + } /* Activate */ pcipcwd_start(); pcipcwd_keepalive(); - return 0; + return nonseekable_open(inode, file); } static int pcipcwd_release(struct inode *inode, struct file *file) @@ -404,8 +570,8 @@ static int pcipcwd_release(struct inode *inode, struct file *file) printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); pcipcwd_keepalive(); } - clear_bit(0, &is_active); expect_release = 0; + clear_bit(0, &is_active); return 0; } @@ -413,15 +579,11 @@ static int pcipcwd_release(struct inode *inode, struct file *file) * /dev/temperature handling */ -static ssize_t pcipcwd_temp_read(struct file *file, char *data, +static ssize_t pcipcwd_temp_read(struct file *file, char __user *data, size_t len, loff_t *ppos) { int temperature; - /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) - return -ESPIPE; - if (pcipcwd_get_temperature(&temperature)) return -EFAULT; @@ -436,7 +598,7 @@ static int pcipcwd_temp_open(struct inode *inode, struct file *file) if (!pcipcwd_private.supports_temp) return -ENODEV; - return 0; + return nonseekable_open(inode, file); } static int pcipcwd_temp_release(struct inode *inode, struct file *file) @@ -462,7 +624,7 @@ static int pcipcwd_notify_sys(struct notifier_block *this, unsigned long code, v * Kernel Interfaces */ -static struct file_operations pcipcwd_fops = { +static const struct file_operations pcipcwd_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .write = pcipcwd_write, @@ -477,7 +639,7 @@ static struct miscdevice pcipcwd_miscdev = { .fops = &pcipcwd_fops, }; -static struct file_operations pcipcwd_temp_fops = { +static const struct file_operations pcipcwd_temp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .read = pcipcwd_temp_read, @@ -499,19 +661,10 @@ static struct notifier_block pcipcwd_notifier = { * Init & exit routines */ -static inline void check_temperature_support(void) -{ - if (inb_p(pcipcwd_private.io_addr) != 0xF0) - pcipcwd_private.supports_temp = 1; -} - static int __devinit pcipcwd_card_init(struct pci_dev *pdev, const struct pci_device_id *ent) { int ret = -EIO; - int got_fw_rev, fw_rev_major, fw_rev_minor; - char fw_ver_str[20]; - char option_switches; cards_found++; if (cards_found == 1) @@ -553,47 +706,18 @@ static int __devinit pcipcwd_card_init(struct pci_dev *pdev, pcipcwd_stop(); /* Check whether or not the card supports the temperature device */ - check_temperature_support(); - - /* Get the Firmware Version */ - got_fw_rev = send_command(CMD_GET_FIRMWARE_VERSION, &fw_rev_major, &fw_rev_minor); - if (got_fw_rev) { - sprintf(fw_ver_str, "%u.%02u", fw_rev_major, fw_rev_minor); - } else { - sprintf(fw_ver_str, ""); - } - - /* Get switch settings */ - option_switches = inb_p(pcipcwd_private.io_addr + 3); + pcipcwd_check_temperature_support(); - printk(KERN_INFO PFX "Found card at port 0x%04x (Firmware: %s) %s temp option\n", - (int) pcipcwd_private.io_addr, fw_ver_str, - (pcipcwd_private.supports_temp ? "with" : "without")); - - printk(KERN_INFO PFX "Option switches (0x%02x): Temperature Reset Enable=%s, Power On Delay=%s\n", - option_switches, - ((option_switches & 0x10) ? "ON" : "OFF"), - ((option_switches & 0x08) ? "ON" : "OFF")); - - if (pcipcwd_private.boot_status & WDIOF_CARDRESET) - printk(KERN_INFO PFX "Previous reset was caused by the Watchdog card\n"); - - if (pcipcwd_private.boot_status & WDIOF_OVERHEAT) - printk(KERN_INFO PFX "Card sensed a CPU Overheat\n"); - - if (pcipcwd_private.boot_status == 0) - printk(KERN_INFO PFX "No previous trip detected - Cold boot or reset\n"); + /* Show info about the card itself */ + pcipcwd_show_card_info(); /* Check that the heartbeat value is within it's range ; if not reset to the default */ - if (heartbeat < 1 || heartbeat > 0xFFFF) { - heartbeat = WATCHDOG_HEARTBEAT; + if (pcipcwd_set_heartbeat(heartbeat)) { + pcipcwd_set_heartbeat(WATCHDOG_HEARTBEAT); printk(KERN_INFO PFX "heartbeat value must be 0