⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pcwd.c

📁 PC watch dog driver
💻 C
📖 第 1 页 / 共 3 页
字号:
/*   *	Berkshire PC Watchdog Card Driver * *	Copyright (c) 1998-2002 Holger Eiboeck  <holger@eiboeck.de>,  *				  All Rights Reserved. * *	http://www.pcwd.de/ * *	Idea and parts of the code by Kenji Hollis <khollis@bitgate.com> * *	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. * *	$Id: pcwd.c,v 1.42 2002/07/26 08:19:00 holgi Exp root $ * *	$Log: pcwd.c,v $ *	Revision 1.42  2002/07/26 08:19:00  holgi *	Added an extra spinlock variable for io operations only (David Chambers) *	to try to prevent deadlocks. Changed io operations to use this lock (David Chambers) *	Explicitly initialize spinlock_t pcwd_lock (David Chambers) *	indeted propperly for kernel submission * *	Revision 1.41  2002/06/11 12:47:47  holgi *	fixed dumb compile error when used direct in kernel found by Martin Josefson *	made CONFIG_WATCHDOG_NOWAYOUT a module parameter *	fixed a bug in case enabling/disabling the board failed *	full IOCTL support to be more compatible with other watchdog drivers *	reorganized some spinlocks *	separated possible /dev/watchdog and /dev/temperature operations * *	Revision 1.40  2001/12/18 10:28:17  holgi *	drop kernel < 2.4 support and all wrappers *	support pci hotplug * *  Revision 1.39  2001/12/18 10:22:09  holgi *  code reformat *  fixed bug in ISA proc output; now more reliable *  fixed Makefile for better 2.4 compatibility *  fixed isa card timeouts *   *	Revision 1.38  2001/09/05 12:38:07  holgi *	corrected bug in /proc buffer calculation *	new command 'tempoffset' (Olaf Christians) * *	Revision 1.37  2001/03/19 12:03:03  holgi *	forgot to initialize pcwd_lock (Martin Josefsson) *	more spin_locks (Friedrich Lobenstock) *	restore capability to compile directly into kernel *	tested compatibility with kernel 2.2.17 and 2.4.2 * *	Revision 1.36  2001/01/23 18:30:06  holgi *	Friedrich Lobenstock <fl@fl.priv.at>: *	  code reformated to only use tabs to indent *	  new command 'debug', separate ISA & PCI timout *	  reset command has a 5 s delay *	  new commands 'clear', 'relay 1noinvert', 'relay 1nvnoinvert' */#include <linux/version.h>#include <linux/module.h>#include <linux/config.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/tty.h>#include <linux/timer.h>#include <linux/kernel.h>#include <linux/wait.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/ioport.h>#include <linux/delay.h>#include <linux/miscdevice.h>#include <linux/fs.h>#include <linux/mm.h>#include <linux/stat.h>#include <linux/smp_lock.h>#include <linux/spinlock.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/reboot.h>#include <linux/notifier.h>#include <linux/init.h>#include <linux/watchdog.h>#ifdef CONFIG_PROC_FS#include <linux/proc_fs.h>#endif#include <asm/io.h>#include <asm/uaccess.h>#include <asm/system.h>#ifndef PCI_VENDOR_ID_QUICKLOGIC#define PCI_VENDOR_ID_QUICKLOGIC    0x11e3#endif#ifndef PCI_DEVICE_ID_WATCHDOG_PCWDPCI#define PCI_DEVICE_ID_WATCHDOG_PCWDPCI 0x5030#endifstatic char rcsid[] = "$Revision: 1.42 $$Date: 2002/07/26 08:19:00 $";/* revision */#define	PCWD_REVISION_A		1#define	PCWD_REVISION_C		2#define PCWD_PCI			3#define PCWD_USB			4/* pcwd_temp_mode */#define CELSIUS				0#define FAHRENHEIT			1/* pcwd_verbose - level of verbosity */#define QUIET				0#define VERBOSE				1	/* normal */#define DEBUG				2	/* print fancy stuff too *//* card_status, pcwd_diag_mode */#define DISABLED		   	0#define ENABLED				1/* mode of operation for relay 2 */#define TEMPERATURE_TRIP	0	/* activate relay 2 during a temperature trip */#define NO_TEMPERATURE_TRIP	1	/* don't do it */#define WD_TIMEOUT			3	/* 1 1/2 seconds for a timeout *//* max. time we give an ISA watchdog card to process a command *//* 500ms for each 4 bit response */#define ISA_COMMAND_TIMEOUT	1000/* according to documentaion 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 */#define PCI_COMMAND_TIMEOUT	150MODULE_PARM(nowayout, "i");MODULE_PARM_DESC(nowayout,		 "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");#ifdef CONFIG_WATCHDOG_NOWAYOUTstatic int nowayout = 1;#elsestatic int nowayout = 0;#endifstatic int revision = PCWD_REVISION_A;static int supports_temp = 0;static int in_use = 0;static int card_status = DISABLED;static int pcwd_verbose = VERBOSE;// static int pcwd_verbose = DEBUG;static int pcwd_temp_mode = CELSIUS;static unsigned long card_ioport = 0x000;static char pcwd_driver_version[40];static char pcwd_fw_string[7];	/* firmware version xx.xxx */static int pcwd_fw_rev_major, pcwd_fw_rev_minor;static int pcwd_diag_mode = DISABLED;static unsigned int ping_counter = 0;static int initial_status, initial_status_set;static int temp_mode_relay2 = TEMPERATURE_TRIP;static int pcwd_temp_panic = DISABLED;static spinlock_t pcwd_io_lock = SPIN_LOCK_UNLOCKED;static spinlock_t pcwd_lock = SPIN_LOCK_UNLOCKED;static voidpcwd_get_version(void){	char *rcsvers, *rcsdate, *tmp;	rcsvers = strchr(rcsid, ' ');	rcsvers++;	tmp = strchr(rcsvers, ' ');	*tmp++ = '\0';	rcsdate = strchr(tmp, ' ');	rcsdate++;	tmp = strrchr(rcsdate, ' ');	*tmp = '\0';	sprintf(pcwd_driver_version, "%s (%s)", rcsvers, rcsdate);}static int __initpcwd_find_card(void){	/* Available I/O port addresses for the PC Watchdog cards. */	static int pcwd_ioports[] = { 0x270, 0x350, 0x370, 0 };	int card_dat, prev_card_dat, count = 0, i;	int rc = 0;	int num_cards = 0;	/* check for ISA cards */	for (i = 0; pcwd_ioports[i] != 0; i++) {		if (rc == 0) {			card_dat = 0x00;			if (check_region(pcwd_ioports[i], 4)) {				printk(KERN_ERR				       "pcwd: Port 0x%x unavailable.\n",				       pcwd_ioports[i]);				return 0;			}			spin_lock(&pcwd_io_lock);			prev_card_dat = inb_p(pcwd_ioports[i]);			count = 0;			if (prev_card_dat != 0xFF) {				while (count < WD_TIMEOUT) {					card_dat = inb_p(pcwd_ioports[i] + 1);					card_dat &= 0x02;					mdelay(500);					if (((card_dat == 0x02)					     && (prev_card_dat == 0x00))					    || ((card_dat == 0x00)						&& (prev_card_dat == 0x02))) {						/* heartbeat found */						card_ioport = pcwd_ioports[i];						num_cards++;						rc = 1;						break;					}					prev_card_dat = card_dat;					count++;				}			}			spin_unlock(&pcwd_io_lock);		}	}	/* check for PCI cards */	if (pci_present()) {		struct pci_dev *dev = (0);		while ((dev = pci_find_device(PCI_VENDOR_ID_QUICKLOGIC,					      PCI_DEVICE_ID_WATCHDOG_PCWDPCI,					      dev)) != (0)) {			num_cards++;			card_ioport = pci_resource_start(dev, 0);			if (pci_enable_device(dev)) {				printk(KERN_ERR				       "pcwd: failed to enable watchdog card!\n");				rc = 0;			} else {				revision = PCWD_PCI;				rc = 1;			}		}	}	if (num_cards > 1) {		printk(KERN_ERR "pcwd: more than 1 watchdog card found!\n");		rc = 0;	}	return rc;}static intpcwd_set_diag(void){	if (revision == PCWD_PCI) {	/* PCI card has no diag mode */		pcwd_diag_mode = ENABLED;	} else {		int i, found = 0, count = 0;		while ((count < 3) && (!found)) {			outb_p(0x80, card_ioport + 2);			udelay(ISA_COMMAND_TIMEOUT);			i = inb(card_ioport);			if (i == 0x00)				found = 1;			else if (i == 0xF3)				outb_p(0x00, card_ioport + 2);			udelay(ISA_COMMAND_TIMEOUT);			count++;		}		if (found) {			pcwd_diag_mode = ENABLED;		} else {			pcwd_diag_mode = DISABLED;		}	}	return pcwd_diag_mode;}static voidpcwd_diag_set_idle(void){	if (revision != PCWD_PCI) {	/* PCI card has no diag mode -> no idle */		outb_p(0x00, card_ioport + 2);		udelay(ISA_COMMAND_TIMEOUT);		(void) inb(card_ioport);		(void) inb(card_ioport);	}}/* send a command to watchdog card and give a 16 bit return value */static intsend_command_ret16(int cmd, int msb, int lsb){	static int response, count;	/* send command to card */	outb_p(lsb, card_ioport + 4);	outb_p(msb, card_ioport + 5);	outb_p(cmd, card_ioport + 6);	if (revision == PCWD_PCI) {		/* 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 		 */		for (count = 0; (count < PCI_COMMAND_TIMEOUT)		     && (!(inb(card_ioport + 2) & 0x40)); count++)			mdelay(1);		if (pcwd_verbose >= DEBUG)			printk(KERN_INFO			       "pcwd: time to process command was: %d ms\n",			       count);		/* read back response */		response = inb(card_ioport + 4) + inb(card_ioport + 5) * 256;		/* clear WRSP bit */		inb(card_ioport + 6);	} else {		/* there is no status bit like the WRSP bit, so */		/* wait max. timeout */		mdelay(ISA_COMMAND_TIMEOUT);		/* read back response */		response = inb(card_ioport + 4) + inb(card_ioport + 5) * 256;	}	return response;}/* send a command to watchdog card and give a 8 bit return value */static intsend_command_ret8(int cmd, int msb, int lsb){	return (send_command_ret16(cmd, msb, lsb) & 0x00ff);}/* send a command to watchdog card and don't care for the result */static inline voidsend_command(int cmd, int msb, int lsb){	send_command_ret16(cmd, msb, lsb);}static intsend_diag_command(int cmd){	int i;	if (revision == PCWD_PCI) {		i = send_command_ret16(cmd, 0, 0);	} else {		if ((pcwd_diag_mode != ENABLED) && (pcwd_set_diag() != ENABLED)) {			printk(KERN_ERR			       "pcwd: send_diag_command diag mode failed");			return (0);		}		outb_p(cmd, card_ioport + 2);		udelay(ISA_COMMAND_TIMEOUT);		i = inb(card_ioport);		i = inb(card_ioport);	}	return i;}static intpcwd_clear_status(void){	if ((revision == PCWD_PCI) || (revision == PCWD_REVISION_C)) {		int cdat;		if (pcwd_verbose >= VERBOSE)			printk(KERN_INFO "pcwd: clearing trip status\n");		cdat = inb_p(card_ioport + 1);		if (pcwd_verbose >= DEBUG) {			printk(KERN_DEBUG "pcwd: status was: 0x%02x\n", cdat);			printk(KERN_DEBUG "pcwd: sending: 0x%02x\n",			       (cdat & 0x40) | 0x01);		}		/* clear trip status & LED and keep mode of relay 2 */		outb_p((cdat & 0x40) | 0x01, card_ioport + 1);		/* clear reset counter */		cdat = send_command_ret8(0x84, 0, 0xff);		if (pcwd_verbose >= DEBUG) {			printk(KERN_DEBUG "pcwd: reset count was: 0x%02x\n",			       cdat);		}		/* set clear boot status */		initial_status |= 0x02;	} else {		/* FIXME: does the ISA card revision A support this? */		/* for now we suppose not */		printk(KERN_ERR "pcwd: clear does not work on revision A\n");		return 1;	}	return 0;}static intpcwd_init_status(void){	if ((revision == PCWD_PCI) || (revision == PCWD_REVISION_C)) {		if (pcwd_verbose >= DEBUG)			printk(KERN_INFO "pcwd: inititializing status\n");		/* init status clear trip status & LED */		outb_p((temp_mode_relay2) ? 0x40 : 0x00, card_ioport + 1);	} else {		/* FIXME: does a ISA card revision A support this? */		/* for now we suppose not */		return 1;	}	return 0;}static intpcwd_reset_pc(void){	if (revision == PCWD_PCI) {		int rc;		printk(KERN_INFO "pcwd: reseting PC!\n");		/* reset with a 5 second delay - 		 * user might want to umount, call sync, ... :) */		rc = send_command_ret8(0x80, 0xa5, 5);		/* FIXME: check ack from card, but it works... */		card_status = DISABLED;		return rc;	} else {		if (pcwd_set_diag() == ENABLED) {			printk(KERN_INFO "pcwd: reseting PC!\n");			send_diag_command(0x86);			if (!(inb_p(card_ioport) == 0xAA)) {				printk(KERN_ERR				       "pcwd: Card did not acknowledge reset attempt.\n");				return 0;			}			pcwd_diag_set_idle();			printk(KERN_INFO "pcwd: reseting PC ACK!\n");			card_status = DISABLED;			return 1;		}	}	return 0;}intpcwd_disable_card(void){	int stat_reg;	spin_lock(&pcwd_io_lock);	outb_p(0xA5, card_ioport + 3);	udelay(ISA_COMMAND_TIMEOUT);	outb_p(0xA5, card_ioport + 3);	udelay(ISA_COMMAND_TIMEOUT);	stat_reg = inb_p(card_ioport + 2);	spin_unlock(&pcwd_io_lock);	if (!(stat_reg & 0x10)) {		printk(KERN_ERR		       "pcwd: Card did not acknowledge disable attempt. Try again.\n");		return 0;	}	card_status = DISABLED;	return 1;}static intpcwd_enable_card(void){	int stat_reg;	spin_lock(&pcwd_io_lock);	outb_p(0x00, card_ioport + 3);	udelay(ISA_COMMAND_TIMEOUT);	stat_reg = inb_p(card_ioport + 2);	spin_unlock(&pcwd_io_lock);	if (stat_reg & 0x10) {		printk(KERN_ERR "pcwd: Card timer not enabled.\n");		return 0;	}	card_status = ENABLED;	return 1;}static intpcwd_get_temperature(void){	int dat = 0;	pcwd_diag_set_idle();	if (supports_temp) {		spin_lock(&pcwd_io_lock);		dat = inb(card_ioport);		dat = inb(card_ioport);		spin_unlock(&pcwd_io_lock);	} else {		dat = 0;	}	if (pcwd_temp_mode == FAHRENHEIT)		dat = (dat * 9 / 5) + 32;	return dat;}static voidpcwd_get_relay2_mode(void){	if (inb_p(card_ioport + 1) & 0x40)		temp_mode_relay2 = NO_TEMPERATURE_TRIP;	else		temp_mode_relay2 = TEMPERATURE_TRIP;	if (pcwd_verbose >= DEBUG)		printk(KERN_INFO "pcwd: mode of relay 2 is: %s\n",		       (temp_mode_relay2 ==			TEMPERATURE_TRIP) ? "temp" : "notemp");}static intpcwd_get_trip_status(void){	int cdat;	if (revision == PCWD_REVISION_A) {		cdat = inb_p(card_ioport);	} else {		cdat = inb_p(card_ioport + 1);	}	if (initial_status_set == 0) {		initial_status_set = 1;		/* don't store HRBT bit - we need it to store the clear status */		initial_status = cdat & (0xff - 0x02);		if (pcwd_verbose >= DEBUG)			printk(KERN_INFO "pcwd: initial status: 0x%02x\n",			       initial_status);	}	return (cdat & 0x01);}static intpcwd_get_switches(void){	int i, j;	if (revision == PCWD_PCI) {		i = inb_p(card_ioport + 3);		return i;	} else {		if (pcwd_set_diag() == ENABLED) {			j = send_diag_command(0x85);			udelay(ISA_COMMAND_TIMEOUT);			i = send_diag_command(0x85);			pcwd_diag_set_idle();			return i;		} else {			return 0;		}	}}static inline voidget_support(void){	if (inb(card_ioport) != 0xF0)		supports_temp = 1;	else		supports_temp = 0;}static inline voidget_revision(void){	if (revision != PCWD_PCI) {		if ((inb(card_ioport + 2) == 0xFF)		    || (inb(card_ioport + 3) == 0xFF))			revision = PCWD_REVISION_A;		else			revision = PCWD_REVISION_C;	}}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -