From: Paul M. <le...@us...> - 2002-05-31 20:12:24
|
Update of /cvsroot/linux-mips/linux/drivers/char In directory usw-pr-cvs1:/tmp/cvs-serv31520/drivers/char Modified Files: Config.in Makefile Added Files: tx4927wdt.c Log Message: TX4927 WDT. --- NEW FILE: tx4927wdt.c --- /* * drivers/char/tx4927wdt.c * * Watchdog driver for integrated watchdog in the TX4927 processors. * * Copyright (C) 2002 Paul Mundt <le...@ch...> * * 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 <linux/config.h> #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/watchdog.h> #include <linux/miscdevice.h> #include <linux/reboot.h> #include <linux/notifier.h> #include <linux/ioport.h> #include <linux/fs.h> #include <asm/io.h> #include <asm/uaccess.h> #include <asm/tx4927.h> #ifndef CONFIG_CPU_TX49XX #error "Can't use TX49XX watchdog on non-TX49XX processor." #endif #ifndef minor #define minor(x) MINOR(x) #endif #define BIT(x) (1UL << (x)) /* * The TX4927 consists of 3 timer channels: * * 0: Interval Timer mode (default) * 1: Pulse Generator mode * 2: Watchdog Timer mode * * The watchdog exists as the third timer channel. When in * watchdog mode, the first 2 timer channels are reserved. */ /* Registers */ #define TX4927_TMTCR2 0xf200 /* Timer Control Register 2 */ #define TX4927_TMTISR2 0xf204 /* Timer Interrupt Status Register 2 */ #define TX4927_TMCPRA2 0xf208 /* Compare Register A 2 */ #define TX4927_TMCPRB2 0xf20c /* Reserved */ #define TX4927_TMITMR2 0xf210 /* Interval Timer Mode Register 2 */ #define TX4927_TMCCDR2 0xf220 /* Divide Cycle Register 2 */ #define TX4927_TMWGMR2 0xf230 /* Reserved */ #define TX4927_TMWTMR2 0xf240 /* Watchdog Timer Mode Register 2 */ #define TX4927_TMTRR2 0xf2f0 /* Timer Read Register 2 */ /* Flags */ #define TMTCR2_TCE BIT(7) /* Timer Counter Enable */ #define TMTCR2_TMODE_HI BIT(1) /* Timer Mode (high bit) */ #define TMTCR2_TMODE_LO BIT(0) /* Timer Mode (low bit) */ #define TMWTMR2_TWIE BIT(15) /* Watchdog Timer Signaling Enable (NMI) */ #define TMWTMR2_WDIS BIT(7) /* Watchdog Timer Disable */ #define TMWTMR2_TWC BIT(0) /* Watchdog Timer Clear */ /* General Watchdog Flags */ #define WDT_OPEN 0 /* Device is open */ #define WDT_NOWAYOUT 1 /* Don't stop the WDT on exit */ #define WDT_GEN_NMI 2 /* Generate a NMI on overflow */ static unsigned long next_heartbeat; static unsigned long tx4927_flags = 0; static struct watchdog_info tx4927_wdt_info; static struct timer_list timer; static int compare = CONFIG_TX4927_WDT_CMP; static int multiplier = CONFIG_TX4927_WDT_MULT; static int divisor = CONFIG_TX4927_WDT_DIV; #ifdef CONFIG_TX4927_WDT_NMI static int action = 1; #else static int action = 0; #endif #ifdef CONFIG_WATCHDOG_NOWAYOUT static int nowayout = 1; #else static int nowayout = 0; #endif static void tx4927_wdt_start(void) { u32 tmp; timer.expires = tx4927_in32(TX4927_TMCPRA2) / divisor; next_heartbeat = tx4927_in32(TX4927_TMCPRA2) * multiplier; add_timer(&timer); tmp = tx4927_in32(TX4927_TMTCR2); tmp &= ~TMTCR2_TMODE_LO; tmp |= TMTCR2_TMODE_HI; tx4927_out32(TX4927_TMTCR2, tmp); /* * Trigger a NMI on overflow if desired. */ if (test_bit(WDT_GEN_NMI, &tx4927_flags)) { tmp = tx4927_in32(TX4927_TMWTMR2); tmp |= TMWTMR2_TWIE; tx4927_out32(TX4927_TMWTMR2, tmp); } /* * Override the default overflow value if desired. */ if (compare) tx4927_out32(TX4927_TMCPRA2, compare); } static void tx4927_wdt_stop(void) { u32 tmp; del_timer(&timer); tmp = tx4927_in32(TX4927_TMWTMR2); tmp |= TMWTMR2_WDIS; tx4927_out32(TX4927_TMWTMR2, tmp); tmp = tx4927_in32(TX4927_TMTCR2); tmp &= ~TMTCR2_TCE; tx4927_out32(TX4927_TMTCR2, tmp); } static void tx4927_wdt_ping(unsigned long data) { u32 tmp; if (!time_before(jiffies, next_heartbeat)) return; tmp = tx4927_in32(TX4927_TMWTMR2); tmp |= TMWTMR2_TWC; tx4927_out32(TX4927_TMWTMR2, tmp); timer.expires = tx4927_in32(TX4927_TMCPRA2) / divisor; add_timer(&timer); } static int tx4927_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch (cmd) { case WDIOC_GETSUPPORT: if (copy_to_user((struct watchdog_info *)arg, &tx4927_wdt_info, sizeof(tx4927_wdt_info))) return -EFAULT; break; case WDIOC_KEEPALIVE: next_heartbeat = tx4927_in32(TX4927_TMCPRA2) * multiplier; break; default: return -ENOTTY; } return 0; } static int tx4927_wdt_open(struct inode *inode, struct file *file) { if (minor(inode->i_rdev) != WATCHDOG_MINOR) return -ENODEV; if (test_and_set_bit(WDT_OPEN, &tx4927_flags)) return -EBUSY; tx4927_wdt_start(); return 0; } static int tx4927_wdt_release(struct inode *inode, struct file *file) { if (minor(inode->i_rdev) != WATCHDOG_MINOR) return -ENODEV; if (!test_bit(WDT_NOWAYOUT, &tx4927_flags)) tx4927_wdt_stop(); clear_bit(WDT_OPEN, &tx4927_flags); return 0; } static ssize_t tx4927_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { if (ppos != &file->f_pos) return -ESPIPE; if (count) { next_heartbeat = tx4927_in32(TX4927_TMCPRA2) * multiplier; return 1; } return 0; } static int tx4927_wdt_notify_sys(struct notifier_block *this, unsigned long code, void *unusued) { if (code == SYS_DOWN || code == SYS_HALT) tx4927_wdt_stop(); return NOTIFY_DONE; } static struct file_operations tx4927_wdt_fops = { owner: THIS_MODULE, llseek: no_llseek, write: tx4927_wdt_write, ioctl: tx4927_wdt_ioctl, open: tx4927_wdt_open, release: tx4927_wdt_release, }; static struct watchdog_info tx4927_wdt_info = { options: WDIOF_KEEPALIVEPING, identity: "TX4927 WDT", firmware_version: 1, }; static struct notifier_block tx4927_wdt_notifier = { notifier_call: tx4927_wdt_notify_sys, priority: 0, }; static struct miscdevice tx4927_wdt_miscdev = { minor: WATCHDOG_MINOR, name: "watchdog", fops: &tx4927_wdt_fops, }; static int __init tx4927_wdt_init(void) { if (misc_register(&tx4927_wdt_miscdev)) return -EINVAL; if (register_reboot_notifier(&tx4927_wdt_notifier)) { misc_deregister(&tx4927_wdt_miscdev); return -EINVAL; } if (nowayout) set_bit(WDT_NOWAYOUT, &tx4927_flags); if (action) set_bit(WDT_GEN_NMI, &tx4927_flags); init_timer(&timer); timer.function = tx4927_wdt_ping; timer.data = 0; return 0; } static void __exit tx4927_wdt_exit(void) { unregister_reboot_notifier(&tx4927_wdt_notifier); misc_deregister(&tx4927_wdt_miscdev); } EXPORT_NO_SYMBOLS; MODULE_AUTHOR("Paul Mundt <le...@ch...>"); MODULE_DESCRIPTION("TX4927 watchdog driver"); MODULE_LICENSE("GPL"); MODULE_PARM(nowayout, "i"); MODULE_PARM_DESC(nowayout, "Watchdog can't be stopped once started. Defaults to CONFIG_WATCHDOG_NOWAYOUT."); MODULE_PARM(multiplier, "i"); MODULE_PARM_DESC(multiplier, "Heartbeat multiplier (next heartbeat = overflow value * multiplier). Defaults to CONFIG_TX4927_WDT_MULT."); MODULE_PARM(divisor, "i"); MODULE_PARM_DESC(divisor, "Timer divisor (next counter clear = overflow value / divisor). Defaults to CONFIG_TX4927_WDT_DIV."); MODULE_PARM(action, "i"); MODULE_PARM_DESC(action, "Action on counter overflow (0 = reset, 1 = NMI). Defaults to reset."); MODULE_PARM(compare, "i"); MODULE_PARM_DESC(compare, "Overflow value. Defaults to 0xffffffff."); module_init(tx4927_wdt_init); module_exit(tx4927_wdt_exit); Index: Config.in =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/char/Config.in,v retrieving revision 1.36 retrieving revision 1.37 diff -u -d -r1.36 -r1.37 --- Config.in 29 May 2002 00:23:16 -0000 1.36 +++ Config.in 31 May 2002 20:12:20 -0000 1.37 @@ -224,6 +224,13 @@ tristate ' ZF MachZ Watchdog' CONFIG_MACHZ_WDT dep_tristate ' Indy/I2 Hardware Watchdog' CONFIG_INDYDOG $CONFIG_SGI_IP22 dep_tristate ' NEC VR41xx Watchdog (DSU)' CONFIG_VR41XX_WDT $CONFIG_CPU_VR41XX + dep_tristate ' Toshiba TX4927 Watchdog' CONFIG_TX4927_WDT $CONFIG_CPU_TX49XX + if [ "$CONFIG_TX4927_WDT" != "n" ]; then + int ' Heartbeat multiplier' CONFIG_TX4927_WDT_MULT 2 + int ' Timer divisor' CONFIG_TX4927_WDT_DIV 2 + bool ' Trigger NMI on overflow' CONFIG_TX4927_WDT_NMI + int ' Overflow value (overrides default)' CONFIG_TX4927_WDT_CMP 0 + fi fi endmenu Index: Makefile =================================================================== RCS file: /cvsroot/linux-mips/linux/drivers/char/Makefile,v retrieving revision 1.30 retrieving revision 1.31 diff -u -d -r1.30 -r1.31 --- Makefile 29 May 2002 00:23:16 -0000 1.30 +++ Makefile 31 May 2002 20:12:20 -0000 1.31 @@ -265,6 +265,7 @@ obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_VR41XX_WDT) += vr41xxwdt.o +obj-$(CONFIG_TX4927_WDT) += tx4927wdt.o obj-$(CONFIG_BUTTONS) += buttons.o obj-$(CONFIG_VR41XX_GPIO_BUTTONS) += gpiobtns.o obj-$(CONFIG_VR41XX_E105_BUTTONS) += e105btns.o |