mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 21:07:02 +09:00
add fiq debugger
This commit is contained in:
@@ -9,7 +9,11 @@
|
||||
};
|
||||
|
||||
chosen {
|
||||
bootargs ="console=ttyS2,115200 androidboot.console=ttyS2";
|
||||
|
||||
};
|
||||
|
||||
ttyFIQ0 {
|
||||
id = <2>;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -17,10 +21,6 @@
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart2 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
&uart3 {
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,9 +232,32 @@ 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 <linux/ctype.h>
|
||||
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)
|
||||
{
|
||||
switch (cpsr & MODE_MASK) {
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
340
arch/arm/mach-rockchip/rk_fiq_debugger.c
Normal file
340
arch/arm/mach-rockchip/rk_fiq_debugger.c
Normal file
@@ -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 <stdarg.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/kfifo.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <asm/fiq_debugger.h>
|
||||
#include <linux/irqchip/arm-gic.h>
|
||||
#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);
|
||||
12
arch/arm/mach-rockchip/rk_fiq_debugger.h
Normal file
12
arch/arm/mach-rockchip/rk_fiq_debugger.h
Normal file
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user