summaryrefslogtreecommitdiff
path: root/arch/arm/common
diff options
context:
space:
mode:
authorDima Zavin <dima@android.com>2011-10-02 20:35:47 -0700
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:39:13 -0800
commit37f2fdb72a756e54dda38372edc3fa84d9aef1a4 (patch)
tree8a936d38c3c4ef7a82fa07049e34d0b477ef08f4 /arch/arm/common
parent985ecccfab39e289fb1f1e1b0641cc2c298bb4ce (diff)
ARM: common: fiq_debugger: add non-fiq debugger support
Add irq-only support to the debugger. This allows the debugger to always run at irq context. This introduces limitations to being able to debug certain kinds of issues, but it is still very useful as a debugging tool. Change-Id: I1e4223e886cb2d90ef5ed31419bdd5cdd7f904ca Signed-off-by: Dima Zavin <dima@android.com>
Diffstat (limited to 'arch/arm/common')
-rw-r--r--arch/arm/common/fiq_debugger.c219
1 files changed, 169 insertions, 50 deletions
diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c
index 71fa962bc535..4c04cec67f9e 100644
--- a/arch/arm/common/fiq_debugger.c
+++ b/arch/arm/common/fiq_debugger.c
@@ -28,6 +28,7 @@
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/slab.h>
+#include <linux/smp.h>
#include <linux/timer.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -53,6 +54,7 @@ struct fiq_debugger_state {
struct fiq_glue_handler handler;
int fiq;
+ int uart_irq;
int signal_irq;
int wakeup_irq;
bool wakeup_irq_no_set_wake;
@@ -130,12 +132,20 @@ static inline void disable_wakeup_irq(struct fiq_debugger_state *state)
}
#endif
+static bool inline debug_have_fiq(struct fiq_debugger_state *state)
+{
+ return (state->fiq >= 0);
+}
+
static void debug_force_irq(struct fiq_debugger_state *state)
{
unsigned int irq = state->signal_irq;
- if (state->pdata->force_irq)
+
+ if (WARN_ON(!debug_have_fiq(state)))
+ return;
+ if (state->pdata->force_irq) {
state->pdata->force_irq(state->pdev, irq);
- else {
+ } else {
struct irq_chip *chip = irq_get_chip(irq);
if (chip && chip->irq_retrigger)
chip->irq_retrigger(irq_get_irq_data(irq));
@@ -447,7 +457,7 @@ void dump_stacktrace(struct fiq_debugger_state *state,
tail = user_backtrace(state, tail);
}
-static void debug_help(struct fiq_debugger_state *state)
+static bool debug_help(struct fiq_debugger_state *state)
{
debug_printf(state, "FIQ Debugger commands:\n"
" pc PC status\n"
@@ -466,15 +476,37 @@ static void debug_help(struct fiq_debugger_state *state)
if (!state->debug_busy) {
strcpy(state->debug_cmd, "help");
state->debug_busy = 1;
- debug_force_irq(state);
+ return true;
}
+
+ return false;
+}
+
+static void take_affinity(void *info)
+{
+ struct fiq_debugger_state *state = info;
+ struct cpumask cpumask;
+
+ cpumask_clear(&cpumask);
+ cpumask_set_cpu(get_cpu(), &cpumask);
+
+ irq_set_affinity(state->uart_irq, &cpumask);
+}
+
+static void switch_cpu(struct fiq_debugger_state *state, int cpu)
+{
+ if (!debug_have_fiq(state))
+ smp_call_function_single(cpu, take_affinity, state, false);
+ state->current_cpu = cpu;
}
-static void debug_exec(struct fiq_debugger_state *state,
+static bool debug_exec(struct fiq_debugger_state *state,
const char *cmd, unsigned *regs, void *svc_sp)
{
+ bool signal_helper = false;
+
if (!strcmp(cmd, "help") || !strcmp(cmd, "?")) {
- debug_help(state);
+ signal_helper |= debug_help(state);
} else if (!strcmp(cmd, "pc")) {
debug_printf(state, " pc %08x cpsr %08x mode %s\n",
regs[15], regs[16], mode_name(regs[16]));
@@ -494,8 +526,10 @@ static void debug_exec(struct fiq_debugger_state *state,
debug_printf(state, "%s\n", linux_banner);
} else if (!strcmp(cmd, "sleep")) {
state->no_sleep = false;
+ debug_printf(state, "enabling sleep\n");
} else if (!strcmp(cmd, "nosleep")) {
state->no_sleep = true;
+ debug_printf(state, "disabling sleep\n");
} else if (!strcmp(cmd, "console")) {
state->console_enable = true;
debug_printf(state, "console mode\n");
@@ -504,7 +538,7 @@ static void debug_exec(struct fiq_debugger_state *state,
} else if (!strncmp(cmd, "cpu ", 4)) {
unsigned long cpu = 0;
if (strict_strtoul(cmd + 4, 10, &cpu) == 0)
- state->current_cpu = cpu;
+ switch_cpu(state, cpu);
else
debug_printf(state, "invalid cpu\n");
debug_printf(state, "cpu %d\n", state->current_cpu);
@@ -518,12 +552,12 @@ static void debug_exec(struct fiq_debugger_state *state,
state->debug_busy = 1;
}
- debug_force_irq(state);
-
- return;
+ return true;
}
if (!state->console_enable)
debug_prompt(state);
+
+ return signal_helper;
}
static void sleep_timer_expired(unsigned long data)
@@ -544,15 +578,11 @@ static void sleep_timer_expired(unsigned long data)
wake_unlock(&state->debugger_wake_lock);
}
-static irqreturn_t wakeup_irq_handler(int irq, void *dev)
+static void handle_wakeup(struct fiq_debugger_state *state)
{
- struct fiq_debugger_state *state = dev;
-
- if (!state->no_sleep)
- debug_puts(state, "WAKEUP\n");
- if (state->ignore_next_wakeup_irq)
+ if (state->wakeup_irq >= 0 && state->ignore_next_wakeup_irq) {
state->ignore_next_wakeup_irq = false;
- else if (!state->uart_clk_enabled) {
+ } else if (!state->uart_clk_enabled) {
wake_lock(&state->debugger_wake_lock);
if (state->clk)
clk_enable(state->clk);
@@ -560,15 +590,22 @@ static irqreturn_t wakeup_irq_handler(int irq, void *dev)
disable_wakeup_irq(state);
mod_timer(&state->sleep_timer, jiffies + HZ / 2);
}
- return IRQ_HANDLED;
}
-static irqreturn_t debug_irq(int irq, void *dev)
+static irqreturn_t wakeup_irq_handler(int irq, void *dev)
{
struct fiq_debugger_state *state = dev;
- if (state->pdata->force_irq_ack)
- state->pdata->force_irq_ack(state->pdev, state->signal_irq);
+ if (!state->no_sleep)
+ debug_puts(state, "WAKEUP\n");
+ handle_wakeup(state);
+
+ return IRQ_HANDLED;
+}
+
+
+static void debug_handle_irq_context(struct fiq_debugger_state *state)
+{
if (!state->no_sleep) {
wake_lock(&state->debugger_wake_lock);
mod_timer(&state->sleep_timer, jiffies + HZ * 5);
@@ -596,7 +633,6 @@ static irqreturn_t debug_irq(int irq, void *dev)
state->debug_busy = 0;
}
- return IRQ_HANDLED;
}
static int debug_getc(struct fiq_debugger_state *state)
@@ -604,30 +640,29 @@ static int debug_getc(struct fiq_debugger_state *state)
return state->pdata->uart_getc(state->pdev);
}
-static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
+static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state,
+ int this_cpu, void *regs, void *svc_sp)
{
- struct fiq_debugger_state *state =
- container_of(h, struct fiq_debugger_state, handler);
int c;
static int last_c;
int count = 0;
- unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu;
+ bool signal_helper = false;
if (this_cpu != state->current_cpu) {
if (state->in_fiq)
- return;
+ return false;
if (atomic_inc_return(&state->unhandled_fiq_count) !=
MAX_UNHANDLED_FIQ_COUNT)
- return;
+ return false;
debug_printf(state, "fiq_debugger: cpu %d not responding, "
"reverting to cpu %d\n", state->current_cpu,
this_cpu);
atomic_set(&state->unhandled_fiq_count, 0);
- state->current_cpu = this_cpu;
- return;
+ switch_cpu(state, this_cpu);
+ return false;
}
state->in_fiq = true;
@@ -648,7 +683,7 @@ static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
#ifdef CONFIG_FIQ_DEBUGGER_CONSOLE
} else if (state->console_enable && state->tty_rbuf) {
fiq_debugger_ringbuf_push(state->tty_rbuf, c);
- debug_force_irq(state);
+ signal_helper = true;
#endif
} else if ((c >= ' ') && (c < 127)) {
if (state->debug_count < (DEBUG_MAX - 1)) {
@@ -670,8 +705,9 @@ static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
if (state->debug_count) {
state->debug_buf[state->debug_count] = 0;
state->debug_count = 0;
- debug_exec(state, state->debug_buf,
- regs, svc_sp);
+ signal_helper |=
+ debug_exec(state, state->debug_buf,
+ regs, svc_sp);
} else {
debug_prompt(state);
}
@@ -684,10 +720,63 @@ static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
/* poke sleep timer if necessary */
if (state->debug_enable && !state->no_sleep)
- debug_force_irq(state);
+ signal_helper = true;
atomic_set(&state->unhandled_fiq_count, 0);
state->in_fiq = false;
+
+ return signal_helper;
+}
+
+static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp)
+{
+ struct fiq_debugger_state *state =
+ container_of(h, struct fiq_debugger_state, handler);
+ unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu;
+ bool need_irq;
+
+ need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp);
+ if (need_irq)
+ debug_force_irq(state);
+}
+
+/*
+ * When not using FIQs, we only use this single interrupt as an entry point.
+ * This just effectively takes over the UART interrupt and does all the work
+ * in this context.
+ */
+static irqreturn_t debug_uart_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+ bool not_done;
+
+ handle_wakeup(state);
+
+ /* handle the debugger irq in regular context */
+ not_done = debug_handle_uart_interrupt(state, smp_processor_id(),
+ get_irq_regs(),
+ current_thread_info());
+ if (not_done)
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * If FIQs are used, not everything can happen in fiq context.
+ * FIQ handler does what it can and then signals this interrupt to finish the
+ * job in irq context.
+ */
+static irqreturn_t debug_signal_irq(int irq, void *dev)
+{
+ struct fiq_debugger_state *state = dev;
+
+ if (state->pdata->force_irq_ack)
+ state->pdata->force_irq_ack(state->pdev, state->signal_irq);
+
+ debug_handle_irq_context(state);
+
+ return IRQ_HANDLED;
}
static void debug_resume(struct fiq_glue_handler *h)
@@ -834,13 +923,23 @@ static int fiq_debugger_probe(struct platform_device *pdev)
int ret;
struct fiq_debugger_pdata *pdata = dev_get_platdata(&pdev->dev);
struct fiq_debugger_state *state;
+ int fiq;
+ int uart_irq;
+
+ if (!pdata->uart_getc || !pdata->uart_putc)
+ return -EINVAL;
- if (!pdata->uart_getc || !pdata->uart_putc || !pdata->fiq_enable)
+ fiq = platform_get_irq_byname(pdev, "fiq");
+ uart_irq = platform_get_irq_byname(pdev, "uart_irq");
+
+ /* uart_irq mode and fiq mode are mutually exclusive, but one of them
+ * is required */
+ if ((uart_irq < 0 && fiq < 0) || (uart_irq >= 0 && fiq >= 0))
+ return -EINVAL;
+ if (fiq >= 0 && !pdata->fiq_enable)
return -EINVAL;
state = kzalloc(sizeof(*state), GFP_KERNEL);
- state->handler.fiq = debug_fiq;
- state->handler.resume = debug_resume;
setup_timer(&state->sleep_timer, sleep_timer_expired,
(unsigned long)state);
state->pdata = pdata;
@@ -849,11 +948,12 @@ static int fiq_debugger_probe(struct platform_device *pdev)
state->debug_enable = initial_debug_enable;
state->console_enable = initial_console_enable;
- state->fiq = platform_get_irq_byname(pdev, "fiq");
+ state->fiq = fiq;
+ state->uart_irq = uart_irq;
state->signal_irq = platform_get_irq_byname(pdev, "signal");
state->wakeup_irq = platform_get_irq_byname(pdev, "wakeup");
- if (state->wakeup_irq < 0)
+ if (state->wakeup_irq < 0 && debug_have_fiq(state))
state->no_sleep = true;
state->ignore_next_wakeup_irq = !state->no_sleep;
@@ -876,21 +976,39 @@ static int fiq_debugger_probe(struct platform_device *pdev)
debug_printf_nfiq(state, "<hit enter %sto activate fiq debugger>\n",
state->no_sleep ? "" : "twice ");
- ret = fiq_glue_register_handler(&state->handler);
- if (ret) {
- pr_err("serial_debugger: could not install fiq handler\n");
- goto err_register_fiq;
- }
+ if (debug_have_fiq(state)) {
+ state->handler.fiq = debug_fiq;
+ state->handler.resume = debug_resume;
+ ret = fiq_glue_register_handler(&state->handler);
+ if (ret) {
+ pr_err("%s: could not install fiq handler\n", __func__);
+ goto err_register_fiq;
+ }
- pdata->fiq_enable(pdev, state->fiq, 1);
+ pdata->fiq_enable(pdev, state->fiq, 1);
+ } else {
+ ret = request_irq(state->uart_irq, debug_uart_irq,
+ 0, "debug", state);
+ if (ret) {
+ pr_err("%s: could not install irq handler\n", __func__);
+ goto err_register_irq;
+ }
+
+ /* for irq-only mode, we want this irq to wake us up, if it
+ * can.
+ */
+ enable_irq_wake(state->uart_irq);
+ }
if (state->clk)
clk_disable(state->clk);
- ret = request_irq(state->signal_irq, debug_irq,
- IRQF_TRIGGER_RISING, "debug", state);
- if (ret)
- pr_err("serial_debugger: could not install signal_irq");
+ if (state->signal_irq >= 0) {
+ ret = request_irq(state->signal_irq, debug_signal_irq,
+ IRQF_TRIGGER_RISING, "debug-signal", state);
+ if (ret)
+ pr_err("serial_debugger: could not install signal_irq");
+ }
if (state->wakeup_irq >= 0) {
ret = request_irq(state->wakeup_irq, wakeup_irq_handler,
@@ -910,7 +1028,7 @@ static int fiq_debugger_probe(struct platform_device *pdev)
}
}
if (state->no_sleep)
- wakeup_irq_handler(state->wakeup_irq, state);
+ handle_wakeup(state);
#if defined(CONFIG_FIQ_DEBUGGER_CONSOLE)
state->console = fiq_debugger_console;
@@ -919,6 +1037,7 @@ static int fiq_debugger_probe(struct platform_device *pdev)
#endif
return 0;
+err_register_irq:
err_register_fiq:
if (pdata->uart_free)
pdata->uart_free(pdev);