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

📄 cpwatchdog.c

📁 该文件是rt_linux
💻 C
📖 第 1 页 / 共 2 页
字号:
		return -ESPIPE;	if (count) {		wd_pingtimer(pTimer);		return 1;	}	return 0;}static ssize_t wd_read(struct file * file, char * buffer,		        size_t count, loff_t *ppos){#ifdef WD_DEBUG	wd_dumpregs();	return(0);#else	return(-EINVAL);#endif /* ifdef WD_DEBUG */}static void wd_interrupt(int irq, void *dev_id, struct pt_regs *regs){	/* Only WD0 will interrupt-- others are NMI and we won't	 * see them here....	 */	spin_lock_irq(&wd_dev.lock);	if((unsigned long)wd_dev.regs == (unsigned long)dev_id)	{		wd_stoptimer(&wd_dev.watchdog[WD0_ID]);		wd_dev.watchdog[WD0_ID].runstatus |=  WD_STAT_SVCD;	}	spin_unlock_irq(&wd_dev.lock);	return;}static struct file_operations wd_fops = {	owner:		THIS_MODULE,	ioctl:		wd_ioctl,	open:		wd_open,	write:		wd_write,	read:		wd_read,	release:	wd_release,};static struct miscdevice wd0_miscdev = { WD0_MINOR, WD0_DEVNAME, &wd_fops };static struct miscdevice wd1_miscdev = { WD1_MINOR, WD1_DEVNAME, &wd_fops };static struct miscdevice wd2_miscdev = { WD2_MINOR, WD2_DEVNAME, &wd_fops };#ifdef WD_DEBUGstatic void wd_dumpregs(void){	/* Reading from downcounters initiates watchdog countdown--	 * Example is included below for illustration purposes.	 */	int i;	printk("%s: dumping register values\n", WD_OBPNAME);	for(i = WD0_ID; i < WD_NUMDEVS; ++i) {			/* printk("\t%s%i: dcntr  at 0x%lx: 0x%x\n", 			 * 	WD_OBPNAME,		 	 *	i,			 *	(unsigned long)(&wd_dev.watchdog[i].regs->dcntr), 			 *	readw(&wd_dev.watchdog[i].regs->dcntr));			 */			printk("\t%s%i: limit  at 0x%lx: 0x%x\n", 				WD_OBPNAME,				i,				(unsigned long)(&wd_dev.watchdog[i].regs->limit), 				readw(&wd_dev.watchdog[i].regs->limit));			printk("\t%s%i: status at 0x%lx: 0x%x\n", 				WD_OBPNAME,				i,				(unsigned long)(&wd_dev.watchdog[i].regs->status), 				readb(&wd_dev.watchdog[i].regs->status));			printk("\t%s%i: driver status: 0x%x\n",				WD_OBPNAME,				i,				wd_getstatus(&wd_dev.watchdog[i]));	}	printk("\tintr_mask  at 0x%lx: 0x%x\n", 		(unsigned long)(&wd_dev.regs->pld_regs.intr_mask), 		readb(&wd_dev.regs->pld_regs.intr_mask));	printk("\tpld_status at 0x%lx: 0x%x\n", 		(unsigned long)(&wd_dev.regs->pld_regs.status), 		readb(&wd_dev.regs->pld_regs.status));}#endif/* Enable or disable watchdog interrupts * Because of the CP1400 defect this should only be * called during initialzation or by wd_[start|stop]timer() * * pTimer 	- pointer to timer device, or NULL to indicate all timers  * enable	- non-zero to enable interrupts, zero to disable */static void wd_toggleintr(struct wd_timer* pTimer, int enable){	unsigned char curregs = wd_readb(&wd_dev.regs->pld_regs.intr_mask);	unsigned char setregs = 		(NULL == pTimer) ? 			(WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : 			(pTimer->intr_mask);	(WD_INTR_ON == enable) ?		(curregs &= ~setregs):		(curregs |=  setregs);	wd_writeb(curregs, &wd_dev.regs->pld_regs.intr_mask);	return;}/* Reset countdown timer with 'limit' value and continue countdown. * This will not start a stopped timer. * * pTimer	- pointer to timer device */static void wd_pingtimer(struct wd_timer* pTimer){	if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) {		wd_readb(&pTimer->regs->dcntr);	}}/* Stop a running watchdog timer-- the timer actually keeps * running, but the interrupt is masked so that no action is * taken upon expiration. * * pTimer	- pointer to timer device */static void wd_stoptimer(struct wd_timer* pTimer){	if(wd_readb(&pTimer->regs->status) & WD_S_RUNNING) {		wd_toggleintr(pTimer, WD_INTR_OFF);		if(wd_dev.isbaddoggie) {			pTimer->runstatus |= WD_STAT_BSTOP;			wd_brokentimer((unsigned long)&wd_dev);		}	}}/* Start a watchdog timer with the specified limit value * If the watchdog is running, it will be restarted with * the provided limit value. * * This function will enable interrupts on the specified * watchdog. * * pTimer	- pointer to timer device * limit	- limit (countdown) value in 1/10th seconds */static void wd_starttimer(struct wd_timer* pTimer){	if(wd_dev.isbaddoggie) {		pTimer->runstatus &= ~WD_STAT_BSTOP;	}	pTimer->runstatus &= ~WD_STAT_SVCD;	wd_writew(pTimer->timeout, &pTimer->regs->limit);	wd_toggleintr(pTimer, WD_INTR_ON);}/* Restarts timer with maximum limit value and * does not unset 'brokenstop' value. */static void wd_resetbrokentimer(struct wd_timer* pTimer){	wd_toggleintr(pTimer, WD_INTR_ON);	wd_writew(WD_BLIMIT, &pTimer->regs->limit);}/* Timer device initialization helper. * Returns 0 on success, other on failure */static int wd_inittimer(int whichdog){	struct miscdevice 				*whichmisc;	volatile struct wd_timer_regblk	*whichregs;	char 							whichident[8];	int								whichmask;	__u16							whichlimit;	switch(whichdog)	{		case WD0_ID:			whichmisc = &wd0_miscdev;			strcpy(whichident, "RIC");			whichregs = &wd_dev.regs->wd0_regs;			whichmask = WD0_INTR_MASK;			whichlimit= (0 == wd0_timeout) 	? 						(wd_dev.opt_timeout): 						(wd0_timeout);			break;		case WD1_ID:			whichmisc = &wd1_miscdev;			strcpy(whichident, "XIR");			whichregs = &wd_dev.regs->wd1_regs;			whichmask = WD1_INTR_MASK;			whichlimit= (0 == wd1_timeout) 	? 						(wd_dev.opt_timeout): 						(wd1_timeout);			break;		case WD2_ID:			whichmisc = &wd2_miscdev;			strcpy(whichident, "POR");			whichregs = &wd_dev.regs->wd2_regs;			whichmask = WD2_INTR_MASK;			whichlimit= (0 == wd2_timeout) 	? 						(wd_dev.opt_timeout): 						(wd2_timeout);			break;		default:			printk("%s: %s: invalid watchdog id: %i\n",				WD_OBPNAME, __FUNCTION__, whichdog);			return(1);	}	if(0 != misc_register(whichmisc))	{		return(1);	}	wd_dev.watchdog[whichdog].regs			= whichregs;	wd_dev.watchdog[whichdog].timeout 		= whichlimit;	wd_dev.watchdog[whichdog].intr_mask		= whichmask;	wd_dev.watchdog[whichdog].runstatus 	&= ~WD_STAT_BSTOP;	wd_dev.watchdog[whichdog].runstatus 	|= WD_STAT_INIT;	printk("%s%i: %s hardware watchdog [%01i.%i sec] %s\n", 		WD_OBPNAME, 		whichdog, 		whichident, 		wd_dev.watchdog[whichdog].timeout / 10,		wd_dev.watchdog[whichdog].timeout % 10,		(0 != wd_dev.opt_enable) ? "in ENABLED mode" : "");	return(0);}/* Timer method called to reset stopped watchdogs-- * because of the PLD bug on CP1400, we cannot mask * interrupts within the PLD so me must continually * reset the timers ad infinitum. */static void wd_brokentimer(unsigned long data){	struct wd_device* pDev = (struct wd_device*)data;	int id, tripped = 0;	/* kill a running timer instance, in case we	 * were called directly instead of by kernel timer	 */	if(timer_pending(&wd_timer)) {		del_timer(&wd_timer);	}	for(id = WD0_ID; id < WD_NUMDEVS; ++id) {		if(pDev->watchdog[id].runstatus & WD_STAT_BSTOP) {			++tripped;			wd_resetbrokentimer(&pDev->watchdog[id]);		}	}	if(tripped) {		/* there is at least one timer brokenstopped-- reschedule */		wd_timer.expires = WD_BTIMEOUT;		add_timer(&wd_timer);	}}static int wd_getstatus(struct wd_timer* pTimer){	unsigned char stat = wd_readb(&pTimer->regs->status);	unsigned char intr = wd_readb(&wd_dev.regs->pld_regs.intr_mask);	unsigned char ret  = WD_STOPPED;	/* determine STOPPED */	if(0 == stat ) { 		return(ret);	}	/* determine EXPIRED vs FREERUN vs RUNNING */	else if(WD_S_EXPIRED & stat) {		ret = WD_EXPIRED;	}	else if(WD_S_RUNNING & stat) {		if(intr & pTimer->intr_mask) {			ret = WD_FREERUN;		}		else {			/* Fudge WD_EXPIRED status for defective CP1400--			 * IF timer is running 			 * 	AND brokenstop is set 			 * 	AND an interrupt has been serviced			 * we are WD_EXPIRED.			 *			 * IF timer is running 			 * 	AND brokenstop is set 			 * 	AND no interrupt has been serviced			 * we are WD_FREERUN.			 */			if(wd_dev.isbaddoggie && (pTimer->runstatus & WD_STAT_BSTOP)) {				if(pTimer->runstatus & WD_STAT_SVCD) {					ret = WD_EXPIRED;				}				else {					/* we could as well pretend we are expired */					ret = WD_FREERUN;				}			}			else {				ret = WD_RUNNING;			}		}	}	/* determine SERVICED */	if(pTimer->runstatus & WD_STAT_SVCD) {		ret |= WD_SERVICED;	}	return(ret);}static int __init wd_init(void){	int 	id;	struct 	linux_ebus *ebus = NULL;	struct 	linux_ebus_device *edev = NULL;	for_each_ebus(ebus) {		for_each_ebusdev(edev, ebus) {			if (!strcmp(edev->prom_name, WD_OBPNAME))				goto ebus_done;		}	}ebus_done:	if(!edev) {		printk("%s: unable to locate device\n", WD_OBPNAME);		return -ENODEV;	}	wd_dev.regs = 		ioremap(edev->resource[0].start, sizeof(struct wd_regblk));	if(NULL == wd_dev.regs) {		printk("%s: unable to map registers\n", WD_OBPNAME);		return(-ENODEV);	}	/* initialize device structure from OBP parameters */	wd_dev.irq 			= edev->irqs[0];	wd_dev.opt_enable	= wd_opt_enable();	wd_dev.opt_reboot	= wd_opt_reboot();	wd_dev.opt_timeout	= wd_opt_timeout();	wd_dev.isbaddoggie	= wd_isbroken();	/* disable all interrupts unless watchdog-enabled? == true */	if(! wd_dev.opt_enable) {		wd_toggleintr(NULL, WD_INTR_OFF);	}	/* register miscellaneous devices */	for(id = WD0_ID; id < WD_NUMDEVS; ++id) {		if(0 != wd_inittimer(id)) {			printk("%s%i: unable to initialize\n", WD_OBPNAME, id);		}	}	/* warn about possible defective PLD */	if(wd_dev.isbaddoggie) {		init_timer(&wd_timer);		wd_timer.function 	= wd_brokentimer;		wd_timer.data		= (unsigned long)&wd_dev;		wd_timer.expires	= WD_BTIMEOUT;		printk("%s: PLD defect workaround enabled for model %s\n",			WD_OBPNAME, WD_BADMODEL);	}	return(0);}static void __exit wd_cleanup(void){	int id;	/* if 'watchdog-enable?' == TRUE, timers are not stopped 	 * when module is unloaded.  All brokenstopped timers will	 * also now eventually trip. 	 */	for(id = WD0_ID; id < WD_NUMDEVS; ++id) {		if(WD_S_RUNNING == wd_readb(&wd_dev.watchdog[id].regs->status)) {			if(wd_dev.opt_enable) {				printk(KERN_WARNING "%s%i: timer not stopped at release\n",					WD_OBPNAME, id);			}			else {				wd_stoptimer(&wd_dev.watchdog[id]);				if(wd_dev.watchdog[id].runstatus & WD_STAT_BSTOP) {					wd_resetbrokentimer(&wd_dev.watchdog[id]);					printk(KERN_WARNING 							"%s%i: defect workaround disabled at release, "\							"timer expires in ~%01i sec\n",							WD_OBPNAME, id, 							wd_readw(&wd_dev.watchdog[id].regs->limit) / 10);				}			}		}	}	if(wd_dev.isbaddoggie && timer_pending(&wd_timer)) {		del_timer(&wd_timer);	}	if(0 != (wd_dev.watchdog[WD0_ID].runstatus & WD_STAT_INIT)) {		misc_deregister(&wd0_miscdev);	}	if(0 != (wd_dev.watchdog[WD1_ID].runstatus & WD_STAT_INIT)) {		misc_deregister(&wd1_miscdev);	}	if(0 != (wd_dev.watchdog[WD2_ID].runstatus & WD_STAT_INIT)) {		misc_deregister(&wd2_miscdev);	}	if(0 != wd_dev.initialized) {		free_irq(wd_dev.irq, (void *)wd_dev.regs);	}	iounmap(wd_dev.regs);}module_init(wd_init);module_exit(wd_cleanup);

⌨️ 快捷键说明

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