diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/ipmi/ipmi_msghandler.c | 12 | ||||
-rw-r--r-- | drivers/char/istallion.c | 8 | ||||
-rw-r--r-- | drivers/char/mem.c | 28 | ||||
-rw-r--r-- | drivers/char/mxser.c | 8 | ||||
-rw-r--r-- | drivers/char/ppdev.c | 3 | ||||
-rw-r--r-- | drivers/char/riscom8.c | 8 | ||||
-rw-r--r-- | drivers/char/sysrq.c | 250 | ||||
-rw-r--r-- | drivers/char/tpm/tpm.h | 1 | ||||
-rw-r--r-- | drivers/char/tty_io.c | 4 | ||||
-rw-r--r-- | drivers/char/watchdog/Kconfig | 10 | ||||
-rw-r--r-- | drivers/char/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/char/watchdog/ep93xx_wdt.c | 257 |
12 files changed, 420 insertions, 170 deletions
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index d745004281d..abd4c5118a1 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -736,7 +736,8 @@ int ipmi_create_user(unsigned int if_num, intf = ipmi_interfaces[if_num]; if ((if_num >= MAX_IPMI_INTERFACES) || IPMI_INVALID_INTERFACE(intf)) { spin_unlock_irqrestore(&interfaces_lock, flags); - return -EINVAL; + rv = -EINVAL; + goto out_kfree; } /* Note that each existing user holds a refcount to the interface. */ @@ -751,14 +752,14 @@ int ipmi_create_user(unsigned int if_num, if (!try_module_get(intf->handlers->owner)) { rv = -ENODEV; - goto out_err; + goto out_kref; } if (intf->handlers->inc_usecount) { rv = intf->handlers->inc_usecount(intf->send_info); if (rv) { module_put(intf->handlers->owner); - goto out_err; + goto out_kref; } } @@ -769,9 +770,10 @@ int ipmi_create_user(unsigned int if_num, *user = new_user; return 0; - out_err: - kfree(new_user); +out_kref: kref_put(&intf->refcount, intf_free); +out_kfree: + kfree(new_user); return rv; } diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c index ede128356af..e5247f85a44 100644 --- a/drivers/char/istallion.c +++ b/drivers/char/istallion.c @@ -378,13 +378,13 @@ MODULE_DESCRIPTION("Stallion Intelligent Multiport Serial Driver"); MODULE_LICENSE("GPL"); -MODULE_PARM(board0, "1-3s"); +module_param_array(board0, charp, NULL, 0); MODULE_PARM_DESC(board0, "Board 0 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board1, "1-3s"); +module_param_array(board1, charp, NULL, 0); MODULE_PARM_DESC(board1, "Board 1 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board2, "1-3s"); +module_param_array(board2, charp, NULL, 0); MODULE_PARM_DESC(board2, "Board 2 config -> name[,ioaddr[,memaddr]"); -MODULE_PARM(board3, "1-3s"); +module_param_array(board3, charp, NULL, 0); MODULE_PARM_DESC(board3, "Board 3 config -> name[,ioaddr[,memaddr]"); #endif diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 29c41f4418c..26d0116b48d 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -216,11 +216,9 @@ static ssize_t write_mem(struct file * file, const char __user * buf, copied = copy_from_user(ptr, buf, sz); if (copied) { - ssize_t ret; - - ret = written + (sz - copied); - if (ret) - return ret; + written += sz - copied; + if (written) + break; return -EFAULT; } buf += sz; @@ -456,11 +454,9 @@ do_write_kmem(void *p, unsigned long realp, const char __user * buf, copied = copy_from_user(ptr, buf, sz); if (copied) { - ssize_t ret; - - ret = written + (sz - copied); - if (ret) - return ret; + written += sz - copied; + if (written) + break; return -EFAULT; } buf += sz; @@ -514,11 +510,10 @@ static ssize_t write_kmem(struct file * file, const char __user * buf, if (len) { written = copy_from_user(kbuf, buf, len); if (written) { - ssize_t ret; - + if (wrote + virtr) + break; free_page((unsigned long)kbuf); - ret = wrote + virtr + (len - written); - return ret ? ret : -EFAULT; + return -EFAULT; } } len = vwrite(kbuf, (char *)p, len); @@ -563,8 +558,11 @@ static ssize_t write_port(struct file * file, const char __user * buf, return -EFAULT; while (count-- > 0 && i < 65536) { char c; - if (__get_user(c, tmp)) + if (__get_user(c, tmp)) { + if (tmp > buf) + break; return -EFAULT; + } outb(c,i); i++; tmp++; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index ea725a9964e..0fb2fb9fb02 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -243,10 +243,10 @@ static int verbose = 0; MODULE_AUTHOR("Casper Yang"); MODULE_DESCRIPTION("MOXA Smartio/Industio Family Multiport Board Device Driver"); -MODULE_PARM(ioaddr, "1-4i"); -MODULE_PARM(ttymajor, "i"); -MODULE_PARM(calloutmajor, "i"); -MODULE_PARM(verbose, "i"); +module_param_array(ioaddr, int, NULL, 0); +module_param(ttymajor, int, 0); +module_param(calloutmajor, int, 0); +module_param(verbose, bool, 0); MODULE_LICENSE("GPL"); struct mxser_log { diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index 306ee0f091a..bee6c47b45b 100644 --- a/drivers/char/ppdev.c +++ b/drivers/char/ppdev.c @@ -65,10 +65,11 @@ #include <linux/parport.h> #include <linux/ctype.h> #include <linux/poll.h> -#include <asm/uaccess.h> +#include <linux/major.h> #include <linux/ppdev.h> #include <linux/smp_lock.h> #include <linux/device.h> +#include <asm/uaccess.h> #define PP_VERSION "ppdev: user-space parallel port driver" #define CHRDEV "ppdev" diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c index 119e629656b..657c0d88f48 100644 --- a/drivers/char/riscom8.c +++ b/drivers/char/riscom8.c @@ -1743,10 +1743,10 @@ static int iobase; static int iobase1; static int iobase2; static int iobase3; -MODULE_PARM(iobase, "i"); -MODULE_PARM(iobase1, "i"); -MODULE_PARM(iobase2, "i"); -MODULE_PARM(iobase3, "i"); +module_param(iobase, int, 0); +module_param(iobase1, int, 0); +module_param(iobase2, int, 0); +module_param(iobase3, int, 0); MODULE_LICENSE("GPL"); #endif /* MODULE */ diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index d58f8231885..35082dc12ea 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -42,16 +42,15 @@ /* Whether we react on sysrq keys or just ignore them */ int sysrq_enabled = 1; -/* Loglevel sysrq handler */ static void sysrq_handle_loglevel(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { int i; i = key - '0'; console_loglevel = 7; printk("Loglevel set to %d\n", i); console_loglevel = i; -} +} static struct sysrq_key_op sysrq_loglevel_op = { .handler = sysrq_handle_loglevel, .help_msg = "loglevel0-8", @@ -59,11 +58,9 @@ static struct sysrq_key_op sysrq_loglevel_op = { .enable_mask = SYSRQ_ENABLE_LOG, }; - -/* SAK sysrq handler */ #ifdef CONFIG_VT static void sysrq_handle_SAK(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { if (tty) do_SAK(tty); @@ -75,12 +72,13 @@ static struct sysrq_key_op sysrq_SAK_op = { .action_msg = "SAK", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; +#else +#define sysrq_SAK_op (*(struct sysrq_key_op *)0) #endif #ifdef CONFIG_VT -/* unraw sysrq handler */ static void sysrq_handle_unraw(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { struct kbd_struct *kbd = &kbd_table[fg_console]; @@ -93,10 +91,11 @@ static struct sysrq_key_op sysrq_unraw_op = { .action_msg = "Keyboard mode set to XLATE", .enable_mask = SYSRQ_ENABLE_KEYBOARD, }; +#else +#define sysrq_unraw_op (*(struct sysrq_key_op *)0) #endif /* CONFIG_VT */ #ifdef CONFIG_KEXEC -/* crashdump sysrq handler */ static void sysrq_handle_crashdump(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { @@ -108,16 +107,16 @@ static struct sysrq_key_op sysrq_crashdump_op = { .action_msg = "Trigger a crashdump", .enable_mask = SYSRQ_ENABLE_DUMP, }; +#else +#define sysrq_crashdump_op (*(struct sysrq_key_op *)0) #endif -/* reboot sysrq handler */ static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { local_irq_enable(); emergency_restart(); } - static struct sysrq_key_op sysrq_reboot_op = { .handler = sysrq_handle_reboot, .help_msg = "reBoot", @@ -126,11 +125,10 @@ static struct sysrq_key_op sysrq_reboot_op = { }; static void sysrq_handle_sync(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { emergency_sync(); } - static struct sysrq_key_op sysrq_sync_op = { .handler = sysrq_handle_sync, .help_msg = "Sync", @@ -139,11 +137,10 @@ static struct sysrq_key_op sysrq_sync_op = { }; static void sysrq_handle_mountro(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { emergency_remount(); } - static struct sysrq_key_op sysrq_mountro_op = { .handler = sysrq_handle_mountro, .help_msg = "Unmount", @@ -151,28 +148,23 @@ static struct sysrq_key_op sysrq_mountro_op = { .enable_mask = SYSRQ_ENABLE_REMOUNT, }; -/* END SYNC SYSRQ HANDLERS BLOCK */ - #ifdef CONFIG_DEBUG_MUTEXES - -static void -sysrq_handle_showlocks(int key, struct pt_regs *pt_regs, struct tty_struct *tty) +static void sysrq_handle_showlocks(int key, struct pt_regs *pt_regs, + struct tty_struct *tty) { mutex_debug_show_all_locks(); } - static struct sysrq_key_op sysrq_showlocks_op = { .handler = sysrq_handle_showlocks, .help_msg = "show-all-locks(D)", .action_msg = "Show Locks Held", }; - +#else +#define sysrq_showlocks_op (*(struct sysrq_key_op *)0) #endif -/* SHOW SYSRQ HANDLERS BLOCK */ - static void sysrq_handle_showregs(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { if (pt_regs) show_regs(pt_regs); @@ -184,9 +176,8 @@ static struct sysrq_key_op sysrq_showregs_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; - static void sysrq_handle_showstate(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { show_state(); } @@ -197,9 +188,8 @@ static struct sysrq_key_op sysrq_showstate_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; - static void sysrq_handle_showmem(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { show_mem(); } @@ -210,13 +200,9 @@ static struct sysrq_key_op sysrq_showmem_op = { .enable_mask = SYSRQ_ENABLE_DUMP, }; -/* SHOW SYSRQ HANDLERS BLOCK */ - - -/* SIGNAL SYSRQ HANDLERS BLOCK */ - -/* signal sysrq helper function - * Sends a signal to all user processes */ +/* + * Signal sysrq helper function. Sends a signal to all user processes. + */ static void send_sig_all(int sig) { struct task_struct *p; @@ -229,7 +215,7 @@ static void send_sig_all(int sig) } static void sysrq_handle_term(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { send_sig_all(SIGTERM); console_loglevel = 8; @@ -243,7 +229,8 @@ static struct sysrq_key_op sysrq_term_op = { static void moom_callback(void *ignored) { - out_of_memory(&NODE_DATA(0)->node_zonelists[ZONE_NORMAL], GFP_KERNEL, 0); + out_of_memory(&NODE_DATA(0)->node_zonelists[ZONE_NORMAL], + GFP_KERNEL, 0); } static DECLARE_WORK(moom_work, moom_callback, NULL); @@ -260,7 +247,7 @@ static struct sysrq_key_op sysrq_moom_op = { }; static void sysrq_handle_kill(int key, struct pt_regs *pt_regs, - struct tty_struct *tty) + struct tty_struct *tty) { send_sig_all(SIGKILL); console_loglevel = 8; @@ -272,8 +259,6 @@ static struct sysrq_key_op sysrq_kill_op = { .enable_mask = SYSRQ_ENABLE_SIGNAL, }; -/* END SIGNAL SYSRQ HANDLERS BLOCK */ - static void sysrq_handle_unrt(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { @@ -288,110 +273,99 @@ static struct sysrq_key_op sysrq_unrt_op = { /* Key Operations table and lock */ static DEFINE_SPINLOCK(sysrq_key_table_lock); -#define SYSRQ_KEY_TABLE_LENGTH 36 -static struct sysrq_key_op *sysrq_key_table[SYSRQ_KEY_TABLE_LENGTH] = { -/* 0 */ &sysrq_loglevel_op, -/* 1 */ &sysrq_loglevel_op, -/* 2 */ &sysrq_loglevel_op, -/* 3 */ &sysrq_loglevel_op, -/* 4 */ &sysrq_loglevel_op, -/* 5 */ &sysrq_loglevel_op, -/* 6 */ &sysrq_loglevel_op, -/* 7 */ &sysrq_loglevel_op, -/* 8 */ &sysrq_loglevel_op, -/* 9 */ &sysrq_loglevel_op, -/* a */ NULL, /* Don't use for system provided sysrqs, - it is handled specially on the sparc - and will never arrive */ -/* b */ &sysrq_reboot_op, -#ifdef CONFIG_KEXEC -/* c */ &sysrq_crashdump_op, -#else -/* c */ NULL, -#endif -#ifdef CONFIG_DEBUG_MUTEXES -/* d */ &sysrq_showlocks_op, -#else -/* d */ NULL, -#endif -/* e */ &sysrq_term_op, -/* f */ &sysrq_moom_op, -/* g */ NULL, -/* h */ NULL, -/* i */ &sysrq_kill_op, -/* j */ NULL, -#ifdef CONFIG_VT -/* k */ &sysrq_SAK_op, -#else -/* k */ NULL, -#endif -/* l */ NULL, -/* m */ &sysrq_showmem_op, -/* n */ &sysrq_unrt_op, -/* o */ NULL, /* This will often be registered - as 'Off' at init time */ -/* p */ &sysrq_showregs_op, -/* q */ NULL, -#ifdef CONFIG_VT -/* r */ &sysrq_unraw_op, -#else -/* r */ NULL, -#endif -/* s */ &sysrq_sync_op, -/* t */ &sysrq_showstate_op, -/* u */ &sysrq_mountro_op, -/* v */ NULL, /* May be assigned at init time by SMP VOYAGER */ -/* w */ NULL, -/* x */ NULL, -/* y */ NULL, -/* z */ NULL + +static struct sysrq_key_op *sysrq_key_table[36] = { + &sysrq_loglevel_op, /* 0 */ + &sysrq_loglevel_op, /* 1 */ + &sysrq_loglevel_op, /* 2 */ + &sysrq_loglevel_op, /* 3 */ + &sysrq_loglevel_op, /* 4 */ + &sysrq_loglevel_op, /* 5 */ + &sysrq_loglevel_op, /* 6 */ + &sysrq_loglevel_op, /* 7 */ + &sysrq_loglevel_op, /* 8 */ + &sysrq_loglevel_op, /* 9 */ + + /* + * Don't use for system provided sysrqs, it is handled specially on + * sparc and will never arrive + */ + NULL, /* a */ + &sysrq_reboot_op, /* b */ + &sysrq_crashdump_op, /* c */ + &sysrq_showlocks_op, /* d */ + &sysrq_term_op, /* e */ + &sysrq_moom_op, /* f */ + NULL, /* g */ + NULL, /* h */ + &sysrq_kill_op, /* i */ + NULL, /* j */ + &sysrq_SAK_op, /* k */ + NULL, /* l */ + &sysrq_showmem_op, /* m */ + &sysrq_unrt_op, /* n */ + /* This will often be registered as 'Off' at init time */ + NULL, /* o */ + &sysrq_showregs_op, /* p */ + NULL, /* q */ + &sysrq_unraw_op, /* r */ + &sysrq_sync_op, /* s */ + &sysrq_showstate_op, /* t */ + &sysrq_mountro_op, /* u */ + /* May be assigned at init time by SMP VOYAGER */ + NULL, /* v */ + NULL, /* w */ + NULL, /* x */ + NULL, /* y */ + NULL /* z */ }; /* key2index calculation, -1 on invalid index */ -static int sysrq_key_table_key2index(int key) { +static int sysrq_key_table_key2index(int key) +{ int retval; - if ((key >= '0') && (key <= '9')) { + + if ((key >= '0') && (key <= '9')) retval = key - '0'; - } else if ((key >= 'a') && (key <= 'z')) { + else if ((key >= 'a') && (key <= 'z')) retval = key + 10 - 'a'; - } else { + else retval = -1; - } return retval; } /* * get and put functions for the table, exposed to modules. */ - -struct sysrq_key_op *__sysrq_get_key_op (int key) { - struct sysrq_key_op *op_p; +struct sysrq_key_op *__sysrq_get_key_op(int key) +{ + struct sysrq_key_op *op_p = NULL; int i; - + i = sysrq_key_table_key2index(key); - op_p = (i == -1) ? NULL : sysrq_key_table[i]; + if (i != -1) + op_p = sysrq_key_table[i]; return op_p; } -static void __sysrq_put_key_op (int key, struct sysrq_key_op *op_p) { - int i; +static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p) +{ + int i = sysrq_key_table_key2index(key); - i = sysrq_key_table_key2index(key); if (i != -1) sysrq_key_table[i] = op_p; } /* - * This is the non-locking version of handle_sysrq - * It must/can only be called by sysrq key handlers, - * as they are inside of the lock + * This is the non-locking version of handle_sysrq. It must/can only be called + * by sysrq key handlers, as they are inside of the lock */ - -void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, int check_mask) +void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, + int check_mask) { struct sysrq_key_op *op_p; int orig_log_level; - int i, j; + int i; unsigned long flags; spin_lock_irqsave(&sysrq_key_table_lock, flags); @@ -401,26 +375,34 @@ void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, in op_p = __sysrq_get_key_op(key); if (op_p) { - /* Should we check for enabled operations (/proc/sysrq-trigger should not) - * and is the invoked operation enabled? */ + /* + * Should we check for enabled operations (/proc/sysrq-trigger + * should not) and is the invoked operation enabled? + */ if (!check_mask || sysrq_enabled == 1 || (sysrq_enabled & op_p->enable_mask)) { - printk ("%s\n", op_p->action_msg); + printk("%s\n", op_p->action_msg); console_loglevel = orig_log_level; op_p->handler(key, pt_regs, tty); - } - else + } else { printk("This sysrq operation is disabled.\n"); + } } else { printk("HELP : "); /* Only print the help msg once per handler */ - for (i=0; i<SYSRQ_KEY_TABLE_LENGTH; i++) - if (sysrq_key_table[i]) { - for (j=0; sysrq_key_table[i] != sysrq_key_table[j]; j++); - if (j == i) - printk ("%s ", sysrq_key_table[i]->help_msg); + for (i = 0; i < ARRAY_SIZE(sysrq_key_table); i++) { + if (sysrq_key_table[i]) { + int j; + + for (j = 0; sysrq_key_table[i] != + sysrq_key_table[j]; j++) + ; + if (j != i) + continue; + printk("%s ", sysrq_key_table[i]->help_msg); + } } - printk ("\n"); + printk("\n"); console_loglevel = orig_log_level; } spin_unlock_irqrestore(&sysrq_key_table_lock, flags); @@ -430,16 +412,17 @@ void __handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty, in * This function is called by the keyboard handler when SysRq is pressed * and any other keycode arrives. */ - void handle_sysrq(int key, struct pt_regs *pt_regs, struct tty_struct *tty) { if (!sysrq_enabled) return; __handle_sysrq(key, pt_regs, tty, 1); } +EXPORT_SYMBOL(handle_sysrq); static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, - struct sysrq_key_op *remove_op_p) { + struct sysrq_key_op *remove_op_p) +{ int retval; unsigned long flags; @@ -452,7 +435,6 @@ static int __sysrq_swap_key_ops(int key, struct sysrq_key_op *insert_op_p, retval = -1; } spin_unlock_irqrestore(&sysrq_key_table_lock, flags); - return retval; } @@ -460,12 +442,10 @@ int register_sysrq_key(int key, struct sysrq_key_op *op_p) { return __sysrq_swap_key_ops(key, op_p, NULL); } +EXPORT_SYMBOL(register_sysrq_key); int unregister_sysrq_key(int key, struct sysrq_key_op *op_p) { return __sysrq_swap_key_ops(key, NULL, op_p); } - -EXPORT_SYMBOL(handle_sysrq); -EXPORT_SYMBOL(register_sysrq_key); EXPORT_SYMBOL(unregister_sysrq_key); diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index fd3a4beaa53..dec0224b447 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -24,6 +24,7 @@ #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/platform_device.h> +#include <linux/io.h> enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 76592ee1fb3..48d795bb8c4 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -354,7 +354,7 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size) EXPORT_SYMBOL_GPL(tty_buffer_request_room); -int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size) +int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, size_t size) { int copied = 0; do { @@ -378,7 +378,7 @@ int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t EXPORT_SYMBOL_GPL(tty_insert_flip_string); -int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, char *flags, size_t size) +int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size) { int copied = 0; do { diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index c0dfcf273f0..16e99db2e12 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -148,6 +148,16 @@ config MPCORE_WATCHDOG To compile this driver as a module, choose M here: the module will be called mpcore_wdt. +config EP93XX_WATCHDOG + tristate "EP93xx Watchdog" + depends on WATCHDOG && ARCH_EP93XX + help + Say Y here if to include support for the watchdog timer + embedded in the Cirrus Logic EP93xx family of devices. + + To compile this driver as a module, choose M here: the + module will be called ep93xx_wdt. + # X86 (i386 + ia64 + x86_64) Architecture config ACQUIRE_WDT diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 36c0b282b8b..d6f27fde990 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o +obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o # X86 (i386 + ia64 + x86_64) Architecture obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o diff --git a/drivers/char/watchdog/ep93xx_wdt.c b/drivers/char/watchdog/ep93xx_wdt.c new file mode 100644 index 00000000000..9021dbb7829 --- /dev/null +++ b/drivers/char/watchdog/ep93xx_wdt.c @@ -0,0 +1,257 @@ +/* + * Watchdog driver for Cirrus Logic EP93xx family of devices. + * + * Copyright (c) 2004 Ray Lehtiniemi + * Copyright (c) 2006 Tower Technologies + * Based on ep93xx driver, bits from alim7101_wdt.c + * + * Authors: Ray Lehtiniemi <rayl@mail.com>, + * Alessandro Zummo <a.zummo@towertech.it> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + * + * This watchdog fires after 250msec, which is a too short interval + * for us to rely on the user space daemon alone. So we ping the + * wdt each ~200msec and eventually stop doing it if the user space + * daemon dies. + * + * TODO: + * + * - Test last reset from watchdog status + * - Add a few missing ioctls + */ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/timer.h> + +#include <asm/hardware.h> +#include <asm/uaccess.h> + +#define WDT_VERSION "0.3" +#define PFX "ep93xx_wdt: " + +/* default timeout (secs) */ +#define WDT_TIMEOUT 30 + +static int nowayout = WATCHDOG_NOWAYOUT; +static int timeout = WDT_TIMEOUT; + +static struct timer_list timer; +static unsigned long next_heartbeat; +static unsigned long wdt_status; +static unsigned long boot_status; + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +#define EP93XX_WDT_REG(x) (EP93XX_WATCHDOG_BASE + (x)) +#define EP93XX_WDT_WATCHDOG EP93XX_WDT_REG(0x00) +#define EP93XX_WDT_WDSTATUS EP93XX_WDT_REG(0x04) + +/* reset the wdt every ~200ms */ +#define WDT_INTERVAL (HZ/5) + +static void wdt_enable(void) +{ + __raw_writew(0xaaaa, EP93XX_WDT_WATCHDOG); +} + +static void wdt_disable(void) +{ + __raw_writew(0xaa55, EP93XX_WDT_WATCHDOG); +} + +static inline void wdt_ping(void) +{ + __raw_writew(0x5555, EP93XX_WDT_WATCHDOG); +} + +static void wdt_startup(void) +{ + next_heartbeat = jiffies + (timeout * HZ); + + wdt_enable(); + mod_timer(&timer, jiffies + WDT_INTERVAL); +} + +static void wdt_shutdown(void) +{ + del_timer_sync(&timer); + wdt_disable(); +} + +static void wdt_keepalive(void) +{ + /* user land ping */ + next_heartbeat = jiffies + (timeout * HZ); +} + +static int ep93xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + wdt_startup(); + + return nonseekable_open(inode, file); +} + +static ssize_t +ep93xx_wdt_write(struct file *file, const char __user *data, size_t len, + loff_t *ppos) +{ + /* Can't seek (pwrite) on this device */ + if (*ppos != file->f_pos) + return -ESPIPE; + + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + else + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_keepalive(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE, + .identity = "EP93xx Watchdog", +}; + +static int +ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOIOCTLCMD; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, (int __user *)arg); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, (int __user *)arg); + break; + + case WDIOC_GETTIMEOUT: + /* actually, it is 0.250 seconds.... */ + ret = put_user(1, (int __user *)arg); + break; + + case WDIOC_KEEPALIVE: + wdt_keepalive(); + ret = 0; + break; + } + return ret; +} + +static int ep93xx_wdt_release(struct inode *inode, struct file *file) +{ + if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) + wdt_shutdown(); + else + printk(KERN_CRIT PFX "Device closed unexpectedly - " + "timer will not stop\n"); + + clear_bit(WDT_IN_USE, &wdt_status); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + return 0; +} + +static struct file_operations ep93xx_wdt_fops = { + .owner = THIS_MODULE, + .write = ep93xx_wdt_write, + .ioctl = ep93xx_wdt_ioctl, + .open = ep93xx_wdt_open, + .release = ep93xx_wdt_release, +}; + +static struct miscdevice ep93xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &ep93xx_wdt_fops, +}; + +static void ep93xx_timer_ping(unsigned long data) +{ + if (time_before(jiffies, next_heartbeat)) + wdt_ping(); + + /* Re-set the timer interval */ + mod_timer(&timer, jiffies + WDT_INTERVAL); +} + +static int __init ep93xx_wdt_init(void) +{ + int err; + + err = misc_register(&ep93xx_wdt_miscdev); + + boot_status = __raw_readl(EP93XX_WDT_WATCHDOG) & 0x01 ? 1 : 0; + + printk(KERN_INFO PFX "EP93XX watchdog, driver version " + WDT_VERSION "%s\n", + (__raw_readl(EP93XX_WDT_WATCHDOG) & 0x08) + ? " (nCS1 disable detected)" : ""); + + if (timeout < 1 || timeout > 3600) { + timeout = WDT_TIMEOUT; + printk(KERN_INFO PFX + "timeout value must be 1<=x<=3600, using %d\n", + timeout); + } + + setup_timer(&timer, ep93xx_timer_ping, 1); + return err; +} + +static void __exit ep93xx_wdt_exit(void) +{ + wdt_shutdown(); + misc_deregister(&ep93xx_wdt_miscdev); +} + +module_init(ep93xx_wdt_init); +module_exit(ep93xx_wdt_exit); + +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +module_param(timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. (1<=timeout<=3600, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); + +MODULE_AUTHOR("Ray Lehtiniemi <rayl@mail.com>," + "Alessandro Zummo <a.zummo@towertech.it>"); +MODULE_DESCRIPTION("EP93xx Watchdog"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(WDT_VERSION); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |