123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- /*
- * drivers/serial/msm_serial_debuger.c
- *
- * Serial Debugger Interface for MSM7K
- *
- * Copyright (C) 2008 Google, Inc.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
- #include <stdarg.h>
- #include <linux/module.h>
- #include <linux/io.h>
- #include <linux/console.h>
- #include <linux/interrupt.h>
- #include <linux/clk.h>
- #include <linux/platform_device.h>
- #include <linux/kernel_debugger.h>
- #include <linux/kernel_stat.h>
- #include <linux/irq.h>
- #include <linux/delay.h>
- #include <mach/system.h>
- #include <mach/fiq.h>
- #include "msm_serial.h"
- static unsigned int debug_port_base;
- static int debug_signal_irq;
- static struct clk *debug_clk;
- static int debug_enable;
- static int debugger_enable;
- static struct {
- unsigned int base;
- int irq;
- struct device *clk_device;
- int signal_irq;
- } init_data;
- static inline void msm_write(unsigned int val, unsigned int off)
- {
- __raw_writel(val, debug_port_base + off);
- }
- static inline unsigned int msm_read(unsigned int off)
- {
- return __raw_readl(debug_port_base + off);
- }
- static void debug_port_init(void)
- {
- /* reset everything */
- msm_write(UART_CR_CMD_RESET_RX, UART_CR);
- msm_write(UART_CR_CMD_RESET_TX, UART_CR);
- msm_write(UART_CR_CMD_RESET_ERR, UART_CR);
- msm_write(UART_CR_CMD_RESET_BREAK_INT, UART_CR);
- msm_write(UART_CR_CMD_RESET_CTS, UART_CR);
- msm_write(UART_CR_CMD_SET_RFR, UART_CR);
- /* setup clock dividers */
- if (clk_get_rate(debug_clk) == 19200000) {
- /* clock is TCXO (19.2MHz) */
- msm_write(0x06, UART_MREG);
- msm_write(0xF1, UART_NREG);
- msm_write(0x0F, UART_DREG);
- msm_write(0x1A, UART_MNDREG);
- } else {
- /* clock must be TCXO/4 */
- msm_write(0x18, UART_MREG);
- msm_write(0xF6, UART_NREG);
- msm_write(0x0F, UART_DREG);
- msm_write(0x0A, UART_MNDREG);
- }
- msm_write(UART_CSR_115200, UART_CSR);
- /* rx interrupt on every character -- keep it simple */
- msm_write(0, UART_RFWR);
- /* enable TX and RX */
- msm_write(0x05, UART_CR);
- /* enable RX interrupt */
- msm_write(UART_IMR_RXLEV, UART_IMR);
- }
- static inline int debug_getc(void)
- {
- if (msm_read(UART_SR) & UART_SR_RX_READY) {
- return msm_read(UART_RF);
- } else {
- return -1;
- }
- }
- static inline void debug_putc(unsigned int c)
- {
- while (!(msm_read(UART_SR) & UART_SR_TX_READY)) ;
- msm_write(c, UART_TF);
- }
- static inline void debug_flush(void)
- {
- while (!(msm_read(UART_SR) & UART_SR_TX_EMPTY)) ;
- }
- static void debug_puts(char *s)
- {
- unsigned c;
- while ((c = *s++)) {
- if (c == '\n')
- debug_putc('\r');
- debug_putc(c);
- }
- }
- static void debug_prompt(void)
- {
- debug_puts("debug> ");
- }
- int log_buf_copy(char *dest, int idx, int len);
- static void dump_kernel_log(void)
- {
- char buf[1024];
- int idx = 0;
- int ret;
- int saved_oip;
- /* setting oops_in_progress prevents log_buf_copy()
- * from trying to take a spinlock which will make it
- * very unhappy in some cases...
- */
- saved_oip = oops_in_progress;
- oops_in_progress = 1;
- for (;;) {
- ret = log_buf_copy(buf, idx, 1023);
- if (ret <= 0)
- break;
- buf[ret] = 0;
- debug_puts(buf);
- idx += ret;
- }
- oops_in_progress = saved_oip;
- }
- static char *mode_name(unsigned cpsr)
- {
- switch (cpsr & MODE_MASK) {
- case USR_MODE: return "USR";
- case FIQ_MODE: return "FIQ";
- case IRQ_MODE: return "IRQ";
- case SVC_MODE: return "SVC";
- case ABT_MODE: return "ABT";
- case UND_MODE: return "UND";
- case SYSTEM_MODE: return "SYS";
- default: return "???";
- }
- }
- #define DEBUG_MAX 64
- static char debug_cmd[DEBUG_MAX];
- static int debug_busy;
- static int debug_abort;
- static int debug_printf(void *cookie, const char *fmt, ...)
- {
- char buf[256];
- va_list ap;
- va_start(ap, fmt);
- vsnprintf(buf, 128, fmt, ap);
- va_end(ap);
- debug_puts(buf);
- return debug_abort;
- }
- /* Safe outside fiq context */
- static int debug_printf_nfiq(void *cookie, const char *fmt, ...)
- {
- char buf[256];
- va_list ap;
- unsigned long irq_flags;
- va_start(ap, fmt);
- vsnprintf(buf, 128, fmt, ap);
- va_end(ap);
- local_irq_save(irq_flags);
- debug_puts(buf);
- debug_flush();
- local_irq_restore(irq_flags);
- return debug_abort;
- }
- #define dprintf(fmt...) debug_printf(0, fmt)
- unsigned int last_irqs[NR_IRQS];
- static void dump_irqs(void)
- {
- int n;
- dprintf("irqnr total since-last status name\n");
- for (n = 1; n < NR_IRQS; n++) {
- struct irqaction *act = irq_desc[n].action;
- if (!act && !kstat_cpu(0).irqs[n])
- continue;
- dprintf("%5d: %10u %11u %8x %s\n", n,
- kstat_cpu(0).irqs[n],
- kstat_cpu(0).irqs[n] - last_irqs[n],
- irq_desc[n].status,
- (act && act->name) ? act->name : "???");
- last_irqs[n] = kstat_cpu(0).irqs[n];
- }
- }
- static void debug_exec(const char *cmd, unsigned *regs)
- {
- if (!strcmp(cmd, "pc")) {
- dprintf(" pc %08x cpsr %08x mode %s\n",
- regs[15], regs[16], mode_name(regs[16]));
- } else if (!strcmp(cmd, "regs")) {
- dprintf(" r0 %08x r1 %08x r2 %08x r3 %08x\n",
- regs[0], regs[1], regs[2], regs[3]);
- dprintf(" r4 %08x r5 %08x r6 %08x r7 %08x\n",
- regs[4], regs[5], regs[6], regs[7]);
- dprintf(" r8 %08x r9 %08x r10 %08x r11 %08x mode %s\n",
- regs[8], regs[9], regs[10], regs[11],
- mode_name(regs[16]));
- dprintf(" ip %08x sp %08x lr %08x pc %08x cpsr %08x\n",
- regs[10], regs[13], regs[14], regs[15], regs[16]);
- } else if (!strcmp(cmd, "reboot")) {
- if (msm_hw_reset_hook)
- msm_hw_reset_hook();
- } else if (!strcmp(cmd, "irqs")) {
- dump_irqs();
- } else if (!strcmp(cmd, "kmsg")) {
- dump_kernel_log();
- } else if (!strcmp(cmd, "version")) {
- dprintf("%s\n", linux_banner);
- } else {
- if (debug_busy) {
- dprintf("command processor busy. trying to abort.\n");
- debug_abort = -1;
- } else {
- strcpy(debug_cmd, cmd);
- debug_busy = 1;
- }
- msm_trigger_irq(debug_signal_irq);
- return;
- }
- debug_prompt();
- }
- static irqreturn_t debug_irq(int irq, void *dev)
- {
- if (debug_busy) {
- struct kdbg_ctxt ctxt;
- ctxt.printf = debug_printf_nfiq;
- kernel_debugger(&ctxt, debug_cmd);
- debug_prompt();
- debug_busy = 0;
- }
- return IRQ_HANDLED;
- }
- static char debug_buf[DEBUG_MAX];
- static int debug_count;
- static void debug_fiq(void *data, void *regs)
- {
- int c;
- static int last_c;
- while ((c = debug_getc()) != -1) {
- if (!debug_enable) {
- if ((c == 13) || (c == 10)) {
- debug_enable = true;
- debug_count = 0;
- debug_prompt();
- }
- } else if ((c >= ' ') && (c < 127)) {
- if (debug_count < (DEBUG_MAX - 1)) {
- debug_buf[debug_count++] = c;
- debug_putc(c);
- }
- } else if ((c == 8) || (c == 127)) {
- if (debug_count > 0) {
- debug_count--;
- debug_putc(8);
- debug_putc(' ');
- debug_putc(8);
- }
- } else if ((c == 13) || (c == 10)) {
- if (c == '\r' || (c == '\n' && last_c != '\r')) {
- debug_putc('\r');
- debug_putc('\n');
- }
- if (debug_count) {
- debug_buf[debug_count] = 0;
- debug_count = 0;
- debug_exec(debug_buf, regs);
- } else {
- debug_prompt();
- }
- }
- last_c = c;
- }
- debug_flush();
- }
- #if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
- static void debug_console_write(struct console *co,
- const char *s, unsigned int count)
- {
- unsigned long irq_flags;
- /* disable irq's while TXing outside of FIQ context */
- local_irq_save(irq_flags);
- while (count--) {
- if (*s == '\n')
- debug_putc('\r');
- debug_putc(*s++);
- }
- debug_flush();
- local_irq_restore(irq_flags);
- }
- static struct console msm_serial_debug_console = {
- .name = "debug_console",
- .write = debug_console_write,
- .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
- };
- #endif
- void msm_serial_debug_enable(int enable) {
- debug_enable = enable;
- }
- void msm_serial_debug_init(unsigned int base, int irq,
- struct device *clk_device, int signal_irq)
- {
- int ret;
- void *port;
- debug_clk = clk_get(clk_device, "uart_clk");
- if (debug_clk)
- clk_enable(debug_clk);
- port = ioremap(base, 4096);
- if (!port)
- return;
- init_data.base = base;
- init_data.irq = irq;
- init_data.clk_device = clk_device;
- init_data.signal_irq = signal_irq;
- debug_port_base = (unsigned int) port;
- debug_signal_irq = signal_irq;
- debug_port_init();
- debug_prompt();
- msm_fiq_select(irq);
- msm_fiq_set_handler(debug_fiq, 0);
- msm_fiq_enable(irq);
- ret = request_irq(signal_irq, debug_irq,
- IRQF_TRIGGER_RISING, "debug", 0);
- if (ret)
- printk(KERN_ERR
- "serial_debugger: could not install signal_irq");
- #if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
- register_console(&msm_serial_debug_console);
- #endif
- debugger_enable = 1;
- }
- static int msm_serial_debug_remove(const char *val, struct kernel_param *kp)
- {
- int ret;
- static int pre_stat = 1;
- ret = param_set_bool(val, kp);
- if (ret)
- return ret;
- if (pre_stat == *(int *)kp->arg)
- return 0;
- pre_stat = *(int *)kp->arg;
- if (*(int *)kp->arg) {
- msm_serial_debug_init(init_data.base, init_data.irq,
- init_data.clk_device, init_data.signal_irq);
- printk(KERN_INFO "enable FIQ serial debugger\n");
- return 0;
- }
- #if defined(CONFIG_MSM_SERIAL_DEBUGGER_CONSOLE)
- unregister_console(&msm_serial_debug_console);
- #endif
- free_irq(init_data.signal_irq, 0);
- msm_fiq_set_handler(NULL, 0);
- msm_fiq_disable(init_data.irq);
- msm_fiq_unselect(init_data.irq);
- clk_disable(debug_clk);
- printk(KERN_INFO "disable FIQ serial debugger\n");
- return 0;
- }
- module_param_call(enable, msm_serial_debug_remove, param_get_bool,
- &debugger_enable, S_IWUSR | S_IRUGO);
|