mv64x60_wdt.c

来自「linux 内核源代码」· C语言 代码 · 共 327 行

C
327
字号
/* * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface * * Author: James Chapman <jchapman@katalix.com> * * Platform-specific setup code should configure the dog to generate * interrupt or reset as required.  This code only enables/disables * and services the watchdog. * * Derived from mpc8xx_wdt.c, with the following copyright. *  * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express * or implied. */#include <linux/fs.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/miscdevice.h>#include <linux/module.h>#include <linux/watchdog.h>#include <linux/platform_device.h>#include <linux/mv643xx.h>#include <asm/uaccess.h>#include <asm/io.h>#define MV64x60_WDT_WDC_OFFSET	0/* * The watchdog configuration register contains a pair of 2-bit fields, *   1.  a reload field, bits 27-26, which triggers a reload of *       the countdown register, and *   2.  an enable field, bits 25-24, which toggles between *       enabling and disabling the watchdog timer. * Bit 31 is a read-only field which indicates whether the * watchdog timer is currently enabled. * * The low 24 bits contain the timer reload value. */#define MV64x60_WDC_ENABLE_SHIFT	24#define MV64x60_WDC_SERVICE_SHIFT	26#define MV64x60_WDC_ENABLED_SHIFT	31#define MV64x60_WDC_ENABLED_TRUE	1#define MV64x60_WDC_ENABLED_FALSE	0/* Flags bits */#define MV64x60_WDOG_FLAG_OPENED	0static unsigned long wdt_flags;static int wdt_status;static void __iomem *mv64x60_wdt_regs;static int mv64x60_wdt_timeout;static int mv64x60_wdt_count;static unsigned int bus_clk;static char expect_close;static DEFINE_SPINLOCK(mv64x60_wdt_spinlock);static int nowayout = WATCHDOG_NOWAYOUT;module_param(nowayout, int, 0);MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");static int mv64x60_wdt_toggle_wdc(int enabled_predicate, int field_shift){	u32 data;	u32 enabled;	int ret = 0;	spin_lock(&mv64x60_wdt_spinlock);	data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);	enabled = (data >> MV64x60_WDC_ENABLED_SHIFT) & 1;	/* only toggle the requested field if enabled state matches predicate */	if ((enabled ^ enabled_predicate) == 0) {		/* We write a 1, then a 2 -- to the appropriate field */		data = (1 << field_shift) | mv64x60_wdt_count;		writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);		data = (2 << field_shift) | mv64x60_wdt_count;		writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);		ret = 1;	}	spin_unlock(&mv64x60_wdt_spinlock);	return ret;}static void mv64x60_wdt_service(void){	mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,			       MV64x60_WDC_SERVICE_SHIFT);}static void mv64x60_wdt_handler_enable(void){	if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_FALSE,				   MV64x60_WDC_ENABLE_SHIFT)) {		mv64x60_wdt_service();		printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");	}}static void mv64x60_wdt_handler_disable(void){	if (mv64x60_wdt_toggle_wdc(MV64x60_WDC_ENABLED_TRUE,				   MV64x60_WDC_ENABLE_SHIFT))		printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");}static void mv64x60_wdt_set_timeout(unsigned int timeout){	/* maximum bus cycle count is 0xFFFFFFFF */	if (timeout > 0xFFFFFFFF / bus_clk)		timeout = 0xFFFFFFFF / bus_clk;	mv64x60_wdt_count = timeout * bus_clk >> 8;	mv64x60_wdt_timeout = timeout;}static int mv64x60_wdt_open(struct inode *inode, struct file *file){	if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))		return -EBUSY;	if (nowayout)		__module_get(THIS_MODULE);	mv64x60_wdt_handler_enable();	return nonseekable_open(inode, file);}static int mv64x60_wdt_release(struct inode *inode, struct file *file){	if (expect_close == 42)		mv64x60_wdt_handler_disable();	else {		printk(KERN_CRIT		       "mv64x60_wdt: unexpected close, not stopping timer!\n");		mv64x60_wdt_service();	}	expect_close = 0;	clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);	return 0;}static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,				 size_t len, loff_t * ppos){	if (len) {		if (!nowayout) {			size_t i;			expect_close = 0;			for (i = 0; i != len; i++) {				char c;				if(get_user(c, data + i))					return -EFAULT;				if (c == 'V')					expect_close = 42;			}		}		mv64x60_wdt_service();	}	return len;}static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,			     unsigned int cmd, unsigned long arg){	int timeout;	int options;	void __user *argp = (void __user *)arg;	static struct watchdog_info info = {		.options =	WDIOF_SETTIMEOUT	|				WDIOF_MAGICCLOSE	|				WDIOF_KEEPALIVEPING,		.firmware_version = 0,		.identity = "MV64x60 watchdog",	};	switch (cmd) {	case WDIOC_GETSUPPORT:		if (copy_to_user(argp, &info, sizeof(info)))			return -EFAULT;		break;	case WDIOC_GETSTATUS:	case WDIOC_GETBOOTSTATUS:		if (put_user(wdt_status, (int __user *)argp))			return -EFAULT;		wdt_status &= ~WDIOF_KEEPALIVEPING;		break;	case WDIOC_GETTEMP:		return -EOPNOTSUPP;	case WDIOC_SETOPTIONS:		if (get_user(options, (int __user *)argp))			return -EFAULT;		if (options & WDIOS_DISABLECARD)			mv64x60_wdt_handler_disable();		if (options & WDIOS_ENABLECARD)			mv64x60_wdt_handler_enable();		break;	case WDIOC_KEEPALIVE:		mv64x60_wdt_service();		wdt_status |= WDIOF_KEEPALIVEPING;		break;	case WDIOC_SETTIMEOUT:		if (get_user(timeout, (int __user *)argp))			return -EFAULT;		mv64x60_wdt_set_timeout(timeout);		/* Fall through */	case WDIOC_GETTIMEOUT:		if (put_user(mv64x60_wdt_timeout, (int __user *)argp))			return -EFAULT;		break;	default:		return -ENOTTY;	}	return 0;}static const struct file_operations mv64x60_wdt_fops = {	.owner = THIS_MODULE,	.llseek = no_llseek,	.write = mv64x60_wdt_write,	.ioctl = mv64x60_wdt_ioctl,	.open = mv64x60_wdt_open,	.release = mv64x60_wdt_release,};static struct miscdevice mv64x60_wdt_miscdev = {	.minor = WATCHDOG_MINOR,	.name = "watchdog",	.fops = &mv64x60_wdt_fops,};static int __devinit mv64x60_wdt_probe(struct platform_device *dev){	struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;	struct resource *r;	int timeout = 10;	bus_clk = 133;			/* in MHz */	if (pdata) {		timeout = pdata->timeout;		bus_clk = pdata->bus_clk;	}	/* Since bus_clk is truncated MHz, actual frequency could be	 * up to 1MHz higher.  Round up, since it's better to time out	 * too late than too soon.	 */	bus_clk++;	bus_clk *= 1000000;		/* convert to Hz */	r = platform_get_resource(dev, IORESOURCE_MEM, 0);	if (!r)		return -ENODEV;	mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);	if (mv64x60_wdt_regs == NULL)		return -ENOMEM;	mv64x60_wdt_set_timeout(timeout);	mv64x60_wdt_handler_disable();	/* in case timer was already running */	return misc_register(&mv64x60_wdt_miscdev);}static int __devexit mv64x60_wdt_remove(struct platform_device *dev){	misc_deregister(&mv64x60_wdt_miscdev);	mv64x60_wdt_handler_disable();	iounmap(mv64x60_wdt_regs);	return 0;}static struct platform_driver mv64x60_wdt_driver = {	.probe = mv64x60_wdt_probe,	.remove = __devexit_p(mv64x60_wdt_remove),	.driver = {		.owner = THIS_MODULE,		.name = MV64x60_WDT_NAME,	},};static int __init mv64x60_wdt_init(void){	printk(KERN_INFO "MV64x60 watchdog driver\n");	return platform_driver_register(&mv64x60_wdt_driver);}static void __exit mv64x60_wdt_exit(void){	platform_driver_unregister(&mv64x60_wdt_driver);}module_init(mv64x60_wdt_init);module_exit(mv64x60_wdt_exit);MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");MODULE_DESCRIPTION("MV64x60 watchdog driver");MODULE_LICENSE("GPL");MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

⌨️ 快捷键说明

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