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

📄 cpwatchdog.c

📁 该文件是rt_linux
💻 C
📖 第 1 页 / 共 2 页
字号:
/* cpwatchdog.c - driver implementation for hardware watchdog * timers found on Sun Microsystems CP1400 and CP1500 boards. * * This device supports both the generic Linux watchdog  * interface and Solaris-compatible ioctls as best it is * able. * * NOTE: 	CP1400 systems appear to have a defective intr_mask * 			register on the PLD, preventing the disabling of * 			timer interrupts.  We use a timer to periodically  * 			reset 'stopped' watchdogs on affected platforms. * * TODO:	DevFS support (/dev/watchdogs/0 ... /dev/watchdogs/2) * * Copyright (c) 2000 Eric Brower (ebrower@usa.net) */#include <linux/kernel.h>#include <linux/module.h>#include <linux/version.h>#include <linux/fs.h>#include <linux/errno.h>#include <linux/major.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/ioport.h>#include <linux/timer.h>#include <asm/irq.h>#include <asm/ebus.h>#include <asm/oplib.h>#include <asm/uaccess.h>#include <asm/watchdog.h>#define WD_OBPNAME	"watchdog"#define WD_BADMODEL "SUNW,501-5336"#define WD_BTIMEOUT	(jiffies + (HZ * 1000))#define WD_BLIMIT	0xFFFF#define WD0_DEVNAME "watchdog0"#define WD1_DEVNAME "watchdog1"#define WD2_DEVNAME "watchdog2"#define WD0_MINOR	212#define WD1_MINOR	213	#define WD2_MINOR	214	/* Internal driver definitions */#define WD0_ID			0		/* Watchdog0						*/#define WD1_ID			1		/* Watchdog1						*/#define WD2_ID			2		/* Watchdog2						*/#define WD_NUMDEVS		3		/* Device contains 3 timers			*/#define WD_INTR_OFF		0		/* Interrupt disable value			*/#define WD_INTR_ON		1		/* Interrupt enable value			*/#define WD_STAT_INIT	0x01	/* Watchdog timer is initialized	*/#define WD_STAT_BSTOP	0x02	/* Watchdog timer is brokenstopped	*/#define WD_STAT_SVCD	0x04	/* Watchdog interrupt occurred		*//* Register value definitions */#define WD0_INTR_MASK	0x01	/* Watchdog device interrupt masks	*/#define WD1_INTR_MASK	0x02#define WD2_INTR_MASK	0x04#define WD_S_RUNNING	0x01	/* Watchdog device status running	*/#define WD_S_EXPIRED	0x02	/* Watchdog device status expired	*//* Sun uses Altera PLD EPF8820ATC144-4  * providing three hardware watchdogs: * * 	1) RIC - sends an interrupt when triggered * 	2) XIR - asserts XIR_B_RESET when triggered, resets CPU * 	3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board * *** Timer register block definition (struct wd_timer_regblk) * * dcntr and limit registers (halfword access):       * ------------------- * | 15 | ...| 1 | 0 | * ------------------- * |-  counter val  -| * ------------------- * dcntr - 	Current 16-bit downcounter value. * 			When downcounter reaches '0' watchdog expires. * 			Reading this register resets downcounter with 'limit' value. * limit - 	16-bit countdown value in 1/10th second increments. * 			Writing this register begins countdown with input value. * 			Reading from this register does not affect counter. * NOTES:	After watchdog reset, dcntr and limit contain '1' * * status register (byte access): * --------------------------- * | 7 | ... | 2 |  1  |  0  | * --------------+------------ * |-   UNUSED  -| EXP | RUN | * --------------------------- * status-	Bit 0 - Watchdog is running * 			Bit 1 - Watchdog has expired * *** PLD register block definition (struct wd_pld_regblk) * * intr_mask register (byte access): * --------------------------------- * | 7 | ... | 3 |  2  |  1  |  0  | * +-------------+------------------ * |-   UNUSED  -| WD3 | WD2 | WD1 | * --------------------------------- * WD3 -  1 == Interrupt disabled for watchdog 3 * WD2 -  1 == Interrupt disabled for watchdog 2 * WD1 -  1 == Interrupt disabled for watchdog 1 * * pld_status register (byte access): * UNKNOWN, MAGICAL MYSTERY REGISTER * */struct wd_timer_regblk {	volatile __u16	dcntr;		/* down counter		- hw	*/	volatile __u16	dcntr_pad;	volatile __u16	limit;		/* limit register	- hw	*/	volatile __u16	limit_pad;	volatile __u8	status;		/* status register	- b		*/	volatile __u8	status_pad;	volatile __u16	status_pad2;	volatile __u32	pad32;		/* yet more padding			*/};struct wd_pld_regblk {	volatile __u8	intr_mask;	/* interrupt mask	- b		*/	volatile __u8	intr_mask_pad;	volatile __u16	intr_mask_pad2;	volatile __u8	status;		/* device status	- b		*/	volatile __u8	status_pad;	volatile __u16	status_pad2;};struct wd_regblk {	volatile struct wd_timer_regblk		wd0_regs;	volatile struct wd_timer_regblk		wd1_regs;	volatile struct wd_timer_regblk		wd2_regs;	volatile struct wd_pld_regblk		pld_regs;};/* Individual timer structure  */struct wd_timer {	__u16			timeout;	__u8			intr_mask;	unsigned char	runstatus;	volatile struct wd_timer_regblk* regs;};/* Device structure */struct wd_device {	int				irq;	spinlock_t		lock;	unsigned char	isbaddoggie;	/* defective PLD */	unsigned char	opt_enable;	unsigned char	opt_reboot;	unsigned short	opt_timeout;	unsigned char	initialized;	struct wd_timer	watchdog[WD_NUMDEVS];	volatile struct	wd_regblk* regs;};static struct wd_device wd_dev = { 		0, SPIN_LOCK_UNLOCKED, 0, 0, 0, 0,};static struct timer_list wd_timer;static int wd0_timeout = 0;static int wd1_timeout = 0;static int wd2_timeout = 0;#ifdef MODULEEXPORT_NO_SYMBOLS;MODULE_PARM		(wd0_timeout, "i");MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");MODULE_PARM 	(wd1_timeout, "i");MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");MODULE_PARM 	(wd2_timeout, "i");MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");MODULE_AUTHOR	("Eric Brower <ebrower@usa.net>");MODULE_DESCRIPTION	("Hardware watchdog driver for Sun Microsystems CP1400/1500");MODULE_LICENSE("GPL");MODULE_SUPPORTED_DEVICE	("watchdog");#endif /* ifdef MODULE *//* Forward declarations of internal methods */#ifdef WD_DEBUGstatic void wd_dumpregs(void);#endifstatic void wd_interrupt(int irq, void *dev_id, struct pt_regs *regs);static void wd_toggleintr(struct wd_timer* pTimer, int enable);static void wd_pingtimer(struct wd_timer* pTimer);static void wd_starttimer(struct wd_timer* pTimer);static void wd_resetbrokentimer(struct wd_timer* pTimer);static void wd_stoptimer(struct wd_timer* pTimer);static void wd_brokentimer(unsigned long data);static int  wd_getstatus(struct wd_timer* pTimer);/* PLD expects words to be written in LSB format, * so we must flip all words prior to writing them to regs */static inline unsigned short flip_word(unsigned short word){	return ((word & 0xff) << 8) | ((word >> 8) & 0xff);}#define wd_writew(val, addr) 	(writew(flip_word(val), addr))#define wd_readw(addr) 			(flip_word(readw(addr)))#define wd_writeb(val, addr) 	(writeb(val, addr))#define wd_readb(addr) 			(readb(addr))/* CP1400s seem to have broken PLD implementations-- * the interrupt_mask register cannot be written, so * no timer interrupts can be masked within the PLD. */static inline int wd_isbroken(void){	/* we could test this by read/write/read/restore	 * on the interrupt mask register only if OBP	 * 'watchdog-enable?' == FALSE, but it seems 	 * ubiquitous on CP1400s	 */	char val[32];	prom_getproperty(prom_root_node, "model", val, sizeof(val));	return((!strcmp(val, WD_BADMODEL)) ? 1 : 0);}		/* Retrieve watchdog-enable? option from OBP * Returns 0 if false, 1 if true */static inline int wd_opt_enable(void){	int opt_node;	opt_node = prom_getchild(prom_root_node);	opt_node = prom_searchsiblings(opt_node, "options");	return((-1 == prom_getint(opt_node, "watchdog-enable?")) ? 0 : 1);}/* Retrieve watchdog-reboot? option from OBP * Returns 0 if false, 1 if true */static inline int wd_opt_reboot(void){	int opt_node;	opt_node = prom_getchild(prom_root_node);	opt_node = prom_searchsiblings(opt_node, "options");	return((-1 == prom_getint(opt_node, "watchdog-reboot?")) ? 0 : 1);}/* Retrieve watchdog-timeout option from OBP * Returns OBP value, or 0 if not located */static inline int wd_opt_timeout(void){	int opt_node;	char value[32];	char *p = value;	opt_node = prom_getchild(prom_root_node);	opt_node = prom_searchsiblings(opt_node, "options");	opt_node = prom_getproperty(opt_node, 								"watchdog-timeout", 								value, 								sizeof(value));	if(-1 != opt_node) {		/* atoi implementation */		for(opt_node = 0; /* nop */; p++) {			if(*p >= '0' && *p <= '9') {				opt_node = (10*opt_node)+(*p-'0');			}			else {				break;			}		}	}	return((-1 == opt_node) ? (0) : (opt_node)); }static int wd_open(struct inode *inode, struct file *f){	switch(MINOR(inode->i_rdev))	{		case WD0_MINOR:			f->private_data = &wd_dev.watchdog[WD0_ID];			break;		case WD1_MINOR:			f->private_data = &wd_dev.watchdog[WD1_ID];			break;		case WD2_MINOR:			f->private_data = &wd_dev.watchdog[WD2_ID];			break;		default:			return(-ENODEV);	}	/* Register IRQ on first open of device */	if(0 == wd_dev.initialized)	{			if (request_irq(wd_dev.irq, 						&wd_interrupt, 						SA_SHIRQ,						WD_OBPNAME,						(void *)wd_dev.regs)) {			printk("%s: Cannot register IRQ %s\n", 				WD_OBPNAME, __irq_itoa(wd_dev.irq));			return(-EBUSY);		}		wd_dev.initialized = 1;	}	MOD_INC_USE_COUNT;	return(0);}static int wd_release(struct inode *inode, struct file *file){	MOD_DEC_USE_COUNT;	return 0;}static int wd_ioctl(struct inode *inode, struct file *file, 		     unsigned int cmd, unsigned long arg){	int 	setopt 				= 0;	struct 	wd_timer* pTimer 	= (struct wd_timer*)file->private_data;	struct 	watchdog_info info 	= {		0,		0,		"Altera EPF8820ATC144-4"	};	if(NULL == pTimer) {		return(-EINVAL);	}	switch(cmd)	{		/* Generic Linux IOCTLs */		case WDIOC_GETSUPPORT:			if(copy_to_user((struct watchdog_info *)arg, 							(struct watchdog_info *)&info, 							sizeof(struct watchdog_info))) {				return(-EFAULT);			}			break;		case WDIOC_GETSTATUS:		case WDIOC_GETBOOTSTATUS:			if (put_user(0, (int *) arg))				return -EFAULT;			break;		case WDIOC_KEEPALIVE:			wd_pingtimer(pTimer);			break;		case WDIOC_SETOPTIONS:			if(copy_from_user(&setopt, (void*) arg, sizeof(unsigned int))) {				return -EFAULT;			}			if(setopt & WDIOS_DISABLECARD) {				if(wd_dev.opt_enable) {					printk(						"%s: cannot disable watchdog in ENABLED mode\n",						WD_OBPNAME);					return(-EINVAL);				}				wd_stoptimer(pTimer);			}			else if(setopt & WDIOS_ENABLECARD) {				wd_starttimer(pTimer);			}			else {				return(-EINVAL);			}				break;		/* Solaris-compatible IOCTLs */		case WIOCGSTAT:			setopt = wd_getstatus(pTimer);			if(copy_to_user((void*)arg, &setopt, sizeof(unsigned int))) {				return(-EFAULT);			}			break;		case WIOCSTART:			wd_starttimer(pTimer);			break;		case WIOCSTOP:			if(wd_dev.opt_enable) {				printk("%s: cannot disable watchdog in ENABLED mode\n",					WD_OBPNAME);				return(-EINVAL);			}			wd_stoptimer(pTimer);			break;		default:			return(-EINVAL);	}	return(0);}static ssize_t wd_write(	struct file 	*file, 							const char		*buf, 							size_t 			count, 							loff_t 			*ppos){	struct wd_timer* pTimer = (struct wd_timer*)file->private_data;	if(NULL == pTimer) {		return(-EINVAL);	}	if (ppos != &file->f_pos)

⌨️ 快捷键说明

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