diff options
author | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2007-10-10 14:58:29 +0200 |
---|---|---|
committer | Haavard Skinnemoen <hskinnemoen@atmel.com> | 2008-01-25 08:31:43 +0100 |
commit | e7ba176b47db2ed53f258a6b4fe9d9fc6fa437a9 (patch) | |
tree | beb9ffab7da0c24f11c04b6eb4ca29b23b1dd07b /arch/avr32/kernel/nmi_debug.c | |
parent | f6135d12db4bed3b992052020f1c50d749cd8dc6 (diff) |
[AVR32] NMI debugging
Change the NMI handler to use the die notifier chain to signal anyone
who cares. Add a simple "nmi debugger" which hooks into this chain and
that may dump registers, task state, etc. when it happens.
Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
Diffstat (limited to 'arch/avr32/kernel/nmi_debug.c')
-rw-r--r-- | arch/avr32/kernel/nmi_debug.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/arch/avr32/kernel/nmi_debug.c b/arch/avr32/kernel/nmi_debug.c new file mode 100644 index 000000000000..3414b8566c29 --- /dev/null +++ b/arch/avr32/kernel/nmi_debug.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2007 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/kdebug.h> +#include <linux/notifier.h> +#include <linux/sched.h> + +#include <asm/irq.h> + +enum nmi_action { + NMI_SHOW_STATE = 1 << 0, + NMI_SHOW_REGS = 1 << 1, + NMI_DIE = 1 << 2, + NMI_DEBOUNCE = 1 << 3, +}; + +static unsigned long nmi_actions; + +static int nmi_debug_notify(struct notifier_block *self, + unsigned long val, void *data) +{ + struct die_args *args = data; + + if (likely(val != DIE_NMI)) + return NOTIFY_DONE; + + if (nmi_actions & NMI_SHOW_STATE) + show_state(); + if (nmi_actions & NMI_SHOW_REGS) + show_regs(args->regs); + if (nmi_actions & NMI_DEBOUNCE) + mdelay(10); + if (nmi_actions & NMI_DIE) + return NOTIFY_BAD; + + return NOTIFY_OK; +} + +static struct notifier_block nmi_debug_nb = { + .notifier_call = nmi_debug_notify, +}; + +static int __init nmi_debug_setup(char *str) +{ + char *p, *sep; + + register_die_notifier(&nmi_debug_nb); + if (nmi_enable()) { + printk(KERN_WARNING "Unable to enable NMI.\n"); + return 0; + } + + if (*str != '=') + return 0; + + for (p = str + 1; *p; p = sep + 1) { + sep = strchr(p, ','); + if (sep) + *sep = 0; + if (strcmp(p, "state") == 0) + nmi_actions |= NMI_SHOW_STATE; + else if (strcmp(p, "regs") == 0) + nmi_actions |= NMI_SHOW_REGS; + else if (strcmp(p, "debounce") == 0) + nmi_actions |= NMI_DEBOUNCE; + else if (strcmp(p, "die") == 0) + nmi_actions |= NMI_DIE; + else + printk(KERN_WARNING "NMI: Unrecognized action `%s'\n", + p); + if (!sep) + break; + } + + return 0; +} +__setup("nmi_debug", nmi_debug_setup); |