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

📄 rm9k_wdt.c

📁 linux 内核源代码
💻 C
字号:
/* *  Watchdog implementation for GPI h/w found on PMC-Sierra RM9xxx *  chips. * *  Copyright (C) 2004 by Basler Vision Technologies AG *  Author: Thomas Koeller <thomas.koeller@baslerweb.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. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/platform_device.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/interrupt.h>#include <linux/fs.h>#include <linux/reboot.h>#include <linux/notifier.h>#include <linux/miscdevice.h>#include <linux/watchdog.h>#include <asm/io.h>#include <asm/atomic.h>#include <asm/processor.h>#include <asm/uaccess.h>#include <asm/system.h>#include <asm/rm9k-ocd.h>#include <rm9k_wdt.h>#define CLOCK                  125000000#define MAX_TIMEOUT_SECONDS    32#define CPCCR                  0x0080#define CPGIG1SR               0x0044#define CPGIG1ER               0x0054/* Function prototypes */static irqreturn_t wdt_gpi_irqhdl(int, void *);static void wdt_gpi_start(void);static void wdt_gpi_stop(void);static void wdt_gpi_set_timeout(unsigned int);static int wdt_gpi_open(struct inode *, struct file *);static int wdt_gpi_release(struct inode *, struct file *);static ssize_t wdt_gpi_write(struct file *, const char __user *, size_t, loff_t *);static long wdt_gpi_ioctl(struct file *, unsigned int, unsigned long);static int wdt_gpi_notify(struct notifier_block *, unsigned long, void *);static const struct resource *wdt_gpi_get_resource(struct platform_device *, const char *, unsigned int);static int __init wdt_gpi_probe(struct device *);static int __exit wdt_gpi_remove(struct device *);static const char wdt_gpi_name[] = "wdt_gpi";static atomic_t opencnt;static int expect_close;static int locked;/* These are set from device resources */static void __iomem * wd_regs;static unsigned int wd_irq, wd_ctr;/* Module arguments */static int timeout = MAX_TIMEOUT_SECONDS;module_param(timeout, int, 0444);MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");static unsigned long resetaddr = 0xbffdc200;module_param(resetaddr, ulong, 0444);MODULE_PARM_DESC(resetaddr, "Address to write to to force a reset");static unsigned long flagaddr = 0xbffdc104;module_param(flagaddr, ulong, 0444);MODULE_PARM_DESC(flagaddr, "Address to write to boot flags to");static int powercycle;module_param(powercycle, bool, 0444);MODULE_PARM_DESC(powercycle, "Cycle power if watchdog expires");static int nowayout = WATCHDOG_NOWAYOUT;module_param(nowayout, bool, 0444);MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started");/* Kernel interfaces */static const struct file_operations fops = {	.owner		= THIS_MODULE,	.open		= wdt_gpi_open,	.release	= wdt_gpi_release,	.write		= wdt_gpi_write,	.unlocked_ioctl	= wdt_gpi_ioctl,};static struct miscdevice miscdev = {	.minor		= WATCHDOG_MINOR,	.name		= wdt_gpi_name,	.fops		= &fops,};static struct notifier_block wdt_gpi_shutdown = {	.notifier_call	= wdt_gpi_notify,};/* Interrupt handler */static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt){	if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1))		return IRQ_NONE;	__raw_writel(0x1, wd_regs + 0x0008);	printk(KERN_CRIT "%s: watchdog expired - resetting system\n",		wdt_gpi_name);	*(volatile char *) flagaddr |= 0x01;	*(volatile char *) resetaddr = powercycle ? 0x01 : 0x2;	iob();	while (1)		cpu_relax();}/* Watchdog functions */static void wdt_gpi_start(void){	u32 reg;	lock_titan_regs();	reg = titan_readl(CPGIG1ER);	titan_writel(reg | (0x100 << wd_ctr), CPGIG1ER);	iob();	unlock_titan_regs();}static void wdt_gpi_stop(void){	u32 reg;	lock_titan_regs();	reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4));	titan_writel(reg, CPCCR);	reg = titan_readl(CPGIG1ER);	titan_writel(reg & ~(0x100 << wd_ctr), CPGIG1ER);	iob();	unlock_titan_regs();}static void wdt_gpi_set_timeout(unsigned int to){	u32 reg;	const u32 wdval = (to * CLOCK) & ~0x0000000f;	lock_titan_regs();	reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4));	titan_writel(reg, CPCCR);	wmb();	__raw_writel(wdval, wd_regs + 0x0000);	wmb();	titan_writel(reg | (0x2 << (wd_ctr * 4)), CPCCR);	wmb();	titan_writel(reg | (0x5 << (wd_ctr * 4)), CPCCR);	iob();	unlock_titan_regs();}/* /dev/watchdog operations */static int wdt_gpi_open(struct inode *inode, struct file *file){	int res;	if (unlikely(atomic_dec_if_positive(&opencnt) < 0))		return -EBUSY;	expect_close = 0;	if (locked) {		module_put(THIS_MODULE);		free_irq(wd_irq, &miscdev);		locked = 0;	}	res = request_irq(wd_irq, wdt_gpi_irqhdl, IRQF_SHARED | IRQF_DISABLED,			  wdt_gpi_name, &miscdev);	if (unlikely(res))		return res;	wdt_gpi_set_timeout(timeout);	wdt_gpi_start();	printk(KERN_INFO "%s: watchdog started, timeout = %u seconds\n",		wdt_gpi_name, timeout);	return nonseekable_open(inode, file);}static int wdt_gpi_release(struct inode *inode, struct file *file){	if (nowayout) {		printk(KERN_INFO "%s: no way out - watchdog left running\n",			wdt_gpi_name);		__module_get(THIS_MODULE);		locked = 1;	} else {		if (expect_close) {			wdt_gpi_stop();			free_irq(wd_irq, &miscdev);			printk(KERN_INFO "%s: watchdog stopped\n", wdt_gpi_name);		} else {			printk(KERN_CRIT "%s: unexpected close() -"				" watchdog left running\n",				wdt_gpi_name);			wdt_gpi_set_timeout(timeout);			__module_get(THIS_MODULE);			locked = 1;		}	}	atomic_inc(&opencnt);	return 0;}static ssize_twdt_gpi_write(struct file *f, const char __user *d, size_t s, loff_t *o){	char val;	wdt_gpi_set_timeout(timeout);	expect_close = (s > 0) && !get_user(val, d) && (val == 'V');	return s ? 1 : 0;}static longwdt_gpi_ioctl(struct file *f, unsigned int cmd, unsigned long arg){	long res = -ENOTTY;	const long size = _IOC_SIZE(cmd);	int stat;	void __user *argp = (void __user *)arg;	static struct watchdog_info wdinfo = {		.identity               = "RM9xxx/GPI watchdog",		.firmware_version       = 0,		.options                = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING	};	if (unlikely(_IOC_TYPE(cmd) != WATCHDOG_IOCTL_BASE))		return -ENOTTY;	if ((_IOC_DIR(cmd) & _IOC_READ)	    && !access_ok(VERIFY_WRITE, arg, size))		return -EFAULT;	if ((_IOC_DIR(cmd) & _IOC_WRITE)	    && !access_ok(VERIFY_READ, arg, size))		return -EFAULT;	expect_close = 0;	switch (cmd) {	case WDIOC_GETSUPPORT:		wdinfo.options = nowayout ?			WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING :			WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE;		res = __copy_to_user(argp, &wdinfo, size) ?  -EFAULT : size;		break;	case WDIOC_GETSTATUS:		break;	case WDIOC_GETBOOTSTATUS:		stat = (*(volatile char *) flagaddr & 0x01)			? WDIOF_CARDRESET : 0;		res = __copy_to_user(argp, &stat, size) ?			-EFAULT : size;		break;	case WDIOC_SETOPTIONS:		break;	case WDIOC_KEEPALIVE:		wdt_gpi_set_timeout(timeout);		res = size;		break;	case WDIOC_SETTIMEOUT:		{			int val;			if (unlikely(__copy_from_user(&val, argp, size))) {				res = -EFAULT;				break;			}			if (val > MAX_TIMEOUT_SECONDS)				val = MAX_TIMEOUT_SECONDS;			timeout = val;			wdt_gpi_set_timeout(val);			res = size;			printk(KERN_INFO "%s: timeout set to %u seconds\n",				wdt_gpi_name, timeout);		}		break;	case WDIOC_GETTIMEOUT:		res = __copy_to_user(argp, &timeout, size) ?			-EFAULT : size;		break;	}	return res;}/* Shutdown notifier */static intwdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused){	if (code == SYS_DOWN || code == SYS_HALT)		wdt_gpi_stop();	return NOTIFY_DONE;}/* Init & exit procedures */static const struct resource *wdt_gpi_get_resource(struct platform_device *pdv, const char *name,		      unsigned int type){	char buf[80];	if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf)		return NULL;	return platform_get_resource_byname(pdv, type, buf);}/* No hotplugging on the platform bus - use __init */static int __init wdt_gpi_probe(struct device *dev){	int res;	struct platform_device * const pdv = to_platform_device(dev);	const struct resource		* const rr = wdt_gpi_get_resource(pdv, WDT_RESOURCE_REGS,						  IORESOURCE_MEM),		* const ri = wdt_gpi_get_resource(pdv, WDT_RESOURCE_IRQ,						  IORESOURCE_IRQ),		* const rc = wdt_gpi_get_resource(pdv, WDT_RESOURCE_COUNTER,						  0);	if (unlikely(!rr || !ri || !rc))		return -ENXIO;	wd_regs = ioremap_nocache(rr->start, rr->end + 1 - rr->start);	if (unlikely(!wd_regs))		return -ENOMEM;	wd_irq = ri->start;	wd_ctr = rc->start;	res = misc_register(&miscdev);	if (res)		iounmap(wd_regs);	else		register_reboot_notifier(&wdt_gpi_shutdown);	return res;}static int __exit wdt_gpi_remove(struct device *dev){	int res;	unregister_reboot_notifier(&wdt_gpi_shutdown);	res = misc_deregister(&miscdev);	iounmap(wd_regs);	wd_regs = NULL;	return res;}/* Device driver init & exit */static struct device_driver wdt_gpi_driver = {	.name		= (char *) wdt_gpi_name,	.bus		= &platform_bus_type,	.owner		= THIS_MODULE,	.probe		= wdt_gpi_probe,	.remove		= __exit_p(wdt_gpi_remove),	.shutdown	= NULL,	.suspend	= NULL,	.resume		= NULL,};static int __init wdt_gpi_init_module(void){	atomic_set(&opencnt, 1);	if (timeout > MAX_TIMEOUT_SECONDS)		timeout = MAX_TIMEOUT_SECONDS;	return driver_register(&wdt_gpi_driver);}static void __exit wdt_gpi_cleanup_module(void){	driver_unregister(&wdt_gpi_driver);}module_init(wdt_gpi_init_module);module_exit(wdt_gpi_cleanup_module);MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");MODULE_DESCRIPTION("Basler eXcite watchdog driver for gpi devices");MODULE_VERSION("0.1");MODULE_LICENSE("GPL");MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

⌨️ 快捷键说明

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