* watchdog driver for W83627HF chip
@ 2002-12-19 2:54 zhz
0 siblings, 0 replies; only message in thread
From: zhz @ 2002-12-19 2:54 UTC (permalink / raw)
To: linux-kernel
[-- Attachment #1: Type: text/plain, Size: 223 bytes --]
hi, man,
I have written the watchdog driver for the Winbond W83627HF
chip. The attachment is the driver source code. Any suggestion is
welcome.
Thanks a lot.
Regards,
Zhou HongZhen
[-- Attachment #2: w83627hf_wdt.c --]
[-- Type: application/octet-stream, Size: 15443 bytes --]
/*
* W83627HF watchdog driver for Linux 2.2.x
*
* (c) Copyright 2002 Zhou HongZhen.
*
* 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.
*
* (c) Copyright 2002 Zhou HongZhen <inetbash@yahoo.com>
* <netdiff@netease.com>
*
* Release 1.0.
*
* Fixes
*
* */
#include <linux/module.h>
#include <linux/config.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <asm/param.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/system.h>
#include <asm/semaphore.h>
#include <linux/sysctl.h>
#include <asm/uaccess.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/watchdog.h>
#if 0
#define W83627HF_WDT_DEBUG
#endif
#if 0
#undef CONFIG_WATCHDOG_NOWAYOUT
#else
#define CONFIG_WATCHDOG_NOWAYOUT
#endif
#ifdef W83627HF_WDT_DEBUG
#define assert_w83627hf_wdt(p) if (1) {\
if (!(p)) \
panic("assert failed in %s line %d\n", __FILE__, __LINE__);\
} else
#else
#define assert_w83627hf_wdt(p)
#endif
#define W83627HF_LPCIP 0x295 /* LPC Index Port */
#define W83627HF_LPCDP 0x296 /* LPC Data Port */
#define W83627HF_EFER 0x2E /* Extended Function Enable Register */
#define W83627HF_EFIR 0x2E /* Extended Function Index Register*/
#define W83627HF_EFDR 0x2F /* Extended Function Data Register */
#define W83627HF_ENTER_EFM 0x87 /* enter externed function mode */
#define W83627HF_EXIT_EFM 0xAA /* exit externed function mode */
/* default watchdog timeout value -- 12s */
#define W83627HF_WDT_DEF_TIMO 0x0C
#define W83627HF_WDT_MAX_TIMO 0xFF
#define W83627HF_WDT_MIN_TIMO 0x01
/* #define W83627HF_WDT_MIN_TIMO 0x05 */
/* default watchdog timeout unit -- second */
#define W83627HF_WDT_DEF_TIMU 0x00
/* only one can open the watchdog device */
static unsigned long wdt_is_open = 0;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
static unsigned long wdt_expect_close = 0;
#endif
/* 0x01--0xFF seconds(minutes). 0x00 to disable watchdog. */
static unsigned int w83627hf_margin = W83627HF_WDT_DEF_TIMO;
static unsigned int w83627hf_timeunit = W83627HF_WDT_DEF_TIMU;
#define W83627HF_WDT_MAGIC_NUM 13
#define W83627HF_WDT_MAGIC_U 3
#define W83627HF_WDT_MAGIC_M 1
/* magic word which change margin and timeunit */
static char w83627hf_wdt_magic[W83627HF_WDT_MAGIC_NUM] = "?M?UFH72638W";
#define w83627hf_wdt_feed() if (1) {\
w83627hf_timeunit = w83627hf_timeunit ? \
0x08 : 0x00;\
\
if (W83627HF_WDT_MAX_TIMO < w83627hf_margin) \
w83627hf_margin = W83627HF_WDT_MAX_TIMO;\
\
if (0!=w83627hf_margin &&\
W83627HF_WDT_MIN_TIMO>w83627hf_margin) \
w83627hf_margin = W83627HF_WDT_MIN_TIMO; \
\
outb(W83627HF_ENTER_EFM, W83627HF_EFER); \
outb(W83627HF_ENTER_EFM, W83627HF_EFER); \
\
outb(0x07, W83627HF_EFIR);\
outb(0x08, W83627HF_EFDR);\
\
outb(0xF5, W83627HF_EFIR);\
outb((unsigned char)w83627hf_timeunit, W83627HF_EFDR);\
\
outb(0xF6, W83627HF_EFIR);\
outb((unsigned char)w83627hf_margin, W83627HF_EFDR);\
\
outb(W83627HF_EXIT_EFM, W83627HF_EFER); \
} else
#define w83627hf_wdt_stop() if (1) {\
outb(W83627HF_ENTER_EFM, W83627HF_EFER); \
outb(W83627HF_ENTER_EFM, W83627HF_EFER); \
\
outb(0x07, W83627HF_EFIR);\
outb(0x08, W83627HF_EFDR);\
\
/* 0x00 to disable watchdog */\
outb(0xF6, W83627HF_EFIR);\
outb(0x00, W83627HF_EFDR);\
\
outb(W83627HF_EXIT_EFM, W83627HF_EFER); \
} else
#ifdef W83627HF_WDT_DEBUG
/* watchdog sysctl configuration(just for debug) */
enum {
SYSCTL_W83627HF_WDT_MARGIN=1,
SYSCTL_W83627HF_WDT_TIMEUNIT,
SYSCTL_W83627HF_WDT_FEED
};
enum {
SYSCTL_W83627HF_WDT=21
};
static int sysctl_w83627hf_wdt_noused = 0;
static int w83627hf_wdt_sysctl_feed(struct ctl_table *table, int write,
struct file *filp, void *buffer, size_t *lenp);
static struct ctl_table sysctl_w83627hf_wdt_dir[] = {
{SYSCTL_W83627HF_WDT_MARGIN, "margin",
&w83627hf_margin, sizeof(int),
0644, 0, &proc_dointvec},
{SYSCTL_W83627HF_WDT_TIMEUNIT, "timeunit",
&w83627hf_timeunit, sizeof(int),
0644, 0, &proc_dointvec},
{SYSCTL_W83627HF_WDT_FEED, "feed",
&sysctl_w83627hf_wdt_noused, sizeof(int),
0200, 0, &w83627hf_wdt_sysctl_feed},
{0}
};
static struct ctl_table sysctl_w83627hf_wdt_root[] = {
{SYSCTL_W83627HF_WDT, "w83627hf_wdt",
0, 0, 0555, sysctl_w83627hf_wdt_dir},
{0}
};
static struct ctl_table_header *sysctl_w83627hf_wdt_header = 0;
/*
* When you write something into /proc/sys/w83627hf_wdt/feed, the dog begin
* watch the system for you.
* Remember to feed it again before the dog burks.
* Set /proc/sys/w83627hf_wdt/margin to 0, and update
* /proc/sys/w83627hf_wdt/feed will stop the dog.
* */
int w83627hf_wdt_sysctl_feed(struct ctl_table *table, int write,
struct file *filp, void *buffer, size_t *lenp)
{
if (!write)
return -EPERM;
w83627hf_wdt_feed();
return 0;
}
int w83627hf_wdt_sysctl_create(void)
{
sysctl_w83627hf_wdt_header =
register_sysctl_table(sysctl_w83627hf_wdt_root, 0);
if (0 == sysctl_w83627hf_wdt_header) {
printk("W83627HF WDT: register_sysctl_table() failed\n");
return -1;
}
return 0;
}
void w83627hf_wdt_sysctl_clean(void)
{
unregister_sysctl_table(sysctl_w83627hf_wdt_header);
sysctl_w83627hf_wdt_header = 0;
}
#endif
/*
* Read the margin and timeunit from watchdog device, just for debug.
* */
static ssize_t w83627hf_wdt_read(struct file *file, char *buf,
size_t len, loff_t *ppos)
{
unsigned char t[2];
assert_w83627hf_wdt(wdt_is_open);
if (ppos != &file->f_pos) {
#ifdef W83627HF_WDT_DEBUG
printk("W83627HF WDT: device can NOT be seek\n");
#endif
return -ESPIPE;
}
if (2 > len) {
#ifdef W83627HF_WDT_DEBUG
printk("W83627HF WDT: read invalid data length %d\n", len);
#endif
return -EINVAL;
}
outb(W83627HF_ENTER_EFM, W83627HF_EFER);
outb(W83627HF_ENTER_EFM, W83627HF_EFER);
outb(0x07, W83627HF_EFIR);
outb(0x08, W83627HF_EFDR);
outb(0xF5, W83627HF_EFIR);
t[0] = (0x08 & inb(W83627HF_EFDR)) ? 0x01 : 0x00;
outb(0xF6, W83627HF_EFIR);
t[1] = inb(W83627HF_EFDR);
outb(W83627HF_EXIT_EFM, W83627HF_EFER);
if (copy_to_user(buf, t, 2)) {
#ifdef W83627HF_WDT_DEBUG
printk("W83627HF WDT: copy_to_user failed\n");
#endif
return -EFAULT;
}
return 2;
}
static ssize_t w83627hf_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
size_t i;
int j = 0, seek = 0;
unsigned int timeunit = 0;
assert_w83627hf_wdt(wdt_is_open);
if (ppos != &file->f_pos) {
#ifdef W83627HF_WDT_DEBUG
printk("W83627HF WDT: device can NOT be seek\n");
#endif
return -ESPIPE;
}
if (0 == len)
goto ping;
for (i=0; i<len; i++){
switch (data[i]) {
case 'W':
j = W83627HF_WDT_MAGIC_NUM - 2;
seek = 1;
break;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
case 'V':
wdt_expect_close = 1;
printk(KERN_DEBUG "expect_close changed\n");
j = 0;
seek = 0;
break;
#endif
default:
if (0 == seek) {
assert_w83627hf_wdt(0 == j);
break;
}
assert_w83627hf_wdt(1 == seek);
assert_w83627hf_wdt(0 <= j - 1);
if (W83627HF_WDT_MAGIC_U == j) {
assert_w83627hf_wdt('?' ==
w83627hf_wdt_magic[j - 1]);
if (1 == data[i])
timeunit = 1;
else
timeunit = 0;
j--;
} else if (W83627HF_WDT_MAGIC_M == j) {
assert_w83627hf_wdt('?' ==
w83627hf_wdt_magic[j - 1]);
if (0<=data[i] &&
W83627HF_WDT_DEF_TIMO>=data[i]) {
w83627hf_margin = data[i];
w83627hf_timeunit = timeunit;
printk(KERN_DEBUG "margin chanded! "
"margin == %d, unit == %d\n",
w83627hf_margin,
w83627hf_timeunit);
}
j = 0;
seek = 0;
} else if (data[i] == w83627hf_wdt_magic[j - 1]) {
j--;
} else {
j = 0;
seek = 0;
}
break;
}
}
ping:
if (wdt_is_open) {
w83627hf_wdt_feed();
}
return len;
}
static int w83627hf_wdt_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
int time;
#endif
struct watchdog_info ident = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
WDIOF_SETTIMEOUT |
#endif
0,
1,
"W83627HF"
};
assert_w83627hf_wdt(wdt_is_open);
if (WATCHDOG_MINOR != MINOR(inode->i_rdev))
return -ENODEV;
switch (cmd) {
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)))
ret = -EFAULT;
break;
case WDIOC_KEEPALIVE:
if (wdt_is_open) {
w83627hf_wdt_feed();
}
break;
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
case WDIOC_SETTIMEOUT:
if (copy_from_user(&time, (int *)arg, sizeof(int))) {
ret = -EFAULT;
break;
}
printk(KERN_DEBUG "W83627HF WDT: w83627hf_margin %d.\n",
w83627hf_margin);
if (W83627HF_WDT_MAX_TIMO < time) {
printk("W83627HF WDT: w83637hf_margin > 0xFF\n");
time = W83627HF_WDT_MAX_TIMO;
}
if (0!=time && W83627HF_WDT_MIN_TIMO>time) {
printk("W83627HF WDT: w83627hf_margin < 0x01\n");
time = W83627HF_WDT_MIN_TIMO;
}
w83627hf_margin = time;
/* Fall */
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,18)
case WDIOC_GETTIMEOUT:
if (copy_to_user((int *)arg, &w83627hf_margin,
sizeof(int)))
ret = -EFAULT;
#endif
break;
#endif
default:
ret = -ENOTTY;
break;
}
return ret;
}
/*
* Once the user application open the device file, watchdog start work.
* You MUST feed the dog before it burks.
* */
static int w83627hf_wdt_open(struct inode *inode, struct file *file)
{
if (WATCHDOG_MINOR != MINOR(inode->i_rdev))
return -ENODEV;
/* only one can open this device */
if (wdt_is_open)
return -EBUSY;
wdt_is_open = 1;
MOD_INC_USE_COUNT;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
wdt_expect_close = 0;
#endif
if (W83627HF_WDT_MAX_TIMO < w83627hf_margin) {
printk("W83627HF WDT: w83627hf_margin > 0xFF\n");
w83627hf_margin = W83627HF_WDT_MAX_TIMO;
}
if (0!=w83627hf_margin &&
W83627HF_WDT_MIN_TIMO>w83627hf_margin) {
printk("W83627HF WDT: w83627hf_margin < 0x01\n");
w83627hf_margin = W83627HF_WDT_MIN_TIMO;
}
/* enter the extended function mode */
outb(W83627HF_ENTER_EFM, W83627HF_EFER);
outb(W83627HF_ENTER_EFM, W83627HF_EFER);
/*
* watchdog timer is controlled by CRF5, CRF6, CRF7 of logical
* device 8.
* */
outb(0x2B, W83627HF_EFIR);
outb(0xC0, W83627HF_EFDR);
/* write CR07 to select logical device A */
outb(0x07, W83627HF_EFIR);
outb(0x0A, W83627HF_EFDR);
/* disable watchdog IRQ */
outb(0xF7, W83627HF_EFIR);
outb(0x00, W83627HF_EFDR);
/* write CR07 to select logical device 8 */
outb(0x07, W83627HF_EFIR);
outb(0x08, W83627HF_EFDR);
/* count time base second(NOT minute). */
outb(0xF5, W83627HF_EFIR);
outb((unsigned char)W83627HF_WDT_DEF_TIMU, W83627HF_EFDR);
/* write the timeout vlaue into CRF6 */
outb(0xF6, W83627HF_EFIR);
outb((unsigned char)w83627hf_margin, W83627HF_EFDR);
/* watchdog will NOT be reset upon a mouse/keyboard interrupt. */
outb(0xF7, W83627HF_EFIR);
outb(0x00, W83627HF_EFDR);
/* exit extended function mode */
outb(W83627HF_EXIT_EFM, W83627HF_EFER);
printk(KERN_INFO "W83627HF WDT: wake up, current timeout %d.\n",
w83627hf_margin);
return 0;
}
static int w83627hf_wdt_release(struct inode *inode, struct file *file)
{
assert_w83627hf_wdt(wdt_is_open);
if (WATCHDOG_MINOR != MINOR(inode->i_rdev))
return -ENODEV;
#ifndef CONFIG_WATCHDOG_NOWAYOUT
if (wdt_expect_close) {
if(wdt_is_open) {
w83627hf_wdt_stop();
}
}
wdt_expect_close = 0;
#endif
wdt_is_open = 0;
MOD_DEC_USE_COUNT;
printk(KERN_INFO "W83627HF WDT: sleeping again.\n");
return 0;
}
/*
* probe the W83627HF chip.
* */
static int w83627hf_probe(void)
{
unsigned char device;
unsigned char chip;
int ret = -1;
if (0 > check_region(W83627HF_LPCIP, 2)) {
ret = -EBUSY;
goto out;
}
outb(0x49, W83627HF_LPCIP);
device = inb(W83627HF_LPCDP);
if (0x01 != (device >> 1)) {
/*printk("W83627HF WDT: Device ID %x != 0x01\n", device >> 1);*/
goto out;
}
outb(0x58, W83627HF_LPCIP);
chip = inb(W83627HF_LPCDP);
if (0x21 != chip) {
/*printk("W83627HF WDT: Chip ID %x != 0x21\n", chip);*/
goto out;
}
/*printk(KERN_INFO "W83627HF WDT: W83627HF chip found\n");*/
ret = 0;
out:
return ret;
}
static int w83627hf_wdt_notify(struct notifier_block *this, unsigned long code,
void *unused)
{
if (SYS_DOWN==code || SYS_HALT==code) {
if (wdt_is_open) {
w83627hf_wdt_stop();
#ifndef CONFIG_WATCHDOG_NOWAYOUT
wdt_expect_close = 0;
#endif
wdt_is_open = 0;
}
}
return NOTIFY_DONE;
}
static struct notifier_block w83627hf_wdt_notifier=
{
w83627hf_wdt_notify,
0,
0
};
static struct file_operations w83627hf_wdt_fops = {
llseek: 0,
read: w83627hf_wdt_read,
write: w83627hf_wdt_write,
readdir: 0,
poll: 0,
ioctl: w83627hf_wdt_ioctl,
mmap: 0,
open: w83627hf_wdt_open,
flush: 0,
release: w83627hf_wdt_release,
};
static struct miscdevice w83627hf_wdt_miscdev = {
WATCHDOG_MINOR,
"watchdog",
&w83627hf_wdt_fops
};
int init_module(void)
{
int ret;
if ((ret = w83627hf_probe()))
goto out;
ret = -1;
request_region(W83627HF_LPCIP, 2, "W83627HF_WDT");
if (misc_register(&w83627hf_wdt_miscdev)) {
printk("W83627HF WDT: misc_register() failed\n");
goto clean_region;
}
if (register_reboot_notifier(&w83627hf_wdt_notifier)) {
printk("W83627HF WDT: register_reboot_notifier() failed\n");
goto clean_miscdev;
}
#ifdef W83627HF_WDT_DEBUG
if (w83627hf_wdt_sysctl_create())
goto clean_notifier;
#endif
if (W83627HF_WDT_MAX_TIMO < w83627hf_margin) {
printk("W83627HF WDT: w83627hf_margin > 0xFF\n");
w83627hf_margin = W83627HF_WDT_MAX_TIMO;
}
if (W83627HF_WDT_MIN_TIMO > w83627hf_margin) {
printk("W83627HF WDT: w83627hf_margin < 0x01\n");
w83627hf_margin = W83627HF_WDT_MIN_TIMO;
}
printk(KERN_INFO "W83627HF WDT: sleeping.\n");
ret = 0;
goto out;
#ifdef W83627HF_WDT_DEBUG
clean_notifier:
unregister_reboot_notifier(&w83627hf_wdt_notifier);
#endif
clean_miscdev:
misc_deregister(&w83627hf_wdt_miscdev);
clean_region:
release_region(W83627HF_LPCIP, 2);
out:
return ret;
}
void cleanup_module(void)
{
#ifdef W83627HF_WDT_DEBUG
w83627hf_wdt_sysctl_clean();
#endif
unregister_reboot_notifier(&w83627hf_wdt_notifier);
misc_deregister(&w83627hf_wdt_miscdev);
release_region(W83627HF_LPCIP, 2);
}
MODULE_PARM(w83627hf_margin, "i");
MODULE_PARM_DESC(w83627hf_magin, "WDT timeout (default 12 <0x01-0xFF>)");
MODULE_AUTHOR("Zhou HongZhen -- <inetbash@yahoo.com>|<netdiff@netease.com>");
MODULE_DESCRIPTION("Watchdog driver for Winbond W83627HF chip");
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,0)
MODULE_LICENSE("GPL");
#endif
EXPORT_NO_SYMBOLS;
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2002-12-19 2:53 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-12-19 2:54 watchdog driver for W83627HF chip zhz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Powered by JetHome