diff -urN linux-2.4.36-wt2.geode-mfgpt/drivers/char/Config.in linux-2.4.36-wt2.geode-wdt/drivers/char/Config.in --- linux-2.4.36-wt2.geode-mfgpt/drivers/char/Config.in 2007-08-07 12:51:46 +0200 +++ linux-2.4.36-wt2.geode-wdt/drivers/char/Config.in 2008-08-18 18:51:57 +0200 @@ -317,6 +317,7 @@ tristate ' MPC8xx Watchdog Timer' CONFIG_8xx_WDT fi tristate ' NexGate NSA onboard Watchdog timer' CONFIG_NSA_WDT + tristate ' Geode GX/LX on-chip Watchdog timer' CONFIG_GEODE_WDT fi endmenu diff -urN linux-2.4.36-wt2.geode-mfgpt/drivers/char/Makefile linux-2.4.36-wt2.geode-wdt/drivers/char/Makefile --- linux-2.4.36-wt2.geode-mfgpt/drivers/char/Makefile 2007-08-07 12:51:46 +0200 +++ linux-2.4.36-wt2.geode-wdt/drivers/char/Makefile 2008-08-18 18:51:57 +0200 @@ -327,6 +327,7 @@ obj-$(CONFIG_WDT_W83627) += wdt83627.o obj-$(CONFIG_W83977EF_WDT) += wdt83977.o obj-$(CONFIG_NSA_WDT) += nsawdt.o +obj-$(CONFIG_GEODE_WDT) += geodewdt.o subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) diff -urN linux-2.4.36-wt2.geode-mfgpt/drivers/char/geodewdt.c linux-2.4.36-wt2.geode-wdt/drivers/char/geodewdt.c --- linux-2.4.36-wt2.geode-mfgpt/drivers/char/geodewdt.c 1970-01-01 01:00:00 +0100 +++ linux-2.4.36-wt2.geode-wdt/drivers/char/geodewdt.c 2008-08-18 18:54:36 +0200 @@ -0,0 +1,256 @@ +/* Watchdog timer for the Geode GX/LX + * + * Copyright (C) 2006, Advanced Micro Devices, Inc. + * Backported to 2.4 by Willy Tarreau + * + * 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. + */ + + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define GEODEWDT_HZ 500 +#define GEODEWDT_SCALE 6 +#define GEODEWDT_MAX_SECONDS 131 + +#define WDT_FLAGS_OPEN 1 +#define WDT_FLAGS_ORPHAN 2 + +/* The defaults for the other timers are 60, so we'll use that too */ + +static int cur_interval = 60; +MODULE_PARM(cur_interval, "i"); +MODULE_PARM_DESC(cur_interval, "Watchdog interval in seconds. 1<= interval <=131, default=60."); + +static int wdt_timer; +static unsigned long wdt_flags; +static int safe_close; + +static void geodewdt_ping(void) +{ + //printk("PING\n"); + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); +} + +static void geodewdt_stop(void) +{ + //printk("STOP\n"); + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); +} + +static int geodewdt_set_heartbeat(int val) +{ + if (val < 1 || val > GEODEWDT_MAX_SECONDS) + return -EINVAL; + + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ); + geode_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0); + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN); + + //printk("HEARTBEAT %d\n", val); + cur_interval = val; + return 0; +} + +static int +geodewdt_open(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) != WATCHDOG_MINOR) + return -ENODEV; + + if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags)) + return -EBUSY; + + if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags)) + MOD_INC_USE_COUNT; + + geodewdt_ping(); + return 0; +} + +static int +geodewdt_release(struct inode *inode, struct file *file) +{ + if (MINOR(inode->i_rdev) != WATCHDOG_MINOR) + return 0; + + if (safe_close) { + geodewdt_stop(); + MOD_DEC_USE_COUNT; + } + else { + printk(KERN_CRIT "Unexpected close - watchdog is not stopping.\n"); + geodewdt_ping(); + + set_bit(WDT_FLAGS_ORPHAN, &wdt_flags); + } + + clear_bit(WDT_FLAGS_OPEN, &wdt_flags); + safe_close = 0; + return 0; +} + +static ssize_t +geodewdt_write(struct file *file, const char __user *data, size_t len, + loff_t *ppos) +{ + if(len) { + size_t i; + safe_close = 0; + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + safe_close = 1; + } + } + + geodewdt_ping(); + return len; +} + +static int +geodewdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int interval; + + static struct watchdog_info ident = { + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING + | WDIOF_MAGICCLOSE, + .firmware_version = 0, + .identity = "Geode Watchdog", + }; + + switch(cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + geodewdt_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(interval, p)) + return -EFAULT; + + if (geodewdt_set_heartbeat(interval)) + return -EINVAL; + +/* Fall through */ + + case WDIOC_GETTIMEOUT: + return put_user(cur_interval, p); + } + + return -ENOTTY; +} + +static int geodewdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if(code==SYS_DOWN || code==SYS_HALT) + geodewdt_stop(); + + return NOTIFY_DONE; +} + +static struct file_operations geodewdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = geodewdt_write, + .ioctl = geodewdt_ioctl, + .open = geodewdt_open, + .release = geodewdt_release, +}; + +static struct miscdevice geodewdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &geodewdt_fops +}; + +static struct notifier_block geodewdt_notifier = { + .notifier_call = geodewdt_notify_sys +}; + +static int __init geodewdt_init(void) +{ + int ret, timer; + + timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY, + MFGPT_DOMAIN_ANY, THIS_MODULE); + + if (timer == -1) { + printk(KERN_ERR "geodewdt: No timers were available\n"); + return -ENODEV; + } + + wdt_timer = timer; + + /* Set up the timer */ + + geode_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, + GEODEWDT_SCALE | (3 << 8)); + + /* Set up comparator 2 to reset when the event fires */ + geode_mfgpt_set_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET); + + /* Set up the initial timeout */ + + geode_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, + cur_interval * GEODEWDT_HZ); + + ret = misc_register(&geodewdt_miscdev); + if (ret) + return ret; + + ret = register_reboot_notifier(&geodewdt_notifier); + + if (ret) + misc_deregister(&geodewdt_miscdev); + + return ret; +} + +static void __exit +geodewdt_exit(void) +{ + misc_deregister(&geodewdt_miscdev); + unregister_reboot_notifier(&geodewdt_notifier); +} + +module_init(geodewdt_init); +module_exit(geodewdt_exit); + +MODULE_AUTHOR("Advanced Micro Devices, Inc"); +MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver"); +MODULE_LICENSE("GPL");