#include <linux/autoconf.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <asm/bootinfo.h>

extern unsigned long mips_machtype;

static unsigned long wdt_is_open;
static struct timer_list wdt_timer;

static void wdt_merlot_refresh(void)
{
    volatile __u32 *wdt;
    switch (mips_machtype) {
    case MACH_ARUBA_AP70:
	wdt = (__u32 *) 0xb8030034;
	*wdt = 0x10000000;
	break;
    default:
	wdt = (__u32 *) 0xbc00300c;
	*wdt = 0x40000000;
	break;
    }
}

static void wdt_merlot_timer_fn(unsigned long data)
{
    wdt_merlot_refresh();
    if (!test_bit(1, &wdt_is_open))
	mod_timer(&wdt_timer, jiffies + HZ);
}

static int wdt_merlot_setup_timer(void)
{

    init_timer(&wdt_timer);
    wdt_timer.function = wdt_merlot_timer_fn;
    wdt_timer.data = 0;
    wdt_timer.expires = jiffies + HZ;
    add_timer(&wdt_timer);
    return 0;
}

static int wdt_open(struct inode *inode, struct file *file)
{
    if (test_and_set_bit(0, &wdt_is_open))
	return -EBUSY;
    set_bit(1, &wdt_is_open);
    return nonseekable_open(inode, file);
}

static ssize_t wdt_write(struct file *file, const char __user * buf, size_t count, loff_t * ppos)
{
    if (count) 	/* something was written */
	wdt_merlot_refresh();
    return count;
}

static int wdt_release(struct inode *inode, struct file *file)
{
    clear_bit(0, &wdt_is_open);
    return 0;
}

static struct file_operations wdt_fops = {
    .owner = THIS_MODULE,
    .llseek = no_llseek,
    .write = wdt_write,
    .open = wdt_open,
    .release = wdt_release,
};

static struct miscdevice wdt_miscdev = {
    .minor = WATCHDOG_MINOR,
    .name = "watchdog",
    .fops = &wdt_fops,
};

static void __exit wdt_exit(void)
{
    misc_deregister(&wdt_miscdev);
}

static int __init wdt_init(void)
{
    int ret;
    ret = misc_register(&wdt_miscdev);
    if (ret) {
	printk(KERN_ERR
	       "wdt: cannot register miscdev on minor=%d (err=%d)\n",
	       WATCHDOG_MINOR, ret);
	misc_deregister(&wdt_miscdev);
	goto out;
    }
    printk("wdt: registered with refresh\n");
    wdt_merlot_refresh();
    wdt_merlot_setup_timer();
  out:
    return ret;
}

module_init(wdt_init);
module_exit(wdt_exit);
