From: hhb Date: Tue, 3 Dec 2013 13:41:29 +0000 (+0800) Subject: add fiq debugger X-Git-Tag: firefly_0821_release~6459 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=1ff10f8f5bedb0d7dbc1d11a4fb4a99d4ec23e40;p=firefly-linux-kernel-4.4.55.git add fiq debugger --- diff --git a/arch/arm/boot/dts/rk3188-tb.dts b/arch/arm/boot/dts/rk3188-tb.dts index a9422f3aad06..6659ed1d4c05 100644 --- a/arch/arm/boot/dts/rk3188-tb.dts +++ b/arch/arm/boot/dts/rk3188-tb.dts @@ -9,15 +9,15 @@ }; chosen { - bootargs ="console=ttyS2,115200 androidboot.console=ttyS2"; + }; -}; -&uart0 { - status = "okay"; + ttyFIQ0 { + id = <2>; + }; }; -&uart2 { +&uart0 { status = "okay"; }; diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi index 9dc1b2fbca2d..96c0b297fe59 100755 --- a/arch/arm/boot/dts/rk3188.dtsi +++ b/arch/arm/boot/dts/rk3188.dtsi @@ -416,4 +416,11 @@ pinctrl-0 = <&uart3_xfer &uart3_cts &uart3_rts>; status = "disabled"; }; + + ttyFIQ0 { + id = <2>; + signal_irq = <112>; + wake_irq = <0>; + status = "disabled"; + }; }; diff --git a/arch/arm/common/fiq_debugger.c b/arch/arm/common/fiq_debugger.c index 65b943c76300..f1771c00398c 100644 --- a/arch/arm/common/fiq_debugger.c +++ b/arch/arm/common/fiq_debugger.c @@ -43,7 +43,14 @@ #include "fiq_debugger_ringbuf.h" +#ifdef CONFIG_RK29_WATCHDOG +extern void rk29_wdt_keepalive(void); +#define wdt_keepalive() rk29_wdt_keepalive() +#else +#define wdt_keepalive() do {} while (0) +#endif #define DEBUG_MAX 64 +#define CMD_COUNT 0x0f #define MAX_UNHANDLED_FIQ_COUNT 1000000 #define MAX_FIQ_DEBUGGER_PORTS 4 @@ -70,6 +77,9 @@ struct fiq_debugger_state { char debug_buf[DEBUG_MAX]; int debug_count; + char cmd_buf[CMD_COUNT+1][DEBUG_MAX]; + int back_pointer; + int current_pointer; bool no_sleep; bool debug_enable; bool ignore_next_wakeup_irq; @@ -123,6 +133,9 @@ module_param_named(debug_enable, initial_debug_enable, bool, 0644); module_param_named(console_enable, initial_console_enable, bool, 0644); module_param_named(kgdb_enable, fiq_kgdb_enable, bool, 0644); +void gic_set_irq_secure(struct irq_data *d); +void gic_set_irq_priority(struct irq_data *d, u8 pri); + #ifdef CONFIG_FIQ_DEBUGGER_WAKEUP_IRQ_ALWAYS_ON static inline void enable_wakeup_irq(struct fiq_debugger_state *state) {} static inline void disable_wakeup_irq(struct fiq_debugger_state *state) {} @@ -204,7 +217,7 @@ static void debug_puts(struct fiq_debugger_state *state, char *s) static void debug_prompt(struct fiq_debugger_state *state) { - debug_puts(state, "debug> "); + debug_puts(state, "FIQ debug> "); } static void dump_kernel_log(struct fiq_debugger_state *state) @@ -219,8 +232,31 @@ static void dump_kernel_log(struct fiq_debugger_state *state) sizeof(buf) - 1, &len)) { buf[len] = 0; debug_puts(state, buf); + wdt_keepalive(); + } +} + +#ifdef CONFIG_RK29_LAST_LOG +#include +extern char *last_log_get(unsigned *size); +static void dump_last_kernel_log(struct fiq_debugger_state *state) +{ + unsigned size, i, c; + char *s = last_log_get(&size); + + for (i = 0; i < size; i++) { + if (i % 1024 == 0) + wdt_keepalive(); + c = s[i]; + if (c == '\n') { + debug_putc(state->pdev, '\r'); + debug_putc(state->pdev, c); + } else if (isascii(c) && isprint(c)) { + debug_putc(state->pdev, c); + } } } +#endif static char *mode_name(unsigned cpsr) { @@ -364,6 +400,7 @@ static void dump_allregs(struct fiq_debugger_state *state, unsigned *regs) static void dump_irqs(struct fiq_debugger_state *state) { int n; + unsigned int cpu; struct irq_desc *desc; debug_printf(state, "irqnr total since-last status name\n"); @@ -378,6 +415,18 @@ static void dump_irqs(struct fiq_debugger_state *state) (act && act->name) ? act->name : "???"); state->last_irqs[n] = kstat_irqs(n); } + +#if 0 //def CONFIG_LOCAL_TIMERS + for (cpu = 0; cpu < NR_CPUS; cpu++) { + + debug_printf(state, "LOC %d: %10u %11u\n", cpu, + __IRQ_STAT(cpu, local_timer_irqs), + __IRQ_STAT(cpu, local_timer_irqs) - + state->last_local_timer_irqs[cpu]); + state->last_local_timer_irqs[cpu] = + __IRQ_STAT(cpu, local_timer_irqs); + } +#endif } struct stacktrace_state { @@ -592,20 +641,61 @@ static void debug_work(struct work_struct *work) /* This function CANNOT be called in FIQ context */ static void debug_irq_exec(struct fiq_debugger_state *state, char *cmd) { + int invalid_cmd = 0; if (!strcmp(cmd, "ps")) do_ps(state); - if (!strcmp(cmd, "sysrq")) + else if (!strcmp(cmd, "sysrq")) do_sysrq(state, 'h'); - if (!strncmp(cmd, "sysrq ", 6)) + else if (!strncmp(cmd, "sysrq ", 6)) do_sysrq(state, cmd[6]); #ifdef CONFIG_KGDB - if (!strcmp(cmd, "kgdb")) + else if (!strcmp(cmd, "kgdb")) do_kgdb(state); #endif - if (!strncmp(cmd, "reboot", 6)) + else if (!strncmp(cmd, "reboot", 6)) debug_schedule_work(state, cmd); + else { + invalid_cmd = 1; + memset(state->debug_buf, 0, DEBUG_MAX); + } + + if (invalid_cmd == 0) { + state->current_pointer = (state->current_pointer-1) & CMD_COUNT; + if (strcmp(state->cmd_buf[state->current_pointer], state->debug_buf)) { + state->current_pointer = (state->current_pointer+1) & CMD_COUNT; + memset(state->cmd_buf[state->current_pointer], 0, DEBUG_MAX); + strcpy(state->cmd_buf[state->current_pointer], state->debug_buf); + } + memset(state->debug_buf, 0, DEBUG_MAX); + state->current_pointer = (state->current_pointer+1) & CMD_COUNT; + state->back_pointer = state->current_pointer; + } } +static char cmd_buf[][16] = { + {"pc"}, + {"regs"}, + {"allregs"}, + {"bt"}, + {"reboot"}, + {"irqs"}, + {"kmsg"}, +#ifdef CONFIG_RK29_LAST_LOG + {"last_kmsg"}, +#endif + {"version"}, + {"sleep"}, + {"nosleep"}, + {"console"}, + {"cpu"}, + {"ps"}, + {"sysrq"}, + {"reset"}, +#ifdef CONFIG_KGDB + {"kgdb"}, +#endif +}; + static void debug_help(struct fiq_debugger_state *state) { debug_printf(state, "FIQ Debugger commands:\n" @@ -618,6 +708,9 @@ static void debug_help(struct fiq_debugger_state *state) " irqs Interupt status\n" " kmsg Kernel log\n" " version Kernel version\n"); +#ifdef CONFIG_RK29_LAST_LOG + debug_printf(state, " last_kmsg Last kernel log\n"); +#endif debug_printf(state, " sleep Allow sleep while in FIQ\n" " nosleep Disable sleep while in FIQ\n" " console Switch terminal to console\n" @@ -646,6 +739,22 @@ 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); +#ifdef CONFIG_PLAT_RK + else { + struct cpumask cpumask; + + if (!cpu_online(cpu)) { + debug_printf(state, "cpu %d offline\n", cpu); + return; + } + + cpumask_clear(&cpumask); + cpumask_set_cpu(cpu, &cpumask); + + irq_set_affinity(state->fiq, &cpumask); + irq_set_affinity(state->uart_irq, &cpumask); + } +#endif state->current_cpu = cpu; } @@ -680,6 +789,10 @@ static bool debug_fiq_exec(struct fiq_debugger_state *state, dump_irqs(state); } else if (!strcmp(cmd, "kmsg")) { dump_kernel_log(state); +#ifdef CONFIG_RK29_LAST_LOG + } else if (!strcmp(cmd, "last_kmsg")) { + dump_last_kernel_log(state); +#endif } else if (!strcmp(cmd, "version")) { debug_printf(state, "%s\n", linux_banner); } else if (!strcmp(cmd, "sleep")) { @@ -808,6 +921,109 @@ static int debug_getc(struct fiq_debugger_state *state) return state->pdata->uart_getc(state->pdev); } + +static int debug_cmd_check_back(struct fiq_debugger_state *state, char c) +{ + char *s; + int i = 0; + if (c == 'A') { + state->back_pointer = (state->back_pointer-1) & CMD_COUNT; + if (state->back_pointer != state->current_pointer) { + s = state->cmd_buf[state->back_pointer]; + if (*s != 0) { + for(i = 0; i < strlen(state->debug_buf)-1; i++) { + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + memset(state->debug_buf, 0, DEBUG_MAX); + strcpy(state->debug_buf, s); + state->debug_count = strlen(state->debug_buf); + debug_printf(state, state->debug_buf); + } else { + state->back_pointer = (state->back_pointer+1) & CMD_COUNT; + } + + } else { + state->back_pointer = (state->back_pointer+1) & CMD_COUNT; + } + } else if (c == 'B') { + + if (state->back_pointer != state->current_pointer) { + state->back_pointer = (state->back_pointer+1) & CMD_COUNT; + if(state->back_pointer == state->current_pointer){ + goto cmd_clear; + } else { + s = state->cmd_buf[state->back_pointer]; + if (*s != 0) { + for(i = 0; i < strlen(state->debug_buf)-1; i++) { + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + memset(state->debug_buf, 0, DEBUG_MAX); + strcpy(state->debug_buf, s); + state->debug_count = strlen(state->debug_buf); + debug_printf(state, state->debug_buf); + } + } + } else { +cmd_clear: + for(i = 0; i < strlen(state->debug_buf)-1; i++) { + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + memset(state->debug_buf, 0, DEBUG_MAX); + state->debug_count = 0; + } + } + return 0; +} + +static void debug_cmd_tab(struct fiq_debugger_state *state) +{ + int i,j; + int count = 0; + + for (i = 0; i < ARRAY_SIZE(cmd_buf); i++) { + cmd_buf[i][15] = 1; + } + + for (j = 1; j <= strlen(state->debug_buf); j++) { + count = 0; + for (i = 0; i < ARRAY_SIZE(cmd_buf); i++) { + if (cmd_buf[i][15] == 1) { + if (strncmp(state->debug_buf, cmd_buf[i], j)) { + cmd_buf[i][15] = 0; + } else { + count++; + } + } + } + if (count == 0) + break; + } + + if (count == 1) { + for (i = 0; i < ARRAY_SIZE(cmd_buf); i++) { + if (cmd_buf[i][15] == 1) + break; + } + + for(j = 0; j < strlen(state->debug_buf); j++) { + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + memset(state->debug_buf, 0, DEBUG_MAX); + strcpy(state->debug_buf, cmd_buf[i]); + state->debug_count = strlen(state->debug_buf); + debug_printf(state, state->debug_buf); + + } +} + static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, int this_cpu, void *regs, void *svc_sp) { @@ -845,14 +1061,29 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, } } else if (c == FIQ_DEBUGGER_BREAK) { state->console_enable = false; - debug_puts(state, "fiq debugger mode\n"); + debug_puts(state, "\nwelcome to fiq debugger mode\n"); + debug_puts(state, "Enter ? to get command help\n"); state->debug_count = 0; + state->back_pointer = CMD_COUNT; + state->current_pointer = CMD_COUNT; + memset(state->cmd_buf, 0, (CMD_COUNT+1)*DEBUG_MAX); debug_prompt(state); #ifdef CONFIG_FIQ_DEBUGGER_CONSOLE } else if (state->console_enable && state->tty_rbuf) { fiq_debugger_ringbuf_push(state->tty_rbuf, c); signal_helper = true; #endif + } else if (last_c == '[' && (c == 'A' || c == 'B' || c == 'C' || c == 'D')) { + if (state->debug_count > 0) { + state->debug_count--; + state->pdata->uart_putc(state->pdev, 8); + state->pdata->uart_putc(state->pdev, ' '); + state->pdata->uart_putc(state->pdev, 8); + } + debug_cmd_check_back(state, c); + //tab + } else if (c == 9) { + debug_cmd_tab(state); } else if ((c >= ' ') && (c < 127)) { if (state->debug_count < (DEBUG_MAX - 1)) { state->debug_buf[state->debug_count++] = c; @@ -876,6 +1107,17 @@ static bool debug_handle_uart_interrupt(struct fiq_debugger_state *state, signal_helper |= debug_fiq_exec(state, state->debug_buf, regs, svc_sp); + if (signal_helper == false) { + state->current_pointer = (state->current_pointer-1) & CMD_COUNT; + if (strcmp(state->cmd_buf[state->current_pointer], state->debug_buf)) { + state->current_pointer = (state->current_pointer+1) & CMD_COUNT; + memset(state->cmd_buf[state->current_pointer], 0, DEBUG_MAX); + strcpy(state->cmd_buf[state->current_pointer], state->debug_buf); + } + memset(state->debug_buf, 0, DEBUG_MAX); + state->current_pointer = (state->current_pointer+1) & CMD_COUNT; + state->back_pointer = state->current_pointer; + } } else { debug_prompt(state); } @@ -904,6 +1146,21 @@ static void debug_fiq(struct fiq_glue_handler *h, void *regs, void *svc_sp) unsigned int this_cpu = THREAD_INFO(svc_sp)->cpu; bool need_irq; + /* RK2928 USB-UART function, otg dp/dm default in uart status; + * connect with otg cable&usb device, dp/dm will be hi-z status + * and make uart controller enter infinite fiq loop + */ +#ifdef CONFIG_RK_USB_UART +#ifdef CONFIG_ARCH_RK2928 + if(!(readl_relaxed(RK2928_GRF_BASE + 0x014c) & (1<<10))){//id low + writel_relaxed(0x34000000, RK2928_GRF_BASE + 0x190); //enter usb phy + } +#elif defined(CONFIG_ARCH_RK3188) + if(!(readl_relaxed(RK30_GRF_BASE + 0x00ac) & (1 << 13))){//id low + writel_relaxed((0x0300 << 16), RK30_GRF_BASE + 0x010c); //enter usb phy + } +#endif +#endif need_irq = debug_handle_uart_interrupt(state, this_cpu, regs, svc_sp); if (need_irq) debug_force_irq(state); @@ -1279,7 +1536,10 @@ static int fiq_debugger_probe(struct platform_device *pdev) pr_err("%s: could not install fiq handler\n", __func__); goto err_register_fiq; } - + //set state->fiq to secure state, so fiq is avalable + gic_set_irq_secure(irq_get_irq_data(state->fiq)); + //set state->fiq priority a little higher than other interrupts (normal is 0xa0) + gic_set_irq_priority(irq_get_irq_data(state->fiq), 0x90); pdata->fiq_enable(pdev, state->fiq, 1); } else { ret = request_irq(state->uart_irq, debug_uart_irq, diff --git a/arch/arm/configs/rockchip_defconfig b/arch/arm/configs/rockchip_defconfig index 4930e8f7abc2..81548b26b079 100644 --- a/arch/arm/configs/rockchip_defconfig +++ b/arch/arm/configs/rockchip_defconfig @@ -34,6 +34,10 @@ CONFIG_ARCH_ROCKCHIP=y CONFIG_ARM_ERRATA_720789=y CONFIG_PL310_ERRATA_753970=y CONFIG_ARM_ERRATA_754322=y +CONFIG_FIQ_DEBUGGER=y +CONFIG_FIQ_DEBUGGER_NO_SLEEP=y +CONFIG_FIQ_DEBUGGER_CONSOLE=y +CONFIG_FIQ_DEBUGGER_CONSOLE_DEFAULT_ENABLE=y CONFIG_SMP=y CONFIG_SCHED_MC=y CONFIG_PREEMPT=y @@ -258,7 +262,6 @@ CONFIG_INPUT_UINPUT=y # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_ROCKCHIP=y -CONFIG_SERIAL_ROCKCHIP_CONSOLE=y # CONFIG_HW_RANDOM is not set CONFIG_I2C=y CONFIG_SPI=y diff --git a/arch/arm/mach-rockchip/Makefile b/arch/arm/mach-rockchip/Makefile index 7cb3156ff216..1a6b65bd1305 100644 --- a/arch/arm/mach-rockchip/Makefile +++ b/arch/arm/mach-rockchip/Makefile @@ -1,3 +1,4 @@ obj-y += common.o obj-y += rk3188.o obj-$(CONFIG_SMP) += platsmp.o +obj-$(CONFIG_FIQ_DEBUGGER) += rk_fiq_debugger.o diff --git a/arch/arm/mach-rockchip/rk3188.c b/arch/arm/mach-rockchip/rk3188.c index 9d8dc8906385..7bbfa61c669e 100644 --- a/arch/arm/mach-rockchip/rk3188.c +++ b/arch/arm/mach-rockchip/rk3188.c @@ -49,7 +49,7 @@ static const char * const rk3188_dt_compat[] = { }; DT_MACHINE_START(RK3188_DT, "Rockchip RK3188 (Flattened Device Tree)") - .nr_irqs = 32*10, + //.nr_irqs = 32*10, .smp = smp_ops(rockchip_smp_ops), .map_io = rk3188_dt_map_io, .init_machine = rk3188_dt_init, diff --git a/arch/arm/mach-rockchip/rk_fiq_debugger.c b/arch/arm/mach-rockchip/rk_fiq_debugger.c new file mode 100644 index 000000000000..7e8c9251ad90 --- /dev/null +++ b/arch/arm/mach-rockchip/rk_fiq_debugger.c @@ -0,0 +1,340 @@ +/* + * arch/arm/plat-rk/rk_fiq_debugger.c + * + * Serial Debugger Interface for Rockchip + * + * Copyright (C) 2012 ROCKCHIP, Inc. + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rk_fiq_debugger.h" + +struct rk_fiq_debugger { + struct fiq_debugger_pdata pdata; + void __iomem *debug_port_base; + bool break_seen; +#ifdef CONFIG_RK_CONSOLE_THREAD + struct task_struct *console_task; +#endif +}; + +static inline void rk_fiq_write(struct rk_fiq_debugger *t, + unsigned int val, unsigned int off) +{ + __raw_writeb(val, t->debug_port_base + off * 4); +} + +static inline unsigned int rk_fiq_read(struct rk_fiq_debugger *t, + unsigned int off) +{ + return __raw_readb(t->debug_port_base + off * 4); +} + +static inline unsigned int rk_fiq_read_lsr(struct rk_fiq_debugger *t) +{ + unsigned int lsr; + + lsr = rk_fiq_read(t, UART_LSR); + if (lsr & UART_LSR_BI) + t->break_seen = true; + + return lsr; +} + +static int debug_port_init(struct platform_device *pdev) +{ + struct rk_fiq_debugger *t; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + + if (rk_fiq_read(t, UART_LSR) & UART_LSR_DR) + (void)rk_fiq_read(t, UART_RX); + /* enable rx and lsr interrupt */ + rk_fiq_write(t, UART_IER_RLSI | UART_IER_RDI, UART_IER); + /* interrupt on every character when receive,but we can enable fifo for TX + I found that if we enable the RX fifo, some problem may vanish such as when + you continuously input characters in the command line the uart irq may be disable + because of the uart irq is served when CPU is at IRQ exception,but it is + found unregistered, so it is disable. + hhb@rock-chips.com */ + rk_fiq_write(t, 0xc1, UART_FCR); + + return 0; +} + +static int debug_getc(struct platform_device *pdev) +{ + unsigned int lsr; + struct rk_fiq_debugger *t; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + + lsr = rk_fiq_read_lsr(t); + + if (lsr & UART_LSR_BI || t->break_seen) { + t->break_seen = false; + return FIQ_DEBUGGER_BREAK; + } + + if (lsr & UART_LSR_DR) + return rk_fiq_read(t, UART_RX); + + return FIQ_DEBUGGER_NO_CHAR; +} + +static void debug_putc(struct platform_device *pdev, unsigned int c) +{ + struct rk_fiq_debugger *t; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + +// while (!(rk_fiq_read_lsr(t) & UART_LSR_THRE)) +// cpu_relax(); + //enable TX FIFO + while (!(rk_fiq_read(t, 0x1F) & 0x02)) + cpu_relax(); + rk_fiq_write(t, c, UART_TX); +} + +static void debug_flush(struct platform_device *pdev) +{ + struct rk_fiq_debugger *t; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + + while (!(rk_fiq_read_lsr(t) & UART_LSR_TEMT)) + cpu_relax(); +} + +#ifdef CONFIG_RK_CONSOLE_THREAD +static DEFINE_KFIFO(fifo, unsigned char, SZ_64K); + +static int console_thread(void *data) +{ + struct platform_device *pdev = data; + struct rk_fiq_debugger *t; + unsigned char c; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (kthread_should_stop()) + break; + set_current_state(TASK_RUNNING); + while (kfifo_get(&fifo, &c)) + debug_putc(pdev, c); + debug_flush(pdev); + } + + return 0; +} + +static void console_write(struct platform_device *pdev, const char *s, unsigned int count) +{ + unsigned char c, r = '\r'; + static bool oops = false; + struct rk_fiq_debugger *t; + t = container_of(dev_get_platdata(&pdev->dev), typeof(*t), pdata); + + if (oops_in_progress || oops) { + debug_flush(pdev); + while (kfifo_get(&fifo, &c)) + debug_putc(pdev, c); + while (count--) { + if (*s == '\n') { + debug_putc(pdev, r); + } + debug_putc(pdev, *s++); + } + debug_flush(pdev); + oops = true; + return; + } + + while (count--) { + if (*s == '\n') { + kfifo_put(&fifo, &r); + } + kfifo_put(&fifo, s++); + } + wake_up_process(t->console_task); +} +#endif + + +static void fiq_enable(struct platform_device *pdev, unsigned int irq, bool on) +{ + if (on) + enable_irq(irq); + else + disable_irq(irq); +} + +static int rk_fiq_debugger_id; + +void rk_serial_debug_init(void __iomem *base, int irq, int signal_irq, int wakeup_irq) +{ + struct rk_fiq_debugger *t = NULL; + struct platform_device *pdev = NULL; + struct resource *res = NULL; + int res_count = 0; + + if (!base) { + pr_err("Invalid fiq debugger uart base\n"); + return; + } + + t = kzalloc(sizeof(struct rk_fiq_debugger), GFP_KERNEL); + if (!t) { + pr_err("Failed to allocate for fiq debugger\n"); + return; + } + + t->pdata.uart_init = debug_port_init; + t->pdata.uart_getc = debug_getc; + t->pdata.uart_putc = debug_putc; +#ifndef CONFIG_RK_CONSOLE_THREAD + t->pdata.uart_flush = debug_flush; +#endif + t->pdata.fiq_enable = fiq_enable; + t->pdata.force_irq = NULL; //force_irq; + t->debug_port_base = base; + + res = kzalloc(sizeof(struct resource) * 3, GFP_KERNEL); + if (!res) { + pr_err("Failed to alloc fiq debugger resources\n"); + goto out2; + } + + pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!pdev) { + pr_err("Failed to alloc fiq debugger platform device\n"); + goto out3; + }; + + if (irq > 0) { + res[0].flags = IORESOURCE_IRQ; + res[0].start = irq; + res[0].end = irq; + res[0].name = "fiq"; + res_count++; + } + + if (signal_irq > 0) { + res[1].flags = IORESOURCE_IRQ; + res[1].start = signal_irq; + res[1].end = signal_irq; + res[1].name = "signal"; + res_count++; + } + + if (wakeup_irq > 0) { + res[2].flags = IORESOURCE_IRQ; + res[2].start = wakeup_irq; + res[2].end = wakeup_irq; + res[2].name = "wakeup"; + res_count++; + } + +#ifdef CONFIG_RK_CONSOLE_THREAD + t->console_task = kthread_create(console_thread, pdev, "kconsole"); + if (!IS_ERR(t->console_task)) { + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + t->pdata.console_write = console_write; + sched_setscheduler_nocheck(t->console_task, SCHED_FIFO, ¶m); + } +#endif + + pdev->name = "fiq_debugger"; + pdev->id = rk_fiq_debugger_id++; + pdev->dev.platform_data = &t->pdata; + pdev->resource = res; + pdev->num_resources = res_count; + if (platform_device_register(pdev)) { + pr_err("Failed to register fiq debugger\n"); + goto out4; + } + return; + +out4: + kfree(pdev); +out3: + kfree(res); +out2: + kfree(t); +} + +static int __init rk_fiq_debugger_init(void) { + + void __iomem *base; + u32 out_values[6] = {0}; + struct device_node *from = NULL; + unsigned int i = 0, id = 0, serial_id = 0, ok = 0; + u32 irq = 32, signal_irq = 0, wake_irq = 0; + + from = of_find_node_by_name(from, "ttyFIQ0"); + + if(!from) { + printk("ttyFIQ0 is missing in device tree!\n"); + return -ENODEV; + } + + if(of_property_read_u32(from, "id", &id)) { + return -EINVAL; + } + + if(of_property_read_u32(from, "signal_irq", &signal_irq)) { + signal_irq = -1; + } + + if(of_property_read_u32(from, "wake_irq", &wake_irq)) { + wake_irq = -1; + } + + from = NULL; + for(i = 0; i < 5; i++) { + from = of_find_node_by_name(from, "serial"); + if(from && !of_property_read_u32(from, "id", &serial_id)) { + if(id == serial_id) { + ok = 1; + break; + } + } + } + + if(ok != 1) + return -EINVAL; + + if(of_property_read_u32_array(from, "interrupts", out_values, 3)) + return -EINVAL; + irq += *(out_values + 1); + base = of_iomap(from, 0); + if(base) + rk_serial_debug_init(base, irq, signal_irq, wake_irq); + return 0; +} + +postcore_initcall_sync(rk_fiq_debugger_init); diff --git a/arch/arm/mach-rockchip/rk_fiq_debugger.h b/arch/arm/mach-rockchip/rk_fiq_debugger.h new file mode 100644 index 000000000000..5c85c0794123 --- /dev/null +++ b/arch/arm/mach-rockchip/rk_fiq_debugger.h @@ -0,0 +1,12 @@ +#ifndef __PLAT_RK_FIQ_DEBUGGER_H +#define __PLAT_RK_FIQ_DEBUGGER_H + +#ifdef CONFIG_FIQ_DEBUGGER +void rk_serial_debug_init(void __iomem *base, int irq, int signal_irq, int wakeup_irq); +#else +static inline void rk_serial_debug_init(void __iomem *base, int irq, int signal_irq, int wakeup_irq) +{ +} +#endif + +#endif diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 65bc83747f66..48eefabac3d5 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -235,13 +235,42 @@ static int gic_set_type(struct irq_data *d, unsigned int type) static int gic_retrigger(struct irq_data *d) { + u32 mask = 1 << (gic_irq(d) % 32); + if (gic_arch_extn.irq_retrigger) return gic_arch_extn.irq_retrigger(d); +#ifdef CONFIG_FIQ_DEBUGGER + /* set irq pending to retrigger to cpu */ + writel_relaxed(mask, gic_dist_base(d) + GIC_DIST_PENDING_SET + (gic_irq(d) / 32) * 4); +#endif /* the genirq layer expects 0 if we can't retrigger in hardware */ return 0; } +#ifdef CONFIG_FIQ_DEBUGGER +/* + * ICDISR each bit 0 -- Secure 1--Non-Secure + */ +void gic_set_irq_secure(struct irq_data *d) +{ + u32 mask = 0; + void __iomem *base = gic_dist_base(d); + + //raw_spin_lock(&irq_controller_lock); + base += GIC_DIST_IGROUP + ((gic_irq(d) / 32) * 4); + mask = readl_relaxed(base); + mask &= ~(1 << (gic_irq(d) % 32)); + writel_relaxed(mask, base); + //raw_spin_unlock(&irq_controller_lock); +} + +void gic_set_irq_priority(struct irq_data *d, u8 pri) +{ + writeb_relaxed(pri, gic_dist_base(d) + GIC_DIST_PRI + gic_irq(d)); +} +#endif + #ifdef CONFIG_SMP static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) @@ -411,7 +440,17 @@ static void __init gic_dist_init(struct gic_chip_data *gic) for (i = 32; i < gic_irqs; i += 32) writel_relaxed(0xffffffff, base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32); +#ifdef CONFIG_FIQ_DEBUGGER + // set all the interrupt to non-secure state + for (i = 0; i < gic_irqs; i += 32) { + writel_relaxed(0xffffffff, base + GIC_DIST_IGROUP + i * 4 / 32); + } + + dsb(); + writel_relaxed(0x3, base + GIC_DIST_CTRL); +#else writel_relaxed(1, base + GIC_DIST_CTRL); +#endif } static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) @@ -450,7 +489,11 @@ static void __cpuinit gic_cpu_init(struct gic_chip_data *gic) writel_relaxed(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4); writel_relaxed(0xf0, base + GIC_CPU_PRIMASK); +#ifdef CONFIG_FIQ_DEBUGGER + writel_relaxed(0x0f, base + GIC_CPU_CTRL); +#else writel_relaxed(1, base + GIC_CPU_CTRL); +#endif } void gic_cpu_if_down(void) @@ -669,8 +712,12 @@ void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) dsb(); /* this always happens on GIC0 */ +#ifdef CONFIG_FIQ_DEBUGGER + /* enable non-secure SGI for GIC with security extensions */ + writel_relaxed(map << 16 | irq | 0x8000, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); +#else writel_relaxed(map << 16 | irq, gic_data_dist_base(&gic_data[0]) + GIC_DIST_SOFTINT); - +#endif raw_spin_unlock_irqrestore(&irq_controller_lock, flags); } #endif @@ -851,8 +898,14 @@ static int gic_irq_domain_xlate(struct irq_domain *d, static int __cpuinit gic_secondary_init(struct notifier_block *nfb, unsigned long action, void *hcpu) { - if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) + if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) { gic_cpu_init(&gic_data[0]); +#ifdef CONFIG_FIQ_DEBUGGER + /*set SGI to none secure state*/ + writel_relaxed(0xffffffff, gic_data_dist_base(&gic_data[0]) + GIC_DIST_IGROUP); + writel_relaxed(0xf, gic_data_cpu_base(&gic_data[0]) + GIC_CPU_CTRL); +#endif + } return NOTIFY_OK; }