it8712f_wdt.c

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

C
401
字号
/* *	IT8712F "Smart Guardian" Watchdog support * *	Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net> * *	Based on info and code taken from: * *	drivers/char/watchdog/scx200_wdt.c *	drivers/hwmon/it87.c *	IT8712F EC-LPC I/O Preliminary Specification 0.9.2.pdf * *	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. * *	The author(s) of this software shall not be held liable for damages *	of any nature resulting due to the use of this software. This *	software is provided AS-IS with no warranties. */#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/init.h>#include <linux/miscdevice.h>#include <linux/watchdog.h>#include <linux/notifier.h>#include <linux/reboot.h>#include <linux/fs.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <asm/uaccess.h>#include <asm/io.h>#define NAME "it8712f_wdt"MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>");MODULE_DESCRIPTION("IT8712F Watchdog Driver");MODULE_LICENSE("GPL");MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);static int margin = 60;		/* in seconds */module_param(margin, int, 0);MODULE_PARM_DESC(margin, "Watchdog margin in seconds");static int nowayout = WATCHDOG_NOWAYOUT;module_param(nowayout, int, 0);MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");static struct semaphore it8712f_wdt_sem;static unsigned expect_close;static spinlock_t io_lock;/* Dog Food address - We use the game port address */static unsigned short address;#define	REG		0x2e	/* The register to read/write */#define	VAL		0x2f	/* The value to read/write */#define	LDN		0x07	/* Register: Logical device select */#define	DEVID		0x20	/* Register: Device ID */#define	DEVREV		0x22	/* Register: Device Revision */#define ACT_REG		0x30	/* LDN Register: Activation */#define BASE_REG	0x60	/* LDN Register: Base address */#define IT8712F_DEVID	0x8712#define LDN_GPIO	0x07	/* GPIO and Watch Dog Timer */#define LDN_GAME 	0x09	/* Game Port */#define WDT_CONTROL	0x71	/* WDT Register: Control */#define WDT_CONFIG	0x72	/* WDT Register: Configuration */#define WDT_TIMEOUT	0x73	/* WDT Register: Timeout Value */#define WDT_RESET_GAME	0x10#define WDT_RESET_KBD	0x20#define WDT_RESET_MOUSE	0x40#define WDT_RESET_CIR	0x80#define WDT_UNIT_SEC	0x80	/* If 0 in MINUTES */#define WDT_OUT_PWROK	0x10#define WDT_OUT_KRST	0x40static intsuperio_inb(int reg){	outb(reg, REG);	return inb(VAL);}static voidsuperio_outb(int val, int reg){	outb(reg, REG);	outb(val, VAL);}static intsuperio_inw(int reg){	int val;	outb(reg++, REG);	val = inb(VAL) << 8;	outb(reg, REG);	val |= inb(VAL);	return val;}static inline voidsuperio_select(int ldn){	outb(LDN, REG);	outb(ldn, VAL);}static inline voidsuperio_enter(void){	spin_lock(&io_lock);	outb(0x87, REG);	outb(0x01, REG);	outb(0x55, REG);	outb(0x55, REG);}static inline voidsuperio_exit(void){	outb(0x02, REG);	outb(0x02, VAL);	spin_unlock(&io_lock);}static inline voidit8712f_wdt_ping(void){	inb(address);}static voidit8712f_wdt_update_margin(void){	int config = WDT_OUT_KRST | WDT_OUT_PWROK;	printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);	/* The timeout register only has 8bits wide */	if (margin < 256)		config |= WDT_UNIT_SEC;	/* else UNIT are MINUTES */	superio_outb(config, WDT_CONFIG);	superio_outb((margin > 255) ? (margin / 60) : margin, WDT_TIMEOUT);}static voidit8712f_wdt_enable(void){	printk(KERN_DEBUG NAME ": enabling watchdog timer\n");	superio_enter();	superio_select(LDN_GPIO);	superio_outb(WDT_RESET_GAME, WDT_CONTROL);	it8712f_wdt_update_margin();	superio_exit();	it8712f_wdt_ping();}static voidit8712f_wdt_disable(void){	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");	superio_enter();	superio_select(LDN_GPIO);	superio_outb(0, WDT_CONFIG);	superio_outb(0, WDT_CONTROL);	superio_outb(0, WDT_TIMEOUT);	superio_exit();}static intit8712f_wdt_notify(struct notifier_block *this,		    unsigned long code, void *unused){	if (code == SYS_HALT || code == SYS_POWER_OFF)		if (!nowayout)			it8712f_wdt_disable();	return NOTIFY_DONE;}static struct notifier_block it8712f_wdt_notifier = {	.notifier_call = it8712f_wdt_notify,};static ssize_tit8712f_wdt_write(struct file *file, const char __user *data,	size_t len, loff_t *ppos){	/* check for a magic close character */	if (len) {		size_t i;		it8712f_wdt_ping();		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;		}	}	return len;}static intit8712f_wdt_ioctl(struct inode *inode, struct file *file,	unsigned int cmd, unsigned long arg){	void __user *argp = (void __user *)arg;	int __user *p = argp;	static struct watchdog_info ident = {		.identity = "IT8712F Watchdog",		.firmware_version = 1,		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,	};	int new_margin;	switch (cmd) {	default:		return -ENOTTY;	case WDIOC_GETSUPPORT:		if (copy_to_user(argp, &ident, sizeof(ident)))			return -EFAULT;		return 0;	case WDIOC_GETSTATUS:	case WDIOC_GETBOOTSTATUS:		return put_user(0, p);	case WDIOC_KEEPALIVE:		it8712f_wdt_ping();		return 0;	case WDIOC_SETTIMEOUT:		if (get_user(new_margin, p))			return -EFAULT;		if (new_margin < 1)			return -EINVAL;		margin = new_margin;		superio_enter();		superio_select(LDN_GPIO);		it8712f_wdt_update_margin();		superio_exit();		it8712f_wdt_ping();	case WDIOC_GETTIMEOUT:		if (put_user(margin, p))			return -EFAULT;		return 0;	}}static intit8712f_wdt_open(struct inode *inode, struct file *file){	/* only allow one at a time */	if (down_trylock(&it8712f_wdt_sem))		return -EBUSY;	it8712f_wdt_enable();	return nonseekable_open(inode, file);}static intit8712f_wdt_release(struct inode *inode, struct file *file){	if (expect_close != 42) {		printk(KERN_WARNING NAME			": watchdog device closed unexpectedly, will not"			" disable the watchdog timer\n");	} else if (!nowayout) {		it8712f_wdt_disable();	}	expect_close = 0;	up(&it8712f_wdt_sem);	return 0;}static struct file_operations it8712f_wdt_fops = {	.owner = THIS_MODULE,	.llseek = no_llseek,	.write = it8712f_wdt_write,	.ioctl = it8712f_wdt_ioctl,	.open = it8712f_wdt_open,	.release = it8712f_wdt_release,};static struct miscdevice it8712f_wdt_miscdev = {	.minor = WATCHDOG_MINOR,	.name = "watchdog",	.fops = &it8712f_wdt_fops,};static int __initit8712f_wdt_find(unsigned short *address){	int err = -ENODEV;	int chip_type;	superio_enter();	chip_type = superio_inw(DEVID);	if (chip_type != IT8712F_DEVID)		goto exit;	superio_select(LDN_GAME);	superio_outb(1, ACT_REG);	if (!(superio_inb(ACT_REG) & 0x01)) {		printk(KERN_ERR NAME ": Device not activated, skipping\n");		goto exit;	}	*address = superio_inw(BASE_REG);	if (*address == 0) {		printk(KERN_ERR NAME ": Base address not set, skipping\n");		goto exit;	}	err = 0;	printk(KERN_DEBUG NAME ": Found IT%04xF chip revision %d - "		"using DogFood address 0x%x\n",		chip_type, superio_inb(DEVREV) & 0x0f, *address);exit:	superio_exit();	return err;}static int __initit8712f_wdt_init(void){	int err = 0;	spin_lock_init(&io_lock);	if (it8712f_wdt_find(&address))		return -ENODEV;	if (!request_region(address, 1, "IT8712F Watchdog")) {		printk(KERN_WARNING NAME ": watchdog I/O region busy\n");		return -EBUSY;	}	it8712f_wdt_disable();	sema_init(&it8712f_wdt_sem, 1);	err = register_reboot_notifier(&it8712f_wdt_notifier);	if (err) {		printk(KERN_ERR NAME ": unable to register reboot notifier\n");		goto out;	}	err = misc_register(&it8712f_wdt_miscdev);	if (err) {		printk(KERN_ERR NAME			": cannot register miscdev on minor=%d (err=%d)\n",			WATCHDOG_MINOR, err);		goto reboot_out;	}	return 0;reboot_out:	unregister_reboot_notifier(&it8712f_wdt_notifier);out:	release_region(address, 1);	return err;}static void __exitit8712f_wdt_exit(void){	misc_deregister(&it8712f_wdt_miscdev);	unregister_reboot_notifier(&it8712f_wdt_notifier);	release_region(address, 1);}module_init(it8712f_wdt_init);module_exit(it8712f_wdt_exit);

⌨️ 快捷键说明

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