diff options
Diffstat (limited to 'arch/nios2/kernel')
-rw-r--r-- | arch/nios2/kernel/Makefile | 2 | ||||
-rw-r--r-- | arch/nios2/kernel/early_printk.c | 118 | ||||
-rw-r--r-- | arch/nios2/kernel/entry.S | 12 | ||||
-rw-r--r-- | arch/nios2/kernel/kgdb.c | 171 | ||||
-rw-r--r-- | arch/nios2/kernel/module.c | 2 | ||||
-rw-r--r-- | arch/nios2/kernel/prom.c | 52 | ||||
-rw-r--r-- | arch/nios2/kernel/setup.c | 4 | ||||
-rw-r--r-- | arch/nios2/kernel/signal.c | 2 |
8 files changed, 359 insertions, 4 deletions
diff --git a/arch/nios2/kernel/Makefile b/arch/nios2/kernel/Makefile index 8ae76823ff9..1aae2570365 100644 --- a/arch/nios2/kernel/Makefile +++ b/arch/nios2/kernel/Makefile @@ -20,5 +20,7 @@ obj-y += syscall_table.o obj-y += time.o obj-y += traps.o +obj-$(CONFIG_EARLY_PRINTK) += early_printk.o +obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_NIOS2_ALIGNMENT_TRAP) += misaligned.o diff --git a/arch/nios2/kernel/early_printk.c b/arch/nios2/kernel/early_printk.c new file mode 100644 index 00000000000..c08e4c1486f --- /dev/null +++ b/arch/nios2/kernel/early_printk.c @@ -0,0 +1,118 @@ +/* + * Early printk for Nios2. + * + * Copyright (C) 2015, Altera Corporation + * Copyright (C) 2010, Tobias Klauser <tklauser@distanz.ch> + * Copyright (C) 2009, Wind River Systems Inc + * Implemented by fredrik.markstrom@gmail.com and ivarholmqvist@gmail.com + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include <linux/console.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/io.h> + +#include <asm/prom.h> + +static unsigned long base_addr; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) + +#define ALTERA_JTAGUART_DATA_REG 0 +#define ALTERA_JTAGUART_CONTROL_REG 4 +#define ALTERA_JTAGUART_CONTROL_WSPACE_MSK 0xFFFF0000 +#define ALTERA_JTAGUART_CONTROL_AC_MSK 0x00000400 + +#define JUART_GET_CR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG)) +#define JUART_SET_CR(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_CONTROL_REG), v) +#define JUART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_JTAGUART_DATA_REG), v) + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + unsigned long status; + + while (n-- && *s) { + while (((status = JUART_GET_CR()) + & ALTERA_JTAGUART_CONTROL_WSPACE_MSK) == 0) { +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + if ((status & ALTERA_JTAGUART_CONTROL_AC_MSK) == 0) + return; /* no connection activity */ +#endif + } + JUART_SET_TX(*s); + s++; + } +} + +#elif defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + +#define ALTERA_UART_TXDATA_REG 4 +#define ALTERA_UART_STATUS_REG 8 +#define ALTERA_UART_STATUS_TRDY 0x0040 + +#define UART_GET_SR() \ + __builtin_ldwio((void *)(base_addr + ALTERA_UART_STATUS_REG)) +#define UART_SET_TX(v) \ + __builtin_stwio((void *)(base_addr + ALTERA_UART_TXDATA_REG), v) + +static void early_console_putc(char c) +{ + while (!(UART_GET_SR() & ALTERA_UART_STATUS_TRDY)) + ; + + UART_SET_TX(c); +} + +static void early_console_write(struct console *con, const char *s, unsigned n) +{ + while (n-- && *s) { + early_console_putc(*s); + if (*s == '\n') + early_console_putc('\r'); + s++; + } +} + +#else +# error Neither SERIAL_ALTERA_JTAGUART_CONSOLE nor SERIAL_ALTERA_UART_CONSOLE \ +selected +#endif + +static struct console early_console_prom = { + .name = "early", + .write = early_console_write, + .flags = CON_PRINTBUFFER | CON_BOOT, + .index = -1 +}; + +void __init setup_early_printk(void) +{ +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE) || \ + defined(CONFIG_SERIAL_ALTERA_UART_CONSOLE) + base_addr = of_early_console(); +#else + base_addr = 0; +#endif + + if (!base_addr) + return; + +#if defined(CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE_BYPASS) + /* Clear activity bit so BYPASS doesn't stall if we've used JTAG for + * downloading the kernel. This might cause early data to be lost even + * if the JTAG terminal is running. + */ + JUART_SET_CR(JUART_GET_CR() | ALTERA_JTAGUART_CONTROL_AC_MSK); +#endif + + early_console = &early_console_prom; + register_console(early_console); + pr_info("early_console initialized at 0x%08lx\n", base_addr); +} diff --git a/arch/nios2/kernel/entry.S b/arch/nios2/kernel/entry.S index 0bdfd13ff98..7729bd3f2e7 100644 --- a/arch/nios2/kernel/entry.S +++ b/arch/nios2/kernel/entry.S @@ -121,7 +121,11 @@ trap_table: .word instruction_trap /* 27 */ .word instruction_trap /* 28 */ .word instruction_trap /* 29 */ - .word instruction_trap /* 30 */ +#ifdef CONFIG_KGDB + .word handle_kgdb_breakpoint /* 30 KGDB breakpoint */ +#else + .word instruction_trap /* 30 */ +#endif .word handle_breakpoint /* 31 */ .text @@ -445,6 +449,12 @@ handle_diverror: call handle_diverror_c br ret_from_exception +#ifdef CONFIG_KGDB +handle_kgdb_breakpoint: + call kgdb_breakpoint_c + br ret_from_exception +#endif + /* * Beware - when entering resume, prev (the current task) is * in r4, next (the new task) is in r5, don't change these diff --git a/arch/nios2/kernel/kgdb.c b/arch/nios2/kernel/kgdb.c new file mode 100644 index 00000000000..117859122d1 --- /dev/null +++ b/arch/nios2/kernel/kgdb.c @@ -0,0 +1,171 @@ +/* + * Nios2 KGDB support + * + * Copyright (C) 2015 Altera Corporation + * Copyright (C) 2011 Tobias Klauser <tklauser@distanz.ch> + * + * Based on the code posted by Kazuyasu on the Altera Forum at: + * http://www.alteraforum.com/forum/showpost.php?p=77003&postcount=20 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/ptrace.h> +#include <linux/kgdb.h> +#include <linux/kdebug.h> +#include <linux/io.h> + +static int wait_for_remote_debugger; + +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = +{ + { "zero", GDB_SIZEOF_REG, -1 }, + { "at", GDB_SIZEOF_REG, offsetof(struct pt_regs, r1) }, + { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, r2) }, + { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, r3) }, + { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, r4) }, + { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, r5) }, + { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, r6) }, + { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, r7) }, + { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, r8) }, + { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, r9) }, + { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, r10) }, + { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, r11) }, + { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, r12) }, + { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, r13) }, + { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, r14) }, + { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, r15) }, + { "r16", GDB_SIZEOF_REG, -1 }, + { "r17", GDB_SIZEOF_REG, -1 }, + { "r18", GDB_SIZEOF_REG, -1 }, + { "r19", GDB_SIZEOF_REG, -1 }, + { "r20", GDB_SIZEOF_REG, -1 }, + { "r21", GDB_SIZEOF_REG, -1 }, + { "r22", GDB_SIZEOF_REG, -1 }, + { "r23", GDB_SIZEOF_REG, -1 }, + { "et", GDB_SIZEOF_REG, -1 }, + { "bt", GDB_SIZEOF_REG, -1 }, + { "gp", GDB_SIZEOF_REG, offsetof(struct pt_regs, gp) }, + { "sp", GDB_SIZEOF_REG, offsetof(struct pt_regs, sp) }, + { "fp", GDB_SIZEOF_REG, offsetof(struct pt_regs, fp) }, + { "ea", GDB_SIZEOF_REG, -1 }, + { "ba", GDB_SIZEOF_REG, -1 }, + { "ra", GDB_SIZEOF_REG, offsetof(struct pt_regs, ra) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, ea) }, + { "status", GDB_SIZEOF_REG, -1 }, + { "estatus", GDB_SIZEOF_REG, offsetof(struct pt_regs, estatus) }, + { "bstatus", GDB_SIZEOF_REG, -1 }, + { "ienable", GDB_SIZEOF_REG, -1 }, + { "ipending", GDB_SIZEOF_REG, -1}, + { "cpuid", GDB_SIZEOF_REG, -1 }, + { "ctl6", GDB_SIZEOF_REG, -1 }, + { "exception", GDB_SIZEOF_REG, -1 }, + { "pteaddr", GDB_SIZEOF_REG, -1 }, + { "tlbacc", GDB_SIZEOF_REG, -1 }, + { "tlbmisc", GDB_SIZEOF_REG, -1 }, + { "eccinj", GDB_SIZEOF_REG, -1 }, + { "badaddr", GDB_SIZEOF_REG, -1 }, + { "config", GDB_SIZEOF_REG, -1 }, + { "mpubase", GDB_SIZEOF_REG, -1 }, + { "mpuacc", GDB_SIZEOF_REG, -1 }, +}; + +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; + + if (dbg_reg_def[regno].offset != -1) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + else + memset(mem, 0, dbg_reg_def[regno].size); + + return dbg_reg_def[regno].name; +} + +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return -EINVAL; + + if (dbg_reg_def[regno].offset != -1) + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); + + return 0; +} + +void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) +{ + memset((char *)gdb_regs, 0, NUMREGBYTES); + gdb_regs[GDB_SP] = p->thread.kregs->sp; + gdb_regs[GDB_PC] = p->thread.kregs->ea; +} + +void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc) +{ + regs->ea = pc; +} + +int kgdb_arch_handle_exception(int vector, int signo, int err_code, + char *remcom_in_buffer, char *remcom_out_buffer, + struct pt_regs *regs) +{ + char *ptr; + unsigned long addr; + + switch (remcom_in_buffer[0]) { + case 's': + case 'c': + /* handle the optional parameters */ + ptr = &remcom_in_buffer[1]; + if (kgdb_hex2long(&ptr, &addr)) + regs->ea = addr; + + return 0; + } + + return -1; /* this means that we do not want to exit from the handler */ +} + +asmlinkage void kgdb_breakpoint_c(struct pt_regs *regs) +{ + /* + * The breakpoint entry code has moved the PC on by 4 bytes, so we must + * move it back. This could be done on the host but we do it here + */ + if (!wait_for_remote_debugger) + regs->ea -= 4; + else /* pass the first trap 30 code */ + wait_for_remote_debugger = 0; + + kgdb_handle_exception(30, SIGTRAP, 0, regs); +} + +int kgdb_arch_init(void) +{ + wait_for_remote_debugger = 1; + return 0; +} + +void kgdb_arch_exit(void) +{ + /* Nothing to do */ +} + +struct kgdb_arch arch_kgdb_ops = { + /* Breakpoint instruction: trap 30 */ + .gdb_bpt_instr = { 0xba, 0x6f, 0x3b, 0x00 }, +}; diff --git a/arch/nios2/kernel/module.c b/arch/nios2/kernel/module.c index cc924a38f22..e2e3f13f98d 100644 --- a/arch/nios2/kernel/module.c +++ b/arch/nios2/kernel/module.c @@ -36,7 +36,7 @@ void *module_alloc(unsigned long size) } /* Free memory returned from module_alloc */ -void module_free(struct module *mod, void *module_region) +void module_memfree(void *module_region) { kfree(module_region); } diff --git a/arch/nios2/kernel/prom.c b/arch/nios2/kernel/prom.c index 0522d3378e3..718dd197909 100644 --- a/arch/nios2/kernel/prom.c +++ b/arch/nios2/kernel/prom.c @@ -1,7 +1,7 @@ /* * Device tree support * - * Copyright (C) 2013 Altera Corporation + * Copyright (C) 2013, 2015 Altera Corporation * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw> * * Based on MIPS support for CONFIG_OF device tree support @@ -30,6 +30,7 @@ #include <linux/of_fdt.h> #include <linux/io.h> +#include <asm/prom.h> #include <asm/sections.h> void __init early_init_dt_add_memory_arch(u64 base, u64 size) @@ -63,3 +64,52 @@ void __init early_init_devtree(void *params) early_init_dt_scan(params); } + +#ifdef CONFIG_EARLY_PRINTK +static int __init early_init_dt_scan_serial(unsigned long node, + const char *uname, int depth, void *data) +{ + u64 *addr64 = (u64 *) data; + const char *p; + + /* only consider serial nodes */ + if (strncmp(uname, "serial", 6) != 0) + return 0; + + p = of_get_flat_dt_prop(node, "compatible", NULL); + if (!p) + return 0; + + /* + * We found an altera_jtaguart but it wasn't configured for console, so + * skip it. + */ +#ifndef CONFIG_SERIAL_ALTERA_JTAGUART_CONSOLE + if (strncmp(p, "altr,juart", 10) == 0) + return 0; +#endif + + /* + * Same for altera_uart. + */ +#ifndef CONFIG_SERIAL_ALTERA_UART_CONSOLE + if (strncmp(p, "altr,uart", 9) == 0) + return 0; +#endif + + *addr64 = fdt_translate_address((const void *)initial_boot_params, + node); + + return *addr64 == OF_BAD_ADDR ? 0 : 1; +} + +unsigned long __init of_early_console(void) +{ + u64 base = 0; + + if (of_scan_flat_dt(early_init_dt_scan_serial, &base)) + return (u32)ioremap(base, 32); + else + return 0; +} +#endif /* CONFIG_EARLY_PRINTK */ diff --git a/arch/nios2/kernel/setup.c b/arch/nios2/kernel/setup.c index cb3121f975d..b101a43d3c5 100644 --- a/arch/nios2/kernel/setup.c +++ b/arch/nios2/kernel/setup.c @@ -139,6 +139,10 @@ void __init setup_arch(char **cmdline_p) console_verbose(); +#ifdef CONFIG_EARLY_PRINTK + setup_early_printk(); +#endif + memory_start = PAGE_ALIGN((unsigned long)__pa(_end)); memory_end = (unsigned long) CONFIG_NIOS2_MEM_BASE + memory_size; diff --git a/arch/nios2/kernel/signal.c b/arch/nios2/kernel/signal.c index f9d27883a71..2d0ea25be17 100644 --- a/arch/nios2/kernel/signal.c +++ b/arch/nios2/kernel/signal.c @@ -200,7 +200,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, /* Set up to return from userspace; jump to fixed address sigreturn trampoline on kuser page. */ - regs->ra = (unsigned long) (0x1040); + regs->ra = (unsigned long) (0x1044); /* Set up registers for signal handler */ regs->sp = (unsigned long) frame; |