diff options
author | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
---|---|---|
committer | André Fabian Silva Delgado <emulatorman@parabola.nu> | 2015-08-05 17:04:01 -0300 |
commit | 57f0f512b273f60d52568b8c6b77e17f5636edc0 (patch) | |
tree | 5e910f0e82173f4ef4f51111366a3f1299037a7b /arch/frv/kernel |
Initial import
Diffstat (limited to 'arch/frv/kernel')
40 files changed, 13832 insertions, 0 deletions
diff --git a/arch/frv/kernel/Makefile b/arch/frv/kernel/Makefile new file mode 100644 index 000000000..3cbb3294b --- /dev/null +++ b/arch/frv/kernel/Makefile @@ -0,0 +1,23 @@ +# +# Makefile for the linux kernel. +# + +heads-y := head-uc-fr401.o head-uc-fr451.o head-uc-fr555.o +heads-$(CONFIG_MMU) := head-mmu-fr451.o + +extra-y:= head.o vmlinux.lds + +obj-y := $(heads-y) entry.o entry-table.o break.o switch_to.o \ + process.o traps.o ptrace.o signal.o dma.o \ + sys_frv.o time.o setup.o frv_ksyms.o \ + debug-stub.o irq.o sleep.o uaccess.o + +obj-$(CONFIG_GDBSTUB) += gdb-stub.o gdb-io.o + +obj-$(CONFIG_MB93091_VDK) += irq-mb93091.o +obj-$(CONFIG_PM) += pm.o cmode.o +obj-$(CONFIG_MB93093_PDK) += pm-mb93093.o +obj-$(CONFIG_FUJITSU_MB93493) += irq-mb93493.o +obj-$(CONFIG_SYSCTL) += sysctl.o +obj-$(CONFIG_FUTEX) += futex.o +obj-$(CONFIG_MODULES) += module.o diff --git a/arch/frv/kernel/asm-offsets.c b/arch/frv/kernel/asm-offsets.c new file mode 100644 index 000000000..8414293f2 --- /dev/null +++ b/arch/frv/kernel/asm-offsets.c @@ -0,0 +1,106 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed + * to extract and format the required data. + */ + +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/personality.h> +#include <linux/kbuild.h> +#include <asm/registers.h> +#include <asm/ucontext.h> +#include <asm/processor.h> +#include <asm/thread_info.h> +#include <asm/gdb-stub.h> + +#define DEF_PTREG(sym, reg) \ + asm volatile("\n->" #sym " %0 offsetof(struct pt_regs, " #reg ")" \ + : : "i" (offsetof(struct pt_regs, reg))) + +#define DEF_IREG(sym, reg) \ + asm volatile("\n->" #sym " %0 offsetof(struct user_context, " #reg ")" \ + : : "i" (offsetof(struct user_context, reg))) + +#define DEF_FREG(sym, reg) \ + asm volatile("\n->" #sym " %0 offsetof(struct user_context, " #reg ")" \ + : : "i" (offsetof(struct user_context, reg))) + +#define DEF_0REG(sym, reg) \ + asm volatile("\n->" #sym " %0 offsetof(struct frv_frame0, " #reg ")" \ + : : "i" (offsetof(struct frv_frame0, reg))) + +void foo(void) +{ + /* offsets into the thread_info structure */ + OFFSET(TI_TASK, thread_info, task); + OFFSET(TI_FLAGS, thread_info, flags); + OFFSET(TI_STATUS, thread_info, status); + OFFSET(TI_CPU, thread_info, cpu); + OFFSET(TI_PREEMPT_COUNT, thread_info, preempt_count); + OFFSET(TI_ADDR_LIMIT, thread_info, addr_limit); + BLANK(); + + /* offsets into register file storage */ + DEF_PTREG(REG_PSR, psr); + DEF_PTREG(REG_ISR, isr); + DEF_PTREG(REG_CCR, ccr); + DEF_PTREG(REG_CCCR, cccr); + DEF_PTREG(REG_LR, lr); + DEF_PTREG(REG_LCR, lcr); + DEF_PTREG(REG_PC, pc); + DEF_PTREG(REG__STATUS, __status); + DEF_PTREG(REG_SYSCALLNO, syscallno); + DEF_PTREG(REG_ORIG_GR8, orig_gr8); + DEF_PTREG(REG_GNER0, gner0); + DEF_PTREG(REG_GNER1, gner1); + DEF_PTREG(REG_IACC0, iacc0); + DEF_PTREG(REG_TBR, tbr); + DEF_PTREG(REG_GR0, tbr); + DEFINE(REG__END, sizeof(struct pt_regs)); + BLANK(); + + DEF_0REG(REG_DCR, debug.dcr); + DEF_0REG(REG_IBAR0, debug.ibar[0]); + DEF_0REG(REG_DBAR0, debug.dbar[0]); + DEF_0REG(REG_DBDR00, debug.dbdr[0][0]); + DEF_0REG(REG_DBMR00, debug.dbmr[0][0]); + BLANK(); + + DEF_IREG(__INT_GR0, i.gr[0]); + DEF_FREG(__USER_FPMEDIA, f); + DEF_FREG(__FPMEDIA_FR0, f.fr[0]); + DEF_FREG(__FPMEDIA_FNER0, f.fner[0]); + DEF_FREG(__FPMEDIA_MSR0, f.msr[0]); + DEF_FREG(__FPMEDIA_ACC0, f.acc[0]); + DEF_FREG(__FPMEDIA_ACCG0, f.accg[0]); + DEF_FREG(__FPMEDIA_FSR0, f.fsr[0]); + BLANK(); + + DEFINE(NR_PT_REGS, sizeof(struct pt_regs) / 4); + DEFINE(NR_USER_INT_REGS, sizeof(struct user_int_regs) / 4); + DEFINE(NR_USER_FPMEDIA_REGS, sizeof(struct user_fpmedia_regs) / 4); + DEFINE(NR_USER_CONTEXT, sizeof(struct user_context) / 4); + DEFINE(FRV_FRAME0_SIZE, sizeof(struct frv_frame0)); + BLANK(); + + /* offsets into thread_struct */ + OFFSET(__THREAD_FRAME, thread_struct, frame); + OFFSET(__THREAD_CURR, thread_struct, curr); + OFFSET(__THREAD_SP, thread_struct, sp); + OFFSET(__THREAD_FP, thread_struct, fp); + OFFSET(__THREAD_LR, thread_struct, lr); + OFFSET(__THREAD_PC, thread_struct, pc); + OFFSET(__THREAD_GR16, thread_struct, gr[0]); + OFFSET(__THREAD_SCHED_LR, thread_struct, sched_lr); + OFFSET(__THREAD_FRAME0, thread_struct, frame0); + OFFSET(__THREAD_USER, thread_struct, user); + BLANK(); + + /* offsets into frv_debug_status */ + OFFSET(DEBUG_BPSR, frv_debug_status, bpsr); + OFFSET(DEBUG_DCR, frv_debug_status, dcr); + OFFSET(DEBUG_BRR, frv_debug_status, brr); + OFFSET(DEBUG_NMAR, frv_debug_status, nmar); + BLANK(); +} diff --git a/arch/frv/kernel/break.S b/arch/frv/kernel/break.S new file mode 100644 index 000000000..cbb6958a3 --- /dev/null +++ b/arch/frv/kernel/break.S @@ -0,0 +1,792 @@ +/* break.S: Break interrupt handling (kept separate from entry.S) + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/linkage.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/thread_info.h> +#include <asm/spr-regs.h> + +#include <asm/errno.h> + +# +# the break handler has its own stack +# + .section .bss..stack + .globl __break_user_context + .balign THREAD_SIZE +__break_stack: + .space THREAD_SIZE - FRV_FRAME0_SIZE +__break_frame_0: + .space FRV_FRAME0_SIZE + +# +# miscellaneous variables +# + .section .bss +#ifdef CONFIG_MMU + .globl __break_tlb_miss_real_return_info +__break_tlb_miss_real_return_info: + .balign 8 + .space 2*4 /* saved PCSR, PSR for TLB-miss handler fixup */ +#endif + +__break_trace_through_exceptions: + .space 4 + +#define CS2_ECS1 0xe1200000 +#define CS2_USERLED 0x4 + +.macro LEDS val,reg +# sethi.p %hi(CS2_ECS1+CS2_USERLED),gr30 +# setlo %lo(CS2_ECS1+CS2_USERLED),gr30 +# setlos #~\val,\reg +# st \reg,@(gr30,gr0) +# setlos #0x5555,\reg +# sethi.p %hi(0xffc00100),gr30 +# setlo %lo(0xffc00100),gr30 +# sth \reg,@(gr30,gr0) +# membar +.endm + +############################################################################### +# +# entry point for Break Exceptions/Interrupts +# +############################################################################### + .section .text..break + .balign 4 + .globl __entry_break +__entry_break: +#ifdef CONFIG_MMU + movgs gr31,scr3 +#endif + LEDS 0x1001,gr31 + + sethi.p %hi(__break_frame_0),gr31 + setlo %lo(__break_frame_0),gr31 + + stdi gr2,@(gr31,#REG_GR(2)) + movsg ccr,gr3 + sti gr3,@(gr31,#REG_CCR) + + # catch the return from a TLB-miss handler that had single-step disabled + # traps will be enabled, so we have to do this now +#ifdef CONFIG_MMU + movsg bpcsr,gr3 + sethi.p %hi(__break_tlb_miss_return_breaks_here),gr2 + setlo %lo(__break_tlb_miss_return_breaks_here),gr2 + subcc gr2,gr3,gr0,icc0 + beq icc0,#2,__break_return_singlestep_tlbmiss +#endif + + # determine whether we have stepped through into an exception + # - we need to take special action to suspend h/w single stepping if we've done + # that, so that the gdbstub doesn't get bogged down endlessly stepping through + # external interrupt handling + movsg bpsr,gr3 + andicc gr3,#BPSR_BET,gr0,icc0 + bne icc0,#2,__break_maybe_userspace /* jump if PSR.ET was 1 */ + + LEDS 0x1003,gr2 + + movsg brr,gr3 + andicc gr3,#BRR_ST,gr0,icc0 + andicc.p gr3,#BRR_SB,gr0,icc1 + bne icc0,#2,__break_step /* jump if single-step caused break */ + beq icc1,#2,__break_continue /* jump if BREAK didn't cause break */ + + LEDS 0x1007,gr2 + + # handle special breaks + movsg bpcsr,gr3 + + sethi.p %hi(__entry_return_singlestep_breaks_here),gr2 + setlo %lo(__entry_return_singlestep_breaks_here),gr2 + subcc gr2,gr3,gr0,icc0 + beq icc0,#2,__break_return_singlestep + + bra __break_continue + + +############################################################################### +# +# handle BREAK instruction in kernel-mode exception epilogue +# +############################################################################### +__break_return_singlestep: + LEDS 0x100f,gr2 + + # special break insn requests single-stepping to be turned back on + # HERE RETT + # PSR.ET 0 0 + # PSR.PS old PSR.S ? + # PSR.S 1 1 + # BPSR.ET 0 1 (can't have caused orig excep otherwise) + # BPSR.BS 1 old PSR.S + movsg dcr,gr2 + sethi.p %hi(DCR_SE),gr3 + setlo %lo(DCR_SE),gr3 + or gr2,gr3,gr2 + movgs gr2,dcr + + movsg psr,gr2 + andi gr2,#PSR_PS,gr2 + slli gr2,#11,gr2 /* PSR.PS -> BPSR.BS */ + ori gr2,#BPSR_BET,gr2 /* 1 -> BPSR.BET */ + movgs gr2,bpsr + + # return to the invoker of the original kernel exception + movsg pcsr,gr2 + movgs gr2,bpcsr + + LEDS 0x101f,gr2 + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + lddi.p @(gr31,#REG_GR(2)),gr2 + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +############################################################################### +# +# handle BREAK instruction in TLB-miss handler return path +# +############################################################################### +#ifdef CONFIG_MMU +__break_return_singlestep_tlbmiss: + LEDS 0x1100,gr2 + + sethi.p %hi(__break_tlb_miss_real_return_info),gr3 + setlo %lo(__break_tlb_miss_real_return_info),gr3 + lddi @(gr3,#0),gr2 + movgs gr2,pcsr + movgs gr3,psr + + bra __break_return_singlestep +#endif + + +############################################################################### +# +# handle single stepping into an exception prologue from kernel mode +# - we try and catch it whilst it is still in the main vector table +# - if we catch it there, we have to jump to the fixup handler +# - there is a fixup table that has a pointer for every 16b slot in the trap +# table +# +############################################################################### +__break_step: + LEDS 0x2003,gr2 + + # external interrupts seem to escape from the trap table before single + # step catches up with them + movsg bpcsr,gr2 + sethi.p %hi(__entry_kernel_external_interrupt),gr3 + setlo %lo(__entry_kernel_external_interrupt),gr3 + subcc.p gr2,gr3,gr0,icc0 + sethi %hi(__entry_uspace_external_interrupt),gr3 + setlo.p %lo(__entry_uspace_external_interrupt),gr3 + beq icc0,#2,__break_step_kernel_external_interrupt + subcc.p gr2,gr3,gr0,icc0 + sethi %hi(__entry_kernel_external_interrupt_virtually_disabled),gr3 + setlo.p %lo(__entry_kernel_external_interrupt_virtually_disabled),gr3 + beq icc0,#2,__break_step_uspace_external_interrupt + subcc.p gr2,gr3,gr0,icc0 + sethi %hi(__entry_kernel_external_interrupt_virtual_reenable),gr3 + setlo.p %lo(__entry_kernel_external_interrupt_virtual_reenable),gr3 + beq icc0,#2,__break_step_kernel_external_interrupt_virtually_disabled + subcc gr2,gr3,gr0,icc0 + beq icc0,#2,__break_step_kernel_external_interrupt_virtual_reenable + + LEDS 0x2007,gr2 + + # the two main vector tables are adjacent on one 8Kb slab + movsg bpcsr,gr2 + setlos #0xffffe000,gr3 + and gr2,gr3,gr2 + sethi.p %hi(__trap_tables),gr3 + setlo %lo(__trap_tables),gr3 + subcc gr2,gr3,gr0,icc0 + bne icc0,#2,__break_continue + + LEDS 0x200f,gr2 + + # skip workaround if so requested by GDB + sethi.p %hi(__break_trace_through_exceptions),gr3 + setlo %lo(__break_trace_through_exceptions),gr3 + ld @(gr3,gr0),gr3 + subcc gr3,gr0,gr0,icc0 + bne icc0,#0,__break_continue + + LEDS 0x201f,gr2 + + # access the fixup table - there's a 1:1 mapping between the slots in the trap tables and + # the slots in the trap fixup tables allowing us to simply divide the offset into the + # former by 4 to access the latter + sethi.p %hi(__trap_tables),gr3 + setlo %lo(__trap_tables),gr3 + movsg bpcsr,gr2 + sub gr2,gr3,gr2 + srli.p gr2,#2,gr2 + + sethi %hi(__trap_fixup_tables),gr3 + setlo.p %lo(__trap_fixup_tables),gr3 + andi gr2,#~3,gr2 + ld @(gr2,gr3),gr2 + jmpil @(gr2,#0) + +# step through an internal exception from kernel mode + .globl __break_step_kernel_softprog_interrupt +__break_step_kernel_softprog_interrupt: + sethi.p %hi(__entry_kernel_softprog_interrupt_reentry),gr3 + setlo %lo(__entry_kernel_softprog_interrupt_reentry),gr3 + bra __break_return_as_kernel_prologue + +# step through an external interrupt from kernel mode + .globl __break_step_kernel_external_interrupt +__break_step_kernel_external_interrupt: + # deal with virtual interrupt disablement + beq icc2,#0,__break_step_kernel_external_interrupt_virtually_disabled + + sethi.p %hi(__entry_kernel_external_interrupt_reentry),gr3 + setlo %lo(__entry_kernel_external_interrupt_reentry),gr3 + +__break_return_as_kernel_prologue: + LEDS 0x203f,gr2 + + movgs gr3,bpcsr + + # do the bit we had to skip +#ifdef CONFIG_MMU + movsg ear0,gr2 /* EAR0 can get clobbered by gdb-stub (ICI/ICEI) */ + movgs gr2,scr2 +#endif + + or.p sp,gr0,gr2 /* set up the stack pointer */ + subi sp,#REG__END,sp + sti.p gr2,@(sp,#REG_SP) + + setlos #REG__STATUS_STEP,gr2 + sti gr2,@(sp,#REG__STATUS) /* record single step status */ + + # cancel single-stepping mode + movsg dcr,gr2 + sethi.p %hi(~DCR_SE),gr3 + setlo %lo(~DCR_SE),gr3 + and gr2,gr3,gr2 + movgs gr2,dcr + + LEDS 0x207f,gr2 + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + lddi.p @(gr31,#REG_GR(2)),gr2 + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +# we single-stepped into an interrupt handler whilst interrupts were merely virtually disabled +# need to really disable interrupts, set flag, fix up and return +__break_step_kernel_external_interrupt_virtually_disabled: + movsg psr,gr2 + andi gr2,#~PSR_PIL,gr2 + ori gr2,#PSR_PIL_14,gr2 /* debugging interrupts only */ + movgs gr2,psr + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + subcc.p gr0,gr0,gr0,icc2 /* leave Z set, clear C */ + + # exceptions must've been enabled and we must've been in supervisor mode + setlos BPSR_BET|BPSR_BS,gr3 + movgs gr3,bpsr + + # return to where the interrupt happened + movsg pcsr,gr2 + movgs gr2,bpcsr + + lddi.p @(gr31,#REG_GR(2)),gr2 + + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +# we stepped through into the virtual interrupt reenablement trap +# +# we also want to single step anyway, but after fixing up so that we get an event on the +# instruction after the broken-into exception returns + .globl __break_step_kernel_external_interrupt_virtual_reenable +__break_step_kernel_external_interrupt_virtual_reenable: + movsg psr,gr2 + andi gr2,#~PSR_PIL,gr2 + movgs gr2,psr + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + subicc gr0,#1,gr0,icc2 /* clear Z, set C */ + + # save the adjusted ICC2 + movsg ccr,gr3 + sti gr3,@(gr31,#REG_CCR) + + # exceptions must've been enabled and we must've been in supervisor mode + setlos BPSR_BET|BPSR_BS,gr3 + movgs gr3,bpsr + + # return to where the trap happened + movsg pcsr,gr2 + movgs gr2,bpcsr + + # and then process the single step + bra __break_continue + +# step through an internal exception from uspace mode + .globl __break_step_uspace_softprog_interrupt +__break_step_uspace_softprog_interrupt: + sethi.p %hi(__entry_uspace_softprog_interrupt_reentry),gr3 + setlo %lo(__entry_uspace_softprog_interrupt_reentry),gr3 + bra __break_return_as_uspace_prologue + +# step through an external interrupt from kernel mode + .globl __break_step_uspace_external_interrupt +__break_step_uspace_external_interrupt: + sethi.p %hi(__entry_uspace_external_interrupt_reentry),gr3 + setlo %lo(__entry_uspace_external_interrupt_reentry),gr3 + +__break_return_as_uspace_prologue: + LEDS 0x20ff,gr2 + + movgs gr3,bpcsr + + # do the bit we had to skip + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + ldi.p @(gr28,#0),gr28 + + setlos #REG__STATUS_STEP,gr2 + sti gr2,@(gr28,#REG__STATUS) /* record single step status */ + + # cancel single-stepping mode + movsg dcr,gr2 + sethi.p %hi(~DCR_SE),gr3 + setlo %lo(~DCR_SE),gr3 + and gr2,gr3,gr2 + movgs gr2,dcr + + LEDS 0x20fe,gr2 + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + lddi.p @(gr31,#REG_GR(2)),gr2 + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +#ifdef CONFIG_MMU +# step through an ITLB-miss handler from user mode + .globl __break_user_insn_tlb_miss +__break_user_insn_tlb_miss: + # we'll want to try the trap stub again + sethi.p %hi(__trap_user_insn_tlb_miss),gr2 + setlo %lo(__trap_user_insn_tlb_miss),gr2 + movgs gr2,bpcsr + +__break_tlb_miss_common: + LEDS 0x2101,gr2 + + # cancel single-stepping mode + movsg dcr,gr2 + sethi.p %hi(~DCR_SE),gr3 + setlo %lo(~DCR_SE),gr3 + and gr2,gr3,gr2 + movgs gr2,dcr + + # we'll swap the real return address for one with a BREAK insn so that we can re-enable + # single stepping on return + movsg pcsr,gr2 + sethi.p %hi(__break_tlb_miss_real_return_info),gr3 + setlo %lo(__break_tlb_miss_real_return_info),gr3 + sti gr2,@(gr3,#0) + + sethi.p %hi(__break_tlb_miss_return_break),gr2 + setlo %lo(__break_tlb_miss_return_break),gr2 + movgs gr2,pcsr + + # we also have to fudge PSR because the return BREAK is in kernel space and we want + # to get a BREAK fault not an access violation should the return be to userspace + movsg psr,gr2 + sti.p gr2,@(gr3,#4) + ori gr2,#PSR_PS,gr2 + movgs gr2,psr + + LEDS 0x2102,gr2 + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + lddi @(gr31,#REG_GR(2)),gr2 + movsg scr3,gr31 + movgs gr0,brr + rett #1 + +# step through a DTLB-miss handler from user mode + .globl __break_user_data_tlb_miss +__break_user_data_tlb_miss: + # we'll want to try the trap stub again + sethi.p %hi(__trap_user_data_tlb_miss),gr2 + setlo %lo(__trap_user_data_tlb_miss),gr2 + movgs gr2,bpcsr + bra __break_tlb_miss_common + +# step through an ITLB-miss handler from kernel mode + .globl __break_kernel_insn_tlb_miss +__break_kernel_insn_tlb_miss: + # we'll want to try the trap stub again + sethi.p %hi(__trap_kernel_insn_tlb_miss),gr2 + setlo %lo(__trap_kernel_insn_tlb_miss),gr2 + movgs gr2,bpcsr + bra __break_tlb_miss_common + +# step through a DTLB-miss handler from kernel mode + .globl __break_kernel_data_tlb_miss +__break_kernel_data_tlb_miss: + # we'll want to try the trap stub again + sethi.p %hi(__trap_kernel_data_tlb_miss),gr2 + setlo %lo(__trap_kernel_data_tlb_miss),gr2 + movgs gr2,bpcsr + bra __break_tlb_miss_common +#endif + +############################################################################### +# +# handle debug events originating with userspace +# +############################################################################### +__break_maybe_userspace: + LEDS 0x3003,gr2 + + setlos #BPSR_BS,gr2 + andcc gr3,gr2,gr0,icc0 + bne icc0,#0,__break_continue /* skip if PSR.S was 1 */ + + movsg brr,gr2 + andicc gr2,#BRR_ST|BRR_SB,gr0,icc0 + beq icc0,#0,__break_continue /* jump if not BREAK or single-step */ + + LEDS 0x3007,gr2 + + # do the first part of the exception prologue here + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + ldi @(gr28,#0),gr28 + andi gr28,#~7,gr28 + + # set up the kernel stack pointer + sti sp ,@(gr28,#REG_SP) + ori gr28,0,sp + sti gr0 ,@(gr28,#REG_GR(28)) + + stdi gr20,@(gr28,#REG_GR(20)) + stdi gr22,@(gr28,#REG_GR(22)) + + movsg tbr,gr20 + movsg bpcsr,gr21 + movsg psr,gr22 + + # determine the exception type and cancel single-stepping mode + or gr0,gr0,gr23 + + movsg dcr,gr2 + sethi.p %hi(DCR_SE),gr3 + setlo %lo(DCR_SE),gr3 + andcc gr2,gr3,gr0,icc0 + beq icc0,#0,__break_no_user_sstep /* must have been a BREAK insn */ + + not gr3,gr3 + and gr2,gr3,gr2 + movgs gr2,dcr + ori gr23,#REG__STATUS_STEP,gr23 + +__break_no_user_sstep: + LEDS 0x300f,gr2 + + movsg brr,gr2 + andi gr2,#BRR_ST|BRR_SB,gr2 + slli gr2,#1,gr2 + or gr23,gr2,gr23 + sti.p gr23,@(gr28,#REG__STATUS) /* record single step status */ + + # adjust the value acquired from TBR - this indicates the exception + setlos #~TBR_TT,gr2 + and.p gr20,gr2,gr20 + setlos #TBR_TT_BREAK,gr2 + or.p gr20,gr2,gr20 + + # fudge PSR.PS and BPSR.BS to return to kernel mode through the trap + # table as trap 126 + andi gr22,#~PSR_PS,gr22 /* PSR.PS should be 0 */ + movgs gr22,psr + + setlos #BPSR_BS,gr2 /* BPSR.BS should be 1 and BPSR.BET 0 */ + movgs gr2,bpsr + + # return through remainder of the exception prologue + # - need to load gr23 with return handler address + sethi.p %hi(__entry_return_from_user_exception),gr23 + setlo %lo(__entry_return_from_user_exception),gr23 + sethi.p %hi(__entry_common),gr3 + setlo %lo(__entry_common),gr3 + movgs gr3,bpcsr + + LEDS 0x301f,gr2 + + ldi @(gr31,#REG_CCR),gr3 + movgs gr3,ccr + lddi.p @(gr31,#REG_GR(2)),gr2 + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +############################################################################### +# +# resume normal debug-mode entry +# +############################################################################### +__break_continue: + LEDS 0x4003,gr2 + + # set up the kernel stack pointer + sti sp,@(gr31,#REG_SP) + + sethi.p %hi(__break_frame_0),sp + setlo %lo(__break_frame_0),sp + + # finish building the exception frame + stdi gr4 ,@(gr31,#REG_GR(4)) + stdi gr6 ,@(gr31,#REG_GR(6)) + stdi gr8 ,@(gr31,#REG_GR(8)) + stdi gr10,@(gr31,#REG_GR(10)) + stdi gr12,@(gr31,#REG_GR(12)) + stdi gr14,@(gr31,#REG_GR(14)) + stdi gr16,@(gr31,#REG_GR(16)) + stdi gr18,@(gr31,#REG_GR(18)) + stdi gr20,@(gr31,#REG_GR(20)) + stdi gr22,@(gr31,#REG_GR(22)) + stdi gr24,@(gr31,#REG_GR(24)) + stdi gr26,@(gr31,#REG_GR(26)) + sti gr0 ,@(gr31,#REG_GR(28)) /* NULL frame pointer */ + sti gr29,@(gr31,#REG_GR(29)) + sti gr30,@(gr31,#REG_GR(30)) + sti gr8 ,@(gr31,#REG_ORIG_GR8) + +#ifdef CONFIG_MMU + movsg scr3,gr19 + sti gr19,@(gr31,#REG_GR(31)) +#endif + + movsg bpsr ,gr19 + movsg tbr ,gr20 + movsg bpcsr,gr21 + movsg psr ,gr22 + movsg isr ,gr23 + movsg cccr ,gr25 + movsg lr ,gr26 + movsg lcr ,gr27 + + andi.p gr22,#~(PSR_S|PSR_ET),gr5 /* rebuild PSR */ + andi gr19,#PSR_ET,gr4 + or.p gr4,gr5,gr5 + srli gr19,#10,gr4 + andi gr4,#PSR_S,gr4 + or.p gr4,gr5,gr5 + + setlos #-1,gr6 + sti gr20,@(gr31,#REG_TBR) + sti gr21,@(gr31,#REG_PC) + sti gr5 ,@(gr31,#REG_PSR) + sti gr23,@(gr31,#REG_ISR) + sti gr25,@(gr31,#REG_CCCR) + stdi gr26,@(gr31,#REG_LR) + sti gr6 ,@(gr31,#REG_SYSCALLNO) + + # store CPU-specific regs + movsg iacc0h,gr4 + movsg iacc0l,gr5 + stdi gr4,@(gr31,#REG_IACC0) + + movsg gner0,gr4 + movsg gner1,gr5 + stdi gr4,@(gr31,#REG_GNER0) + + # build the debug register frame + movsg brr,gr4 + movgs gr0,brr + movsg nmar,gr5 + movsg dcr,gr6 + + sethi.p %hi(__debug_status),gr7 + setlo %lo(__debug_status),gr7 + + stdi gr4 ,@(gr7,#DEBUG_BRR) + sti gr19,@(gr7,#DEBUG_BPSR) + sti.p gr6 ,@(gr7,#DEBUG_DCR) + + # trap exceptions during break handling and disable h/w breakpoints/watchpoints + sethi %hi(DCR_EBE),gr5 + setlo.p %lo(DCR_EBE),gr5 + sethi %hi(__entry_breaktrap_table),gr4 + setlo %lo(__entry_breaktrap_table),gr4 + movgs gr5,dcr + movgs gr4,tbr + + # set up kernel global registers + sethi.p %hi(__kernel_current_task),gr5 + setlo %lo(__kernel_current_task),gr5 + ld @(gr5,gr0),gr29 + ldi.p @(gr29,#4),gr15 ; __current_thread_info = current->thread_info + + sethi %hi(_gp),gr16 + setlo.p %lo(_gp),gr16 + + # make sure we (the kernel) get div-zero and misalignment exceptions + setlos #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5 + movgs gr5,isr + + # enter the GDB stub + LEDS 0x4007,gr2 + + or.p gr0,gr0,fp + call debug_stub + + LEDS 0x403f,gr2 + + # return from break + lddi @(gr31,#REG_IACC0),gr4 + movgs gr4,iacc0h + movgs gr5,iacc0l + + lddi @(gr31,#REG_GNER0),gr4 + movgs gr4,gner0 + movgs gr5,gner1 + + lddi @(gr31,#REG_LR) ,gr26 + lddi @(gr31,#REG_CCR) ,gr24 + lddi @(gr31,#REG_PSR) ,gr22 + ldi @(gr31,#REG_PC) ,gr21 + ldi @(gr31,#REG_TBR) ,gr20 + + sethi.p %hi(__debug_status),gr6 + setlo %lo(__debug_status),gr6 + ldi.p @(gr6,#DEBUG_DCR) ,gr6 + + andi gr22,#PSR_S,gr19 /* rebuild BPSR */ + andi.p gr22,#PSR_ET,gr5 + slli gr19,#10,gr19 + or gr5,gr19,gr19 + + movgs gr6 ,dcr + movgs gr19,bpsr + movgs gr20,tbr + movgs gr21,bpcsr + movgs gr23,isr + movgs gr24,ccr + movgs gr25,cccr + movgs gr26,lr + movgs gr27,lcr + + LEDS 0x407f,gr2 + +#ifdef CONFIG_MMU + ldi @(gr31,#REG_GR(31)),gr2 + movgs gr2,scr3 +#endif + + ldi @(gr31,#REG_GR(30)),gr30 + ldi @(gr31,#REG_GR(29)),gr29 + lddi @(gr31,#REG_GR(26)),gr26 + lddi @(gr31,#REG_GR(24)),gr24 + lddi @(gr31,#REG_GR(22)),gr22 + lddi @(gr31,#REG_GR(20)),gr20 + lddi @(gr31,#REG_GR(18)),gr18 + lddi @(gr31,#REG_GR(16)),gr16 + lddi @(gr31,#REG_GR(14)),gr14 + lddi @(gr31,#REG_GR(12)),gr12 + lddi @(gr31,#REG_GR(10)),gr10 + lddi @(gr31,#REG_GR(8)) ,gr8 + lddi @(gr31,#REG_GR(6)) ,gr6 + lddi @(gr31,#REG_GR(4)) ,gr4 + lddi @(gr31,#REG_GR(2)) ,gr2 + ldi.p @(gr31,#REG_SP) ,sp + + xor gr31,gr31,gr31 + movgs gr0,brr +#ifdef CONFIG_MMU + movsg scr3,gr31 +#endif + rett #1 + +################################################################################################### +# +# GDB stub "system calls" +# +################################################################################################### + +#ifdef CONFIG_GDBSTUB + # void gdbstub_console_write(struct console *con, const char *p, unsigned n) + .globl gdbstub_console_write +gdbstub_console_write: + break + bralr +#endif + + # GDB stub BUG() trap + # GR8 is the proposed signal number + .globl __debug_bug_trap +__debug_bug_trap: + break + bralr + + # transfer kernel exeception to GDB for handling + .globl __break_hijack_kernel_event +__break_hijack_kernel_event: + break + .globl __break_hijack_kernel_event_breaks_here +__break_hijack_kernel_event_breaks_here: + nop + +#ifdef CONFIG_MMU + # handle a return from TLB-miss that requires single-step reactivation + .globl __break_tlb_miss_return_break +__break_tlb_miss_return_break: + break +__break_tlb_miss_return_breaks_here: + nop +#endif + + # guard the first .text label in the next file from confusion + nop diff --git a/arch/frv/kernel/cmode.S b/arch/frv/kernel/cmode.S new file mode 100644 index 000000000..53deeb5d7 --- /dev/null +++ b/arch/frv/kernel/cmode.S @@ -0,0 +1,189 @@ +/* cmode.S: clock mode management + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Woodhouse (dwmw2@infradead.org) + * + * 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. + * + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/errno.h> +#include <asm/cache.h> +#include <asm/spr-regs.h> + +#define __addr_MASK 0xfeff9820 /* interrupt controller mask */ + +#define __addr_SDRAMC 0xfe000400 /* SDRAM controller regs */ +#define SDRAMC_DSTS 0x28 /* SDRAM status */ +#define SDRAMC_DSTS_SSI 0x00000001 /* indicates that the SDRAM is in self-refresh mode */ +#define SDRAMC_DRCN 0x30 /* SDRAM refresh control */ +#define SDRAMC_DRCN_SR 0x00000001 /* transition SDRAM into self-refresh mode */ +#define __addr_CLKC 0xfeff9a00 +#define CLKC_SWCMODE 0x00000008 +#define __addr_LEDS 0xe1200004 + +.macro li v r + sethi.p %hi(\v),\r + setlo %lo(\v),\r +.endm + + .text + .balign 4 + + +############################################################################### +# +# Change CMODE +# - void frv_change_cmode(int cmode) +# +############################################################################### + .globl frv_change_cmode + .type frv_change_cmode,@function + +.macro LEDS v +#ifdef DEBUG_CMODE + setlos #~\v,gr10 + sti gr10,@(gr11,#0) + membar +#endif +.endm + +frv_change_cmode: + movsg lr,gr9 +#ifdef DEBUG_CMODE + li __addr_LEDS,gr11 +#endif + dcef @(gr0,gr0),#1 + + # Shift argument left by 24 bits to fit in SWCMODE register later. + slli gr8,#24,gr8 + + # (1) Set '0' in the PSR.ET bit, and prohibit interrupts. + movsg psr,gr14 + andi gr14,#~PSR_ET,gr3 + movgs gr3,psr + +#if 0 // Fujitsu recommend to skip this and will update docs. + # (2) Set '0' to all bits of the MASK register of the interrupt + # controller, and mask interrupts. + li __addr_MASK,gr12 + ldi @(gr12,#0),gr13 + li 0xffff0000,gr4 + sti gr4,@(gr12,#0) +#endif + + # (3) Stop the transfer function of DMAC. Stop all the bus masters + # to access SDRAM and the internal resources. + + # (already done by caller) + + # (4) Preload a series of following instructions to the instruction + # cache. + li #__cmode_icache_lock_start,gr3 + li #__cmode_icache_lock_end,gr4 + +1: icpl gr3,gr0,#1 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,1b + + # Set up addresses in regs for later steps. + setlos SDRAMC_DRCN_SR,gr3 + li __addr_SDRAMC,gr4 + li __addr_CLKC,gr5 + ldi @(gr5,#0),gr6 + li #0x80000000,gr7 + or gr6,gr7,gr6 + + bra __cmode_icache_lock_start + + .balign L1_CACHE_BYTES +__cmode_icache_lock_start: + + # (5) Flush the content of all caches by the DCEF instruction. + dcef @(gr0,gr0),#1 + + # (6) Execute loading the dummy for SDRAM. + ldi @(gr9,#0),gr0 + + # (7) Set '1' to the DRCN.SR bit, and change SDRAM to the + # self-refresh mode. Execute the dummy load to all memory + # devices set to cacheable on the external bus side in parallel + # with this. + sti gr3,@(gr4,#SDRAMC_DRCN) + + # (8) Execute memory barrier instruction (MEMBAR). + membar + + # (9) Read the DSTS register repeatedly until '1' stands in the + # DSTS.SSI field. +1: ldi @(gr4,#SDRAMC_DSTS),gr3 + andicc gr3,#SDRAMC_DSTS_SSI,gr3,icc0 + beq icc0,#0,1b + + # (10) Execute memory barrier instruction (MEMBAR). + membar + +#if 1 + # (11) Set the value of CMODE that you want to change to + # SWCMODE.SWCM[3:0]. + sti gr8,@(gr5,#CLKC_SWCMODE) + + # (12) Set '1' to the CLKC.SWEN bit. In that case, do not change + # fields other than SWEN of the CLKC register. + sti gr6,@(gr5,#0) +#endif + # (13) Execute the instruction just after the memory barrier + # instruction that executes the self-loop 256 times. (Meanwhile, + # the CMODE switch is done.) + membar + setlos #256,gr7 +2: subicc gr7,#1,gr7,icc0 + bne icc0,#2,2b + + LEDS 0x36 + + # (14) Release the self-refresh of SDRAM. + sti gr0,@(gr4,#SDRAMC_DRCN) + + # Wait for it... +3: ldi @(gr4,#SDRAMC_DSTS),gr3 + andicc gr3,#SDRAMC_DSTS_SSI,gr3,icc0 + bne icc0,#2,3b + +#if 0 + li 0x0100000,gr10 +4: subicc gr10,#1,gr10,icc0 + + bne icc0,#0,4b +#endif + +__cmode_icache_lock_end: + + li #__cmode_icache_lock_start,gr3 + li #__cmode_icache_lock_end,gr4 + +4: icul gr3 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,4b + +#if 0 // Fujitsu recommend to skip this and will update docs. + # (15) Release the interrupt mask setting of the MASK register of + # the interrupt controller if necessary. + sti gr13,@(gr12,#0) +#endif + # (16) Set 1' in the PSR.ET bit, and permit interrupt. + movgs gr14,psr + + bralr + + .size frv_change_cmode, .-frv_change_cmode diff --git a/arch/frv/kernel/debug-stub.c b/arch/frv/kernel/debug-stub.c new file mode 100644 index 000000000..a0228f717 --- /dev/null +++ b/arch/frv/kernel/debug-stub.c @@ -0,0 +1,258 @@ +/* debug-stub.c: debug-mode stub + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/serial_reg.h> +#include <linux/start_kernel.h> + +#include <asm/serial-regs.h> +#include <asm/timer-regs.h> +#include <asm/irc-regs.h> +#include <asm/gdb-stub.h> +#include "gdb-io.h" + +/* CPU board CON5 */ +#define __UART0(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X))) + +#define LSR_WAIT_FOR0(STATE) \ +do { \ +} while (!(__UART0(LSR) & UART_LSR_##STATE)) + +#define FLOWCTL_QUERY0(LINE) ({ __UART0(MSR) & UART_MSR_##LINE; }) +#define FLOWCTL_CLEAR0(LINE) do { __UART0(MCR) &= ~UART_MCR_##LINE; } while (0) +#define FLOWCTL_SET0(LINE) do { __UART0(MCR) |= UART_MCR_##LINE; } while (0) + +#define FLOWCTL_WAIT_FOR0(LINE) \ +do { \ + gdbstub_do_rx(); \ +} while(!FLOWCTL_QUERY(LINE)) + +struct frv_debug_status __debug_status; + +static void __init debug_stub_init(void); + +/*****************************************************************************/ +/* + * debug mode handler stub + * - we come here with the CPU in debug mode and with exceptions disabled + * - handle debugging services for userspace + */ +asmlinkage void debug_stub(void) +{ + unsigned long hsr0; + int type = 0; + + static u8 inited = 0; + if (!inited) { + debug_stub_init(); + type = -1; + inited = 1; + } + + hsr0 = __get_HSR(0); + if (hsr0 & HSR0_ETMD) + __set_HSR(0, hsr0 & ~HSR0_ETMD); + + /* disable single stepping */ + __debug_status.dcr &= ~DCR_SE; + + /* kernel mode can propose an exception be handled in debug mode by jumping to a special + * location */ + if (__debug_frame->pc == (unsigned long) __break_hijack_kernel_event_breaks_here) { + /* replace the debug frame with the kernel frame and discard + * the top kernel context */ + *__debug_frame = *__frame; + __frame = __debug_frame->next_frame; + __debug_status.brr = (__debug_frame->tbr & TBR_TT) << 12; + __debug_status.brr |= BRR_EB; + } + + if (__debug_frame->pc == (unsigned long) __debug_bug_trap + 4) { + __debug_frame->pc = __debug_frame->lr; + type = __debug_frame->gr8; + } + +#ifdef CONFIG_GDBSTUB + gdbstub(type); +#endif + + if (hsr0 & HSR0_ETMD) + __set_HSR(0, __get_HSR(0) | HSR0_ETMD); + +} /* end debug_stub() */ + +/*****************************************************************************/ +/* + * debug stub initialisation + */ +static void __init debug_stub_init(void) +{ + __set_IRR(6, 0xff000000); /* map ERRs to NMI */ + __set_IITMR(1, 0x20000000); /* ERR0/1, UART0/1 IRQ detect levels */ + + asm volatile(" movgs gr0,ibar0 \n" + " movgs gr0,ibar1 \n" + " movgs gr0,ibar2 \n" + " movgs gr0,ibar3 \n" + " movgs gr0,dbar0 \n" + " movgs gr0,dbmr00 \n" + " movgs gr0,dbmr01 \n" + " movgs gr0,dbdr00 \n" + " movgs gr0,dbdr01 \n" + " movgs gr0,dbar1 \n" + " movgs gr0,dbmr10 \n" + " movgs gr0,dbmr11 \n" + " movgs gr0,dbdr10 \n" + " movgs gr0,dbdr11 \n" + ); + + /* deal with debugging stub initialisation and initial pause */ + if (__debug_frame->pc == (unsigned long) __debug_stub_init_break) + __debug_frame->pc = (unsigned long) start_kernel; + + /* enable the debug events we want to trap */ + __debug_status.dcr = DCR_EBE; + +#ifdef CONFIG_GDBSTUB + gdbstub_init(); +#endif + + __clr_MASK_all(); + __clr_MASK(15); + __clr_RC(15); + +} /* end debug_stub_init() */ + +/*****************************************************************************/ +/* + * kernel "exit" trap for gdb stub + */ +void debug_stub_exit(int status) +{ + +#ifdef CONFIG_GDBSTUB + gdbstub_exit(status); +#endif + +} /* end debug_stub_exit() */ + +/*****************************************************************************/ +/* + * send string to serial port + */ +void debug_to_serial(const char *p, int n) +{ + char ch; + + for (; n > 0; n--) { + ch = *p++; + FLOWCTL_SET0(DTR); + LSR_WAIT_FOR0(THRE); + // FLOWCTL_WAIT_FOR(CTS); + + if (ch == 0x0a) { + __UART0(TX) = 0x0d; + mb(); + LSR_WAIT_FOR0(THRE); + // FLOWCTL_WAIT_FOR(CTS); + } + __UART0(TX) = ch; + mb(); + + FLOWCTL_CLEAR0(DTR); + } + +} /* end debug_to_serial() */ + +/*****************************************************************************/ +/* + * send string to serial port + */ +void debug_to_serial2(const char *fmt, ...) +{ + va_list va; + char buf[64]; + int n; + + va_start(va, fmt); + n = vsprintf(buf, fmt, va); + va_end(va); + + debug_to_serial(buf, n); + +} /* end debug_to_serial2() */ + +/*****************************************************************************/ +/* + * set up the ttyS0 serial port baud rate timers + */ +void __init console_set_baud(unsigned baud) +{ + unsigned value, high, low; + u8 lcr; + + /* work out the divisor to give us the nearest higher baud rate */ + value = __serial_clock_speed_HZ / 16 / baud; + + /* determine the baud rate range */ + high = __serial_clock_speed_HZ / 16 / value; + low = __serial_clock_speed_HZ / 16 / (value + 1); + + /* pick the nearest bound */ + if (low + (high - low) / 2 > baud) + value++; + + lcr = __UART0(LCR); + __UART0(LCR) |= UART_LCR_DLAB; + mb(); + __UART0(DLL) = value & 0xff; + __UART0(DLM) = (value >> 8) & 0xff; + mb(); + __UART0(LCR) = lcr; + mb(); + +} /* end console_set_baud() */ + +/*****************************************************************************/ +/* + * + */ +int __init console_get_baud(void) +{ + unsigned value; + u8 lcr; + + lcr = __UART0(LCR); + __UART0(LCR) |= UART_LCR_DLAB; + mb(); + value = __UART0(DLM) << 8; + value |= __UART0(DLL); + __UART0(LCR) = lcr; + mb(); + + return value; +} /* end console_get_baud() */ + +/*****************************************************************************/ +/* + * display BUG() info + */ +#ifndef CONFIG_NO_KERNEL_MSG +void __debug_bug_printk(const char *file, unsigned line) +{ + printk("kernel BUG at %s:%d!\n", file, line); + +} /* end __debug_bug_printk() */ +#endif diff --git a/arch/frv/kernel/dma.c b/arch/frv/kernel/dma.c new file mode 100644 index 000000000..156184e17 --- /dev/null +++ b/arch/frv/kernel/dma.c @@ -0,0 +1,463 @@ +/* dma.c: DMA controller management on FR401 and the like + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <asm/dma.h> +#include <asm/gpio-regs.h> +#include <asm/irc-regs.h> +#include <asm/cpu-irqs.h> + +struct frv_dma_channel { + uint8_t flags; +#define FRV_DMA_FLAGS_RESERVED 0x01 +#define FRV_DMA_FLAGS_INUSE 0x02 +#define FRV_DMA_FLAGS_PAUSED 0x04 + uint8_t cap; /* capabilities available */ + int irq; /* completion IRQ */ + uint32_t dreqbit; + uint32_t dackbit; + uint32_t donebit; + const unsigned long ioaddr; /* DMA controller regs addr */ + const char *devname; + dma_irq_handler_t handler; + void *data; +}; + + +#define __get_DMAC(IO,X) ({ *(volatile unsigned long *)((IO) + DMAC_##X##x); }) + +#define __set_DMAC(IO,X,V) \ +do { \ + *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V); \ + mb(); \ +} while(0) + +#define ___set_DMAC(IO,X,V) \ +do { \ + *(volatile unsigned long *)((IO) + DMAC_##X##x) = (V); \ +} while(0) + + +static struct frv_dma_channel frv_dma_channels[FRV_DMA_NCHANS] = { + [0] = { + .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE, + .irq = IRQ_CPU_DMA0, + .dreqbit = SIR_DREQ0_INPUT, + .dackbit = SOR_DACK0_OUTPUT, + .donebit = SOR_DONE0_OUTPUT, + .ioaddr = 0xfe000900, + }, + [1] = { + .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK | FRV_DMA_CAP_DONE, + .irq = IRQ_CPU_DMA1, + .dreqbit = SIR_DREQ1_INPUT, + .dackbit = SOR_DACK1_OUTPUT, + .donebit = SOR_DONE1_OUTPUT, + .ioaddr = 0xfe000980, + }, + [2] = { + .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK, + .irq = IRQ_CPU_DMA2, + .dreqbit = SIR_DREQ2_INPUT, + .dackbit = SOR_DACK2_OUTPUT, + .ioaddr = 0xfe000a00, + }, + [3] = { + .cap = FRV_DMA_CAP_DREQ | FRV_DMA_CAP_DACK, + .irq = IRQ_CPU_DMA3, + .dreqbit = SIR_DREQ3_INPUT, + .dackbit = SOR_DACK3_OUTPUT, + .ioaddr = 0xfe000a80, + }, + [4] = { + .cap = FRV_DMA_CAP_DREQ, + .irq = IRQ_CPU_DMA4, + .dreqbit = SIR_DREQ4_INPUT, + .ioaddr = 0xfe001000, + }, + [5] = { + .cap = FRV_DMA_CAP_DREQ, + .irq = IRQ_CPU_DMA5, + .dreqbit = SIR_DREQ5_INPUT, + .ioaddr = 0xfe001080, + }, + [6] = { + .cap = FRV_DMA_CAP_DREQ, + .irq = IRQ_CPU_DMA6, + .dreqbit = SIR_DREQ6_INPUT, + .ioaddr = 0xfe001100, + }, + [7] = { + .cap = FRV_DMA_CAP_DREQ, + .irq = IRQ_CPU_DMA7, + .dreqbit = SIR_DREQ7_INPUT, + .ioaddr = 0xfe001180, + }, +}; + +static DEFINE_RWLOCK(frv_dma_channels_lock); + +unsigned long frv_dma_inprogress; + +#define frv_clear_dma_inprogress(channel) \ + atomic_clear_mask(1 << (channel), &frv_dma_inprogress); + +#define frv_set_dma_inprogress(channel) \ + atomic_set_mask(1 << (channel), &frv_dma_inprogress); + +/*****************************************************************************/ +/* + * DMA irq handler - determine channel involved, grab status and call real handler + */ +static irqreturn_t dma_irq_handler(int irq, void *_channel) +{ + struct frv_dma_channel *channel = _channel; + + frv_clear_dma_inprogress(channel - frv_dma_channels); + return channel->handler(channel - frv_dma_channels, + __get_DMAC(channel->ioaddr, CSTR), + channel->data); + +} /* end dma_irq_handler() */ + +/*****************************************************************************/ +/* + * Determine which DMA controllers are present on this CPU + */ +void __init frv_dma_init(void) +{ + unsigned long psr = __get_PSR(); + int num_dma, i; + + /* First, determine how many DMA channels are available */ + switch (PSR_IMPLE(psr)) { + case PSR_IMPLE_FR405: + case PSR_IMPLE_FR451: + case PSR_IMPLE_FR501: + case PSR_IMPLE_FR551: + num_dma = FRV_DMA_8CHANS; + break; + + case PSR_IMPLE_FR401: + default: + num_dma = FRV_DMA_4CHANS; + break; + } + + /* Now mark all of the non-existent channels as reserved */ + for(i = num_dma; i < FRV_DMA_NCHANS; i++) + frv_dma_channels[i].flags = FRV_DMA_FLAGS_RESERVED; + +} /* end frv_dma_init() */ + +/*****************************************************************************/ +/* + * allocate a DMA controller channel and the IRQ associated with it + */ +int frv_dma_open(const char *devname, + unsigned long dmamask, + int dmacap, + dma_irq_handler_t handler, + unsigned long irq_flags, + void *data) +{ + struct frv_dma_channel *channel; + int dma, ret; + uint32_t val; + + write_lock(&frv_dma_channels_lock); + + ret = -ENOSPC; + + for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { + channel = &frv_dma_channels[dma]; + + if (!test_bit(dma, &dmamask)) + continue; + + if ((channel->cap & dmacap) != dmacap) + continue; + + if (!frv_dma_channels[dma].flags) + goto found; + } + + goto out; + + found: + ret = request_irq(channel->irq, dma_irq_handler, irq_flags, devname, channel); + if (ret < 0) + goto out; + + /* okay, we've allocated all the resources */ + channel = &frv_dma_channels[dma]; + + channel->flags |= FRV_DMA_FLAGS_INUSE; + channel->devname = devname; + channel->handler = handler; + channel->data = data; + + /* Now make sure we are set up for DMA and not GPIO */ + /* SIR bit must be set for DMA to work */ + __set_SIR(channel->dreqbit | __get_SIR()); + /* SOR bits depend on what the caller requests */ + val = __get_SOR(); + if(dmacap & FRV_DMA_CAP_DACK) + val |= channel->dackbit; + else + val &= ~channel->dackbit; + if(dmacap & FRV_DMA_CAP_DONE) + val |= channel->donebit; + else + val &= ~channel->donebit; + __set_SOR(val); + + ret = dma; + out: + write_unlock(&frv_dma_channels_lock); + return ret; +} /* end frv_dma_open() */ + +EXPORT_SYMBOL(frv_dma_open); + +/*****************************************************************************/ +/* + * close a DMA channel and its associated interrupt + */ +void frv_dma_close(int dma) +{ + struct frv_dma_channel *channel = &frv_dma_channels[dma]; + unsigned long flags; + + write_lock_irqsave(&frv_dma_channels_lock, flags); + + free_irq(channel->irq, channel); + frv_dma_stop(dma); + + channel->flags &= ~FRV_DMA_FLAGS_INUSE; + + write_unlock_irqrestore(&frv_dma_channels_lock, flags); +} /* end frv_dma_close() */ + +EXPORT_SYMBOL(frv_dma_close); + +/*****************************************************************************/ +/* + * set static configuration on a DMA channel + */ +void frv_dma_config(int dma, unsigned long ccfr, unsigned long cctr, unsigned long apr) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + + ___set_DMAC(ioaddr, CCFR, ccfr); + ___set_DMAC(ioaddr, CCTR, cctr); + ___set_DMAC(ioaddr, APR, apr); + mb(); + +} /* end frv_dma_config() */ + +EXPORT_SYMBOL(frv_dma_config); + +/*****************************************************************************/ +/* + * start a DMA channel + */ +void frv_dma_start(int dma, + unsigned long sba, unsigned long dba, + unsigned long pix, unsigned long six, unsigned long bcl) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + + ___set_DMAC(ioaddr, SBA, sba); + ___set_DMAC(ioaddr, DBA, dba); + ___set_DMAC(ioaddr, PIX, pix); + ___set_DMAC(ioaddr, SIX, six); + ___set_DMAC(ioaddr, BCL, bcl); + ___set_DMAC(ioaddr, CSTR, 0); + mb(); + + __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT); + frv_set_dma_inprogress(dma); + +} /* end frv_dma_start() */ + +EXPORT_SYMBOL(frv_dma_start); + +/*****************************************************************************/ +/* + * restart a DMA channel that's been stopped in circular addressing mode by comparison-end + */ +void frv_dma_restart_circular(int dma, unsigned long six) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + + ___set_DMAC(ioaddr, SIX, six); + ___set_DMAC(ioaddr, CSTR, __get_DMAC(ioaddr, CSTR) & ~DMAC_CSTRx_CE); + mb(); + + __set_DMAC(ioaddr, CCTR, __get_DMAC(ioaddr, CCTR) | DMAC_CCTRx_ACT); + frv_set_dma_inprogress(dma); + +} /* end frv_dma_restart_circular() */ + +EXPORT_SYMBOL(frv_dma_restart_circular); + +/*****************************************************************************/ +/* + * stop a DMA channel + */ +void frv_dma_stop(int dma) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + uint32_t cctr; + + ___set_DMAC(ioaddr, CSTR, 0); + cctr = __get_DMAC(ioaddr, CCTR); + cctr &= ~(DMAC_CCTRx_IE | DMAC_CCTRx_ACT); + cctr |= DMAC_CCTRx_FC; /* fifo clear */ + __set_DMAC(ioaddr, CCTR, cctr); + __set_DMAC(ioaddr, BCL, 0); + frv_clear_dma_inprogress(dma); +} /* end frv_dma_stop() */ + +EXPORT_SYMBOL(frv_dma_stop); + +/*****************************************************************************/ +/* + * test interrupt status of DMA channel + */ +int is_frv_dma_interrupting(int dma) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + + return __get_DMAC(ioaddr, CSTR) & (1 << 23); + +} /* end is_frv_dma_interrupting() */ + +EXPORT_SYMBOL(is_frv_dma_interrupting); + +/*****************************************************************************/ +/* + * dump data about a DMA channel + */ +void frv_dma_dump(int dma) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + unsigned long cstr, pix, six, bcl; + + cstr = __get_DMAC(ioaddr, CSTR); + pix = __get_DMAC(ioaddr, PIX); + six = __get_DMAC(ioaddr, SIX); + bcl = __get_DMAC(ioaddr, BCL); + + printk("DMA[%d] cstr=%lx pix=%lx six=%lx bcl=%lx\n", dma, cstr, pix, six, bcl); + +} /* end frv_dma_dump() */ + +EXPORT_SYMBOL(frv_dma_dump); + +/*****************************************************************************/ +/* + * pause all DMA controllers + * - called by clock mangling routines + * - caller must be holding interrupts disabled + */ +void frv_dma_pause_all(void) +{ + struct frv_dma_channel *channel; + unsigned long ioaddr; + unsigned long cstr, cctr; + int dma; + + write_lock(&frv_dma_channels_lock); + + for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { + channel = &frv_dma_channels[dma]; + + if (!(channel->flags & FRV_DMA_FLAGS_INUSE)) + continue; + + ioaddr = channel->ioaddr; + cctr = __get_DMAC(ioaddr, CCTR); + if (cctr & DMAC_CCTRx_ACT) { + cctr &= ~DMAC_CCTRx_ACT; + __set_DMAC(ioaddr, CCTR, cctr); + + do { + cstr = __get_DMAC(ioaddr, CSTR); + } while (cstr & DMAC_CSTRx_BUSY); + + if (cstr & DMAC_CSTRx_FED) + channel->flags |= FRV_DMA_FLAGS_PAUSED; + frv_clear_dma_inprogress(dma); + } + } + +} /* end frv_dma_pause_all() */ + +EXPORT_SYMBOL(frv_dma_pause_all); + +/*****************************************************************************/ +/* + * resume paused DMA controllers + * - called by clock mangling routines + * - caller must be holding interrupts disabled + */ +void frv_dma_resume_all(void) +{ + struct frv_dma_channel *channel; + unsigned long ioaddr; + unsigned long cstr, cctr; + int dma; + + for (dma = FRV_DMA_NCHANS - 1; dma >= 0; dma--) { + channel = &frv_dma_channels[dma]; + + if (!(channel->flags & FRV_DMA_FLAGS_PAUSED)) + continue; + + ioaddr = channel->ioaddr; + cstr = __get_DMAC(ioaddr, CSTR); + cstr &= ~(DMAC_CSTRx_FED | DMAC_CSTRx_INT); + __set_DMAC(ioaddr, CSTR, cstr); + + cctr = __get_DMAC(ioaddr, CCTR); + cctr |= DMAC_CCTRx_ACT; + __set_DMAC(ioaddr, CCTR, cctr); + + channel->flags &= ~FRV_DMA_FLAGS_PAUSED; + frv_set_dma_inprogress(dma); + } + + write_unlock(&frv_dma_channels_lock); + +} /* end frv_dma_resume_all() */ + +EXPORT_SYMBOL(frv_dma_resume_all); + +/*****************************************************************************/ +/* + * dma status clear + */ +void frv_dma_status_clear(int dma) +{ + unsigned long ioaddr = frv_dma_channels[dma].ioaddr; + uint32_t cctr; + ___set_DMAC(ioaddr, CSTR, 0); + + cctr = __get_DMAC(ioaddr, CCTR); +} /* end frv_dma_status_clear() */ + +EXPORT_SYMBOL(frv_dma_status_clear); diff --git a/arch/frv/kernel/entry-table.S b/arch/frv/kernel/entry-table.S new file mode 100644 index 000000000..06c5ae191 --- /dev/null +++ b/arch/frv/kernel/entry-table.S @@ -0,0 +1,329 @@ +/* entry-table.S: main trap vector tables and exception jump table + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + * + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/spr-regs.h> + +############################################################################### +# +# Declare the main trap and vector tables +# +# There are six tables: +# +# (1) The trap table for debug mode +# (2) The trap table for kernel mode +# (3) The trap table for user mode +# +# The CPU jumps to an appropriate slot in the appropriate table to perform +# exception processing. We have three different tables for the three +# different CPU modes because there is no hardware differentiation between +# stack pointers for these three modes, and so we have to invent one when +# crossing mode boundaries. +# +# (4) The exception handler vector table +# +# The user and kernel trap tables use the same prologue for normal +# exception processing. The prologue then jumps to the handler in this +# table, as indexed by the exception ID from the TBR. +# +# (5) The fixup table for kernel-trap single-step +# (6) The fixup table for user-trap single-step +# +# Due to the way single-stepping works on this CPU (single-step is not +# disabled when crossing exception boundaries, only when in debug mode), +# we have to catch the single-step event in break.S and jump to the fixup +# routine pointed to by this table. +# +# The linker script places the user mode and kernel mode trap tables on to +# the same 8Kb page, so that break.S can be more efficient when performing +# single-step bypass management +# +############################################################################### + + # trap table for entry from debug mode + .section .trap.break,"ax" + .balign 256*16 + .globl __entry_breaktrap_table +__entry_breaktrap_table: + + # trap table for entry from user mode + .section .trap.user,"ax" + .balign 256*16 + .globl __entry_usertrap_table +__entry_usertrap_table: + + # trap table for entry from kernel mode + .section .trap.kernel,"ax" + .balign 256*16 + .globl __entry_kerneltrap_table +__entry_kerneltrap_table: + + # exception handler jump table + .section .trap.vector,"ax" + .balign 256*4 + .globl __entry_vector_table +__entry_vector_table: + + # trap fixup table for single-stepping in user mode + .section .trap.fixup.user,"a" + .balign 256*4 + .globl __break_usertrap_fixup_table +__break_usertrap_fixup_table: + + # trap fixup table for single-stepping in user mode + .section .trap.fixup.kernel,"a" + .balign 256*4 + .globl __break_kerneltrap_fixup_table +__break_kerneltrap_fixup_table: + + # handler declaration for a software or program interrupt +.macro VECTOR_SOFTPROG tbr_tt, vec + .section .trap.user + .org \tbr_tt + bra __entry_uspace_softprog_interrupt + .section .trap.fixup.user + .org \tbr_tt >> 2 + .long __break_step_uspace_softprog_interrupt + .section .trap.kernel + .org \tbr_tt + bra __entry_kernel_softprog_interrupt + .section .trap.fixup.kernel + .org \tbr_tt >> 2 + .long __break_step_kernel_softprog_interrupt + .section .trap.vector + .org \tbr_tt >> 2 + .long \vec +.endm + + # handler declaration for a maskable external interrupt +.macro VECTOR_IRQ tbr_tt, vec + .section .trap.user + .org \tbr_tt + bra __entry_uspace_external_interrupt + .section .trap.fixup.user + .org \tbr_tt >> 2 + .long __break_step_uspace_external_interrupt + .section .trap.kernel + .org \tbr_tt + # deal with virtual interrupt disablement + beq icc2,#0,__entry_kernel_external_interrupt_virtually_disabled + bra __entry_kernel_external_interrupt + .section .trap.fixup.kernel + .org \tbr_tt >> 2 + .long __break_step_kernel_external_interrupt + .section .trap.vector + .org \tbr_tt >> 2 + .long \vec +.endm + + # handler declaration for an NMI external interrupt +.macro VECTOR_NMI tbr_tt, vec + .section .trap.user + .org \tbr_tt + break + break + break + break + .section .trap.kernel + .org \tbr_tt + break + break + break + break + .section .trap.vector + .org \tbr_tt >> 2 + .long \vec +.endm + + # handler declaration for an MMU only software or program interrupt +.macro VECTOR_SP_MMU tbr_tt, vec +#ifdef CONFIG_MMU + VECTOR_SOFTPROG \tbr_tt, \vec +#else + VECTOR_NMI \tbr_tt, 0 +#endif +.endm + + +############################################################################### +# +# specification of the vectors +# - note: each macro inserts code into multiple sections +# +############################################################################### + VECTOR_SP_MMU TBR_TT_INSTR_MMU_MISS, __entry_insn_mmu_miss + VECTOR_SOFTPROG TBR_TT_INSTR_ACC_ERROR, __entry_insn_access_error + VECTOR_SOFTPROG TBR_TT_INSTR_ACC_EXCEP, __entry_insn_access_exception + VECTOR_SOFTPROG TBR_TT_PRIV_INSTR, __entry_privileged_instruction + VECTOR_SOFTPROG TBR_TT_ILLEGAL_INSTR, __entry_illegal_instruction + VECTOR_SOFTPROG TBR_TT_FP_EXCEPTION, __entry_media_exception + VECTOR_SOFTPROG TBR_TT_MP_EXCEPTION, __entry_media_exception + VECTOR_SOFTPROG TBR_TT_DATA_ACC_ERROR, __entry_data_access_error + VECTOR_SP_MMU TBR_TT_DATA_MMU_MISS, __entry_data_mmu_miss + VECTOR_SOFTPROG TBR_TT_DATA_ACC_EXCEP, __entry_data_access_exception + VECTOR_SOFTPROG TBR_TT_DATA_STR_ERROR, __entry_data_store_error + VECTOR_SOFTPROG TBR_TT_DIVISION_EXCEP, __entry_division_exception + +#ifdef CONFIG_MMU + .section .trap.user + .org TBR_TT_INSTR_TLB_MISS + .globl __trap_user_insn_tlb_miss +__trap_user_insn_tlb_miss: + movsg ear0,gr28 /* faulting address */ + movsg scr0,gr31 /* get mapped PTD coverage start address */ + xor.p gr28,gr31,gr31 /* compare addresses */ + bra __entry_user_insn_tlb_miss + + .org TBR_TT_DATA_TLB_MISS + .globl __trap_user_data_tlb_miss +__trap_user_data_tlb_miss: + movsg ear0,gr28 /* faulting address */ + movsg scr1,gr31 /* get mapped PTD coverage start address */ + xor.p gr28,gr31,gr31 /* compare addresses */ + bra __entry_user_data_tlb_miss + + .section .trap.kernel + .org TBR_TT_INSTR_TLB_MISS + .globl __trap_kernel_insn_tlb_miss +__trap_kernel_insn_tlb_miss: + movsg ear0,gr29 /* faulting address */ + movsg scr0,gr31 /* get mapped PTD coverage start address */ + xor.p gr29,gr31,gr31 /* compare addresses */ + bra __entry_kernel_insn_tlb_miss + + .org TBR_TT_DATA_TLB_MISS + .globl __trap_kernel_data_tlb_miss +__trap_kernel_data_tlb_miss: + movsg ear0,gr29 /* faulting address */ + movsg scr1,gr31 /* get mapped PTD coverage start address */ + xor.p gr29,gr31,gr31 /* compare addresses */ + bra __entry_kernel_data_tlb_miss + + .section .trap.fixup.user + .org TBR_TT_INSTR_TLB_MISS >> 2 + .globl __trap_fixup_user_insn_tlb_miss +__trap_fixup_user_insn_tlb_miss: + .long __break_user_insn_tlb_miss + .org TBR_TT_DATA_TLB_MISS >> 2 + .globl __trap_fixup_user_data_tlb_miss +__trap_fixup_user_data_tlb_miss: + .long __break_user_data_tlb_miss + + .section .trap.fixup.kernel + .org TBR_TT_INSTR_TLB_MISS >> 2 + .globl __trap_fixup_kernel_insn_tlb_miss +__trap_fixup_kernel_insn_tlb_miss: + .long __break_kernel_insn_tlb_miss + .org TBR_TT_DATA_TLB_MISS >> 2 + .globl __trap_fixup_kernel_data_tlb_miss +__trap_fixup_kernel_data_tlb_miss: + .long __break_kernel_data_tlb_miss + + .section .trap.vector + .org TBR_TT_INSTR_TLB_MISS >> 2 + .long __entry_insn_mmu_fault + .org TBR_TT_DATA_TLB_MISS >> 2 + .long __entry_data_mmu_fault +#endif + + VECTOR_SP_MMU TBR_TT_DATA_DAT_EXCEP, __entry_data_dat_fault + VECTOR_NMI TBR_TT_DECREMENT_TIMER, __entry_do_NMI + VECTOR_SOFTPROG TBR_TT_COMPOUND_EXCEP, __entry_compound_exception + VECTOR_IRQ TBR_TT_INTERRUPT_1, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_2, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_3, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_4, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_5, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_6, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_7, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_8, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_9, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_10, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_11, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_12, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_13, __entry_do_IRQ + VECTOR_IRQ TBR_TT_INTERRUPT_14, __entry_do_IRQ + VECTOR_NMI TBR_TT_INTERRUPT_15, __entry_do_NMI + + # miscellaneous user mode entry points + .section .trap.user + .org TBR_TT_TRAP0 + .rept 127 + bra __entry_uspace_softprog_interrupt + .long 0,0,0 + .endr + .org TBR_TT_BREAK + bra __entry_break + .long 0,0,0 + + .section .trap.fixup.user + .org TBR_TT_TRAP0 >> 2 + .rept 127 + .long __break_step_uspace_softprog_interrupt + .endr + .org TBR_TT_BREAK >> 2 + .long 0 + + # miscellaneous kernel mode entry points + .section .trap.kernel + .org TBR_TT_TRAP0 + bra __entry_kernel_softprog_interrupt + .org TBR_TT_TRAP1 + bra __entry_kernel_softprog_interrupt + + # trap #2 in kernel - reenable interrupts + .org TBR_TT_TRAP2 + bra __entry_kernel_external_interrupt_virtual_reenable + + # miscellaneous kernel traps + .org TBR_TT_TRAP3 + .rept 124 + bra __entry_kernel_softprog_interrupt + .long 0,0,0 + .endr + .org TBR_TT_BREAK + bra __entry_break + .long 0,0,0 + + .section .trap.fixup.kernel + .org TBR_TT_TRAP0 >> 2 + .long __break_step_kernel_softprog_interrupt + .long __break_step_kernel_softprog_interrupt + .long __break_step_kernel_external_interrupt_virtual_reenable + .rept 124 + .long __break_step_kernel_softprog_interrupt + .endr + .org TBR_TT_BREAK >> 2 + .long 0 + + # miscellaneous debug mode entry points + .section .trap.break + .org TBR_TT_BREAK + movsg bpcsr,gr30 + jmpl @(gr30,gr0) + + # miscellaneous vectors + .section .trap.vector + .org TBR_TT_TRAP0 >> 2 + .long system_call + .rept 119 + .long __entry_unsupported_trap + .endr + + # userspace atomic op emulation, traps 120-126 + .rept 7 + .long __entry_atomic_op + .endr + + .org TBR_TT_BREAK >> 2 + .long __entry_debug_exception diff --git a/arch/frv/kernel/entry.S b/arch/frv/kernel/entry.S new file mode 100644 index 000000000..dfcd263c0 --- /dev/null +++ b/arch/frv/kernel/entry.S @@ -0,0 +1,1519 @@ +/* entry.S: FR-V entry + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + * + * + * Entry to the kernel is "interesting": + * (1) There are no stack pointers, not even for the kernel + * (2) General Registers should not be clobbered + * (3) There are no kernel-only data registers + * (4) Since all addressing modes are wrt to a General Register, no global + * variables can be reached + * + * We deal with this by declaring that we shall kill GR28 on entering the + * kernel from userspace + * + * However, since break interrupts can interrupt the CPU even when PSR.ET==0, + * they can't rely on GR28 to be anything useful, and so need to clobber a + * separate register (GR31). Break interrupts are managed in break.S + * + * GR29 _is_ saved, and holds the current task pointer globally + * + */ + +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/ptrace.h> +#include <asm/errno.h> +#include <asm/cache.h> +#include <asm/spr-regs.h> + +#define nr_syscalls ((syscall_table_size)/4) + + .section .text..entry + .balign 4 + +.macro LEDS val +# sethi.p %hi(0xe1200004),gr30 +# setlo %lo(0xe1200004),gr30 +# setlos #~\val,gr31 +# st gr31,@(gr30,gr0) +# sethi.p %hi(0xffc00100),gr30 +# setlo %lo(0xffc00100),gr30 +# sth gr0,@(gr30,gr0) +# membar +.endm + +.macro LEDS32 +# not gr31,gr31 +# sethi.p %hi(0xe1200004),gr30 +# setlo %lo(0xe1200004),gr30 +# st.p gr31,@(gr30,gr0) +# srli gr31,#16,gr31 +# sethi.p %hi(0xffc00100),gr30 +# setlo %lo(0xffc00100),gr30 +# sth gr31,@(gr30,gr0) +# membar +.endm + +############################################################################### +# +# entry point for External interrupts received whilst executing userspace code +# +############################################################################### + .globl __entry_uspace_external_interrupt + .type __entry_uspace_external_interrupt,@function +__entry_uspace_external_interrupt: + LEDS 0x6200 + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + ldi @(gr28,#0),gr28 + + # handle h/w single-step through exceptions + sti gr0,@(gr28,#REG__STATUS) + + .globl __entry_uspace_external_interrupt_reentry +__entry_uspace_external_interrupt_reentry: + LEDS 0x6201 + + setlos #REG__END,gr30 + dcpl gr28,gr30,#0 + + # finish building the exception frame + sti sp, @(gr28,#REG_SP) + stdi gr2, @(gr28,#REG_GR(2)) + stdi gr4, @(gr28,#REG_GR(4)) + stdi gr6, @(gr28,#REG_GR(6)) + stdi gr8, @(gr28,#REG_GR(8)) + stdi gr10,@(gr28,#REG_GR(10)) + stdi gr12,@(gr28,#REG_GR(12)) + stdi gr14,@(gr28,#REG_GR(14)) + stdi gr16,@(gr28,#REG_GR(16)) + stdi gr18,@(gr28,#REG_GR(18)) + stdi gr20,@(gr28,#REG_GR(20)) + stdi gr22,@(gr28,#REG_GR(22)) + stdi gr24,@(gr28,#REG_GR(24)) + stdi gr26,@(gr28,#REG_GR(26)) + sti gr0, @(gr28,#REG_GR(28)) + sti gr29,@(gr28,#REG_GR(29)) + stdi.p gr30,@(gr28,#REG_GR(30)) + + # set up the kernel stack pointer + ori gr28,0,sp + + movsg tbr ,gr20 + movsg psr ,gr22 + movsg pcsr,gr21 + movsg isr ,gr23 + movsg ccr ,gr24 + movsg cccr,gr25 + movsg lr ,gr26 + movsg lcr ,gr27 + + setlos.p #-1,gr4 + andi gr22,#PSR_PS,gr5 /* try to rebuild original PSR value */ + andi.p gr22,#~(PSR_PS|PSR_S),gr6 + slli gr5,#1,gr5 + or gr6,gr5,gr5 + andi gr5,#~PSR_ET,gr5 + + sti gr20,@(gr28,#REG_TBR) + sti gr21,@(gr28,#REG_PC) + sti gr5 ,@(gr28,#REG_PSR) + sti gr23,@(gr28,#REG_ISR) + stdi gr24,@(gr28,#REG_CCR) + stdi gr26,@(gr28,#REG_LR) + sti gr4 ,@(gr28,#REG_SYSCALLNO) + + movsg iacc0h,gr4 + movsg iacc0l,gr5 + stdi gr4,@(gr28,#REG_IACC0) + + movsg gner0,gr4 + movsg gner1,gr5 + stdi.p gr4,@(gr28,#REG_GNER0) + + # interrupts start off fully disabled in the interrupt handler + subcc gr0,gr0,gr0,icc2 /* set Z and clear C */ + + # set up kernel global registers + sethi.p %hi(__kernel_current_task),gr5 + setlo %lo(__kernel_current_task),gr5 + sethi.p %hi(_gp),gr16 + setlo %lo(_gp),gr16 + ldi @(gr5,#0),gr29 + ldi.p @(gr29,#4),gr15 ; __current_thread_info = current->thread_info + + # make sure we (the kernel) get div-zero and misalignment exceptions + setlos #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5 + movgs gr5,isr + + # switch to the kernel trap table + sethi.p %hi(__entry_kerneltrap_table),gr6 + setlo %lo(__entry_kerneltrap_table),gr6 + movgs gr6,tbr + + # set the return address + sethi.p %hi(__entry_return_from_user_interrupt),gr4 + setlo %lo(__entry_return_from_user_interrupt),gr4 + movgs gr4,lr + + # raise the minimum interrupt priority to 15 (NMI only) and enable exceptions + movsg psr,gr4 + + ori gr4,#PSR_PIL_14,gr4 + movgs gr4,psr + ori gr4,#PSR_PIL_14|PSR_ET,gr4 + movgs gr4,psr + + LEDS 0x6202 + bra do_IRQ + + .size __entry_uspace_external_interrupt,.-__entry_uspace_external_interrupt + +############################################################################### +# +# entry point for External interrupts received whilst executing kernel code +# - on arriving here, the following registers should already be set up: +# GR15 - current thread_info struct pointer +# GR16 - kernel GP-REL pointer +# GR29 - current task struct pointer +# TBR - kernel trap vector table +# ISR - kernel's preferred integer controls +# +############################################################################### + .globl __entry_kernel_external_interrupt + .type __entry_kernel_external_interrupt,@function +__entry_kernel_external_interrupt: + LEDS 0x6210 +// sub sp,gr15,gr31 +// LEDS32 + + # set up the stack pointer + or.p sp,gr0,gr30 + subi sp,#REG__END,sp + sti gr30,@(sp,#REG_SP) + + # handle h/w single-step through exceptions + sti gr0,@(sp,#REG__STATUS) + + .globl __entry_kernel_external_interrupt_reentry +__entry_kernel_external_interrupt_reentry: + LEDS 0x6211 + + # set up the exception frame + setlos #REG__END,gr30 + dcpl sp,gr30,#0 + + sti.p gr28,@(sp,#REG_GR(28)) + ori sp,0,gr28 + + # finish building the exception frame + stdi gr2,@(gr28,#REG_GR(2)) + stdi gr4,@(gr28,#REG_GR(4)) + stdi gr6,@(gr28,#REG_GR(6)) + stdi gr8,@(gr28,#REG_GR(8)) + stdi gr10,@(gr28,#REG_GR(10)) + stdi gr12,@(gr28,#REG_GR(12)) + stdi gr14,@(gr28,#REG_GR(14)) + stdi gr16,@(gr28,#REG_GR(16)) + stdi gr18,@(gr28,#REG_GR(18)) + stdi gr20,@(gr28,#REG_GR(20)) + stdi gr22,@(gr28,#REG_GR(22)) + stdi gr24,@(gr28,#REG_GR(24)) + stdi gr26,@(gr28,#REG_GR(26)) + sti gr29,@(gr28,#REG_GR(29)) + stdi.p gr30,@(gr28,#REG_GR(30)) + + # note virtual interrupts will be fully enabled upon return + subicc gr0,#1,gr0,icc2 /* clear Z, set C */ + + movsg tbr ,gr20 + movsg psr ,gr22 + movsg pcsr,gr21 + movsg isr ,gr23 + movsg ccr ,gr24 + movsg cccr,gr25 + movsg lr ,gr26 + movsg lcr ,gr27 + + setlos.p #-1,gr4 + andi gr22,#PSR_PS,gr5 /* try to rebuild original PSR value */ + andi.p gr22,#~(PSR_PS|PSR_S),gr6 + slli gr5,#1,gr5 + or gr6,gr5,gr5 + andi.p gr5,#~PSR_ET,gr5 + + # set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel + # - for an explanation of how it works, see: Documentation/frv/atomic-ops.txt + andi gr25,#~0xc0,gr25 + + sti gr20,@(gr28,#REG_TBR) + sti gr21,@(gr28,#REG_PC) + sti gr5 ,@(gr28,#REG_PSR) + sti gr23,@(gr28,#REG_ISR) + stdi gr24,@(gr28,#REG_CCR) + stdi gr26,@(gr28,#REG_LR) + sti gr4 ,@(gr28,#REG_SYSCALLNO) + + movsg iacc0h,gr4 + movsg iacc0l,gr5 + stdi gr4,@(gr28,#REG_IACC0) + + movsg gner0,gr4 + movsg gner1,gr5 + stdi.p gr4,@(gr28,#REG_GNER0) + + # interrupts start off fully disabled in the interrupt handler + subcc gr0,gr0,gr0,icc2 /* set Z and clear C */ + + # set the return address + sethi.p %hi(__entry_return_from_kernel_interrupt),gr4 + setlo %lo(__entry_return_from_kernel_interrupt),gr4 + movgs gr4,lr + + # clear power-saving mode flags + movsg hsr0,gr4 + andi gr4,#~HSR0_PDM,gr4 + movgs gr4,hsr0 + + # raise the minimum interrupt priority to 15 (NMI only) and enable exceptions + movsg psr,gr4 + ori gr4,#PSR_PIL_14,gr4 + movgs gr4,psr + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + + LEDS 0x6212 + bra do_IRQ + + .size __entry_kernel_external_interrupt,.-__entry_kernel_external_interrupt + +############################################################################### +# +# deal with interrupts that were actually virtually disabled +# - we need to really disable them, flag the fact and return immediately +# - if you change this, you must alter break.S also +# +############################################################################### + .balign L1_CACHE_BYTES + .globl __entry_kernel_external_interrupt_virtually_disabled + .type __entry_kernel_external_interrupt_virtually_disabled,@function +__entry_kernel_external_interrupt_virtually_disabled: + movsg psr,gr30 + andi gr30,#~PSR_PIL,gr30 + ori gr30,#PSR_PIL_14,gr30 ; debugging interrupts only + movgs gr30,psr + subcc gr0,gr0,gr0,icc2 ; leave Z set, clear C + rett #0 + + .size __entry_kernel_external_interrupt_virtually_disabled,.-__entry_kernel_external_interrupt_virtually_disabled + +############################################################################### +# +# deal with re-enablement of interrupts that were pending when virtually re-enabled +# - set ICC2.C, re-enable the real interrupts and return +# - we can clear ICC2.Z because we shouldn't be here if it's not 0 [due to TIHI] +# - if you change this, you must alter break.S also +# +############################################################################### + .balign L1_CACHE_BYTES + .globl __entry_kernel_external_interrupt_virtual_reenable + .type __entry_kernel_external_interrupt_virtual_reenable,@function +__entry_kernel_external_interrupt_virtual_reenable: + movsg psr,gr30 + andi gr30,#~PSR_PIL,gr30 ; re-enable interrupts + movgs gr30,psr + subicc gr0,#1,gr0,icc2 ; clear Z, set C + rett #0 + + .size __entry_kernel_external_interrupt_virtual_reenable,.-__entry_kernel_external_interrupt_virtual_reenable + +############################################################################### +# +# entry point for Software and Progam interrupts generated whilst executing userspace code +# +############################################################################### + .globl __entry_uspace_softprog_interrupt + .type __entry_uspace_softprog_interrupt,@function + .globl __entry_uspace_handle_mmu_fault +__entry_uspace_softprog_interrupt: + LEDS 0x6000 +#ifdef CONFIG_MMU + movsg ear0,gr28 +__entry_uspace_handle_mmu_fault: + movgs gr28,scr2 +#endif + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + ldi @(gr28,#0),gr28 + + # handle h/w single-step through exceptions + sti gr0,@(gr28,#REG__STATUS) + + .globl __entry_uspace_softprog_interrupt_reentry +__entry_uspace_softprog_interrupt_reentry: + LEDS 0x6001 + + setlos #REG__END,gr30 + dcpl gr28,gr30,#0 + + # set up the kernel stack pointer + sti.p sp,@(gr28,#REG_SP) + ori gr28,0,sp + sti gr0,@(gr28,#REG_GR(28)) + + stdi gr20,@(gr28,#REG_GR(20)) + stdi gr22,@(gr28,#REG_GR(22)) + + movsg tbr,gr20 + movsg pcsr,gr21 + movsg psr,gr22 + + sethi.p %hi(__entry_return_from_user_exception),gr23 + setlo %lo(__entry_return_from_user_exception),gr23 + + bra __entry_common + + .size __entry_uspace_softprog_interrupt,.-__entry_uspace_softprog_interrupt + + # single-stepping was disabled on entry to a TLB handler that then faulted +#ifdef CONFIG_MMU + .globl __entry_uspace_handle_mmu_fault_sstep +__entry_uspace_handle_mmu_fault_sstep: + movgs gr28,scr2 + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + ldi @(gr28,#0),gr28 + + # flag single-step re-enablement + sti gr0,@(gr28,#REG__STATUS) + bra __entry_uspace_softprog_interrupt_reentry +#endif + + +############################################################################### +# +# entry point for Software and Progam interrupts generated whilst executing kernel code +# +############################################################################### + .globl __entry_kernel_softprog_interrupt + .type __entry_kernel_softprog_interrupt,@function +__entry_kernel_softprog_interrupt: + LEDS 0x6004 + +#ifdef CONFIG_MMU + movsg ear0,gr30 + movgs gr30,scr2 +#endif + + .globl __entry_kernel_handle_mmu_fault +__entry_kernel_handle_mmu_fault: + # set up the stack pointer + subi sp,#REG__END,sp + sti sp,@(sp,#REG_SP) + sti sp,@(sp,#REG_SP-4) + andi sp,#~7,sp + + # handle h/w single-step through exceptions + sti gr0,@(sp,#REG__STATUS) + + .globl __entry_kernel_softprog_interrupt_reentry +__entry_kernel_softprog_interrupt_reentry: + LEDS 0x6005 + + setlos #REG__END,gr30 + dcpl sp,gr30,#0 + + # set up the exception frame + sti.p gr28,@(sp,#REG_GR(28)) + ori sp,0,gr28 + + stdi gr20,@(gr28,#REG_GR(20)) + stdi gr22,@(gr28,#REG_GR(22)) + + ldi @(sp,#REG_SP),gr22 /* reconstruct the old SP */ + addi gr22,#REG__END,gr22 + sti gr22,@(sp,#REG_SP) + + # set CCCR.CC3 to Undefined to abort atomic-modify completion inside the kernel + # - for an explanation of how it works, see: Documentation/frv/atomic-ops.txt + movsg cccr,gr20 + andi gr20,#~0xc0,gr20 + movgs gr20,cccr + + movsg tbr,gr20 + movsg pcsr,gr21 + movsg psr,gr22 + + sethi.p %hi(__entry_return_from_kernel_exception),gr23 + setlo %lo(__entry_return_from_kernel_exception),gr23 + bra __entry_common + + .size __entry_kernel_softprog_interrupt,.-__entry_kernel_softprog_interrupt + + # single-stepping was disabled on entry to a TLB handler that then faulted +#ifdef CONFIG_MMU + .globl __entry_kernel_handle_mmu_fault_sstep +__entry_kernel_handle_mmu_fault_sstep: + # set up the stack pointer + subi sp,#REG__END,sp + sti sp,@(sp,#REG_SP) + sti sp,@(sp,#REG_SP-4) + andi sp,#~7,sp + + # flag single-step re-enablement + sethi #REG__STATUS_STEP,gr30 + sti gr30,@(sp,#REG__STATUS) + bra __entry_kernel_softprog_interrupt_reentry +#endif + + +############################################################################### +# +# the rest of the kernel entry point code +# - on arriving here, the following registers should be set up: +# GR1 - kernel stack pointer +# GR7 - syscall number (trap 0 only) +# GR8-13 - syscall args (trap 0 only) +# GR20 - saved TBR +# GR21 - saved PC +# GR22 - saved PSR +# GR23 - return handler address +# GR28 - exception frame on stack +# SCR2 - saved EAR0 where applicable (clobbered by ICI & ICEF insns on FR451) +# PSR - PSR.S 1, PSR.ET 0 +# +############################################################################### + .globl __entry_common + .type __entry_common,@function +__entry_common: + LEDS 0x6008 + + # finish building the exception frame + stdi gr2,@(gr28,#REG_GR(2)) + stdi gr4,@(gr28,#REG_GR(4)) + stdi gr6,@(gr28,#REG_GR(6)) + stdi gr8,@(gr28,#REG_GR(8)) + stdi gr10,@(gr28,#REG_GR(10)) + stdi gr12,@(gr28,#REG_GR(12)) + stdi gr14,@(gr28,#REG_GR(14)) + stdi gr16,@(gr28,#REG_GR(16)) + stdi gr18,@(gr28,#REG_GR(18)) + stdi gr24,@(gr28,#REG_GR(24)) + stdi gr26,@(gr28,#REG_GR(26)) + sti gr29,@(gr28,#REG_GR(29)) + stdi gr30,@(gr28,#REG_GR(30)) + + movsg lcr ,gr27 + movsg lr ,gr26 + movgs gr23,lr + movsg cccr,gr25 + movsg ccr ,gr24 + movsg isr ,gr23 + + setlos.p #-1,gr4 + andi gr22,#PSR_PS,gr5 /* try to rebuild original PSR value */ + andi.p gr22,#~(PSR_PS|PSR_S),gr6 + slli gr5,#1,gr5 + or gr6,gr5,gr5 + andi gr5,#~PSR_ET,gr5 + + sti gr20,@(gr28,#REG_TBR) + sti gr21,@(gr28,#REG_PC) + sti gr5 ,@(gr28,#REG_PSR) + sti gr23,@(gr28,#REG_ISR) + stdi gr24,@(gr28,#REG_CCR) + stdi gr26,@(gr28,#REG_LR) + sti gr4 ,@(gr28,#REG_SYSCALLNO) + + movsg iacc0h,gr4 + movsg iacc0l,gr5 + stdi gr4,@(gr28,#REG_IACC0) + + movsg gner0,gr4 + movsg gner1,gr5 + stdi.p gr4,@(gr28,#REG_GNER0) + + # set up virtual interrupt disablement + subicc gr0,#1,gr0,icc2 /* clear Z flag, set C flag */ + + # set up kernel global registers + sethi.p %hi(__kernel_current_task),gr5 + setlo %lo(__kernel_current_task),gr5 + sethi.p %hi(_gp),gr16 + setlo %lo(_gp),gr16 + ldi @(gr5,#0),gr29 + ldi @(gr29,#4),gr15 ; __current_thread_info = current->thread_info + + # switch to the kernel trap table + sethi.p %hi(__entry_kerneltrap_table),gr6 + setlo %lo(__entry_kerneltrap_table),gr6 + movgs gr6,tbr + + # make sure we (the kernel) get div-zero and misalignment exceptions + setlos #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5 + movgs gr5,isr + + # clear power-saving mode flags + movsg hsr0,gr4 + andi gr4,#~HSR0_PDM,gr4 + movgs gr4,hsr0 + + # multiplex again using old TBR as a guide + setlos.p #TBR_TT,gr3 + sethi %hi(__entry_vector_table),gr6 + and.p gr20,gr3,gr5 + setlo %lo(__entry_vector_table),gr6 + srli gr5,#2,gr5 + ld @(gr5,gr6),gr5 + + LEDS 0x6009 + jmpl @(gr5,gr0) + + + .size __entry_common,.-__entry_common + +############################################################################### +# +# handle instruction MMU fault +# +############################################################################### +#ifdef CONFIG_MMU + .globl __entry_insn_mmu_fault +__entry_insn_mmu_fault: + LEDS 0x6010 + setlos #0,gr8 + movsg esr0,gr9 + movsg scr2,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + + sethi.p %hi(do_page_fault),gr5 + setlo %lo(do_page_fault),gr5 + jmpl @(gr5,gr0) ; call do_page_fault(0,esr0,ear0) +#endif + + +############################################################################### +# +# handle instruction access error +# +############################################################################### + .globl __entry_insn_access_error +__entry_insn_access_error: + LEDS 0x6011 + sethi.p %hi(insn_access_error),gr5 + setlo %lo(insn_access_error),gr5 + movsg esfr1,gr8 + movsg epcr0,gr9 + movsg esr0,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call insn_access_error(esfr1,epcr0,esr0) + +############################################################################### +# +# handle various instructions of dubious legality +# +############################################################################### + .globl __entry_unsupported_trap + .globl __entry_illegal_instruction + .globl __entry_privileged_instruction + .globl __entry_debug_exception +__entry_unsupported_trap: + subi gr21,#4,gr21 + sti gr21,@(gr28,#REG_PC) +__entry_illegal_instruction: +__entry_privileged_instruction: +__entry_debug_exception: + LEDS 0x6012 + sethi.p %hi(illegal_instruction),gr5 + setlo %lo(illegal_instruction),gr5 + movsg esfr1,gr8 + movsg epcr0,gr9 + movsg esr0,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call ill_insn(esfr1,epcr0,esr0) + +############################################################################### +# +# handle atomic operation emulation for userspace +# +############################################################################### + .globl __entry_atomic_op +__entry_atomic_op: + LEDS 0x6012 + sethi.p %hi(atomic_operation),gr5 + setlo %lo(atomic_operation),gr5 + movsg esfr1,gr8 + movsg epcr0,gr9 + movsg esr0,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call atomic_operation(esfr1,epcr0,esr0) + +############################################################################### +# +# handle media exception +# +############################################################################### + .globl __entry_media_exception +__entry_media_exception: + LEDS 0x6013 + sethi.p %hi(media_exception),gr5 + setlo %lo(media_exception),gr5 + movsg msr0,gr8 + movsg msr1,gr9 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call media_excep(msr0,msr1) + +############################################################################### +# +# handle data MMU fault +# handle data DAT fault (write-protect exception) +# +############################################################################### +#ifdef CONFIG_MMU + .globl __entry_data_mmu_fault +__entry_data_mmu_fault: + .globl __entry_data_dat_fault +__entry_data_dat_fault: + LEDS 0x6014 + setlos #1,gr8 + movsg esr0,gr9 + movsg scr2,gr10 ; saved EAR0 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + + sethi.p %hi(do_page_fault),gr5 + setlo %lo(do_page_fault),gr5 + jmpl @(gr5,gr0) ; call do_page_fault(1,esr0,ear0) +#endif + +############################################################################### +# +# handle data and instruction access exceptions +# +############################################################################### + .globl __entry_insn_access_exception + .globl __entry_data_access_exception +__entry_insn_access_exception: +__entry_data_access_exception: + LEDS 0x6016 + sethi.p %hi(memory_access_exception),gr5 + setlo %lo(memory_access_exception),gr5 + movsg esr0,gr8 + movsg scr2,gr9 ; saved EAR0 + movsg epcr0,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call memory_access_error(esr0,ear0,epcr0) + +############################################################################### +# +# handle data access error +# +############################################################################### + .globl __entry_data_access_error +__entry_data_access_error: + LEDS 0x6016 + sethi.p %hi(data_access_error),gr5 + setlo %lo(data_access_error),gr5 + movsg esfr1,gr8 + movsg esr15,gr9 + movsg ear15,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call data_access_error(esfr1,esr15,ear15) + +############################################################################### +# +# handle data store error +# +############################################################################### + .globl __entry_data_store_error +__entry_data_store_error: + LEDS 0x6017 + sethi.p %hi(data_store_error),gr5 + setlo %lo(data_store_error),gr5 + movsg esfr1,gr8 + movsg esr14,gr9 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call data_store_error(esfr1,esr14) + +############################################################################### +# +# handle division exception +# +############################################################################### + .globl __entry_division_exception +__entry_division_exception: + LEDS 0x6018 + sethi.p %hi(division_exception),gr5 + setlo %lo(division_exception),gr5 + movsg esfr1,gr8 + movsg esr0,gr9 + movsg isr,gr10 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call div_excep(esfr1,esr0,isr) + +############################################################################### +# +# handle compound exception +# +############################################################################### + .globl __entry_compound_exception +__entry_compound_exception: + LEDS 0x6019 + sethi.p %hi(compound_exception),gr5 + setlo %lo(compound_exception),gr5 + movsg esfr1,gr8 + movsg esr0,gr9 + movsg esr14,gr10 + movsg esr15,gr11 + movsg msr0,gr12 + movsg msr1,gr13 + + # now that we've accessed the exception regs, we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + jmpl @(gr5,gr0) ; call comp_excep(esfr1,esr0,esr14,esr15,msr0,msr1) + +############################################################################### +# +# handle interrupts and NMIs +# +############################################################################### + .globl __entry_do_IRQ +__entry_do_IRQ: + LEDS 0x6020 + + # we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + bra do_IRQ + + .globl __entry_do_NMI +__entry_do_NMI: + LEDS 0x6021 + + # we can enable exceptions + movsg psr,gr4 + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + bra do_NMI + +############################################################################### +# +# the return path for a newly forked child process +# - __switch_to() saved the old current pointer in GR8 for us +# +############################################################################### + .globl ret_from_fork +ret_from_fork: + LEDS 0x6100 + call schedule_tail + + # fork & co. return 0 to child + setlos.p #0,gr8 + bra __syscall_exit + + .globl ret_from_kernel_thread +ret_from_kernel_thread: + lddi.p @(gr28,#REG_GR(8)),gr20 + call schedule_tail + calll.p @(gr21,gr0) + or gr20,gr20,gr8 + bra __syscall_exit + +################################################################################################### +# +# Return to user mode is not as complex as all this looks, +# but we want the default path for a system call return to +# go as quickly as possible which is why some of this is +# less clear than it otherwise should be. +# +################################################################################################### + .balign L1_CACHE_BYTES + .globl system_call +system_call: + LEDS 0x6101 + movsg psr,gr4 ; enable exceptions + ori gr4,#PSR_ET,gr4 + movgs gr4,psr + + sti gr7,@(gr28,#REG_SYSCALLNO) + sti.p gr8,@(gr28,#REG_ORIG_GR8) + + subicc gr7,#nr_syscalls,gr0,icc0 + bnc icc0,#0,__syscall_badsys + + ldi @(gr15,#TI_FLAGS),gr4 + andicc gr4,#_TIF_SYSCALL_TRACE,gr0,icc0 + bne icc0,#0,__syscall_trace_entry + +__syscall_call: + slli.p gr7,#2,gr7 + sethi %hi(sys_call_table),gr5 + setlo %lo(sys_call_table),gr5 + ld @(gr5,gr7),gr4 + calll @(gr4,gr0) + + +############################################################################### +# +# return to interrupted process +# +############################################################################### +__syscall_exit: + LEDS 0x6300 + + # keep current PSR in GR23 + movsg psr,gr23 + + ldi @(gr28,#REG_PSR),gr22 + + sti.p gr8,@(gr28,#REG_GR(8)) ; save return value + + # rebuild saved psr - execve will change it for init/main.c + srli gr22,#1,gr5 + andi.p gr22,#~PSR_PS,gr22 + andi gr5,#PSR_PS,gr5 + or gr5,gr22,gr22 + ori.p gr22,#PSR_S,gr22 + + # make sure we don't miss an interrupt setting need_resched or sigpending between + # sampling and the RETT + ori gr23,#PSR_PIL_14,gr23 + movgs gr23,psr + + ldi @(gr15,#TI_FLAGS),gr4 + andicc gr4,#_TIF_ALLWORK_MASK,gr0,icc0 + bne icc0,#0,__syscall_exit_work + + # restore all registers and return +__entry_return_direct: + LEDS 0x6301 + + andi gr22,#~PSR_ET,gr22 + movgs gr22,psr + + ldi @(gr28,#REG_ISR),gr23 + lddi @(gr28,#REG_CCR),gr24 + lddi @(gr28,#REG_LR) ,gr26 + ldi @(gr28,#REG_PC) ,gr21 + ldi @(gr28,#REG_TBR),gr20 + + movgs gr20,tbr + movgs gr21,pcsr + movgs gr23,isr + movgs gr24,ccr + movgs gr25,cccr + movgs gr26,lr + movgs gr27,lcr + + lddi @(gr28,#REG_GNER0),gr4 + movgs gr4,gner0 + movgs gr5,gner1 + + lddi @(gr28,#REG_IACC0),gr4 + movgs gr4,iacc0h + movgs gr5,iacc0l + + lddi @(gr28,#REG_GR(4)) ,gr4 + lddi @(gr28,#REG_GR(6)) ,gr6 + lddi @(gr28,#REG_GR(8)) ,gr8 + lddi @(gr28,#REG_GR(10)),gr10 + lddi @(gr28,#REG_GR(12)),gr12 + lddi @(gr28,#REG_GR(14)),gr14 + lddi @(gr28,#REG_GR(16)),gr16 + lddi @(gr28,#REG_GR(18)),gr18 + lddi @(gr28,#REG_GR(20)),gr20 + lddi @(gr28,#REG_GR(22)),gr22 + lddi @(gr28,#REG_GR(24)),gr24 + lddi @(gr28,#REG_GR(26)),gr26 + ldi @(gr28,#REG_GR(29)),gr29 + lddi @(gr28,#REG_GR(30)),gr30 + + # check to see if a debugging return is required + LEDS 0x67f0 + movsg ccr,gr2 + ldi @(gr28,#REG__STATUS),gr3 + andicc gr3,#REG__STATUS_STEP,gr0,icc0 + bne icc0,#0,__entry_return_singlestep + movgs gr2,ccr + + ldi @(gr28,#REG_SP) ,sp + lddi @(gr28,#REG_GR(2)) ,gr2 + ldi @(gr28,#REG_GR(28)),gr28 + + LEDS 0x67fe +// movsg pcsr,gr31 +// LEDS32 + +#if 0 + # store the current frame in the workram on the FR451 + movgs gr28,scr2 + sethi.p %hi(0xfe800000),gr28 + setlo %lo(0xfe800000),gr28 + + stdi gr2,@(gr28,#REG_GR(2)) + stdi gr4,@(gr28,#REG_GR(4)) + stdi gr6,@(gr28,#REG_GR(6)) + stdi gr8,@(gr28,#REG_GR(8)) + stdi gr10,@(gr28,#REG_GR(10)) + stdi gr12,@(gr28,#REG_GR(12)) + stdi gr14,@(gr28,#REG_GR(14)) + stdi gr16,@(gr28,#REG_GR(16)) + stdi gr18,@(gr28,#REG_GR(18)) + stdi gr24,@(gr28,#REG_GR(24)) + stdi gr26,@(gr28,#REG_GR(26)) + sti gr29,@(gr28,#REG_GR(29)) + stdi gr30,@(gr28,#REG_GR(30)) + + movsg tbr ,gr30 + sti gr30,@(gr28,#REG_TBR) + movsg pcsr,gr30 + sti gr30,@(gr28,#REG_PC) + movsg psr ,gr30 + sti gr30,@(gr28,#REG_PSR) + movsg isr ,gr30 + sti gr30,@(gr28,#REG_ISR) + movsg ccr ,gr30 + movsg cccr,gr31 + stdi gr30,@(gr28,#REG_CCR) + movsg lr ,gr30 + movsg lcr ,gr31 + stdi gr30,@(gr28,#REG_LR) + sti gr0 ,@(gr28,#REG_SYSCALLNO) + movsg scr2,gr28 +#endif + + rett #0 + + # return via break.S +__entry_return_singlestep: + movgs gr2,ccr + lddi @(gr28,#REG_GR(2)) ,gr2 + ldi @(gr28,#REG_SP) ,sp + ldi @(gr28,#REG_GR(28)),gr28 + LEDS 0x67ff + break + .globl __entry_return_singlestep_breaks_here +__entry_return_singlestep_breaks_here: + nop + + +############################################################################### +# +# return to a process interrupted in kernel space +# - we need to consider preemption if that is enabled +# +############################################################################### + .balign L1_CACHE_BYTES +__entry_return_from_kernel_exception: + LEDS 0x6302 + movsg psr,gr23 + ori gr23,#PSR_PIL_14,gr23 + movgs gr23,psr + bra __entry_return_direct + + .balign L1_CACHE_BYTES +__entry_return_from_kernel_interrupt: + LEDS 0x6303 + movsg psr,gr23 + ori gr23,#PSR_PIL_14,gr23 + movgs gr23,psr + +#ifdef CONFIG_PREEMPT + ldi @(gr15,#TI_PRE_COUNT),gr5 + subicc gr5,#0,gr0,icc0 + beq icc0,#0,__entry_return_direct + + subcc gr0,gr0,gr0,icc2 /* set Z and clear C */ + call preempt_schedule_irq +#endif + bra __entry_return_direct + + +############################################################################### +# +# perform work that needs to be done immediately before resumption +# +############################################################################### + .globl __entry_return_from_user_exception + .balign L1_CACHE_BYTES +__entry_return_from_user_exception: + LEDS 0x6501 + +__entry_resume_userspace: + # make sure we don't miss an interrupt setting need_resched or sigpending between + # sampling and the RETT + movsg psr,gr23 + ori gr23,#PSR_PIL_14,gr23 + movgs gr23,psr + +__entry_return_from_user_interrupt: + LEDS 0x6402 + ldi @(gr15,#TI_FLAGS),gr4 + andicc gr4,#_TIF_WORK_MASK,gr0,icc0 + beq icc0,#1,__entry_return_direct + +__entry_work_pending: + LEDS 0x6404 + andicc gr4,#_TIF_NEED_RESCHED,gr0,icc0 + beq icc0,#1,__entry_work_notifysig + +__entry_work_resched: + LEDS 0x6408 + movsg psr,gr23 + andi gr23,#~PSR_PIL,gr23 + movgs gr23,psr + call schedule + movsg psr,gr23 + ori gr23,#PSR_PIL_14,gr23 + movgs gr23,psr + + LEDS 0x6401 + ldi @(gr15,#TI_FLAGS),gr4 + andicc gr4,#_TIF_WORK_MASK,gr0,icc0 + beq icc0,#1,__entry_return_direct + andicc gr4,#_TIF_NEED_RESCHED,gr0,icc0 + bne icc0,#1,__entry_work_resched + +__entry_work_notifysig: + LEDS 0x6410 + ori.p gr4,#0,gr8 + call do_notify_resume + bra __entry_resume_userspace + + # perform syscall entry tracing +__syscall_trace_entry: + LEDS 0x6320 + call syscall_trace_entry + + lddi.p @(gr28,#REG_GR(8)) ,gr8 + ori gr8,#0,gr7 ; syscall_trace_entry() returned new syscallno + lddi @(gr28,#REG_GR(10)),gr10 + lddi.p @(gr28,#REG_GR(12)),gr12 + + subicc gr7,#nr_syscalls,gr0,icc0 + bnc icc0,#0,__syscall_badsys + bra __syscall_call + + # perform syscall exit tracing +__syscall_exit_work: + LEDS 0x6340 + andicc gr22,#PSR_PS,gr0,icc1 ; don't handle on return to kernel mode + andicc.p gr4,#_TIF_SYSCALL_TRACE,gr0,icc0 + bne icc1,#0,__entry_return_direct + beq icc0,#1,__entry_work_pending + + movsg psr,gr23 + andi gr23,#~PSR_PIL,gr23 ; could let syscall_trace_exit() call schedule() + movgs gr23,psr + + call syscall_trace_exit + bra __entry_resume_userspace + +__syscall_badsys: + LEDS 0x6380 + setlos #-ENOSYS,gr8 + sti gr8,@(gr28,#REG_GR(8)) ; save return value + bra __entry_resume_userspace + + +############################################################################### +# +# syscall vector table +# +############################################################################### + .section .rodata +ALIGN + .globl sys_call_table +sys_call_table: + .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_lchown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_ni_syscall // sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_ni_syscall // sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys( */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall /* old old uname syscall */ + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_ni_syscall // sys_sgetmask + .long sys_ni_syscall // sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_ni_syscall // sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_ni_syscall // sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long sys_ni_syscall /* old_select slot */ + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_swapon + .long sys_reboot + .long sys_ni_syscall // old_readdir + .long sys_ni_syscall /* 90 */ /* old_mmap slot */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm for i386 */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall /* obsolete olduname( syscall */ + .long sys_ni_syscall /* iopl for i386 */ /* 110 */ + .long sys_vhangup + .long sys_ni_syscall /* obsolete idle( syscall */ + .long sys_ni_syscall /* vm86old for i386 */ + .long sys_wait4 + .long sys_swapoff /* 115 */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_ni_syscall /* old "cacheflush" */ + .long sys_adjtimex + .long sys_mprotect /* 125 */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "create_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_msync + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_mlock /* 150 */ + .long sys_munlock + .long sys_mlockall + .long sys_munlockall + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_mremap + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_ni_syscall /* for vm86 */ + .long sys_ni_syscall /* Old sys_query_module */ + .long sys_poll + .long sys_ni_syscall /* Old nfsservctl */ + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_chown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_lchown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_chown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_mincore + .long sys_madvise + .long sys_getdents64 /* 220 */ + .long sys_fcntl64 + .long sys_ni_syscall /* reserved for TUX */ + .long sys_ni_syscall /* Reserved for Security */ + .long sys_gettid + .long sys_readahead /* 225 */ + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr + .long sys_getxattr + .long sys_lgetxattr /* 230 */ + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr + .long sys_flistxattr + .long sys_removexattr /* 235 */ + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_tkill + .long sys_sendfile64 + .long sys_futex /* 240 */ + .long sys_sched_setaffinity + .long sys_sched_getaffinity + .long sys_ni_syscall //sys_set_thread_area + .long sys_ni_syscall //sys_get_thread_area + .long sys_io_setup /* 245 */ + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel + .long sys_fadvise64 /* 250 */ + .long sys_ni_syscall + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 255 */ + .long sys_epoll_wait + .long sys_remap_file_pages + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 260 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 265 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 270 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_ni_syscall /* sys_vserver */ + .long sys_mbind + .long sys_get_mempolicy + .long sys_set_mempolicy + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive /* 280 */ + .long sys_mq_notify + .long sys_mq_getsetattr + .long sys_ni_syscall /* reserved for kexec */ + .long sys_waitid + .long sys_ni_syscall /* 285 */ /* available */ + .long sys_add_key + .long sys_request_key + .long sys_keyctl + .long sys_ioprio_set + .long sys_ioprio_get /* 290 */ + .long sys_inotify_init + .long sys_inotify_add_watch + .long sys_inotify_rm_watch + .long sys_migrate_pages + .long sys_openat /* 295 */ + .long sys_mkdirat + .long sys_mknodat + .long sys_fchownat + .long sys_futimesat + .long sys_fstatat64 /* 300 */ + .long sys_unlinkat + .long sys_renameat + .long sys_linkat + .long sys_symlinkat + .long sys_readlinkat /* 305 */ + .long sys_fchmodat + .long sys_faccessat + .long sys_pselect6 + .long sys_ppoll + .long sys_unshare /* 310 */ + .long sys_set_robust_list + .long sys_get_robust_list + .long sys_splice + .long sys_sync_file_range + .long sys_tee /* 315 */ + .long sys_vmsplice + .long sys_move_pages + .long sys_getcpu + .long sys_epoll_pwait + .long sys_utimensat /* 320 */ + .long sys_signalfd + .long sys_timerfd_create + .long sys_eventfd + .long sys_fallocate + .long sys_timerfd_settime /* 325 */ + .long sys_timerfd_gettime + .long sys_signalfd4 + .long sys_eventfd2 + .long sys_epoll_create1 + .long sys_dup3 /* 330 */ + .long sys_pipe2 + .long sys_inotify_init1 + .long sys_preadv + .long sys_pwritev + .long sys_rt_tgsigqueueinfo /* 335 */ + .long sys_perf_event_open + .long sys_setns + +syscall_table_size = (. - sys_call_table) diff --git a/arch/frv/kernel/frv_ksyms.c b/arch/frv/kernel/frv_ksyms.c new file mode 100644 index 000000000..86c516d96 --- /dev/null +++ b/arch/frv/kernel/frv_ksyms.c @@ -0,0 +1,113 @@ +#include <linux/module.h> +#include <linux/linkage.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/user.h> +#include <linux/elfcore.h> +#include <linux/in6.h> +#include <linux/interrupt.h> + +#include <asm/setup.h> +#include <asm/pgalloc.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/checksum.h> +#include <asm/hardirq.h> +#include <asm/cacheflush.h> + +extern long __memcpy_user(void *dst, const void *src, size_t count); +extern long __memset_user(void *dst, const void *src, size_t count); + +/* platform dependent support */ + +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); + +EXPORT_SYMBOL(ip_fast_csum); + +#if 0 +EXPORT_SYMBOL(local_irq_count); +EXPORT_SYMBOL(local_bh_count); +#endif + +EXPORT_SYMBOL(__res_bus_clock_speed_HZ); +EXPORT_SYMBOL(__page_offset); +EXPORT_SYMBOL(__memcpy_user); +EXPORT_SYMBOL(__memset_user); +EXPORT_SYMBOL(frv_dcache_writeback); +EXPORT_SYMBOL(frv_cache_invalidate); +EXPORT_SYMBOL(frv_icache_invalidate); +EXPORT_SYMBOL(frv_cache_wback_inv); + +#ifndef CONFIG_MMU +EXPORT_SYMBOL(memory_start); +EXPORT_SYMBOL(memory_end); +#endif + +EXPORT_SYMBOL(__debug_bug_trap); + +/* The following are special because they're not called + explicitly (the C compiler generates them). Fortunately, + their interface isn't gonna change any time soon now, so + it's OK to leave it out of version control. */ +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); + +EXPORT_SYMBOL(__outsl_ns); +EXPORT_SYMBOL(__insl_ns); + +#ifdef CONFIG_FRV_OUTOFLINE_ATOMIC_OPS +EXPORT_SYMBOL(atomic_test_and_ANDNOT_mask); +EXPORT_SYMBOL(atomic_test_and_OR_mask); +EXPORT_SYMBOL(atomic_test_and_XOR_mask); +EXPORT_SYMBOL(atomic_add_return); +EXPORT_SYMBOL(atomic_sub_return); +EXPORT_SYMBOL(__xchg_32); +EXPORT_SYMBOL(__cmpxchg_32); +#endif +EXPORT_SYMBOL(atomic64_add_return); +EXPORT_SYMBOL(atomic64_sub_return); +EXPORT_SYMBOL(__xchg_64); +EXPORT_SYMBOL(__cmpxchg_64); + +EXPORT_SYMBOL(__debug_bug_printk); +EXPORT_SYMBOL(__delay_loops_MHz); + +/* + * libgcc functions - functions that are used internally by the + * compiler... (prototypes are not correct though, but that + * doesn't really matter since they're not versioned). + */ +extern void __gcc_bcmp(void); +extern void __ashldi3(void); +extern void __ashrdi3(void); +extern void __cmpdi2(void); +extern void __divdi3(void); +extern void __lshrdi3(void); +extern void __moddi3(void); +extern void __muldi3(void); +extern void __mulll(void); +extern void __umulll(void); +extern void __negdi2(void); +extern void __ucmpdi2(void); +extern void __udivdi3(void); +extern void __udivmoddi4(void); +extern void __umoddi3(void); + + /* gcc lib functions */ +//EXPORT_SYMBOL(__gcc_bcmp); +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +//EXPORT_SYMBOL(__cmpdi2); +//EXPORT_SYMBOL(__divdi3); +EXPORT_SYMBOL(__lshrdi3); +//EXPORT_SYMBOL(__moddi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__mulll); +EXPORT_SYMBOL(__umulll); +EXPORT_SYMBOL(__negdi2); +EXPORT_SYMBOL(__ucmpdi2); +//EXPORT_SYMBOL(__udivdi3); +//EXPORT_SYMBOL(__udivmoddi4); +//EXPORT_SYMBOL(__umoddi3); diff --git a/arch/frv/kernel/futex.c b/arch/frv/kernel/futex.c new file mode 100644 index 000000000..d155ca9e5 --- /dev/null +++ b/arch/frv/kernel/futex.c @@ -0,0 +1,242 @@ +/* futex.c: futex operations + * + * Copyright (C) 2005 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/futex.h> +#include <linux/uaccess.h> +#include <asm/futex.h> +#include <asm/errno.h> + +/* + * the various futex operations; MMU fault checking is ignored under no-MMU + * conditions + */ +static inline int atomic_futex_op_xchg_set(int oparg, u32 __user *uaddr, int *_oldval) +{ + int oldval, ret; + + asm("0: \n" + " orcc gr0,gr0,gr0,icc3 \n" /* set ICC3.Z */ + " ckeq icc3,cc7 \n" + "1: ld.p %M0,%1 \n" /* LD.P/ORCR must be atomic */ + " orcr cc7,cc7,cc3 \n" /* set CC3 to true */ + "2: cst.p %3,%M0 ,cc3,#1 \n" + " corcc gr29,gr29,gr0 ,cc3,#1 \n" /* clear ICC3.Z if store happens */ + " beq icc3,#0,0b \n" + " setlos 0,%2 \n" + "3: \n" + ".subsection 2 \n" + "4: setlos %5,%2 \n" + " bra 3b \n" + ".previous \n" + ".section __ex_table,\"a\" \n" + " .balign 8 \n" + " .long 1b,4b \n" + " .long 2b,4b \n" + ".previous" + : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg) + : "3"(oparg), "i"(-EFAULT) + : "memory", "cc7", "cc3", "icc3" + ); + + *_oldval = oldval; + return ret; +} + +static inline int atomic_futex_op_xchg_add(int oparg, u32 __user *uaddr, int *_oldval) +{ + int oldval, ret; + + asm("0: \n" + " orcc gr0,gr0,gr0,icc3 \n" /* set ICC3.Z */ + " ckeq icc3,cc7 \n" + "1: ld.p %M0,%1 \n" /* LD.P/ORCR must be atomic */ + " orcr cc7,cc7,cc3 \n" /* set CC3 to true */ + " add %1,%3,%3 \n" + "2: cst.p %3,%M0 ,cc3,#1 \n" + " corcc gr29,gr29,gr0 ,cc3,#1 \n" /* clear ICC3.Z if store happens */ + " beq icc3,#0,0b \n" + " setlos 0,%2 \n" + "3: \n" + ".subsection 2 \n" + "4: setlos %5,%2 \n" + " bra 3b \n" + ".previous \n" + ".section __ex_table,\"a\" \n" + " .balign 8 \n" + " .long 1b,4b \n" + " .long 2b,4b \n" + ".previous" + : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg) + : "3"(oparg), "i"(-EFAULT) + : "memory", "cc7", "cc3", "icc3" + ); + + *_oldval = oldval; + return ret; +} + +static inline int atomic_futex_op_xchg_or(int oparg, u32 __user *uaddr, int *_oldval) +{ + int oldval, ret; + + asm("0: \n" + " orcc gr0,gr0,gr0,icc3 \n" /* set ICC3.Z */ + " ckeq icc3,cc7 \n" + "1: ld.p %M0,%1 \n" /* LD.P/ORCR must be atomic */ + " orcr cc7,cc7,cc3 \n" /* set CC3 to true */ + " or %1,%3,%3 \n" + "2: cst.p %3,%M0 ,cc3,#1 \n" + " corcc gr29,gr29,gr0 ,cc3,#1 \n" /* clear ICC3.Z if store happens */ + " beq icc3,#0,0b \n" + " setlos 0,%2 \n" + "3: \n" + ".subsection 2 \n" + "4: setlos %5,%2 \n" + " bra 3b \n" + ".previous \n" + ".section __ex_table,\"a\" \n" + " .balign 8 \n" + " .long 1b,4b \n" + " .long 2b,4b \n" + ".previous" + : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg) + : "3"(oparg), "i"(-EFAULT) + : "memory", "cc7", "cc3", "icc3" + ); + + *_oldval = oldval; + return ret; +} + +static inline int atomic_futex_op_xchg_and(int oparg, u32 __user *uaddr, int *_oldval) +{ + int oldval, ret; + + asm("0: \n" + " orcc gr0,gr0,gr0,icc3 \n" /* set ICC3.Z */ + " ckeq icc3,cc7 \n" + "1: ld.p %M0,%1 \n" /* LD.P/ORCR must be atomic */ + " orcr cc7,cc7,cc3 \n" /* set CC3 to true */ + " and %1,%3,%3 \n" + "2: cst.p %3,%M0 ,cc3,#1 \n" + " corcc gr29,gr29,gr0 ,cc3,#1 \n" /* clear ICC3.Z if store happens */ + " beq icc3,#0,0b \n" + " setlos 0,%2 \n" + "3: \n" + ".subsection 2 \n" + "4: setlos %5,%2 \n" + " bra 3b \n" + ".previous \n" + ".section __ex_table,\"a\" \n" + " .balign 8 \n" + " .long 1b,4b \n" + " .long 2b,4b \n" + ".previous" + : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg) + : "3"(oparg), "i"(-EFAULT) + : "memory", "cc7", "cc3", "icc3" + ); + + *_oldval = oldval; + return ret; +} + +static inline int atomic_futex_op_xchg_xor(int oparg, u32 __user *uaddr, int *_oldval) +{ + int oldval, ret; + + asm("0: \n" + " orcc gr0,gr0,gr0,icc3 \n" /* set ICC3.Z */ + " ckeq icc3,cc7 \n" + "1: ld.p %M0,%1 \n" /* LD.P/ORCR must be atomic */ + " orcr cc7,cc7,cc3 \n" /* set CC3 to true */ + " xor %1,%3,%3 \n" + "2: cst.p %3,%M0 ,cc3,#1 \n" + " corcc gr29,gr29,gr0 ,cc3,#1 \n" /* clear ICC3.Z if store happens */ + " beq icc3,#0,0b \n" + " setlos 0,%2 \n" + "3: \n" + ".subsection 2 \n" + "4: setlos %5,%2 \n" + " bra 3b \n" + ".previous \n" + ".section __ex_table,\"a\" \n" + " .balign 8 \n" + " .long 1b,4b \n" + " .long 2b,4b \n" + ".previous" + : "+U"(*uaddr), "=&r"(oldval), "=&r"(ret), "=r"(oparg) + : "3"(oparg), "i"(-EFAULT) + : "memory", "cc7", "cc3", "icc3" + ); + + *_oldval = oldval; + return ret; +} + +/*****************************************************************************/ +/* + * do the futex operations + */ +int futex_atomic_op_inuser(int encoded_op, u32 __user *uaddr) +{ + int op = (encoded_op >> 28) & 7; + int cmp = (encoded_op >> 24) & 15; + int oparg = (encoded_op << 8) >> 20; + int cmparg = (encoded_op << 20) >> 20; + int oldval = 0, ret; + + if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28)) + oparg = 1 << oparg; + + if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32))) + return -EFAULT; + + pagefault_disable(); + + switch (op) { + case FUTEX_OP_SET: + ret = atomic_futex_op_xchg_set(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ADD: + ret = atomic_futex_op_xchg_add(oparg, uaddr, &oldval); + break; + case FUTEX_OP_OR: + ret = atomic_futex_op_xchg_or(oparg, uaddr, &oldval); + break; + case FUTEX_OP_ANDN: + ret = atomic_futex_op_xchg_and(~oparg, uaddr, &oldval); + break; + case FUTEX_OP_XOR: + ret = atomic_futex_op_xchg_xor(oparg, uaddr, &oldval); + break; + default: + ret = -ENOSYS; + break; + } + + pagefault_enable(); + + if (!ret) { + switch (cmp) { + case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break; + case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break; + case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break; + case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break; + case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break; + case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break; + default: ret = -ENOSYS; break; + } + } + + return ret; + +} /* end futex_atomic_op_inuser() */ diff --git a/arch/frv/kernel/gdb-io.c b/arch/frv/kernel/gdb-io.c new file mode 100644 index 000000000..0707d3507 --- /dev/null +++ b/arch/frv/kernel/gdb-io.c @@ -0,0 +1,215 @@ +/* gdb-io.c: FR403 GDB stub I/O + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/serial_reg.h> + +#include <asm/pgtable.h> +#include <asm/irc-regs.h> +#include <asm/timer-regs.h> +#include <asm/gdb-stub.h> +#include "gdb-io.h" + +#ifdef CONFIG_GDBSTUB_UART0 +#define __UART(X) (*(volatile uint8_t *)(UART0_BASE + (UART_##X))) +#define __UART_IRR_NMI 0xff0f0000 +#else /* CONFIG_GDBSTUB_UART1 */ +#define __UART(X) (*(volatile uint8_t *)(UART1_BASE + (UART_##X))) +#define __UART_IRR_NMI 0xfff00000 +#endif + +#define LSR_WAIT_FOR(STATE) \ +do { \ + gdbstub_do_rx(); \ +} while (!(__UART(LSR) & UART_LSR_##STATE)) + +#define FLOWCTL_QUERY(LINE) ({ __UART(MSR) & UART_MSR_##LINE; }) +#define FLOWCTL_CLEAR(LINE) do { __UART(MCR) &= ~UART_MCR_##LINE; mb(); } while (0) +#define FLOWCTL_SET(LINE) do { __UART(MCR) |= UART_MCR_##LINE; mb(); } while (0) + +#define FLOWCTL_WAIT_FOR(LINE) \ +do { \ + gdbstub_do_rx(); \ +} while(!FLOWCTL_QUERY(LINE)) + +/*****************************************************************************/ +/* + * initialise the GDB stub + * - called with PSR.ET==0, so can't incur external interrupts + */ +void gdbstub_io_init(void) +{ + /* set up the serial port */ + __UART(LCR) = UART_LCR_WLEN8; /* 1N8 */ + __UART(FCR) = + UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT | + UART_FCR_TRIGGER_1; + + FLOWCTL_CLEAR(DTR); + FLOWCTL_SET(RTS); + +// gdbstub_set_baud(115200); + + /* we want to get serial receive interrupts */ + __UART(IER) = UART_IER_RDI | UART_IER_RLSI; + mb(); + + __set_IRR(6, __UART_IRR_NMI); /* map ERRs and UARTx to NMI */ + +} /* end gdbstub_io_init() */ + +/*****************************************************************************/ +/* + * set up the GDB stub serial port baud rate timers + */ +void gdbstub_set_baud(unsigned baud) +{ + unsigned value, high, low; + u8 lcr; + + /* work out the divisor to give us the nearest higher baud rate */ + value = __serial_clock_speed_HZ / 16 / baud; + + /* determine the baud rate range */ + high = __serial_clock_speed_HZ / 16 / value; + low = __serial_clock_speed_HZ / 16 / (value + 1); + + /* pick the nearest bound */ + if (low + (high - low) / 2 > baud) + value++; + + lcr = __UART(LCR); + __UART(LCR) |= UART_LCR_DLAB; + mb(); + __UART(DLL) = value & 0xff; + __UART(DLM) = (value >> 8) & 0xff; + mb(); + __UART(LCR) = lcr; + mb(); + +} /* end gdbstub_set_baud() */ + +/*****************************************************************************/ +/* + * receive characters into the receive FIFO + */ +void gdbstub_do_rx(void) +{ + unsigned ix, nix; + + ix = gdbstub_rx_inp; + + while (__UART(LSR) & UART_LSR_DR) { + nix = (ix + 2) & 0xfff; + if (nix == gdbstub_rx_outp) + break; + + gdbstub_rx_buffer[ix++] = __UART(LSR); + gdbstub_rx_buffer[ix++] = __UART(RX); + ix = nix; + } + + gdbstub_rx_inp = ix; + + __clr_RC(15); + __clr_IRL(); + +} /* end gdbstub_do_rx() */ + +/*****************************************************************************/ +/* + * wait for a character to come from the debugger + */ +int gdbstub_rx_char(unsigned char *_ch, int nonblock) +{ + unsigned ix; + u8 ch, st; + + *_ch = 0xff; + + if (gdbstub_rx_unget) { + *_ch = gdbstub_rx_unget; + gdbstub_rx_unget = 0; + return 0; + } + + try_again: + gdbstub_do_rx(); + + /* pull chars out of the buffer */ + ix = gdbstub_rx_outp; + if (ix == gdbstub_rx_inp) { + if (nonblock) + return -EAGAIN; + //watchdog_alert_counter = 0; + goto try_again; + } + + st = gdbstub_rx_buffer[ix++]; + ch = gdbstub_rx_buffer[ix++]; + gdbstub_rx_outp = ix & 0x00000fff; + + if (st & UART_LSR_BI) { + gdbstub_proto("### GDB Rx Break Detected ###\n"); + return -EINTR; + } + else if (st & (UART_LSR_FE|UART_LSR_OE|UART_LSR_PE)) { + gdbstub_io("### GDB Rx Error (st=%02x) ###\n",st); + return -EIO; + } + else { + gdbstub_io("### GDB Rx %02x (st=%02x) ###\n",ch,st); + *_ch = ch & 0x7f; + return 0; + } + +} /* end gdbstub_rx_char() */ + +/*****************************************************************************/ +/* + * send a character to the debugger + */ +void gdbstub_tx_char(unsigned char ch) +{ + FLOWCTL_SET(DTR); + LSR_WAIT_FOR(THRE); +// FLOWCTL_WAIT_FOR(CTS); + + if (ch == 0x0a) { + __UART(TX) = 0x0d; + mb(); + LSR_WAIT_FOR(THRE); +// FLOWCTL_WAIT_FOR(CTS); + } + __UART(TX) = ch; + mb(); + + FLOWCTL_CLEAR(DTR); +} /* end gdbstub_tx_char() */ + +/*****************************************************************************/ +/* + * send a character to the debugger + */ +void gdbstub_tx_flush(void) +{ + LSR_WAIT_FOR(TEMT); + LSR_WAIT_FOR(THRE); + FLOWCTL_CLEAR(DTR); +} /* end gdbstub_tx_flush() */ diff --git a/arch/frv/kernel/gdb-io.h b/arch/frv/kernel/gdb-io.h new file mode 100644 index 000000000..138714bac --- /dev/null +++ b/arch/frv/kernel/gdb-io.h @@ -0,0 +1,55 @@ +/* gdb-io.h: FR403 GDB I/O port defs + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _GDB_IO_H +#define _GDB_IO_H + +#include <asm/serial-regs.h> + +#undef UART_RX +#undef UART_TX +#undef UART_DLL +#undef UART_DLM +#undef UART_IER +#undef UART_IIR +#undef UART_FCR +#undef UART_LCR +#undef UART_MCR +#undef UART_LSR +#undef UART_MSR +#undef UART_SCR + +#define UART_RX 0*8 /* In: Receive buffer (DLAB=0) */ +#define UART_TX 0*8 /* Out: Transmit buffer (DLAB=0) */ +#define UART_DLL 0*8 /* Out: Divisor Latch Low (DLAB=1) */ +#define UART_DLM 1*8 /* Out: Divisor Latch High (DLAB=1) */ +#define UART_IER 1*8 /* Out: Interrupt Enable Register */ +#define UART_IIR 2*8 /* In: Interrupt ID Register */ +#define UART_FCR 2*8 /* Out: FIFO Control Register */ +#define UART_LCR 3*8 /* Out: Line Control Register */ +#define UART_MCR 4*8 /* Out: Modem Control Register */ +#define UART_LSR 5*8 /* In: Line Status Register */ +#define UART_MSR 6*8 /* In: Modem Status Register */ +#define UART_SCR 7*8 /* I/O: Scratch Register */ + +#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */ +#define UART_LCR_SBC 0x40 /* Set break control */ +#define UART_LCR_SPAR 0x20 /* Stick parity (?) */ +#define UART_LCR_EPAR 0x10 /* Even parity select */ +#define UART_LCR_PARITY 0x08 /* Parity Enable */ +#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */ +#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */ +#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */ +#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */ +#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */ + + +#endif /* _GDB_IO_H */ diff --git a/arch/frv/kernel/gdb-stub.c b/arch/frv/kernel/gdb-stub.c new file mode 100644 index 000000000..bbe78b0bf --- /dev/null +++ b/arch/frv/kernel/gdb-stub.c @@ -0,0 +1,2149 @@ +/* gdb-stub.c: FRV GDB stub + * + * Copyright (C) 2003,4 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from Linux/MIPS version, Copyright (C) 1995 Andreas Busse + * + * 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. + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + * + * ============== + * MORE EXAMPLES: + * ============== + * + * For reference -- the following are the steps that one + * company took (RidgeRun Inc) to get remote gdb debugging + * going. In this scenario the host machine was a PC and the + * target platform was a Galileo EVB64120A MIPS evaluation + * board. + * + * Step 1: + * First download gdb-5.0.tar.gz from the internet. + * and then build/install the package. + * + * Example: + * $ tar zxf gdb-5.0.tar.gz + * $ cd gdb-5.0 + * $ ./configure --target=frv-elf-gdb + * $ make + * $ frv-elf-gdb + * + * Step 2: + * Configure linux for remote debugging and build it. + * + * Example: + * $ cd ~/linux + * $ make menuconfig <go to "Kernel Hacking" and turn on remote debugging> + * $ make vmlinux + * + * Step 3: + * Download the kernel to the remote target and start + * the kernel running. It will promptly halt and wait + * for the host gdb session to connect. It does this + * since the "Kernel Hacking" option has defined + * CONFIG_REMOTE_DEBUG which in turn enables your calls + * to: + * set_debug_traps(); + * breakpoint(); + * + * Step 4: + * Start the gdb session on the host. + * + * Example: + * $ frv-elf-gdb vmlinux + * (gdb) set remotebaud 115200 + * (gdb) target remote /dev/ttyS1 + * ...at this point you are connected to + * the remote target and can use gdb + * in the normal fasion. Setting + * breakpoints, single stepping, + * printing variables, etc. + * + */ + +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/nmi.h> + +#include <asm/asm-offsets.h> +#include <asm/pgtable.h> +#include <asm/gdb-stub.h> + +#define LEDS(x) do { /* *(u32*)0xe1200004 = ~(x); mb(); */ } while(0) + +#undef GDBSTUB_DEBUG_PROTOCOL + +extern void debug_to_serial(const char *p, int n); +extern void gdbstub_console_write(struct console *co, const char *p, unsigned n); + +extern volatile uint32_t __break_error_detect[3]; /* ESFR1, ESR15, EAR15 */ + +struct __debug_amr { + unsigned long L, P; +} __attribute__((aligned(8))); + +struct __debug_mmu { + struct { + unsigned long hsr0, pcsr, esr0, ear0, epcr0; +#ifdef CONFIG_MMU + unsigned long tplr, tppr, tpxr, cxnr; +#endif + } regs; + + struct __debug_amr iamr[16]; + struct __debug_amr damr[16]; + +#ifdef CONFIG_MMU + struct __debug_amr tlb[64*2]; +#endif +}; + +static struct __debug_mmu __debug_mmu; + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +#define BREAK_INSN 0x801000c0 /* use "break" as bkpt */ + +static const char gdbstub_banner[] = "Linux/FR-V GDB Stub (c) RedHat 2003\n"; + +volatile u8 gdbstub_rx_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +volatile u32 gdbstub_rx_inp = 0; +volatile u32 gdbstub_rx_outp = 0; +volatile u8 gdbstub_rx_overflow = 0; +u8 gdbstub_rx_unget = 0; + +/* set with GDB whilst running to permit step through exceptions */ +extern volatile u32 __attribute__((section(".bss"))) gdbstub_trace_through_exceptions; + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; + +static const char *regnames[] = { + "PSR ", "ISR ", "CCR ", "CCCR", + "LR ", "LCR ", "PC ", "_stt", + "sys ", "GR8*", "GNE0", "GNE1", + "IACH", "IACL", + "TBR ", "SP ", "FP ", "GR3 ", + "GR4 ", "GR5 ", "GR6 ", "GR7 ", + "GR8 ", "GR9 ", "GR10", "GR11", + "GR12", "GR13", "GR14", "GR15", + "GR16", "GR17", "GR18", "GR19", + "GR20", "GR21", "GR22", "GR23", + "GR24", "GR25", "GR26", "GR27", + "EFRM", "CURR", "GR30", "BFRM" +}; + +struct gdbstub_bkpt { + unsigned long addr; /* address of breakpoint */ + unsigned len; /* size of breakpoint */ + uint32_t originsns[7]; /* original instructions */ +}; + +static struct gdbstub_bkpt gdbstub_bkpts[256]; + +/* + * local prototypes + */ + +static void gdbstub_recv_packet(char *buffer); +static int gdbstub_send_packet(char *buffer); +static int gdbstub_compute_signal(unsigned long tbr); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, unsigned long *intValue); +static unsigned char *mem2hex(const void *mem, char *buf, int count, int may_fault); +static char *hex2mem(const char *buf, void *_mem, int count); + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +void gdbstub_printk(const char *fmt, ...) +{ + static char buf[1024]; + va_list args; + int len; + + /* Emit the output into the temporary buffer */ + va_start(args, fmt); + len = vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + debug_to_serial(buf, len); +} + +static inline char *gdbstub_strcpy(char *dst, const char *src) +{ + int loop = 0; + while ((dst[loop] = src[loop])) + loop++; + return dst; +} + +static void gdbstub_purge_cache(void) +{ + asm volatile(" dcef @(gr0,gr0),#1 \n" + " icei @(gr0,gr0),#1 \n" + " membar \n" + " bar \n" + ); +} + +/*****************************************************************************/ +/* + * scan for the sequence $<data>#<checksum> + */ +static void gdbstub_recv_packet(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + unsigned char ch; + int count, i, ret, error; + + for (;;) { + /* wait around for the start character, ignore all other characters */ + do { + gdbstub_rx_char(&ch, 0); + } while (ch != '$'); + + checksum = 0; + xmitcsum = -1; + count = 0; + error = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ret = gdbstub_rx_char(&ch, 0); + if (ret < 0) + error = ret; + + if (ch == '#') + break; + checksum += ch; + buffer[count] = ch; + count++; + } + + if (error == -EIO) { + gdbstub_proto("### GDB Rx Error - Skipping packet ###\n"); + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_tx_char('-'); + continue; + } + + if (count >= BUFMAX || error) + continue; + + buffer[count] = 0; + + /* read the checksum */ + ret = gdbstub_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum = hex(ch) << 4; + + ret = gdbstub_rx_char(&ch, 0); + if (ret < 0) + error = ret; + xmitcsum |= hex(ch); + + if (error) { + if (error == -EIO) + gdbstub_proto("### GDB Rx Error - Skipping packet\n"); + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_tx_char('-'); + continue; + } + + /* check the checksum */ + if (checksum != xmitcsum) { + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_tx_char('-'); /* failed checksum */ + continue; + } + + gdbstub_proto("### GDB Rx '$%s#%02x' ###\n", buffer, checksum); + gdbstub_proto("### GDB Tx ACK\n"); + gdbstub_tx_char('+'); /* successful transfer */ + + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + gdbstub_tx_char(buffer[0]); + gdbstub_tx_char(buffer[1]); + + /* remove sequence chars from buffer */ + count = 0; + while (buffer[count]) count++; + for (i=3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + + break; + } +} /* end gdbstub_recv_packet() */ + +/*****************************************************************************/ +/* + * send the packet in buffer. + * - return 0 if successfully ACK'd + * - return 1 if abandoned due to new incoming packet + */ +static int gdbstub_send_packet(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* $<packet info>#<checksum> */ + gdbstub_proto("### GDB Tx '%s' ###\n", buffer); + + do { + gdbstub_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + gdbstub_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_tx_char('#'); + gdbstub_tx_char(hex_asc_hi(checksum)); + gdbstub_tx_char(hex_asc_lo(checksum)); + + } while (gdbstub_rx_char(&ch,0), +#ifdef GDBSTUB_DEBUG_PROTOCOL + ch=='-' && (gdbstub_proto("### GDB Rx NAK\n"),0), + ch!='-' && ch!='+' && (gdbstub_proto("### GDB Rx ??? %02x\n",ch),0), +#endif + ch!='+' && ch!='$'); + + if (ch=='+') { + gdbstub_proto("### GDB Rx ACK\n"); + return 0; + } + + gdbstub_proto("### GDB Tx Abandoned\n"); + gdbstub_rx_unget = ch; + return 1; +} /* end gdbstub_send_packet() */ + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, unsigned long *_value) +{ + int count = 0, ch; + + *_value = 0; + while (**ptr) { + ch = hex(**ptr); + if (ch < 0) + break; + + *_value = (*_value << 4) | ((uint8_t) ch & 0xf); + count++; + + (*ptr)++; + } + + return count; +} + +/*****************************************************************************/ +/* + * probe an address to see whether it maps to anything + */ +static inline int gdbstub_addr_probe(const void *vaddr) +{ +#ifdef CONFIG_MMU + unsigned long paddr; + + asm("lrad %1,%0,#1,#0,#0" : "=r"(paddr) : "r"(vaddr)); + if (!(paddr & xAMPRx_V)) + return 0; +#endif + + return 1; +} /* end gdbstub_addr_probe() */ + +#ifdef CONFIG_MMU +static unsigned long __saved_dampr, __saved_damlr; + +static inline unsigned long gdbstub_virt_to_pte(unsigned long vaddr) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + unsigned long val, dampr5; + + pgd = (pgd_t *) __get_DAMLR(3) + pgd_index(vaddr); + pud = pud_offset(pgd, vaddr); + pmd = pmd_offset(pud, vaddr); + + if (pmd_bad(*pmd) || !pmd_present(*pmd)) + return 0; + + /* make sure dampr5 maps to the correct pmd */ + dampr5 = __get_DAMPR(5); + val = pmd_val(*pmd); + __set_DAMPR(5, val | xAMPRx_L | xAMPRx_SS_16Kb | xAMPRx_S | xAMPRx_C | xAMPRx_V); + + /* now its safe to access pmd */ + pte = (pte_t *)__get_DAMLR(5) + __pte_index(vaddr); + if (pte_present(*pte)) + val = pte_val(*pte); + else + val = 0; + + /* restore original dampr5 */ + __set_DAMPR(5, dampr5); + + return val; +} +#endif + +static inline int gdbstub_addr_map(const void *vaddr) +{ +#ifdef CONFIG_MMU + unsigned long pte; + + __saved_dampr = __get_DAMPR(2); + __saved_damlr = __get_DAMLR(2); +#endif + if (gdbstub_addr_probe(vaddr)) + return 1; +#ifdef CONFIG_MMU + pte = gdbstub_virt_to_pte((unsigned long) vaddr); + if (pte) { + __set_DAMPR(2, pte); + __set_DAMLR(2, (unsigned long) vaddr & PAGE_MASK); + return 1; + } +#endif + return 0; +} + +static inline void gdbstub_addr_unmap(void) +{ +#ifdef CONFIG_MMU + __set_DAMPR(2, __saved_dampr); + __set_DAMLR(2, __saved_damlr); +#endif +} + +/* + * access potentially dodgy memory through a potentially dodgy pointer + */ +static inline int gdbstub_read_dword(const void *addr, uint32_t *_res) +{ + unsigned long brr; + uint32_t res; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " ld%I2 %M2,%0 \n" + " movsg brr,%1 \n" + : "=r"(res), "=r"(brr) + : "m"(*(uint32_t *) addr)); + *_res = res; + gdbstub_addr_unmap(); + return likely(!brr); +} + +static inline int gdbstub_write_dword(void *addr, uint32_t val) +{ + unsigned long brr; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " st%I2 %1,%M2 \n" + " movsg brr,%0 \n" + : "=r"(brr) + : "r"(val), "m"(*(uint32_t *) addr)); + gdbstub_addr_unmap(); + return likely(!brr); +} + +static inline int gdbstub_read_word(const void *addr, uint16_t *_res) +{ + unsigned long brr; + uint16_t res; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " lduh%I2 %M2,%0 \n" + " movsg brr,%1 \n" + : "=r"(res), "=r"(brr) + : "m"(*(uint16_t *) addr)); + *_res = res; + gdbstub_addr_unmap(); + return likely(!brr); +} + +static inline int gdbstub_write_word(void *addr, uint16_t val) +{ + unsigned long brr; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " sth%I2 %1,%M2 \n" + " movsg brr,%0 \n" + : "=r"(brr) + : "r"(val), "m"(*(uint16_t *) addr)); + gdbstub_addr_unmap(); + return likely(!brr); +} + +static inline int gdbstub_read_byte(const void *addr, uint8_t *_res) +{ + unsigned long brr; + uint8_t res; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " ldub%I2 %M2,%0 \n" + " movsg brr,%1 \n" + : "=r"(res), "=r"(brr) + : "m"(*(uint8_t *) addr)); + *_res = res; + gdbstub_addr_unmap(); + return likely(!brr); +} + +static inline int gdbstub_write_byte(void *addr, uint8_t val) +{ + unsigned long brr; + + if (!gdbstub_addr_map(addr)) + return 0; + + asm volatile(" movgs gr0,brr \n" + " stb%I2 %1,%M2 \n" + " movsg brr,%0 \n" + : "=r"(brr) + : "r"(val), "m"(*(uint8_t *) addr)); + gdbstub_addr_unmap(); + return likely(!brr); +} + +static void __gdbstub_console_write(struct console *co, const char *p, unsigned n) +{ + char outbuf[26]; + int qty; + + outbuf[0] = 'O'; + + while (n > 0) { + qty = 1; + + while (n > 0 && qty < 20) { + mem2hex(p, outbuf + qty, 2, 0); + qty += 2; + if (*p == 0x0a) { + outbuf[qty++] = '0'; + outbuf[qty++] = 'd'; + } + p++; + n--; + } + + outbuf[qty] = 0; + gdbstub_send_packet(outbuf); + } +} + +#if 0 +void debug_to_serial(const char *p, int n) +{ + gdbstub_console_write(NULL,p,n); +} +#endif + +#ifdef CONFIG_GDB_CONSOLE + +static struct console gdbstub_console = { + .name = "gdb", + .write = gdbstub_console_write, /* in break.S */ + .flags = CON_PRINTBUFFER, + .index = -1, +}; + +#endif + +/*****************************************************************************/ +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * - if successful, return a pointer to the last char put in buf (NUL) + * - in case of mem fault, return NULL + * may_fault is non-zero if we are reading from arbitrary memory, but is currently + * not used. + */ +static unsigned char *mem2hex(const void *_mem, char *buf, int count, int may_fault) +{ + const uint8_t *mem = _mem; + uint8_t ch[4] __attribute__((aligned(4))); + + if ((uint32_t)mem&1 && count>=1) { + if (!gdbstub_read_byte(mem,ch)) + return NULL; + buf = hex_byte_pack(buf, ch[0]); + mem++; + count--; + } + + if ((uint32_t)mem&3 && count>=2) { + if (!gdbstub_read_word(mem,(uint16_t *)ch)) + return NULL; + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); + mem += 2; + count -= 2; + } + + while (count>=4) { + if (!gdbstub_read_dword(mem,(uint32_t *)ch)) + return NULL; + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); + buf = hex_byte_pack(buf, ch[2]); + buf = hex_byte_pack(buf, ch[3]); + mem += 4; + count -= 4; + } + + if (count>=2) { + if (!gdbstub_read_word(mem,(uint16_t *)ch)) + return NULL; + buf = hex_byte_pack(buf, ch[0]); + buf = hex_byte_pack(buf, ch[1]); + mem += 2; + count -= 2; + } + + if (count>=1) { + if (!gdbstub_read_byte(mem,ch)) + return NULL; + buf = hex_byte_pack(buf, ch[0]); + } + + *buf = 0; + + return buf; +} /* end mem2hex() */ + +/*****************************************************************************/ +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte of buffer consumed + */ +static char *hex2mem(const char *buf, void *_mem, int count) +{ + uint8_t *mem = _mem; + union { + uint32_t l; + uint16_t w; + uint8_t b[4]; + } ch; + + if ((u32)mem&1 && count>=1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (!gdbstub_write_byte(mem,ch.b[0])) + return NULL; + mem++; + count--; + } + + if ((u32)mem&3 && count>=2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (!gdbstub_write_word(mem,ch.w)) + return NULL; + mem += 2; + count -= 2; + } + + while (count>=4) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + ch.b[2] = hex(*buf++) << 4; + ch.b[2] |= hex(*buf++); + ch.b[3] = hex(*buf++) << 4; + ch.b[3] |= hex(*buf++); + if (!gdbstub_write_dword(mem,ch.l)) + return NULL; + mem += 4; + count -= 4; + } + + if (count>=2) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + ch.b[1] = hex(*buf++) << 4; + ch.b[1] |= hex(*buf++); + if (!gdbstub_write_word(mem,ch.w)) + return NULL; + mem += 2; + count -= 2; + } + + if (count>=1) { + ch.b[0] = hex(*buf++) << 4; + ch.b[0] |= hex(*buf++); + if (!gdbstub_write_byte(mem,ch.b[0])) + return NULL; + } + + return (char *) buf; +} /* end hex2mem() */ + +/*****************************************************************************/ +/* + * This table contains the mapping between FRV TBR.TT exception codes, + * and signals, which are primarily what GDB understands. It also + * indicates which hardware traps we need to commandeer when + * initializing the stub. + */ +static const struct brr_to_sig_map { + unsigned long brr_mask; /* BRR bitmask */ + unsigned long tbr_tt; /* TBR.TT code (in BRR.EBTT) */ + unsigned int signo; /* Signal that we map this into */ +} brr_to_sig_map[] = { + { BRR_EB, TBR_TT_INSTR_ACC_ERROR, SIGSEGV }, + { BRR_EB, TBR_TT_ILLEGAL_INSTR, SIGILL }, + { BRR_EB, TBR_TT_PRIV_INSTR, SIGILL }, + { BRR_EB, TBR_TT_MP_EXCEPTION, SIGFPE }, + { BRR_EB, TBR_TT_DATA_ACC_ERROR, SIGSEGV }, + { BRR_EB, TBR_TT_DATA_STR_ERROR, SIGSEGV }, + { BRR_EB, TBR_TT_DIVISION_EXCEP, SIGFPE }, + { BRR_EB, TBR_TT_COMPOUND_EXCEP, SIGSEGV }, + { BRR_EB, TBR_TT_INTERRUPT_13, SIGALRM }, /* watchdog */ + { BRR_EB, TBR_TT_INTERRUPT_14, SIGINT }, /* GDB serial */ + { BRR_EB, TBR_TT_INTERRUPT_15, SIGQUIT }, /* NMI */ + { BRR_CB, 0, SIGUSR1 }, + { BRR_TB, 0, SIGUSR2 }, + { BRR_DBNEx, 0, SIGTRAP }, + { BRR_DBx, 0, SIGTRAP }, /* h/w watchpoint */ + { BRR_IBx, 0, SIGTRAP }, /* h/w breakpoint */ + { BRR_CBB, 0, SIGTRAP }, + { BRR_SB, 0, SIGTRAP }, + { BRR_ST, 0, SIGTRAP }, /* single step */ + { 0, 0, SIGHUP } /* default */ +}; + +/*****************************************************************************/ +/* + * convert the FRV BRR register contents into a UNIX signal number + */ +static inline int gdbstub_compute_signal(unsigned long brr) +{ + const struct brr_to_sig_map *map; + unsigned long tbr = (brr & BRR_EBTT) >> 12; + + for (map = brr_to_sig_map; map->brr_mask; map++) + if (map->brr_mask & brr) + if (!map->tbr_tt || map->tbr_tt == tbr) + break; + + return map->signo; +} /* end gdbstub_compute_signal() */ + +/*****************************************************************************/ +/* + * set a software breakpoint or a hardware breakpoint or watchpoint + */ +static int gdbstub_set_breakpoint(unsigned long type, unsigned long addr, unsigned long len) +{ + unsigned long tmp; + int bkpt, loop, xloop; + + union { + struct { + unsigned long mask0, mask1; + }; + uint8_t bytes[8]; + } dbmr; + + //gdbstub_printk("setbkpt(%ld,%08lx,%ld)\n", type, addr, len); + + switch (type) { + /* set software breakpoint */ + case 0: + if (addr & 3 || len > 7*4) + return -EINVAL; + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (!gdbstub_bkpts[bkpt].addr) + break; + if (bkpt < 0) + return -ENOSPC; + + for (loop = 0; loop < len/4; loop++) + if (!gdbstub_read_dword(&((uint32_t *) addr)[loop], + &gdbstub_bkpts[bkpt].originsns[loop])) + return -EFAULT; + + for (loop = 0; loop < len/4; loop++) + if (!gdbstub_write_dword(&((uint32_t *) addr)[loop], + BREAK_INSN) + ) { + /* need to undo the changes if possible */ + for (xloop = 0; xloop < loop; xloop++) + gdbstub_write_dword(&((uint32_t *) addr)[xloop], + gdbstub_bkpts[bkpt].originsns[xloop]); + return -EFAULT; + } + + gdbstub_bkpts[bkpt].addr = addr; + gdbstub_bkpts[bkpt].len = len; + +#if 0 + gdbstub_printk("Set BKPT[%02x]: %08lx #%d {%04x, %04x} -> { %04x, %04x }\n", + bkpt, + gdbstub_bkpts[bkpt].addr, + gdbstub_bkpts[bkpt].len, + gdbstub_bkpts[bkpt].originsns[0], + gdbstub_bkpts[bkpt].originsns[1], + ((uint32_t *) addr)[0], + ((uint32_t *) addr)[1] + ); +#endif + return 0; + + /* set hardware breakpoint */ + case 1: + if (addr & 3 || len != 4) + return -EINVAL; + + if (!(__debug_regs->dcr & DCR_IBE0)) { + //gdbstub_printk("set h/w break 0: %08lx\n", addr); + __debug_regs->dcr |= DCR_IBE0; + __debug_regs->ibar[0] = addr; + asm volatile("movgs %0,ibar0" : : "r"(addr)); + return 0; + } + + if (!(__debug_regs->dcr & DCR_IBE1)) { + //gdbstub_printk("set h/w break 1: %08lx\n", addr); + __debug_regs->dcr |= DCR_IBE1; + __debug_regs->ibar[1] = addr; + asm volatile("movgs %0,ibar1" : : "r"(addr)); + return 0; + } + + if (!(__debug_regs->dcr & DCR_IBE2)) { + //gdbstub_printk("set h/w break 2: %08lx\n", addr); + __debug_regs->dcr |= DCR_IBE2; + __debug_regs->ibar[2] = addr; + asm volatile("movgs %0,ibar2" : : "r"(addr)); + return 0; + } + + if (!(__debug_regs->dcr & DCR_IBE3)) { + //gdbstub_printk("set h/w break 3: %08lx\n", addr); + __debug_regs->dcr |= DCR_IBE3; + __debug_regs->ibar[3] = addr; + asm volatile("movgs %0,ibar3" : : "r"(addr)); + return 0; + } + + return -ENOSPC; + + /* set data read/write/access watchpoint */ + case 2: + case 3: + case 4: + if ((addr & ~7) != ((addr + len - 1) & ~7)) + return -EINVAL; + + tmp = addr & 7; + + memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes)); + for (loop = 0; loop < len; loop++) + dbmr.bytes[tmp + loop] = 0; + + addr &= ~7; + + if (!(__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0))) { + //gdbstub_printk("set h/w watchpoint 0 type %ld: %08lx\n", type, addr); + tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0; + + __debug_regs->dcr |= tmp; + __debug_regs->dbar[0] = addr; + __debug_regs->dbmr[0][0] = dbmr.mask0; + __debug_regs->dbmr[0][1] = dbmr.mask1; + __debug_regs->dbdr[0][0] = 0; + __debug_regs->dbdr[0][1] = 0; + + asm volatile(" movgs %0,dbar0 \n" + " movgs %1,dbmr00 \n" + " movgs %2,dbmr01 \n" + " movgs gr0,dbdr00 \n" + " movgs gr0,dbdr01 \n" + : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1)); + return 0; + } + + if (!(__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1))) { + //gdbstub_printk("set h/w watchpoint 1 type %ld: %08lx\n", type, addr); + tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1; + + __debug_regs->dcr |= tmp; + __debug_regs->dbar[1] = addr; + __debug_regs->dbmr[1][0] = dbmr.mask0; + __debug_regs->dbmr[1][1] = dbmr.mask1; + __debug_regs->dbdr[1][0] = 0; + __debug_regs->dbdr[1][1] = 0; + + asm volatile(" movgs %0,dbar1 \n" + " movgs %1,dbmr10 \n" + " movgs %2,dbmr11 \n" + " movgs gr0,dbdr10 \n" + " movgs gr0,dbdr11 \n" + : : "r"(addr), "r"(dbmr.mask0), "r"(dbmr.mask1)); + return 0; + } + + return -ENOSPC; + + default: + return -EINVAL; + } + +} /* end gdbstub_set_breakpoint() */ + +/*****************************************************************************/ +/* + * clear a breakpoint or watchpoint + */ +int gdbstub_clear_breakpoint(unsigned long type, unsigned long addr, unsigned long len) +{ + unsigned long tmp; + int bkpt, loop; + + union { + struct { + unsigned long mask0, mask1; + }; + uint8_t bytes[8]; + } dbmr; + + //gdbstub_printk("clearbkpt(%ld,%08lx,%ld)\n", type, addr, len); + + switch (type) { + /* clear software breakpoint */ + case 0: + for (bkpt = 255; bkpt >= 0; bkpt--) + if (gdbstub_bkpts[bkpt].addr == addr && gdbstub_bkpts[bkpt].len == len) + break; + if (bkpt < 0) + return -ENOENT; + + gdbstub_bkpts[bkpt].addr = 0; + + for (loop = 0; loop < len/4; loop++) + if (!gdbstub_write_dword(&((uint32_t *) addr)[loop], + gdbstub_bkpts[bkpt].originsns[loop])) + return -EFAULT; + return 0; + + /* clear hardware breakpoint */ + case 1: + if (addr & 3 || len != 4) + return -EINVAL; + +#define __get_ibar(X) ({ unsigned long x; asm volatile("movsg ibar"#X",%0" : "=r"(x)); x; }) + + if (__debug_regs->dcr & DCR_IBE0 && __get_ibar(0) == addr) { + //gdbstub_printk("clear h/w break 0: %08lx\n", addr); + __debug_regs->dcr &= ~DCR_IBE0; + __debug_regs->ibar[0] = 0; + asm volatile("movgs gr0,ibar0"); + return 0; + } + + if (__debug_regs->dcr & DCR_IBE1 && __get_ibar(1) == addr) { + //gdbstub_printk("clear h/w break 1: %08lx\n", addr); + __debug_regs->dcr &= ~DCR_IBE1; + __debug_regs->ibar[1] = 0; + asm volatile("movgs gr0,ibar1"); + return 0; + } + + if (__debug_regs->dcr & DCR_IBE2 && __get_ibar(2) == addr) { + //gdbstub_printk("clear h/w break 2: %08lx\n", addr); + __debug_regs->dcr &= ~DCR_IBE2; + __debug_regs->ibar[2] = 0; + asm volatile("movgs gr0,ibar2"); + return 0; + } + + if (__debug_regs->dcr & DCR_IBE3 && __get_ibar(3) == addr) { + //gdbstub_printk("clear h/w break 3: %08lx\n", addr); + __debug_regs->dcr &= ~DCR_IBE3; + __debug_regs->ibar[3] = 0; + asm volatile("movgs gr0,ibar3"); + return 0; + } + + return -EINVAL; + + /* clear data read/write/access watchpoint */ + case 2: + case 3: + case 4: + if ((addr & ~7) != ((addr + len - 1) & ~7)) + return -EINVAL; + + tmp = addr & 7; + + memset(dbmr.bytes, 0xff, sizeof(dbmr.bytes)); + for (loop = 0; loop < len; loop++) + dbmr.bytes[tmp + loop] = 0; + + addr &= ~7; + +#define __get_dbar(X) ({ unsigned long x; asm volatile("movsg dbar"#X",%0" : "=r"(x)); x; }) +#define __get_dbmr0(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"0,%0" : "=r"(x)); x; }) +#define __get_dbmr1(X) ({ unsigned long x; asm volatile("movsg dbmr"#X"1,%0" : "=r"(x)); x; }) + + /* consider DBAR 0 */ + tmp = type==2 ? DCR_DWBE0 : type==3 ? DCR_DRBE0 : DCR_DRBE0|DCR_DWBE0; + + if ((__debug_regs->dcr & (DCR_DRBE0|DCR_DWBE0)) != tmp || + __get_dbar(0) != addr || + __get_dbmr0(0) != dbmr.mask0 || + __get_dbmr1(0) != dbmr.mask1) + goto skip_dbar0; + + //gdbstub_printk("clear h/w watchpoint 0 type %ld: %08lx\n", type, addr); + __debug_regs->dcr &= ~(DCR_DRBE0|DCR_DWBE0); + __debug_regs->dbar[0] = 0; + __debug_regs->dbmr[0][0] = 0; + __debug_regs->dbmr[0][1] = 0; + __debug_regs->dbdr[0][0] = 0; + __debug_regs->dbdr[0][1] = 0; + + asm volatile(" movgs gr0,dbar0 \n" + " movgs gr0,dbmr00 \n" + " movgs gr0,dbmr01 \n" + " movgs gr0,dbdr00 \n" + " movgs gr0,dbdr01 \n"); + return 0; + + skip_dbar0: + /* consider DBAR 0 */ + tmp = type==2 ? DCR_DWBE1 : type==3 ? DCR_DRBE1 : DCR_DRBE1|DCR_DWBE1; + + if ((__debug_regs->dcr & (DCR_DRBE1|DCR_DWBE1)) != tmp || + __get_dbar(1) != addr || + __get_dbmr0(1) != dbmr.mask0 || + __get_dbmr1(1) != dbmr.mask1) + goto skip_dbar1; + + //gdbstub_printk("clear h/w watchpoint 1 type %ld: %08lx\n", type, addr); + __debug_regs->dcr &= ~(DCR_DRBE1|DCR_DWBE1); + __debug_regs->dbar[1] = 0; + __debug_regs->dbmr[1][0] = 0; + __debug_regs->dbmr[1][1] = 0; + __debug_regs->dbdr[1][0] = 0; + __debug_regs->dbdr[1][1] = 0; + + asm volatile(" movgs gr0,dbar1 \n" + " movgs gr0,dbmr10 \n" + " movgs gr0,dbmr11 \n" + " movgs gr0,dbdr10 \n" + " movgs gr0,dbdr11 \n"); + return 0; + + skip_dbar1: + return -ENOSPC; + + default: + return -EINVAL; + } +} /* end gdbstub_clear_breakpoint() */ + +/*****************************************************************************/ +/* + * check a for an internal software breakpoint, and wind the PC back if necessary + */ +static void gdbstub_check_breakpoint(void) +{ + unsigned long addr = __debug_frame->pc - 4; + int bkpt; + + for (bkpt = 255; bkpt >= 0; bkpt--) + if (gdbstub_bkpts[bkpt].addr == addr) + break; + if (bkpt >= 0) + __debug_frame->pc = addr; + + //gdbstub_printk("alter pc [%d] %08lx\n", bkpt, __debug_frame->pc); + +} /* end gdbstub_check_breakpoint() */ + +/*****************************************************************************/ +/* + * + */ +static void __maybe_unused gdbstub_show_regs(void) +{ + unsigned long *reg; + int loop; + + gdbstub_printk("\n"); + + gdbstub_printk("Frame: @%p [%s]\n", + __debug_frame, + __debug_frame->psr & PSR_S ? "kernel" : "user"); + + reg = (unsigned long *) __debug_frame; + for (loop = 0; loop < NR_PT_REGS; loop++) { + printk("%s %08lx", regnames[loop + 0], reg[loop + 0]); + + if (loop == NR_PT_REGS - 1 || loop % 5 == 4) + printk("\n"); + else + printk(" | "); + } + + gdbstub_printk("Process %s (pid: %d)\n", current->comm, current->pid); +} /* end gdbstub_show_regs() */ + +/*****************************************************************************/ +/* + * dump debugging regs + */ +static void __maybe_unused gdbstub_dump_debugregs(void) +{ + gdbstub_printk("DCR %08lx ", __debug_status.dcr); + gdbstub_printk("BRR %08lx\n", __debug_status.brr); + + gdbstub_printk("IBAR0 %08lx ", __get_ibar(0)); + gdbstub_printk("IBAR1 %08lx ", __get_ibar(1)); + gdbstub_printk("IBAR2 %08lx ", __get_ibar(2)); + gdbstub_printk("IBAR3 %08lx\n", __get_ibar(3)); + + gdbstub_printk("DBAR0 %08lx ", __get_dbar(0)); + gdbstub_printk("DBMR00 %08lx ", __get_dbmr0(0)); + gdbstub_printk("DBMR01 %08lx\n", __get_dbmr1(0)); + + gdbstub_printk("DBAR1 %08lx ", __get_dbar(1)); + gdbstub_printk("DBMR10 %08lx ", __get_dbmr0(1)); + gdbstub_printk("DBMR11 %08lx\n", __get_dbmr1(1)); + + gdbstub_printk("\n"); +} /* end gdbstub_dump_debugregs() */ + +/*****************************************************************************/ +/* + * dump the MMU state into a structure so that it can be accessed with GDB + */ +void gdbstub_get_mmu_state(void) +{ + asm volatile("movsg hsr0,%0" : "=r"(__debug_mmu.regs.hsr0)); + asm volatile("movsg pcsr,%0" : "=r"(__debug_mmu.regs.pcsr)); + asm volatile("movsg esr0,%0" : "=r"(__debug_mmu.regs.esr0)); + asm volatile("movsg ear0,%0" : "=r"(__debug_mmu.regs.ear0)); + asm volatile("movsg epcr0,%0" : "=r"(__debug_mmu.regs.epcr0)); + + /* read the protection / SAT registers */ + __debug_mmu.iamr[0].L = __get_IAMLR(0); + __debug_mmu.iamr[0].P = __get_IAMPR(0); + __debug_mmu.iamr[1].L = __get_IAMLR(1); + __debug_mmu.iamr[1].P = __get_IAMPR(1); + __debug_mmu.iamr[2].L = __get_IAMLR(2); + __debug_mmu.iamr[2].P = __get_IAMPR(2); + __debug_mmu.iamr[3].L = __get_IAMLR(3); + __debug_mmu.iamr[3].P = __get_IAMPR(3); + __debug_mmu.iamr[4].L = __get_IAMLR(4); + __debug_mmu.iamr[4].P = __get_IAMPR(4); + __debug_mmu.iamr[5].L = __get_IAMLR(5); + __debug_mmu.iamr[5].P = __get_IAMPR(5); + __debug_mmu.iamr[6].L = __get_IAMLR(6); + __debug_mmu.iamr[6].P = __get_IAMPR(6); + __debug_mmu.iamr[7].L = __get_IAMLR(7); + __debug_mmu.iamr[7].P = __get_IAMPR(7); + __debug_mmu.iamr[8].L = __get_IAMLR(8); + __debug_mmu.iamr[8].P = __get_IAMPR(8); + __debug_mmu.iamr[9].L = __get_IAMLR(9); + __debug_mmu.iamr[9].P = __get_IAMPR(9); + __debug_mmu.iamr[10].L = __get_IAMLR(10); + __debug_mmu.iamr[10].P = __get_IAMPR(10); + __debug_mmu.iamr[11].L = __get_IAMLR(11); + __debug_mmu.iamr[11].P = __get_IAMPR(11); + __debug_mmu.iamr[12].L = __get_IAMLR(12); + __debug_mmu.iamr[12].P = __get_IAMPR(12); + __debug_mmu.iamr[13].L = __get_IAMLR(13); + __debug_mmu.iamr[13].P = __get_IAMPR(13); + __debug_mmu.iamr[14].L = __get_IAMLR(14); + __debug_mmu.iamr[14].P = __get_IAMPR(14); + __debug_mmu.iamr[15].L = __get_IAMLR(15); + __debug_mmu.iamr[15].P = __get_IAMPR(15); + + __debug_mmu.damr[0].L = __get_DAMLR(0); + __debug_mmu.damr[0].P = __get_DAMPR(0); + __debug_mmu.damr[1].L = __get_DAMLR(1); + __debug_mmu.damr[1].P = __get_DAMPR(1); + __debug_mmu.damr[2].L = __get_DAMLR(2); + __debug_mmu.damr[2].P = __get_DAMPR(2); + __debug_mmu.damr[3].L = __get_DAMLR(3); + __debug_mmu.damr[3].P = __get_DAMPR(3); + __debug_mmu.damr[4].L = __get_DAMLR(4); + __debug_mmu.damr[4].P = __get_DAMPR(4); + __debug_mmu.damr[5].L = __get_DAMLR(5); + __debug_mmu.damr[5].P = __get_DAMPR(5); + __debug_mmu.damr[6].L = __get_DAMLR(6); + __debug_mmu.damr[6].P = __get_DAMPR(6); + __debug_mmu.damr[7].L = __get_DAMLR(7); + __debug_mmu.damr[7].P = __get_DAMPR(7); + __debug_mmu.damr[8].L = __get_DAMLR(8); + __debug_mmu.damr[8].P = __get_DAMPR(8); + __debug_mmu.damr[9].L = __get_DAMLR(9); + __debug_mmu.damr[9].P = __get_DAMPR(9); + __debug_mmu.damr[10].L = __get_DAMLR(10); + __debug_mmu.damr[10].P = __get_DAMPR(10); + __debug_mmu.damr[11].L = __get_DAMLR(11); + __debug_mmu.damr[11].P = __get_DAMPR(11); + __debug_mmu.damr[12].L = __get_DAMLR(12); + __debug_mmu.damr[12].P = __get_DAMPR(12); + __debug_mmu.damr[13].L = __get_DAMLR(13); + __debug_mmu.damr[13].P = __get_DAMPR(13); + __debug_mmu.damr[14].L = __get_DAMLR(14); + __debug_mmu.damr[14].P = __get_DAMPR(14); + __debug_mmu.damr[15].L = __get_DAMLR(15); + __debug_mmu.damr[15].P = __get_DAMPR(15); + +#ifdef CONFIG_MMU + do { + /* read the DAT entries from the TLB */ + struct __debug_amr *p; + int loop; + + asm volatile("movsg tplr,%0" : "=r"(__debug_mmu.regs.tplr)); + asm volatile("movsg tppr,%0" : "=r"(__debug_mmu.regs.tppr)); + asm volatile("movsg tpxr,%0" : "=r"(__debug_mmu.regs.tpxr)); + asm volatile("movsg cxnr,%0" : "=r"(__debug_mmu.regs.cxnr)); + + p = __debug_mmu.tlb; + + /* way 0 */ + asm volatile("movgs %0,tpxr" :: "r"(0 << TPXR_WAY_SHIFT)); + for (loop = 0; loop < 64; loop++) { + asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT)); + asm volatile("movsg tplr,%0" : "=r"(p->L)); + asm volatile("movsg tppr,%0" : "=r"(p->P)); + p++; + } + + /* way 1 */ + asm volatile("movgs %0,tpxr" :: "r"(1 << TPXR_WAY_SHIFT)); + for (loop = 0; loop < 64; loop++) { + asm volatile("tlbpr %0,gr0,#1,#0" :: "r"(loop << PAGE_SHIFT)); + asm volatile("movsg tplr,%0" : "=r"(p->L)); + asm volatile("movsg tppr,%0" : "=r"(p->P)); + p++; + } + + asm volatile("movgs %0,tplr" :: "r"(__debug_mmu.regs.tplr)); + asm volatile("movgs %0,tppr" :: "r"(__debug_mmu.regs.tppr)); + asm volatile("movgs %0,tpxr" :: "r"(__debug_mmu.regs.tpxr)); + } while(0); +#endif + +} /* end gdbstub_get_mmu_state() */ + +/* + * handle general query commands of the form 'qXXXXX' + */ +static void gdbstub_handle_query(void) +{ + if (strcmp(input_buffer, "qAttached") == 0) { + /* return current thread ID */ + sprintf(output_buffer, "1"); + return; + } + + if (strcmp(input_buffer, "qC") == 0) { + /* return current thread ID */ + sprintf(output_buffer, "QC 0"); + return; + } + + if (strcmp(input_buffer, "qOffsets") == 0) { + /* return relocation offset of text and data segments */ + sprintf(output_buffer, "Text=0;Data=0;Bss=0"); + return; + } + + if (strcmp(input_buffer, "qSymbol::") == 0) { + sprintf(output_buffer, "OK"); + return; + } + + if (strcmp(input_buffer, "qSupported") == 0) { + /* query of supported features */ + sprintf(output_buffer, "PacketSize=%u;ReverseContinue-;ReverseStep-", + sizeof(input_buffer)); + return; + } + + gdbstub_strcpy(output_buffer,"E01"); +} + +/*****************************************************************************/ +/* + * handle event interception and GDB remote protocol processing + * - on entry: + * PSR.ET==0, PSR.S==1 and the CPU is in debug mode + * __debug_frame points to the saved registers + * __frame points to the kernel mode exception frame, if it was in kernel + * mode when the break happened + */ +void gdbstub(int sigval) +{ + unsigned long addr, length, loop, dbar, temp, temp2, temp3; + uint32_t zero; + char *ptr; + int flush_cache = 0; + + LEDS(0x5000); + + if (sigval < 0) { +#ifndef CONFIG_GDBSTUB_IMMEDIATE + /* return immediately if GDB immediate activation option not set */ + return; +#else + sigval = SIGINT; +#endif + } + + save_user_regs(&__debug_frame0->uc); + +#if 0 + gdbstub_printk("--> gdbstub() %08x %p %08x %08x\n", + __debug_frame->pc, + __debug_frame, + __debug_regs->brr, + __debug_regs->bpsr); +// gdbstub_show_regs(); +#endif + + LEDS(0x5001); + + /* if we were interrupted by input on the serial gdbstub serial port, + * restore the context prior to the interrupt so that we return to that + * directly + */ + temp = (unsigned long) __entry_kerneltrap_table; + temp2 = (unsigned long) __entry_usertrap_table; + temp3 = __debug_frame->pc & ~15; + + if (temp3 == temp + TBR_TT_INTERRUPT_15 || + temp3 == temp2 + TBR_TT_INTERRUPT_15 + ) { + asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc)); + __debug_frame->psr |= PSR_ET; + __debug_frame->psr &= ~PSR_S; + if (__debug_frame->psr & PSR_PS) + __debug_frame->psr |= PSR_S; + __debug_status.brr = (__debug_frame->tbr & TBR_TT) << 12; + __debug_status.brr |= BRR_EB; + sigval = SIGINT; + } + + /* handle the decrement timer going off (FR451 only) */ + if (temp3 == temp + TBR_TT_DECREMENT_TIMER || + temp3 == temp2 + TBR_TT_DECREMENT_TIMER + ) { + asm volatile("movgs %0,timerd" :: "r"(10000000)); + asm volatile("movsg pcsr,%0" : "=r"(__debug_frame->pc)); + __debug_frame->psr |= PSR_ET; + __debug_frame->psr &= ~PSR_S; + if (__debug_frame->psr & PSR_PS) + __debug_frame->psr |= PSR_S; + __debug_status.brr = (__debug_frame->tbr & TBR_TT) << 12; + __debug_status.brr |= BRR_EB; + sigval = SIGXCPU; + } + + LEDS(0x5002); + + /* after a BREAK insn, the PC lands on the far side of it */ + if (__debug_status.brr & BRR_SB) + gdbstub_check_breakpoint(); + + LEDS(0x5003); + + /* handle attempts to write console data via GDB "O" commands */ + if (__debug_frame->pc == (unsigned long) gdbstub_console_write + 4) { + __gdbstub_console_write((struct console *) __debug_frame->gr8, + (const char *) __debug_frame->gr9, + (unsigned) __debug_frame->gr10); + goto done; + } + + if (gdbstub_rx_unget) { + sigval = SIGINT; + goto packet_waiting; + } + + if (!sigval) + sigval = gdbstub_compute_signal(__debug_status.brr); + + LEDS(0x5004); + + /* send a message to the debugger's user saying what happened if it may + * not be clear cut (we can't map exceptions onto signals properly) + */ + if (sigval != SIGINT && sigval != SIGTRAP && sigval != SIGILL) { + static const char title[] = "Break "; + static const char crlf[] = "\r\n"; + unsigned long brr = __debug_status.brr; + char hx; + + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(title, ptr, sizeof(title) - 1,0); + + hx = hex_asc_hi(brr >> 24); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_lo(brr >> 24); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_hi(brr >> 16); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_lo(brr >> 16); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_hi(brr >> 8); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_lo(brr >> 8); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_hi(brr); + ptr = hex_byte_pack(ptr, hx); + hx = hex_asc_lo(brr); + ptr = hex_byte_pack(ptr, hx); + + ptr = mem2hex(crlf, ptr, sizeof(crlf) - 1, 0); + *ptr = 0; + gdbstub_send_packet(output_buffer); /* send it off... */ + } + + LEDS(0x5005); + + /* tell the debugger that an exception has occurred */ + ptr = output_buffer; + + /* Send trap type (converted to signal) */ + *ptr++ = 'T'; + ptr = hex_byte_pack(ptr, sigval); + + /* Send Error PC */ + ptr = hex_byte_pack(ptr, GDB_REG_PC); + *ptr++ = ':'; + ptr = mem2hex(&__debug_frame->pc, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send frame pointer + */ + ptr = hex_byte_pack(ptr, GDB_REG_FP); + *ptr++ = ':'; + ptr = mem2hex(&__debug_frame->fp, ptr, 4, 0); + *ptr++ = ';'; + + /* + * Send stack pointer + */ + ptr = hex_byte_pack(ptr, GDB_REG_SP); + *ptr++ = ':'; + ptr = mem2hex(&__debug_frame->sp, ptr, 4, 0); + *ptr++ = ';'; + + *ptr++ = 0; + gdbstub_send_packet(output_buffer); /* send it off... */ + + LEDS(0x5006); + + packet_waiting: + gdbstub_get_mmu_state(); + + /* wait for input from remote GDB */ + while (1) { + output_buffer[0] = 0; + + LEDS(0x5007); + gdbstub_recv_packet(input_buffer); + LEDS(0x5600 | input_buffer[0]); + + switch (input_buffer[0]) { + /* request repeat of last signal number */ + case '?': + output_buffer[0] = 'S'; + output_buffer[1] = hex_asc_hi(sigval); + output_buffer[2] = hex_asc_lo(sigval); + output_buffer[3] = 0; + break; + + case 'd': + /* toggle debug flag */ + break; + + /* return the value of the CPU registers + * - GR0, GR1, GR2, GR3, GR4, GR5, GR6, GR7, + * - GR8, GR9, GR10, GR11, GR12, GR13, GR14, GR15, + * - GR16, GR17, GR18, GR19, GR20, GR21, GR22, GR23, + * - GR24, GR25, GR26, GR27, GR28, GR29, GR30, GR31, + * - GR32, GR33, GR34, GR35, GR36, GR37, GR38, GR39, + * - GR40, GR41, GR42, GR43, GR44, GR45, GR46, GR47, + * - GR48, GR49, GR50, GR51, GR52, GR53, GR54, GR55, + * - GR56, GR57, GR58, GR59, GR60, GR61, GR62, GR63, + * - FP0, FP1, FP2, FP3, FP4, FP5, FP6, FP7, + * - FP8, FP9, FP10, FP11, FP12, FP13, FP14, FP15, + * - FP16, FP17, FP18, FP19, FP20, FP21, FP22, FP23, + * - FP24, FP25, FP26, FP27, FP28, FP29, FP30, FP31, + * - FP32, FP33, FP34, FP35, FP36, FP37, FP38, FP39, + * - FP40, FP41, FP42, FP43, FP44, FP45, FP46, FP47, + * - FP48, FP49, FP50, FP51, FP52, FP53, FP54, FP55, + * - FP56, FP57, FP58, FP59, FP60, FP61, FP62, FP63, + * - PC, PSR, CCR, CCCR, + * - _X132, _X133, _X134 + * - TBR, BRR, DBAR0, DBAR1, DBAR2, DBAR3, + * - _X141, _X142, _X143, _X144, + * - LR, LCR + */ + case 'g': + zero = 0; + ptr = output_buffer; + + /* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */ + ptr = mem2hex(&zero, ptr, 4, 0); + + for (loop = 1; loop <= 27; loop++) + ptr = mem2hex(&__debug_user_context->i.gr[loop], ptr, 4, 0); + temp = (unsigned long) __frame; + ptr = mem2hex(&temp, ptr, 4, 0); + ptr = mem2hex(&__debug_user_context->i.gr[29], ptr, 4, 0); + ptr = mem2hex(&__debug_user_context->i.gr[30], ptr, 4, 0); +#ifdef CONFIG_MMU + ptr = mem2hex(&__debug_user_context->i.gr[31], ptr, 4, 0); +#else + temp = (unsigned long) __debug_frame; + ptr = mem2hex(&temp, ptr, 4, 0); +#endif + + for (loop = 32; loop <= 63; loop++) + ptr = mem2hex(&__debug_user_context->i.gr[loop], ptr, 4, 0); + + /* deal with FR0-FR63 */ + for (loop = 0; loop <= 63; loop++) + ptr = mem2hex(&__debug_user_context->f.fr[loop], ptr, 4, 0); + + /* deal with special registers */ + ptr = mem2hex(&__debug_frame->pc, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->psr, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->ccr, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->cccr, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&zero, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->tbr, ptr, 4, 0); + ptr = mem2hex(&__debug_status.brr , ptr, 4, 0); + + asm volatile("movsg dbar0,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg dbar1,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg dbar2,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg dbar3,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + + asm volatile("movsg scr0,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg scr1,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg scr2,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + asm volatile("movsg scr3,%0" : "=r"(dbar)); + ptr = mem2hex(&dbar, ptr, 4, 0); + + ptr = mem2hex(&__debug_frame->lr, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->lcr, ptr, 4, 0); + + ptr = mem2hex(&__debug_frame->iacc0, ptr, 8, 0); + + ptr = mem2hex(&__debug_user_context->f.fsr[0], ptr, 4, 0); + + for (loop = 0; loop <= 7; loop++) + ptr = mem2hex(&__debug_user_context->f.acc[loop], ptr, 4, 0); + + ptr = mem2hex(&__debug_user_context->f.accg, ptr, 8, 0); + + for (loop = 0; loop <= 1; loop++) + ptr = mem2hex(&__debug_user_context->f.msr[loop], ptr, 4, 0); + + ptr = mem2hex(&__debug_frame->gner0, ptr, 4, 0); + ptr = mem2hex(&__debug_frame->gner1, ptr, 4, 0); + + ptr = mem2hex(&__debug_user_context->f.fner[0], ptr, 4, 0); + ptr = mem2hex(&__debug_user_context->f.fner[1], ptr, 4, 0); + + break; + + /* set the values of the CPU registers */ + case 'G': + ptr = &input_buffer[1]; + + /* deal with GR0, GR1-GR27, GR28-GR31, GR32-GR63 */ + ptr = hex2mem(ptr, &temp, 4); + + for (loop = 1; loop <= 27; loop++) + ptr = hex2mem(ptr, &__debug_user_context->i.gr[loop], 4); + + ptr = hex2mem(ptr, &temp, 4); + __frame = (struct pt_regs *) temp; + ptr = hex2mem(ptr, &__debug_frame->gr29, 4); + ptr = hex2mem(ptr, &__debug_frame->gr30, 4); +#ifdef CONFIG_MMU + ptr = hex2mem(ptr, &__debug_frame->gr31, 4); +#else + ptr = hex2mem(ptr, &temp, 4); +#endif + + for (loop = 32; loop <= 63; loop++) + ptr = hex2mem(ptr, &__debug_user_context->i.gr[loop], 4); + + /* deal with FR0-FR63 */ + for (loop = 0; loop <= 63; loop++) + ptr = mem2hex(&__debug_user_context->f.fr[loop], ptr, 4, 0); + + /* deal with special registers */ + ptr = hex2mem(ptr, &__debug_frame->pc, 4); + ptr = hex2mem(ptr, &__debug_frame->psr, 4); + ptr = hex2mem(ptr, &__debug_frame->ccr, 4); + ptr = hex2mem(ptr, &__debug_frame->cccr,4); + + for (loop = 132; loop <= 140; loop++) + ptr = hex2mem(ptr, &temp, 4); + + ptr = hex2mem(ptr, &temp, 4); + asm volatile("movgs %0,scr0" :: "r"(temp)); + ptr = hex2mem(ptr, &temp, 4); + asm volatile("movgs %0,scr1" :: "r"(temp)); + ptr = hex2mem(ptr, &temp, 4); + asm volatile("movgs %0,scr2" :: "r"(temp)); + ptr = hex2mem(ptr, &temp, 4); + asm volatile("movgs %0,scr3" :: "r"(temp)); + + ptr = hex2mem(ptr, &__debug_frame->lr, 4); + ptr = hex2mem(ptr, &__debug_frame->lcr, 4); + + ptr = hex2mem(ptr, &__debug_frame->iacc0, 8); + + ptr = hex2mem(ptr, &__debug_user_context->f.fsr[0], 4); + + for (loop = 0; loop <= 7; loop++) + ptr = hex2mem(ptr, &__debug_user_context->f.acc[loop], 4); + + ptr = hex2mem(ptr, &__debug_user_context->f.accg, 8); + + for (loop = 0; loop <= 1; loop++) + ptr = hex2mem(ptr, &__debug_user_context->f.msr[loop], 4); + + ptr = hex2mem(ptr, &__debug_frame->gner0, 4); + ptr = hex2mem(ptr, &__debug_frame->gner1, 4); + + ptr = hex2mem(ptr, &__debug_user_context->f.fner[0], 4); + ptr = hex2mem(ptr, &__debug_user_context->f.fner[1], 4); + + gdbstub_strcpy(output_buffer,"OK"); + break; + + /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) + ) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + gdbstub_strcpy (output_buffer, "E03"); + } + else { + gdbstub_strcpy(output_buffer,"E01"); + } + break; + + /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) && + *ptr++ == ',' && + hexToInt(&ptr, &length) && + *ptr++ == ':' + ) { + if (hex2mem(ptr, (char *)addr, length)) { + gdbstub_strcpy(output_buffer, "OK"); + } + else { + gdbstub_strcpy(output_buffer, "E03"); + } + } + else + gdbstub_strcpy(output_buffer, "E02"); + + flush_cache = 1; + break; + + /* pNN: Read value of reg N and return it */ + case 'p': + /* return no value, indicating that we don't support + * this command and that gdb should use 'g' instead */ + break; + + /* PNN,=RRRRRRRR: Write value R to reg N return OK */ + case 'P': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr, &addr) || + *ptr++ != '=' || + !hexToInt(&ptr, &temp) + ) { + gdbstub_strcpy(output_buffer, "E01"); + break; + } + + temp2 = 1; + switch (addr) { + case GDB_REG_GR(0): + break; + case GDB_REG_GR(1) ... GDB_REG_GR(63): + __debug_user_context->i.gr[addr - GDB_REG_GR(0)] = temp; + break; + case GDB_REG_FR(0) ... GDB_REG_FR(63): + __debug_user_context->f.fr[addr - GDB_REG_FR(0)] = temp; + break; + case GDB_REG_PC: + __debug_user_context->i.pc = temp; + break; + case GDB_REG_PSR: + __debug_user_context->i.psr = temp; + break; + case GDB_REG_CCR: + __debug_user_context->i.ccr = temp; + break; + case GDB_REG_CCCR: + __debug_user_context->i.cccr = temp; + break; + case GDB_REG_BRR: + __debug_status.brr = temp; + break; + case GDB_REG_LR: + __debug_user_context->i.lr = temp; + break; + case GDB_REG_LCR: + __debug_user_context->i.lcr = temp; + break; + case GDB_REG_FSR0: + __debug_user_context->f.fsr[0] = temp; + break; + case GDB_REG_ACC(0) ... GDB_REG_ACC(7): + __debug_user_context->f.acc[addr - GDB_REG_ACC(0)] = temp; + break; + case GDB_REG_ACCG(0): + *(uint32_t *) &__debug_user_context->f.accg[0] = temp; + break; + case GDB_REG_ACCG(4): + *(uint32_t *) &__debug_user_context->f.accg[4] = temp; + break; + case GDB_REG_MSR(0) ... GDB_REG_MSR(1): + __debug_user_context->f.msr[addr - GDB_REG_MSR(0)] = temp; + break; + case GDB_REG_GNER(0) ... GDB_REG_GNER(1): + __debug_user_context->i.gner[addr - GDB_REG_GNER(0)] = temp; + break; + case GDB_REG_FNER(0) ... GDB_REG_FNER(1): + __debug_user_context->f.fner[addr - GDB_REG_FNER(0)] = temp; + break; + default: + temp2 = 0; + break; + } + + if (temp2) { + gdbstub_strcpy(output_buffer, "OK"); + } + else { + gdbstub_strcpy(output_buffer, "E02"); + } + break; + + /* cAA..AA Continue at address AA..AA(optional) */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + __debug_frame->pc = addr; + goto done; + + /* kill the program */ + case 'k' : + goto done; /* just continue */ + + /* detach */ + case 'D': + gdbstub_strcpy(output_buffer, "OK"); + break; + + /* reset the whole machine (FIXME: system dependent) */ + case 'r': + break; + + + /* step to next instruction */ + case 's': + __debug_regs->dcr |= DCR_SE; + __debug_status.dcr |= DCR_SE; + goto done; + + /* extended command */ + case 'v': + if (strcmp(input_buffer, "vCont?") == 0) { + output_buffer[0] = 0; + break; + } + goto unsupported_cmd; + + /* set baud rate (bBB) */ + case 'b': + ptr = &input_buffer[1]; + if (!hexToInt(&ptr, &temp)) { + gdbstub_strcpy(output_buffer,"B01"); + break; + } + + if (temp) { + /* ack before changing speed */ + gdbstub_send_packet("OK"); + gdbstub_set_baud(temp); + } + break; + + /* set breakpoint */ + case 'Z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr,&temp) || *ptr++ != ',' || + !hexToInt(&ptr,&addr) || *ptr++ != ',' || + !hexToInt(&ptr,&length) + ) { + gdbstub_strcpy(output_buffer,"E01"); + break; + } + + if (temp >= 5) { + gdbstub_strcpy(output_buffer,"E03"); + break; + } + + if (gdbstub_set_breakpoint(temp, addr, length) < 0) { + gdbstub_strcpy(output_buffer,"E03"); + break; + } + + if (temp == 0) + flush_cache = 1; /* soft bkpt by modified memory */ + + gdbstub_strcpy(output_buffer,"OK"); + break; + + /* clear breakpoint */ + case 'z': + ptr = &input_buffer[1]; + + if (!hexToInt(&ptr,&temp) || *ptr++ != ',' || + !hexToInt(&ptr,&addr) || *ptr++ != ',' || + !hexToInt(&ptr,&length) + ) { + gdbstub_strcpy(output_buffer,"E01"); + break; + } + + if (temp >= 5) { + gdbstub_strcpy(output_buffer,"E03"); + break; + } + + if (gdbstub_clear_breakpoint(temp, addr, length) < 0) { + gdbstub_strcpy(output_buffer,"E03"); + break; + } + + if (temp == 0) + flush_cache = 1; /* soft bkpt by modified memory */ + + gdbstub_strcpy(output_buffer,"OK"); + break; + + /* Thread-setting packet */ + case 'H': + gdbstub_strcpy(output_buffer, "OK"); + break; + + case 'q': + gdbstub_handle_query(); + break; + + default: + unsupported_cmd: + gdbstub_proto("### GDB Unsupported Cmd '%s'\n",input_buffer); + gdbstub_strcpy(output_buffer,"E01"); + break; + } + + /* reply to the request */ + LEDS(0x5009); + gdbstub_send_packet(output_buffer); + } + + done: + restore_user_regs(&__debug_frame0->uc); + + //gdbstub_dump_debugregs(); + //gdbstub_printk("<-- gdbstub() %08x\n", __debug_frame->pc); + + /* need to flush the instruction cache before resuming, as we may have + * deposited a breakpoint, and the icache probably has no way of + * knowing that a data ref to some location may have changed something + * that is in the instruction cache. NB: We flush both caches, just to + * be sure... + */ + + /* note: flushing the icache will clobber EAR0 on the FR451 */ + if (flush_cache) + gdbstub_purge_cache(); + + LEDS(0x5666); + +} /* end gdbstub() */ + +/*****************************************************************************/ +/* + * initialise the GDB stub + */ +void __init gdbstub_init(void) +{ +#ifdef CONFIG_GDBSTUB_IMMEDIATE + unsigned char ch; + int ret; +#endif + + gdbstub_printk("%s", gdbstub_banner); + + gdbstub_io_init(); + + /* try to talk to GDB (or anyone insane enough to want to type GDB protocol by hand) */ + gdbstub_proto("### GDB Tx ACK\n"); + gdbstub_tx_char('+'); /* 'hello world' */ + +#ifdef CONFIG_GDBSTUB_IMMEDIATE + gdbstub_printk("GDB Stub waiting for packet\n"); + + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + do { gdbstub_rx_char(&ch, 0); } while (ch != '$'); + do { gdbstub_rx_char(&ch, 0); } while (ch != '#'); + do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat first csum byte */ + do { ret = gdbstub_rx_char(&ch, 0); } while (ret != 0); /* eat second csum byte */ + + gdbstub_proto("### GDB Tx NAK\n"); + gdbstub_tx_char('-'); /* nak it */ + +#else + gdbstub_printk("GDB Stub set\n"); +#endif + +#if 0 + /* send banner */ + ptr = output_buffer; + *ptr++ = 'O'; + ptr = mem2hex(gdbstub_banner, ptr, sizeof(gdbstub_banner) - 1, 0); + gdbstub_send_packet(output_buffer); +#endif +#if defined(CONFIG_GDB_CONSOLE) && defined(CONFIG_GDBSTUB_IMMEDIATE) + register_console(&gdbstub_console); +#endif + +} /* end gdbstub_init() */ + +/*****************************************************************************/ +/* + * register the console at a more appropriate time + */ +#if defined (CONFIG_GDB_CONSOLE) && !defined(CONFIG_GDBSTUB_IMMEDIATE) +static int __init gdbstub_postinit(void) +{ + printk("registering console\n"); + register_console(&gdbstub_console); + return 0; +} /* end gdbstub_postinit() */ + +__initcall(gdbstub_postinit); +#endif + +/*****************************************************************************/ +/* + * send an exit message to GDB + */ +void gdbstub_exit(int status) +{ + unsigned char checksum; + int count; + unsigned char ch; + + sprintf(output_buffer,"W%02x",status&0xff); + + gdbstub_tx_char('$'); + checksum = 0; + count = 0; + + while ((ch = output_buffer[count]) != 0) { + gdbstub_tx_char(ch); + checksum += ch; + count += 1; + } + + gdbstub_tx_char('#'); + gdbstub_tx_char(hex_asc_hi(checksum)); + gdbstub_tx_char(hex_asc_lo(checksum)); + + /* make sure the output is flushed, or else RedBoot might clobber it */ + gdbstub_tx_char('-'); + gdbstub_tx_flush(); + +} /* end gdbstub_exit() */ + +/*****************************************************************************/ +/* + * GDB wants to call malloc() and free() to allocate memory for calling kernel + * functions directly from its command line + */ +static void *malloc(size_t size) __maybe_unused; +static void *malloc(size_t size) +{ + return kmalloc(size, GFP_ATOMIC); +} + +static void free(void *p) __maybe_unused; +static void free(void *p) +{ + kfree(p); +} + +static uint32_t ___get_HSR0(void) __maybe_unused; +static uint32_t ___get_HSR0(void) +{ + return __get_HSR(0); +} + +static uint32_t ___set_HSR0(uint32_t x) __maybe_unused; +static uint32_t ___set_HSR0(uint32_t x) +{ + __set_HSR(0, x); + return __get_HSR(0); +} diff --git a/arch/frv/kernel/head-mmu-fr451.S b/arch/frv/kernel/head-mmu-fr451.S new file mode 100644 index 000000000..98f87d586 --- /dev/null +++ b/arch/frv/kernel/head-mmu-fr451.S @@ -0,0 +1,374 @@ +/* head-mmu-fr451.S: FR451 mmu-linux specific bits of initialisation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/mem-layout.h> +#include <asm/spr-regs.h> +#include <asm/mb86943a.h> +#include "head.inc" + + +#define __400_DBR0 0xfe000e00 +#define __400_DBR1 0xfe000e08 +#define __400_DBR2 0xfe000e10 +#define __400_DBR3 0xfe000e18 +#define __400_DAM0 0xfe000f00 +#define __400_DAM1 0xfe000f08 +#define __400_DAM2 0xfe000f10 +#define __400_DAM3 0xfe000f18 +#define __400_LGCR 0xfe000010 +#define __400_LCR 0xfe000100 +#define __400_LSBR 0xfe000c00 + + __INIT + .balign 4 + +############################################################################### +# +# describe the position and layout of the SDRAM controller registers +# +# ENTRY: EXIT: +# GR5 - cacheline size +# GR11 - displacement of 2nd SDRAM addr reg from GR14 +# GR12 - displacement of 3rd SDRAM addr reg from GR14 +# GR13 - displacement of 4th SDRAM addr reg from GR14 +# GR14 - address of 1st SDRAM addr reg +# GR15 - amount to shift address by to match SDRAM addr reg +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# CC0 - T if DBR0 is present +# CC1 - T if DBR1 is present +# CC2 - T if DBR2 is present +# CC3 - T if DBR3 is present +# +############################################################################### + .globl __head_fr451_describe_sdram +__head_fr451_describe_sdram: + sethi.p %hi(__400_DBR0),gr14 + setlo %lo(__400_DBR0),gr14 + setlos.p #__400_DBR1-__400_DBR0,gr11 + setlos #__400_DBR2-__400_DBR0,gr12 + setlos.p #__400_DBR3-__400_DBR0,gr13 + setlos #32,gr5 ; cacheline size + setlos.p #0,gr15 ; amount to shift addr reg by + setlos #0x00ff,gr4 + movgs gr4,cccr ; extant DARS/DAMK regs + bralr + +############################################################################### +# +# rearrange the bus controller registers +# +# ENTRY: EXIT: +# GR26 &__head_reference [saved] +# GR30 LED address revised LED address +# +############################################################################### + .globl __head_fr451_set_busctl +__head_fr451_set_busctl: + sethi.p %hi(__400_LGCR),gr4 + setlo %lo(__400_LGCR),gr4 + sethi.p %hi(__400_LSBR),gr10 + setlo %lo(__400_LSBR),gr10 + sethi.p %hi(__400_LCR),gr11 + setlo %lo(__400_LCR),gr11 + + # set the bus controller + ldi @(gr4,#0),gr5 + ori gr5,#0xff,gr5 ; make sure all chip-selects are enabled + sti gr5,@(gr4,#0) + + sethi.p %hi(__region_CS1),gr4 + setlo %lo(__region_CS1),gr4 + sethi.p %hi(__region_CS1_M),gr5 + setlo %lo(__region_CS1_M),gr5 + sethi.p %hi(__region_CS1_C),gr6 + setlo %lo(__region_CS1_C),gr6 + sti gr4,@(gr10,#1*0x08) + sti gr5,@(gr10,#1*0x08+0x100) + sti gr6,@(gr11,#1*0x08) + sethi.p %hi(__region_CS2),gr4 + setlo %lo(__region_CS2),gr4 + sethi.p %hi(__region_CS2_M),gr5 + setlo %lo(__region_CS2_M),gr5 + sethi.p %hi(__region_CS2_C),gr6 + setlo %lo(__region_CS2_C),gr6 + sti gr4,@(gr10,#2*0x08) + sti gr5,@(gr10,#2*0x08+0x100) + sti gr6,@(gr11,#2*0x08) + sethi.p %hi(__region_CS3),gr4 + setlo %lo(__region_CS3),gr4 + sethi.p %hi(__region_CS3_M),gr5 + setlo %lo(__region_CS3_M),gr5 + sethi.p %hi(__region_CS3_C),gr6 + setlo %lo(__region_CS3_C),gr6 + sti gr4,@(gr10,#3*0x08) + sti gr5,@(gr10,#3*0x08+0x100) + sti gr6,@(gr11,#3*0x08) + sethi.p %hi(__region_CS4),gr4 + setlo %lo(__region_CS4),gr4 + sethi.p %hi(__region_CS4_M),gr5 + setlo %lo(__region_CS4_M),gr5 + sethi.p %hi(__region_CS4_C),gr6 + setlo %lo(__region_CS4_C),gr6 + sti gr4,@(gr10,#4*0x08) + sti gr5,@(gr10,#4*0x08+0x100) + sti gr6,@(gr11,#4*0x08) + sethi.p %hi(__region_CS5),gr4 + setlo %lo(__region_CS5),gr4 + sethi.p %hi(__region_CS5_M),gr5 + setlo %lo(__region_CS5_M),gr5 + sethi.p %hi(__region_CS5_C),gr6 + setlo %lo(__region_CS5_C),gr6 + sti gr4,@(gr10,#5*0x08) + sti gr5,@(gr10,#5*0x08+0x100) + sti gr6,@(gr11,#5*0x08) + sethi.p %hi(__region_CS6),gr4 + setlo %lo(__region_CS6),gr4 + sethi.p %hi(__region_CS6_M),gr5 + setlo %lo(__region_CS6_M),gr5 + sethi.p %hi(__region_CS6_C),gr6 + setlo %lo(__region_CS6_C),gr6 + sti gr4,@(gr10,#6*0x08) + sti gr5,@(gr10,#6*0x08+0x100) + sti gr6,@(gr11,#6*0x08) + sethi.p %hi(__region_CS7),gr4 + setlo %lo(__region_CS7),gr4 + sethi.p %hi(__region_CS7_M),gr5 + setlo %lo(__region_CS7_M),gr5 + sethi.p %hi(__region_CS7_C),gr6 + setlo %lo(__region_CS7_C),gr6 + sti gr4,@(gr10,#7*0x08) + sti gr5,@(gr10,#7*0x08+0x100) + sti gr6,@(gr11,#7*0x08) + membar + bar + + # adjust LED bank address +#ifdef CONFIG_MB93091_VDK + sethi.p %hi(__region_CS2 + 0x01200004),gr30 + setlo %lo(__region_CS2 + 0x01200004),gr30 +#endif + bralr + +############################################################################### +# +# determine the total SDRAM size +# +# ENTRY: EXIT: +# GR25 - SDRAM size +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +############################################################################### + .globl __head_fr451_survey_sdram +__head_fr451_survey_sdram: + sethi.p %hi(__400_DAM0),gr11 + setlo %lo(__400_DAM0),gr11 + sethi.p %hi(__400_DBR0),gr12 + setlo %lo(__400_DBR0),gr12 + + sethi.p %hi(0xfe000000),gr17 ; unused SDRAM DBR value + setlo %lo(0xfe000000),gr17 + setlos #0,gr25 + + ldi @(gr12,#0x00),gr4 ; DAR0 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS0 + ldi @(gr11,#0x00),gr6 ; DAM0: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS0: + + ldi @(gr12,#0x08),gr4 ; DAR1 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS1 + ldi @(gr11,#0x08),gr6 ; DAM1: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS1: + + ldi @(gr12,#0x10),gr4 ; DAR2 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS2 + ldi @(gr11,#0x10),gr6 ; DAM2: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS2: + + ldi @(gr12,#0x18),gr4 ; DAR3 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS3 + ldi @(gr11,#0x18),gr6 ; DAM3: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS3: + bralr + +############################################################################### +# +# set the protection map with the I/DAMPR registers +# +# ENTRY: EXIT: +# GR25 SDRAM size [saved] +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +# +# Using this map: +# REGISTERS ADDRESS RANGE VIEW +# =============== ====================== =============================== +# IAMPR0/DAMPR0 0xC0000000-0xCFFFFFFF Cached kernel RAM Window +# DAMPR11 0xE0000000-0xFFFFFFFF Uncached I/O +# +############################################################################### + .globl __head_fr451_set_protection +__head_fr451_set_protection: + movsg lr,gr27 + + # set the I/O region protection registers for FR451 in MMU mode +#define PGPROT_IO xAMPRx_L|xAMPRx_M|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V + + sethi.p %hi(__region_IO),gr5 + setlo %lo(__region_IO),gr5 + setlos #PGPROT_IO|xAMPRx_SS_512Mb,gr4 + or gr4,gr5,gr4 + movgs gr5,damlr11 ; General I/O tile + movgs gr4,dampr11 + + # need to open a window onto at least part of the RAM for the kernel's use + sethi.p %hi(__sdram_base),gr8 + setlo %lo(__sdram_base),gr8 ; physical address + sethi.p %hi(__page_offset),gr9 + setlo %lo(__page_offset),gr9 ; virtual address + + setlos #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr11 + or gr8,gr11,gr8 + + movgs gr9,iamlr0 ; mapped from real address 0 + movgs gr8,iampr0 ; cached kernel memory at 0xC0000000 + movgs gr9,damlr0 + movgs gr8,dampr0 + + # set a temporary mapping for the kernel running at address 0 until we've turned on the MMU + sethi.p %hi(__sdram_base),gr9 + setlo %lo(__sdram_base),gr9 ; virtual address + + and.p gr4,gr11,gr4 + and gr5,gr11,gr5 + or.p gr4,gr11,gr4 + or gr5,gr11,gr5 + + movgs gr9,iamlr1 ; mapped from real address 0 + movgs gr8,iampr1 ; cached kernel memory at 0x00000000 + movgs gr9,damlr1 + movgs gr8,dampr1 + + # we use DAMR2-10 for kmap_atomic(), cache flush and TLB management + # since the DAMLR regs are not going to change, we can set them now + # also set up IAMLR2 to the same as DAMLR5 + sethi.p %hi(KMAP_ATOMIC_PRIMARY_FRAME),gr4 + setlo %lo(KMAP_ATOMIC_PRIMARY_FRAME),gr4 + sethi.p %hi(PAGE_SIZE),gr5 + setlo %lo(PAGE_SIZE),gr5 + + movgs gr4,damlr2 + movgs gr4,iamlr2 + add gr4,gr5,gr4 + movgs gr4,damlr3 + add gr4,gr5,gr4 + movgs gr4,damlr4 + add gr4,gr5,gr4 + movgs gr4,damlr5 + add gr4,gr5,gr4 + movgs gr4,damlr6 + add gr4,gr5,gr4 + movgs gr4,damlr7 + add gr4,gr5,gr4 + movgs gr4,damlr8 + add gr4,gr5,gr4 + movgs gr4,damlr9 + add gr4,gr5,gr4 + movgs gr4,damlr10 + + movgs gr0,dampr2 + movgs gr0,dampr4 + movgs gr0,dampr5 + movgs gr0,dampr6 + movgs gr0,dampr7 + movgs gr0,dampr8 + movgs gr0,dampr9 + movgs gr0,dampr10 + + movgs gr0,iamlr3 + movgs gr0,iamlr4 + movgs gr0,iamlr5 + movgs gr0,iamlr6 + movgs gr0,iamlr7 + + movgs gr0,iampr2 + movgs gr0,iampr3 + movgs gr0,iampr4 + movgs gr0,iampr5 + movgs gr0,iampr6 + movgs gr0,iampr7 + + # start in TLB context 0 with the swapper's page tables + movgs gr0,cxnr + + sethi.p %hi(swapper_pg_dir),gr4 + setlo %lo(swapper_pg_dir),gr4 + sethi.p %hi(__page_offset),gr5 + setlo %lo(__page_offset),gr5 + sub gr4,gr5,gr4 + movgs gr4,ttbr + setlos #xAMPRx_L|xAMPRx_M|xAMPRx_SS_16Kb|xAMPRx_S|xAMPRx_C|xAMPRx_V,gr5 + or gr4,gr5,gr4 + movgs gr4,dampr3 + + # the FR451 also has an extra trap base register + movsg tbr,gr4 + movgs gr4,btbr + + LEDS 0x3300 + jmpl @(gr27,gr0) + +############################################################################### +# +# finish setting up the protection registers +# +############################################################################### + .globl __head_fr451_finalise_protection +__head_fr451_finalise_protection: + # turn on the timers as appropriate + movgs gr0,timerh + movgs gr0,timerl + movgs gr0,timerd + movsg hsr0,gr4 + sethi.p %hi(HSR0_ETMI),gr5 + setlo %lo(HSR0_ETMI),gr5 + or gr4,gr5,gr4 + movgs gr4,hsr0 + + # clear the TLB entry cache + movgs gr0,iamlr1 + movgs gr0,iampr1 + movgs gr0,damlr1 + movgs gr0,dampr1 + + # clear the PGE cache + sethi.p %hi(__flush_tlb_all),gr4 + setlo %lo(__flush_tlb_all),gr4 + jmpl @(gr4,gr0) diff --git a/arch/frv/kernel/head-uc-fr401.S b/arch/frv/kernel/head-uc-fr401.S new file mode 100644 index 000000000..438643cfa --- /dev/null +++ b/arch/frv/kernel/head-uc-fr401.S @@ -0,0 +1,311 @@ +/* head-uc-fr401.S: FR401/3/5 uc-linux specific bits of initialisation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/spr-regs.h> +#include <asm/mb86943a.h> +#include "head.inc" + + +#define __400_DBR0 0xfe000e00 +#define __400_DBR1 0xfe000e08 +#define __400_DBR2 0xfe000e10 /* not on FR401 */ +#define __400_DBR3 0xfe000e18 /* not on FR401 */ +#define __400_DAM0 0xfe000f00 +#define __400_DAM1 0xfe000f08 +#define __400_DAM2 0xfe000f10 /* not on FR401 */ +#define __400_DAM3 0xfe000f18 /* not on FR401 */ +#define __400_LGCR 0xfe000010 +#define __400_LCR 0xfe000100 +#define __400_LSBR 0xfe000c00 + + __INIT + .balign 4 + +############################################################################### +# +# describe the position and layout of the SDRAM controller registers +# +# ENTRY: EXIT: +# GR5 - cacheline size +# GR11 - displacement of 2nd SDRAM addr reg from GR14 +# GR12 - displacement of 3rd SDRAM addr reg from GR14 +# GR13 - displacement of 4th SDRAM addr reg from GR14 +# GR14 - address of 1st SDRAM addr reg +# GR15 - amount to shift address by to match SDRAM addr reg +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# CC0 - T if DBR0 is present +# CC1 - T if DBR1 is present +# CC2 - T if DBR2 is present (not FR401/FR401A) +# CC3 - T if DBR3 is present (not FR401/FR401A) +# +############################################################################### + .globl __head_fr401_describe_sdram +__head_fr401_describe_sdram: + sethi.p %hi(__400_DBR0),gr14 + setlo %lo(__400_DBR0),gr14 + setlos.p #__400_DBR1-__400_DBR0,gr11 + setlos #__400_DBR2-__400_DBR0,gr12 + setlos.p #__400_DBR3-__400_DBR0,gr13 + setlos #32,gr5 ; cacheline size + setlos.p #0,gr15 ; amount to shift addr reg by + + # specify which DBR regs are present + setlos #0x00ff,gr4 + movgs gr4,cccr + movsg psr,gr3 ; check for FR401/FR401A + srli gr3,#25,gr3 + subicc gr3,#0x20>>1,gr0,icc0 + bnelr icc0,#1 + setlos #0x000f,gr4 + movgs gr4,cccr + bralr + +############################################################################### +# +# rearrange the bus controller registers +# +# ENTRY: EXIT: +# GR26 &__head_reference [saved] +# GR30 LED address revised LED address +# +############################################################################### + .globl __head_fr401_set_busctl +__head_fr401_set_busctl: + sethi.p %hi(__400_LGCR),gr4 + setlo %lo(__400_LGCR),gr4 + sethi.p %hi(__400_LSBR),gr10 + setlo %lo(__400_LSBR),gr10 + sethi.p %hi(__400_LCR),gr11 + setlo %lo(__400_LCR),gr11 + + # set the bus controller + ldi @(gr4,#0),gr5 + ori gr5,#0xff,gr5 ; make sure all chip-selects are enabled + sti gr5,@(gr4,#0) + + sethi.p %hi(__region_CS1),gr4 + setlo %lo(__region_CS1),gr4 + sethi.p %hi(__region_CS1_M),gr5 + setlo %lo(__region_CS1_M),gr5 + sethi.p %hi(__region_CS1_C),gr6 + setlo %lo(__region_CS1_C),gr6 + sti gr4,@(gr10,#1*0x08) + sti gr5,@(gr10,#1*0x08+0x100) + sti gr6,@(gr11,#1*0x08) + sethi.p %hi(__region_CS2),gr4 + setlo %lo(__region_CS2),gr4 + sethi.p %hi(__region_CS2_M),gr5 + setlo %lo(__region_CS2_M),gr5 + sethi.p %hi(__region_CS2_C),gr6 + setlo %lo(__region_CS2_C),gr6 + sti gr4,@(gr10,#2*0x08) + sti gr5,@(gr10,#2*0x08+0x100) + sti gr6,@(gr11,#2*0x08) + sethi.p %hi(__region_CS3),gr4 + setlo %lo(__region_CS3),gr4 + sethi.p %hi(__region_CS3_M),gr5 + setlo %lo(__region_CS3_M),gr5 + sethi.p %hi(__region_CS3_C),gr6 + setlo %lo(__region_CS3_C),gr6 + sti gr4,@(gr10,#3*0x08) + sti gr5,@(gr10,#3*0x08+0x100) + sti gr6,@(gr11,#3*0x08) + sethi.p %hi(__region_CS4),gr4 + setlo %lo(__region_CS4),gr4 + sethi.p %hi(__region_CS4_M),gr5 + setlo %lo(__region_CS4_M),gr5 + sethi.p %hi(__region_CS4_C),gr6 + setlo %lo(__region_CS4_C),gr6 + sti gr4,@(gr10,#4*0x08) + sti gr5,@(gr10,#4*0x08+0x100) + sti gr6,@(gr11,#4*0x08) + sethi.p %hi(__region_CS5),gr4 + setlo %lo(__region_CS5),gr4 + sethi.p %hi(__region_CS5_M),gr5 + setlo %lo(__region_CS5_M),gr5 + sethi.p %hi(__region_CS5_C),gr6 + setlo %lo(__region_CS5_C),gr6 + sti gr4,@(gr10,#5*0x08) + sti gr5,@(gr10,#5*0x08+0x100) + sti gr6,@(gr11,#5*0x08) + sethi.p %hi(__region_CS6),gr4 + setlo %lo(__region_CS6),gr4 + sethi.p %hi(__region_CS6_M),gr5 + setlo %lo(__region_CS6_M),gr5 + sethi.p %hi(__region_CS6_C),gr6 + setlo %lo(__region_CS6_C),gr6 + sti gr4,@(gr10,#6*0x08) + sti gr5,@(gr10,#6*0x08+0x100) + sti gr6,@(gr11,#6*0x08) + sethi.p %hi(__region_CS7),gr4 + setlo %lo(__region_CS7),gr4 + sethi.p %hi(__region_CS7_M),gr5 + setlo %lo(__region_CS7_M),gr5 + sethi.p %hi(__region_CS7_C),gr6 + setlo %lo(__region_CS7_C),gr6 + sti gr4,@(gr10,#7*0x08) + sti gr5,@(gr10,#7*0x08+0x100) + sti gr6,@(gr11,#7*0x08) + membar + bar + + # adjust LED bank address + sethi.p %hi(LED_ADDR - 0x20000000 +__region_CS2),gr30 + setlo %lo(LED_ADDR - 0x20000000 +__region_CS2),gr30 + bralr + +############################################################################### +# +# determine the total SDRAM size +# +# ENTRY: EXIT: +# GR25 - SDRAM size +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +############################################################################### + .globl __head_fr401_survey_sdram +__head_fr401_survey_sdram: + sethi.p %hi(__400_DAM0),gr11 + setlo %lo(__400_DAM0),gr11 + sethi.p %hi(__400_DBR0),gr12 + setlo %lo(__400_DBR0),gr12 + + sethi.p %hi(0xfe000000),gr17 ; unused SDRAM DBR value + setlo %lo(0xfe000000),gr17 + setlos #0,gr25 + + ldi @(gr12,#0x00),gr4 ; DAR0 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS0 + ldi @(gr11,#0x00),gr6 ; DAM0: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS0: + + ldi @(gr12,#0x08),gr4 ; DAR1 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS1 + ldi @(gr11,#0x08),gr6 ; DAM1: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS1: + + # FR401/FR401A does not have DCS2/3 + movsg psr,gr3 + srli gr3,#25,gr3 + subicc gr3,#0x20>>1,gr0,icc0 + beq icc0,#0,__head_no_DCS3 + + ldi @(gr12,#0x10),gr4 ; DAR2 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS2 + ldi @(gr11,#0x10),gr6 ; DAM2: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS2: + + ldi @(gr12,#0x18),gr4 ; DAR3 + subcc gr4,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS3 + ldi @(gr11,#0x18),gr6 ; DAM3: bits 31:20 match addr 31:20 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS3: + bralr + +############################################################################### +# +# set the protection map with the I/DAMPR registers +# +# ENTRY: EXIT: +# GR25 SDRAM size [saved] +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +############################################################################### + .globl __head_fr401_set_protection +__head_fr401_set_protection: + movsg lr,gr27 + + # set the I/O region protection registers for FR401/3/5 + sethi.p %hi(__region_IO),gr5 + setlo %lo(__region_IO),gr5 + ori gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5 + movgs gr0,iampr7 + movgs gr5,dampr7 ; General I/O tile + + # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible + # - start with the highest numbered registers + sethi.p %hi(__kernel_image_end),gr8 + setlo %lo(__kernel_image_end),gr8 + sethi.p %hi(32768),gr4 ; allow for a maximal allocator bitmap + setlo %lo(32768),gr4 + add gr8,gr4,gr8 + sethi.p %hi(1024*2048-1),gr4 ; round up to nearest 2MiB + setlo %lo(1024*2048-1),gr4 + add.p gr8,gr4,gr8 + not gr4,gr4 + and gr8,gr4,gr8 + + sethi.p %hi(__page_offset),gr9 + setlo %lo(__page_offset),gr9 + add gr9,gr25,gr9 + + # GR8 = base of uncovered RAM + # GR9 = top of uncovered RAM + +#ifdef CONFIG_MB93093_PDK + sethi.p %hi(__region_CS2),gr4 + setlo %lo(__region_CS2),gr4 + ori gr4,#xAMPRx_SS_1Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr4 + movgs gr4,dampr6 + movgs gr0,iampr6 +#else + call __head_split_region + movgs gr4,iampr6 + movgs gr5,dampr6 +#endif + call __head_split_region + movgs gr4,iampr5 + movgs gr5,dampr5 + call __head_split_region + movgs gr4,iampr4 + movgs gr5,dampr4 + call __head_split_region + movgs gr4,iampr3 + movgs gr5,dampr3 + call __head_split_region + movgs gr4,iampr2 + movgs gr5,dampr2 + call __head_split_region + movgs gr4,iampr1 + movgs gr5,dampr1 + + # cover kernel core image with kernel-only segment + sethi.p %hi(__page_offset),gr8 + setlo %lo(__page_offset),gr8 + call __head_split_region + +#ifdef CONFIG_PROTECT_KERNEL + ori.p gr4,#xAMPRx_S_KERNEL,gr4 + ori gr5,#xAMPRx_S_KERNEL,gr5 +#endif + + movgs gr4,iampr0 + movgs gr5,dampr0 + jmpl @(gr27,gr0) diff --git a/arch/frv/kernel/head-uc-fr451.S b/arch/frv/kernel/head-uc-fr451.S new file mode 100644 index 000000000..b2a76c4a1 --- /dev/null +++ b/arch/frv/kernel/head-uc-fr451.S @@ -0,0 +1,174 @@ +/* head-uc-fr451.S: FR451 uc-linux specific bits of initialisation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/spr-regs.h> +#include <asm/mb86943a.h> +#include "head.inc" + + +#define __400_DBR0 0xfe000e00 +#define __400_DBR1 0xfe000e08 +#define __400_DBR2 0xfe000e10 +#define __400_DBR3 0xfe000e18 +#define __400_DAM0 0xfe000f00 +#define __400_DAM1 0xfe000f08 +#define __400_DAM2 0xfe000f10 +#define __400_DAM3 0xfe000f18 +#define __400_LGCR 0xfe000010 +#define __400_LCR 0xfe000100 +#define __400_LSBR 0xfe000c00 + + __INIT + .balign 4 + +############################################################################### +# +# set the protection map with the I/DAMPR registers +# +# ENTRY: EXIT: +# GR25 SDRAM size [saved] +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +############################################################################### + .globl __head_fr451_set_protection +__head_fr451_set_protection: + movsg lr,gr27 + + movgs gr0,dampr10 + movgs gr0,damlr10 + movgs gr0,dampr9 + movgs gr0,damlr9 + movgs gr0,dampr8 + movgs gr0,damlr8 + + # set the I/O region protection registers for FR401/3/5 + sethi.p %hi(__region_IO),gr5 + setlo %lo(__region_IO),gr5 + sethi.p %hi(0x1fffffff),gr7 + setlo %lo(0x1fffffff),gr7 + ori gr5,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5 + movgs gr5,dampr11 ; General I/O tile + movgs gr7,damlr11 + + # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible + # - start with the highest numbered registers + sethi.p %hi(__kernel_image_end),gr8 + setlo %lo(__kernel_image_end),gr8 + sethi.p %hi(32768),gr4 ; allow for a maximal allocator bitmap + setlo %lo(32768),gr4 + add gr8,gr4,gr8 + sethi.p %hi(1024*2048-1),gr4 ; round up to nearest 2MiB + setlo %lo(1024*2048-1),gr4 + add.p gr8,gr4,gr8 + not gr4,gr4 + and gr8,gr4,gr8 + + sethi.p %hi(__page_offset),gr9 + setlo %lo(__page_offset),gr9 + add gr9,gr25,gr9 + + sethi.p %hi(0xffffc000),gr11 + setlo %lo(0xffffc000),gr11 + + # GR8 = base of uncovered RAM + # GR9 = top of uncovered RAM + # GR11 = xAMLR mask + LEDS 0x3317 + call __head_split_region + movgs gr4,iampr7 + movgs gr6,iamlr7 + movgs gr5,dampr7 + movgs gr7,damlr7 + + LEDS 0x3316 + call __head_split_region + movgs gr4,iampr6 + movgs gr6,iamlr6 + movgs gr5,dampr6 + movgs gr7,damlr6 + + LEDS 0x3315 + call __head_split_region + movgs gr4,iampr5 + movgs gr6,iamlr5 + movgs gr5,dampr5 + movgs gr7,damlr5 + + LEDS 0x3314 + call __head_split_region + movgs gr4,iampr4 + movgs gr6,iamlr4 + movgs gr5,dampr4 + movgs gr7,damlr4 + + LEDS 0x3313 + call __head_split_region + movgs gr4,iampr3 + movgs gr6,iamlr3 + movgs gr5,dampr3 + movgs gr7,damlr3 + + LEDS 0x3312 + call __head_split_region + movgs gr4,iampr2 + movgs gr6,iamlr2 + movgs gr5,dampr2 + movgs gr7,damlr2 + + LEDS 0x3311 + call __head_split_region + movgs gr4,iampr1 + movgs gr6,iamlr1 + movgs gr5,dampr1 + movgs gr7,damlr1 + + # cover kernel core image with kernel-only segment + LEDS 0x3310 + sethi.p %hi(__page_offset),gr8 + setlo %lo(__page_offset),gr8 + call __head_split_region + +#ifdef CONFIG_PROTECT_KERNEL + ori.p gr4,#xAMPRx_S_KERNEL,gr4 + ori gr5,#xAMPRx_S_KERNEL,gr5 +#endif + + movgs gr4,iampr0 + movgs gr6,iamlr0 + movgs gr5,dampr0 + movgs gr7,damlr0 + + # start in TLB context 0 with no page tables + movgs gr0,cxnr + movgs gr0,ttbr + + # the FR451 also has an extra trap base register + movsg tbr,gr4 + movgs gr4,btbr + + # turn on the timers as appropriate + movgs gr0,timerh + movgs gr0,timerl + movgs gr0,timerd + movsg hsr0,gr4 + sethi.p %hi(HSR0_ETMI),gr5 + setlo %lo(HSR0_ETMI),gr5 + or gr4,gr5,gr4 + movgs gr4,hsr0 + + LEDS 0x3300 + jmpl @(gr27,gr0) diff --git a/arch/frv/kernel/head-uc-fr555.S b/arch/frv/kernel/head-uc-fr555.S new file mode 100644 index 000000000..5497aaf34 --- /dev/null +++ b/arch/frv/kernel/head-uc-fr555.S @@ -0,0 +1,347 @@ +/* head-uc-fr555.S: FR555 uc-linux specific bits of initialisation + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/spr-regs.h> +#include <asm/mb86943a.h> +#include "head.inc" + + +#define __551_DARS0 0xfeff0100 +#define __551_DARS1 0xfeff0104 +#define __551_DARS2 0xfeff0108 +#define __551_DARS3 0xfeff010c +#define __551_DAMK0 0xfeff0110 +#define __551_DAMK1 0xfeff0114 +#define __551_DAMK2 0xfeff0118 +#define __551_DAMK3 0xfeff011c +#define __551_LCR 0xfeff1100 +#define __551_LSBR 0xfeff1c00 + + __INIT + .balign 4 + +############################################################################### +# +# describe the position and layout of the SDRAM controller registers +# +# ENTRY: EXIT: +# GR5 - cacheline size +# GR11 - displacement of 2nd SDRAM addr reg from GR14 +# GR12 - displacement of 3rd SDRAM addr reg from GR14 +# GR13 - displacement of 4th SDRAM addr reg from GR14 +# GR14 - address of 1st SDRAM addr reg +# GR15 - amount to shift address by to match SDRAM addr reg +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# CC0 - T if DARS0 is present +# CC1 - T if DARS1 is present +# CC2 - T if DARS2 is present +# CC3 - T if DARS3 is present +# +############################################################################### + .globl __head_fr555_describe_sdram +__head_fr555_describe_sdram: + sethi.p %hi(__551_DARS0),gr14 + setlo %lo(__551_DARS0),gr14 + setlos.p #__551_DARS1-__551_DARS0,gr11 + setlos #__551_DARS2-__551_DARS0,gr12 + setlos.p #__551_DARS3-__551_DARS0,gr13 + setlos #64,gr5 ; cacheline size + setlos #20,gr15 ; amount to shift addr by + setlos #0x00ff,gr4 + movgs gr4,cccr ; extant DARS/DAMK regs + bralr + +############################################################################### +# +# rearrange the bus controller registers +# +# ENTRY: EXIT: +# GR26 &__head_reference [saved] +# GR30 LED address revised LED address +# +############################################################################### + .globl __head_fr555_set_busctl +__head_fr555_set_busctl: + LEDS 0x100f + sethi.p %hi(__551_LSBR),gr10 + setlo %lo(__551_LSBR),gr10 + sethi.p %hi(__551_LCR),gr11 + setlo %lo(__551_LCR),gr11 + + # set the bus controller + sethi.p %hi(__region_CS1),gr4 + setlo %lo(__region_CS1),gr4 + sethi.p %hi(__region_CS1_M),gr5 + setlo %lo(__region_CS1_M),gr5 + sethi.p %hi(__region_CS1_C),gr6 + setlo %lo(__region_CS1_C),gr6 + sti gr4,@(gr10,#1*0x08) + sti gr5,@(gr10,#1*0x08+0x100) + sti gr6,@(gr11,#1*0x08) + sethi.p %hi(__region_CS2),gr4 + setlo %lo(__region_CS2),gr4 + sethi.p %hi(__region_CS2_M),gr5 + setlo %lo(__region_CS2_M),gr5 + sethi.p %hi(__region_CS2_C),gr6 + setlo %lo(__region_CS2_C),gr6 + sti gr4,@(gr10,#2*0x08) + sti gr5,@(gr10,#2*0x08+0x100) + sti gr6,@(gr11,#2*0x08) + sethi.p %hi(__region_CS3),gr4 + setlo %lo(__region_CS3),gr4 + sethi.p %hi(__region_CS3_M),gr5 + setlo %lo(__region_CS3_M),gr5 + sethi.p %hi(__region_CS3_C),gr6 + setlo %lo(__region_CS3_C),gr6 + sti gr4,@(gr10,#3*0x08) + sti gr5,@(gr10,#3*0x08+0x100) + sti gr6,@(gr11,#3*0x08) + sethi.p %hi(__region_CS4),gr4 + setlo %lo(__region_CS4),gr4 + sethi.p %hi(__region_CS4_M),gr5 + setlo %lo(__region_CS4_M),gr5 + sethi.p %hi(__region_CS4_C),gr6 + setlo %lo(__region_CS4_C),gr6 + sti gr4,@(gr10,#4*0x08) + sti gr5,@(gr10,#4*0x08+0x100) + sti gr6,@(gr11,#4*0x08) + sethi.p %hi(__region_CS5),gr4 + setlo %lo(__region_CS5),gr4 + sethi.p %hi(__region_CS5_M),gr5 + setlo %lo(__region_CS5_M),gr5 + sethi.p %hi(__region_CS5_C),gr6 + setlo %lo(__region_CS5_C),gr6 + sti gr4,@(gr10,#5*0x08) + sti gr5,@(gr10,#5*0x08+0x100) + sti gr6,@(gr11,#5*0x08) + sethi.p %hi(__region_CS6),gr4 + setlo %lo(__region_CS6),gr4 + sethi.p %hi(__region_CS6_M),gr5 + setlo %lo(__region_CS6_M),gr5 + sethi.p %hi(__region_CS6_C),gr6 + setlo %lo(__region_CS6_C),gr6 + sti gr4,@(gr10,#6*0x08) + sti gr5,@(gr10,#6*0x08+0x100) + sti gr6,@(gr11,#6*0x08) + sethi.p %hi(__region_CS7),gr4 + setlo %lo(__region_CS7),gr4 + sethi.p %hi(__region_CS7_M),gr5 + setlo %lo(__region_CS7_M),gr5 + sethi.p %hi(__region_CS7_C),gr6 + setlo %lo(__region_CS7_C),gr6 + sti gr4,@(gr10,#7*0x08) + sti gr5,@(gr10,#7*0x08+0x100) + sti gr6,@(gr11,#7*0x08) + membar + bar + + # adjust LED bank address +#ifdef CONFIG_MB93091_VDK + sethi.p %hi(LED_ADDR - 0x20000000 +__region_CS2),gr30 + setlo %lo(LED_ADDR - 0x20000000 +__region_CS2),gr30 +#endif + bralr + +############################################################################### +# +# determine the total SDRAM size +# +# ENTRY: EXIT: +# GR25 - SDRAM size +# GR26 &__head_reference [saved] +# GR30 LED address [saved] +# +############################################################################### + .globl __head_fr555_survey_sdram +__head_fr555_survey_sdram: + sethi.p %hi(__551_DAMK0),gr11 + setlo %lo(__551_DAMK0),gr11 + sethi.p %hi(__551_DARS0),gr12 + setlo %lo(__551_DARS0),gr12 + + sethi.p %hi(0xfff),gr17 ; unused SDRAM AMK value + setlo %lo(0xfff),gr17 + setlos #0,gr25 + + ldi @(gr11,#0x00),gr6 ; DAMK0: bits 11:0 match addr 11:0 + subcc gr6,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS0 + ldi @(gr12,#0x00),gr4 ; DARS0 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS0: + + ldi @(gr11,#0x04),gr6 ; DAMK1: bits 11:0 match addr 11:0 + subcc gr6,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS1 + ldi @(gr12,#0x04),gr4 ; DARS1 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS1: + + ldi @(gr11,#0x8),gr6 ; DAMK2: bits 11:0 match addr 11:0 + subcc gr6,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS2 + ldi @(gr12,#0x8),gr4 ; DARS2 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS2: + + ldi @(gr11,#0xc),gr6 ; DAMK3: bits 11:0 match addr 11:0 + subcc gr6,gr17,gr0,icc0 + beq icc0,#0,__head_no_DCS3 + ldi @(gr12,#0xc),gr4 ; DARS3 + add gr25,gr6,gr25 + addi gr25,#1,gr25 +__head_no_DCS3: + + slli gr25,#20,gr25 ; shift [11:0] -> [31:20] + bralr + +############################################################################### +# +# set the protection map with the I/DAMPR registers +# +# ENTRY: EXIT: +# GR25 SDRAM size saved +# GR30 LED address saved +# +############################################################################### + .globl __head_fr555_set_protection +__head_fr555_set_protection: + movsg lr,gr27 + + sethi.p %hi(0xfff00000),gr11 + setlo %lo(0xfff00000),gr11 + + # set the I/O region protection registers for FR555 + sethi.p %hi(__region_IO),gr7 + setlo %lo(__region_IO),gr7 + ori gr7,#xAMPRx_SS_512Mb|xAMPRx_S_KERNEL|xAMPRx_C|xAMPRx_V,gr5 + movgs gr0,iampr15 + movgs gr0,iamlr15 + movgs gr5,dampr15 + movgs gr7,damlr15 + + # need to tile the remaining IAMPR/DAMPR registers to cover as much of the RAM as possible + # - start with the highest numbered registers + sethi.p %hi(__kernel_image_end),gr8 + setlo %lo(__kernel_image_end),gr8 + sethi.p %hi(32768),gr4 ; allow for a maximal allocator bitmap + setlo %lo(32768),gr4 + add gr8,gr4,gr8 + sethi.p %hi(1024*2048-1),gr4 ; round up to nearest 2MiB + setlo %lo(1024*2048-1),gr4 + add.p gr8,gr4,gr8 + not gr4,gr4 + and gr8,gr4,gr8 + + sethi.p %hi(__page_offset),gr9 + setlo %lo(__page_offset),gr9 + add gr9,gr25,gr9 + + # GR8 = base of uncovered RAM + # GR9 = top of uncovered RAM + # GR11 - mask for DAMLR/IAMLR regs + # + call __head_split_region + movgs gr4,iampr14 + movgs gr6,iamlr14 + movgs gr5,dampr14 + movgs gr7,damlr14 + call __head_split_region + movgs gr4,iampr13 + movgs gr6,iamlr13 + movgs gr5,dampr13 + movgs gr7,damlr13 + call __head_split_region + movgs gr4,iampr12 + movgs gr6,iamlr12 + movgs gr5,dampr12 + movgs gr7,damlr12 + call __head_split_region + movgs gr4,iampr11 + movgs gr6,iamlr11 + movgs gr5,dampr11 + movgs gr7,damlr11 + call __head_split_region + movgs gr4,iampr10 + movgs gr6,iamlr10 + movgs gr5,dampr10 + movgs gr7,damlr10 + call __head_split_region + movgs gr4,iampr9 + movgs gr6,iamlr9 + movgs gr5,dampr9 + movgs gr7,damlr9 + call __head_split_region + movgs gr4,iampr8 + movgs gr6,iamlr8 + movgs gr5,dampr8 + movgs gr7,damlr8 + + call __head_split_region + movgs gr4,iampr7 + movgs gr6,iamlr7 + movgs gr5,dampr7 + movgs gr7,damlr7 + call __head_split_region + movgs gr4,iampr6 + movgs gr6,iamlr6 + movgs gr5,dampr6 + movgs gr7,damlr6 + call __head_split_region + movgs gr4,iampr5 + movgs gr6,iamlr5 + movgs gr5,dampr5 + movgs gr7,damlr5 + call __head_split_region + movgs gr4,iampr4 + movgs gr6,iamlr4 + movgs gr5,dampr4 + movgs gr7,damlr4 + call __head_split_region + movgs gr4,iampr3 + movgs gr6,iamlr3 + movgs gr5,dampr3 + movgs gr7,damlr3 + call __head_split_region + movgs gr4,iampr2 + movgs gr6,iamlr2 + movgs gr5,dampr2 + movgs gr7,damlr2 + call __head_split_region + movgs gr4,iampr1 + movgs gr6,iamlr1 + movgs gr5,dampr1 + movgs gr7,damlr1 + + # cover kernel core image with kernel-only segment + sethi.p %hi(__page_offset),gr8 + setlo %lo(__page_offset),gr8 + call __head_split_region + +#ifdef CONFIG_PROTECT_KERNEL + ori.p gr4,#xAMPRx_S_KERNEL,gr4 + ori gr5,#xAMPRx_S_KERNEL,gr5 +#endif + + movgs gr4,iampr0 + movgs gr6,iamlr0 + movgs gr5,dampr0 + movgs gr7,damlr0 + jmpl @(gr27,gr0) diff --git a/arch/frv/kernel/head.S b/arch/frv/kernel/head.S new file mode 100644 index 000000000..a7d0bea9c --- /dev/null +++ b/arch/frv/kernel/head.S @@ -0,0 +1,638 @@ +/* head.S: kernel entry point for FR-V kernel + * + * Copyright (C) 2003, 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/init.h> +#include <linux/threads.h> +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/ptrace.h> +#include <asm/page.h> +#include <asm/spr-regs.h> +#include <asm/mb86943a.h> +#include <asm/cache.h> +#include "head.inc" + +############################################################################### +# +# void _boot(unsigned long magic, char *command_line) __attribute__((noreturn)) +# +# - if magic is 0xdead1eaf, then command_line is assumed to point to the kernel +# command line string +# +############################################################################### + __HEAD + .balign 4 + + .globl _boot, __head_reference + .type _boot,@function +_boot: +__head_reference: + sethi.p %hi(LED_ADDR),gr30 + setlo %lo(LED_ADDR),gr30 + + LEDS 0x0000 + + # calculate reference address for PC-relative stuff + call 0f +0: movsg lr,gr26 + addi gr26,#__head_reference-0b,gr26 + + # invalidate and disable both of the caches and turn off the memory access checking + dcef @(gr0,gr0),1 + bar + + sethi.p %hi(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4 + setlo %lo(~(HSR0_ICE|HSR0_DCE|HSR0_CBM|HSR0_EIMMU|HSR0_EDMMU)),gr4 + movsg hsr0,gr5 + and gr4,gr5,gr5 + movgs gr5,hsr0 + movsg hsr0,gr5 + + LEDS 0x0001 + + icei @(gr0,gr0),1 + dcei @(gr0,gr0),1 + bar + + # turn the instruction cache back on + sethi.p %hi(HSR0_ICE),gr4 + setlo %lo(HSR0_ICE),gr4 + movsg hsr0,gr5 + or gr4,gr5,gr5 + movgs gr5,hsr0 + movsg hsr0,gr5 + + bar + + LEDS 0x0002 + + # retrieve the parameters (including command line) before we overwrite them + sethi.p %hi(0xdead1eaf),gr7 + setlo %lo(0xdead1eaf),gr7 + subcc gr7,gr8,gr0,icc0 + bne icc0,#0,__head_no_parameters + + sethi.p %hi(redboot_command_line-1),gr6 + setlo %lo(redboot_command_line-1),gr6 + sethi.p %hi(__head_reference),gr4 + setlo %lo(__head_reference),gr4 + sub gr6,gr4,gr6 + add.p gr6,gr26,gr6 + subi gr9,#1,gr9 + setlos.p #511,gr4 + setlos #1,gr5 + +__head_copy_cmdline: + ldubu.p @(gr9,gr5),gr16 + subicc gr4,#1,gr4,icc0 + stbu.p gr16,@(gr6,gr5) + subicc gr16,#0,gr0,icc1 + bls icc0,#0,__head_end_cmdline + bne icc1,#1,__head_copy_cmdline +__head_end_cmdline: + stbu gr0,@(gr6,gr5) +__head_no_parameters: + +############################################################################### +# +# we need to relocate the SDRAM to 0x00000000 (linux) or 0xC0000000 (uClinux) +# - note that we're going to have to run entirely out of the icache whilst +# fiddling with the SDRAM controller registers +# +############################################################################### +#ifdef CONFIG_MMU + call __head_fr451_describe_sdram + +#else + movsg psr,gr5 + srli gr5,#28,gr5 + subicc gr5,#3,gr0,icc0 + beq icc0,#0,__head_fr551_sdram + + call __head_fr401_describe_sdram + bra __head_do_sdram + +__head_fr551_sdram: + call __head_fr555_describe_sdram + LEDS 0x000d + +__head_do_sdram: +#endif + + # preload the registers with invalid values in case any DBR/DARS are marked not present + sethi.p %hi(0xfe000000),gr17 ; unused SDRAM DBR value + setlo %lo(0xfe000000),gr17 + or.p gr17,gr0,gr20 + or gr17,gr0,gr21 + or.p gr17,gr0,gr22 + or gr17,gr0,gr23 + + # consult the SDRAM controller CS address registers + cld @(gr14,gr0 ),gr20, cc0,#1 ; DBR0 / DARS0 + cld @(gr14,gr11),gr21, cc1,#1 ; DBR1 / DARS1 + cld @(gr14,gr12),gr22, cc2,#1 ; DBR2 / DARS2 + cld.p @(gr14,gr13),gr23, cc3,#1 ; DBR3 / DARS3 + + sll gr20,gr15,gr20 ; shift values up for FR551 + sll gr21,gr15,gr21 + sll gr22,gr15,gr22 + sll gr23,gr15,gr23 + + LEDS 0x0003 + + # assume the lowest valid CS line to be the SDRAM base and get its address + subcc gr20,gr17,gr0,icc0 + subcc.p gr21,gr17,gr0,icc1 + subcc gr22,gr17,gr0,icc2 + subcc.p gr23,gr17,gr0,icc3 + ckne icc0,cc4 ; T if DBR0 != 0xfe000000 + ckne icc1,cc5 + ckne icc2,cc6 + ckne icc3,cc7 + cor gr23,gr0,gr24, cc7,#1 ; GR24 = SDRAM base + cor gr22,gr0,gr24, cc6,#1 + cor gr21,gr0,gr24, cc5,#1 + cor gr20,gr0,gr24, cc4,#1 + + # calculate the displacement required to get the SDRAM into the right place in memory + sethi.p %hi(__sdram_base),gr16 + setlo %lo(__sdram_base),gr16 + sub gr16,gr24,gr16 ; delta = __sdram_base - DBRx + + # calculate the new values to go in the controller regs + cadd.p gr20,gr16,gr20, cc4,#1 ; DCS#0 (new) = DCS#0 (old) + delta + cadd gr21,gr16,gr21, cc5,#1 + cadd.p gr22,gr16,gr22, cc6,#1 + cadd gr23,gr16,gr23, cc7,#1 + + srl gr20,gr15,gr20 ; shift values down for FR551 + srl gr21,gr15,gr21 + srl gr22,gr15,gr22 + srl gr23,gr15,gr23 + + # work out the address at which the reg updater resides and lock it into icache + # also work out the address the updater will jump to when finished + sethi.p %hi(__head_move_sdram-__head_reference),gr18 + setlo %lo(__head_move_sdram-__head_reference),gr18 + sethi.p %hi(__head_sdram_moved-__head_reference),gr19 + setlo %lo(__head_sdram_moved-__head_reference),gr19 + add.p gr18,gr26,gr18 + add gr19,gr26,gr19 + add.p gr19,gr16,gr19 ; moved = addr + (__sdram_base - DBRx) + add gr18,gr5,gr4 ; two cachelines probably required + + icpl gr18,gr0,#1 ; load and lock the cachelines + icpl gr4,gr0,#1 + LEDS 0x0004 + membar + bar + jmpl @(gr18,gr0) + + .balign L1_CACHE_BYTES +__head_move_sdram: + cst gr20,@(gr14,gr0 ), cc4,#1 + cst gr21,@(gr14,gr11), cc5,#1 + cst gr22,@(gr14,gr12), cc6,#1 + cst gr23,@(gr14,gr13), cc7,#1 + cld @(gr14,gr0 ),gr20, cc4,#1 + cld @(gr14,gr11),gr21, cc5,#1 + cld @(gr14,gr12),gr22, cc4,#1 + cld @(gr14,gr13),gr23, cc7,#1 + bar + membar + jmpl @(gr19,gr0) + + .balign L1_CACHE_BYTES +__head_sdram_moved: + icul gr18 + add gr18,gr5,gr4 + icul gr4 + icei @(gr0,gr0),1 + dcei @(gr0,gr0),1 + + LEDS 0x0005 + + # recalculate reference address + call 0f +0: movsg lr,gr26 + addi gr26,#__head_reference-0b,gr26 + + +############################################################################### +# +# move the kernel image down to the bottom of the SDRAM +# +############################################################################### + sethi.p %hi(__kernel_image_size_no_bss+15),gr4 + setlo %lo(__kernel_image_size_no_bss+15),gr4 + srli.p gr4,#4,gr4 ; count + or gr26,gr26,gr16 ; source + + sethi.p %hi(__sdram_base),gr17 ; destination + setlo %lo(__sdram_base),gr17 + + setlos #8,gr5 + sub.p gr16,gr5,gr16 ; adjust src for LDDU + sub gr17,gr5,gr17 ; adjust dst for LDDU + + sethi.p %hi(__head_move_kernel-__head_reference),gr18 + setlo %lo(__head_move_kernel-__head_reference),gr18 + sethi.p %hi(__head_kernel_moved-__head_reference+__sdram_base),gr19 + setlo %lo(__head_kernel_moved-__head_reference+__sdram_base),gr19 + add gr18,gr26,gr18 + icpl gr18,gr0,#1 + jmpl @(gr18,gr0) + + .balign 32 +__head_move_kernel: + lddu @(gr16,gr5),gr10 + lddu @(gr16,gr5),gr12 + stdu.p gr10,@(gr17,gr5) + subicc gr4,#1,gr4,icc0 + stdu.p gr12,@(gr17,gr5) + bhi icc0,#0,__head_move_kernel + jmpl @(gr19,gr0) + + .balign 32 +__head_kernel_moved: + icul gr18 + icei @(gr0,gr0),1 + dcei @(gr0,gr0),1 + + LEDS 0x0006 + + # recalculate reference address + call 0f +0: movsg lr,gr26 + addi gr26,#__head_reference-0b,gr26 + + +############################################################################### +# +# rearrange the iomem map and set the protection registers +# +############################################################################### + +#ifdef CONFIG_MMU + LEDS 0x3301 + call __head_fr451_set_busctl + LEDS 0x3303 + call __head_fr451_survey_sdram + LEDS 0x3305 + call __head_fr451_set_protection + +#else + movsg psr,gr5 + srli gr5,#PSR_IMPLE_SHIFT,gr5 + subicc gr5,#PSR_IMPLE_FR551,gr0,icc0 + beq icc0,#0,__head_fr555_memmap + subicc gr5,#PSR_IMPLE_FR451,gr0,icc0 + beq icc0,#0,__head_fr451_memmap + + LEDS 0x3101 + call __head_fr401_set_busctl + LEDS 0x3103 + call __head_fr401_survey_sdram + LEDS 0x3105 + call __head_fr401_set_protection + bra __head_done_memmap + +__head_fr451_memmap: + LEDS 0x3301 + call __head_fr401_set_busctl + LEDS 0x3303 + call __head_fr401_survey_sdram + LEDS 0x3305 + call __head_fr451_set_protection + bra __head_done_memmap + +__head_fr555_memmap: + LEDS 0x3501 + call __head_fr555_set_busctl + LEDS 0x3503 + call __head_fr555_survey_sdram + LEDS 0x3505 + call __head_fr555_set_protection + +__head_done_memmap: +#endif + LEDS 0x0007 + +############################################################################### +# +# turn the data cache and MMU on +# - for the FR451 this'll mean that the window through which the kernel is +# viewed will change +# +############################################################################### + +#ifdef CONFIG_MMU +#define MMUMODE HSR0_EIMMU|HSR0_EDMMU|HSR0_EXMMU|HSR0_EDAT|HSR0_XEDAT +#else +#define MMUMODE HSR0_EIMMU|HSR0_EDMMU +#endif + + movsg hsr0,gr5 + + sethi.p %hi(MMUMODE),gr4 + setlo %lo(MMUMODE),gr4 + or gr4,gr5,gr5 + +#if defined(CONFIG_FRV_DEFL_CACHE_WTHRU) + sethi.p %hi(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4 + setlo %lo(HSR0_DCE|HSR0_CBM_WRITE_THRU),gr4 +#elif defined(CONFIG_FRV_DEFL_CACHE_WBACK) + sethi.p %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4 + setlo %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4 +#elif defined(CONFIG_FRV_DEFL_CACHE_WBEHIND) + sethi.p %hi(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4 + setlo %lo(HSR0_DCE|HSR0_CBM_COPY_BACK),gr4 + + movsg psr,gr6 + srli gr6,#24,gr6 + cmpi gr6,#0x50,icc0 // FR451 + beq icc0,#0,0f + cmpi gr6,#0x40,icc0 // FR405 + bne icc0,#0,1f +0: + # turn off write-allocate + sethi.p %hi(HSR0_NWA),gr6 + setlo %lo(HSR0_NWA),gr6 + or gr4,gr6,gr4 +1: + +#else +#error No default cache configuration set +#endif + + or gr4,gr5,gr5 + movgs gr5,hsr0 + bar + + LEDS 0x0008 + + sethi.p %hi(__head_mmu_enabled),gr19 + setlo %lo(__head_mmu_enabled),gr19 + jmpl @(gr19,gr0) + +__head_mmu_enabled: + icei @(gr0,gr0),#1 + dcei @(gr0,gr0),#1 + + LEDS 0x0009 + +#ifdef CONFIG_MMU + call __head_fr451_finalise_protection +#endif + + LEDS 0x000a + +############################################################################### +# +# set up the runtime environment +# +############################################################################### + + # clear the BSS area + sethi.p %hi(__bss_start),gr4 + setlo %lo(__bss_start),gr4 + sethi.p %hi(_end),gr5 + setlo %lo(_end),gr5 + or.p gr0,gr0,gr18 + or gr0,gr0,gr19 + +0: + stdi gr18,@(gr4,#0) + stdi gr18,@(gr4,#8) + stdi gr18,@(gr4,#16) + stdi.p gr18,@(gr4,#24) + addi gr4,#24,gr4 + subcc gr5,gr4,gr0,icc0 + bhi icc0,#2,0b + + LEDS 0x000b + + # save the SDRAM details + sethi.p %hi(__sdram_old_base),gr4 + setlo %lo(__sdram_old_base),gr4 + st gr24,@(gr4,gr0) + + sethi.p %hi(__sdram_base),gr5 + setlo %lo(__sdram_base),gr5 + sethi.p %hi(memory_start),gr4 + setlo %lo(memory_start),gr4 + st gr5,@(gr4,gr0) + + add gr25,gr5,gr25 + sethi.p %hi(memory_end),gr4 + setlo %lo(memory_end),gr4 + st gr25,@(gr4,gr0) + + # point the TBR at the kernel trap table + sethi.p %hi(__entry_kerneltrap_table),gr4 + setlo %lo(__entry_kerneltrap_table),gr4 + movgs gr4,tbr + + # set up the exception frame for init + sethi.p %hi(__kernel_frame0_ptr),gr28 + setlo %lo(__kernel_frame0_ptr),gr28 + sethi.p %hi(_gp),gr16 + setlo %lo(_gp),gr16 + sethi.p %hi(__entry_usertrap_table),gr4 + setlo %lo(__entry_usertrap_table),gr4 + + lddi @(gr28,#0),gr28 ; load __frame & current + ldi.p @(gr29,#4),gr15 ; set current_thread + + or gr0,gr0,fp + or gr28,gr0,sp + + sti.p gr4,@(gr28,REG_TBR) + setlos #ISR_EDE|ISR_DTT_DIVBYZERO|ISR_EMAM_EXCEPTION,gr5 + movgs gr5,isr + + # turn on and off various CPU services + movsg psr,gr22 + sethi.p %hi(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4 + setlo %lo(#PSR_EM|PSR_EF|PSR_CM|PSR_NEM),gr4 + or gr22,gr4,gr22 + movgs gr22,psr + + andi gr22,#~(PSR_PIL|PSR_PS|PSR_S),gr22 + ori gr22,#PSR_ET,gr22 + sti gr22,@(gr28,REG_PSR) + + +############################################################################### +# +# set up the registers and jump into the kernel +# +############################################################################### + + LEDS 0x000c + + sethi.p #0xe5e5,gr3 + setlo #0xe5e5,gr3 + or.p gr3,gr0,gr4 + or gr3,gr0,gr5 + or.p gr3,gr0,gr6 + or gr3,gr0,gr7 + or.p gr3,gr0,gr8 + or gr3,gr0,gr9 + or.p gr3,gr0,gr10 + or gr3,gr0,gr11 + or.p gr3,gr0,gr12 + or gr3,gr0,gr13 + or.p gr3,gr0,gr14 + or gr3,gr0,gr17 + or.p gr3,gr0,gr18 + or gr3,gr0,gr19 + or.p gr3,gr0,gr20 + or gr3,gr0,gr21 + or.p gr3,gr0,gr23 + or gr3,gr0,gr24 + or.p gr3,gr0,gr25 + or gr3,gr0,gr26 + or.p gr3,gr0,gr27 +# or gr3,gr0,gr30 + or gr3,gr0,gr31 + movgs gr0,lr + movgs gr0,lcr + movgs gr0,ccr + movgs gr0,cccr + + # initialise the virtual interrupt handling + subcc gr0,gr0,gr0,icc2 /* set Z, clear C */ + +#ifdef CONFIG_MMU + movgs gr3,scr2 + movgs gr3,scr3 +#endif + + LEDS 0x0fff + + # invoke the debugging stub if present + # - arch/frv/kernel/debug-stub.c will shift control directly to init/main.c + # (it will not return here) + break + .globl __debug_stub_init_break +__debug_stub_init_break: + + # however, if you need to use an ICE, and don't care about using any userspace + # debugging tools (such as the ptrace syscall), you can just step over the break + # above and get to the kernel this way + # look at arch/frv/kernel/debug-stub.c: debug_stub_init() to see what you've missed + call start_kernel + + .globl __head_end +__head_end: + .size _boot, .-_boot + + # provide a point for GDB to place a break + .section .text..start,"ax" + .globl _start + .balign 4 +_start: + call _boot + + .previous +############################################################################### +# +# split a tile off of the region defined by GR8-GR9 +# +# ENTRY: EXIT: +# GR4 - IAMPR value representing tile +# GR5 - DAMPR value representing tile +# GR6 - IAMLR value representing tile +# GR7 - DAMLR value representing tile +# GR8 region base pointer [saved] +# GR9 region top pointer updated to exclude new tile +# GR11 xAMLR mask [saved] +# GR25 SDRAM size [saved] +# GR30 LED address [saved] +# +# - GR8 and GR9 should be rounded up/down to the nearest megabyte before calling +# +############################################################################### + .globl __head_split_region + .type __head_split_region,@function +__head_split_region: + subcc.p gr9,gr8,gr4,icc0 + setlos #31,gr5 + scan.p gr4,gr0,gr6 + beq icc0,#0,__head_region_empty + sub.p gr5,gr6,gr6 ; bit number of highest set bit (1MB=>20) + setlos #1,gr4 + sll.p gr4,gr6,gr4 ; size of region (1 << bitno) + subi gr6,#17,gr6 ; 1MB => 0x03 + slli.p gr6,#4,gr6 ; 1MB => 0x30 + sub gr9,gr4,gr9 ; move uncovered top down + + or gr9,gr6,gr4 + ori gr4,#xAMPRx_S_USER|xAMPRx_C_CACHED|xAMPRx_V,gr4 + or.p gr4,gr0,gr5 + + and gr4,gr11,gr6 + and.p gr5,gr11,gr7 + bralr + +__head_region_empty: + or.p gr0,gr0,gr4 + or gr0,gr0,gr5 + or.p gr0,gr0,gr6 + or gr0,gr0,gr7 + bralr + .size __head_split_region, .-__head_split_region + +############################################################################### +# +# write the 32-bit hex number in GR8 to ttyS0 +# +############################################################################### +#if 0 + .globl __head_write_to_ttyS0 + .type __head_write_to_ttyS0,@function +__head_write_to_ttyS0: + sethi.p %hi(0xfeff9c00),gr31 + setlo %lo(0xfeff9c00),gr31 + setlos #8,gr20 + +0: ldubi @(gr31,#5*8),gr21 + andi gr21,#0x60,gr21 + subicc gr21,#0x60,gr21,icc0 + bne icc0,#0,0b + +1: srli gr8,#28,gr21 + slli gr8,#4,gr8 + + addi gr21,#'0',gr21 + subicc gr21,#'9',gr0,icc0 + bls icc0,#2,2f + addi gr21,#'A'-'0'-10,gr21 +2: + stbi gr21,@(gr31,#0*8) + subicc gr20,#1,gr20,icc0 + bhi icc0,#2,1b + + setlos #'\r',gr21 + stbi gr21,@(gr31,#0*8) + + setlos #'\n',gr21 + stbi gr21,@(gr31,#0*8) + +3: ldubi @(gr31,#5*8),gr21 + andi gr21,#0x60,gr21 + subicc gr21,#0x60,gr21,icc0 + bne icc0,#0,3b + bralr + + .size __head_write_to_ttyS0, .-__head_write_to_ttyS0 +#endif diff --git a/arch/frv/kernel/head.inc b/arch/frv/kernel/head.inc new file mode 100644 index 000000000..bff66628b --- /dev/null +++ b/arch/frv/kernel/head.inc @@ -0,0 +1,50 @@ +/* head.inc: head common definitions -*- asm -*- + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + + +#if defined(CONFIG_MB93090_MB00) +#define LED_ADDR (0x21200000+4) + +.macro LEDS val + sethi.p %hi(0xFFC00030),gr3 + setlo %lo(0xFFC00030),gr3 + lduh @(gr3,gr0),gr3 + andicc gr3,#0x100,gr0,icc0 + bne icc0,0,999f + + setlos #~\val,gr3 + st gr3,@(gr30,gr0) + membar + dcf @(gr30,gr0) + 999: +.endm + +#elif defined(CONFIG_MB93093_PDK) +#define LED_ADDR (0x20000023) + +.macro LEDS val + setlos #\val,gr3 + stb gr3,@(gr30,gr0) + membar +.endm + +#else +#define LED_ADDR 0 + +.macro LEDS val +.endm +#endif + +#ifdef CONFIG_MMU +__sdram_base = 0x00000000 /* base address to which SDRAM relocated */ +#else +__sdram_base = __page_offset /* base address to which SDRAM relocated */ +#endif diff --git a/arch/frv/kernel/irq-mb93091.c b/arch/frv/kernel/irq-mb93091.c new file mode 100644 index 000000000..091b2839b --- /dev/null +++ b/arch/frv/kernel/irq-mb93091.c @@ -0,0 +1,157 @@ +/* irq-mb93091.c: MB93091 FPGA interrupt handling + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/irc-regs.h> + +#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR)) + +#define __get_IMR() ({ __reg16(0xffc00004); }) +#define __set_IMR(M) do { __reg16(0xffc00004) = (M); wmb(); } while(0) +#define __get_IFR() ({ __reg16(0xffc0000c); }) +#define __clr_IFR(M) do { __reg16(0xffc0000c) = ~(M); wmb(); } while(0) + + +/* + * on-motherboard FPGA PIC operations + */ +static void frv_fpga_mask(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr |= 1 << (d->irq - IRQ_BASE_FPGA); + + __set_IMR(imr); +} + +static void frv_fpga_ack(struct irq_data *d) +{ + __clr_IFR(1 << (d->irq - IRQ_BASE_FPGA)); +} + +static void frv_fpga_mask_ack(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr |= 1 << (d->irq - IRQ_BASE_FPGA); + __set_IMR(imr); + + __clr_IFR(1 << (d->irq - IRQ_BASE_FPGA)); +} + +static void frv_fpga_unmask(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr &= ~(1 << (d->irq - IRQ_BASE_FPGA)); + + __set_IMR(imr); +} + +static struct irq_chip frv_fpga_pic = { + .name = "mb93091", + .irq_ack = frv_fpga_ack, + .irq_mask = frv_fpga_mask, + .irq_mask_ack = frv_fpga_mask_ack, + .irq_unmask = frv_fpga_unmask, +}; + +/* + * FPGA PIC interrupt handler + */ +static irqreturn_t fpga_interrupt(int irq, void *_mask) +{ + uint16_t imr, mask = (unsigned long) _mask; + + imr = __get_IMR(); + mask = mask & ~imr & __get_IFR(); + + /* poll all the triggered IRQs */ + while (mask) { + int irq; + + asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask)); + irq = 31 - irq; + mask &= ~(1 << irq); + + generic_handle_irq(IRQ_BASE_FPGA + irq); + } + + return IRQ_HANDLED; +} + +/* + * define an interrupt action for each FPGA PIC output + * - use dev_id to indicate the FPGA PIC input to output mappings + */ +static struct irqaction fpga_irq[4] = { + [0] = { + .handler = fpga_interrupt, + .flags = IRQF_SHARED, + .name = "fpga.0", + .dev_id = (void *) 0x0028UL, + }, + [1] = { + .handler = fpga_interrupt, + .flags = IRQF_SHARED, + .name = "fpga.1", + .dev_id = (void *) 0x0050UL, + }, + [2] = { + .handler = fpga_interrupt, + .flags = IRQF_SHARED, + .name = "fpga.2", + .dev_id = (void *) 0x1c00UL, + }, + [3] = { + .handler = fpga_interrupt, + .flags = IRQF_SHARED, + .name = "fpga.3", + .dev_id = (void *) 0x6386UL, + } +}; + +/* + * initialise the motherboard FPGA's PIC + */ +void __init fpga_init(void) +{ + int irq; + + /* all PIC inputs are all set to be low-level driven, apart from the + * NMI button (15) which is fixed at falling-edge + */ + __set_IMR(0x7ffe); + __clr_IFR(0x0000); + + for (irq = IRQ_BASE_FPGA + 1; irq <= IRQ_BASE_FPGA + 14; irq++) + irq_set_chip_and_handler(irq, &frv_fpga_pic, handle_level_irq); + + irq_set_chip_and_handler(IRQ_FPGA_NMI, &frv_fpga_pic, handle_edge_irq); + + /* the FPGA drives the first four external IRQ inputs on the CPU PIC */ + setup_irq(IRQ_CPU_EXTERNAL0, &fpga_irq[0]); + setup_irq(IRQ_CPU_EXTERNAL1, &fpga_irq[1]); + setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[2]); + setup_irq(IRQ_CPU_EXTERNAL3, &fpga_irq[3]); +} diff --git a/arch/frv/kernel/irq-mb93093.c b/arch/frv/kernel/irq-mb93093.c new file mode 100644 index 000000000..1f3015cf8 --- /dev/null +++ b/arch/frv/kernel/irq-mb93093.c @@ -0,0 +1,129 @@ +/* irq-mb93093.c: MB93093 FPGA interrupt handling + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/irc-regs.h> + +#define __reg16(ADDR) (*(volatile unsigned short *)(__region_CS2 + (ADDR))) + +#define __get_IMR() ({ __reg16(0x0a); }) +#define __set_IMR(M) do { __reg16(0x0a) = (M); wmb(); } while(0) +#define __get_IFR() ({ __reg16(0x02); }) +#define __clr_IFR(M) do { __reg16(0x02) = ~(M); wmb(); } while(0) + +/* + * off-CPU FPGA PIC operations + */ +static void frv_fpga_mask(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr |= 1 << (d->irq - IRQ_BASE_FPGA); + __set_IMR(imr); +} + +static void frv_fpga_ack(struct irq_data *d) +{ + __clr_IFR(1 << (d->irq - IRQ_BASE_FPGA)); +} + +static void frv_fpga_mask_ack(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr |= 1 << (d->irq - IRQ_BASE_FPGA); + __set_IMR(imr); + + __clr_IFR(1 << (d->irq - IRQ_BASE_FPGA)); +} + +static void frv_fpga_unmask(struct irq_data *d) +{ + uint16_t imr = __get_IMR(); + + imr &= ~(1 << (d->irq - IRQ_BASE_FPGA)); + + __set_IMR(imr); +} + +static struct irq_chip frv_fpga_pic = { + .name = "mb93093", + .irq_ack = frv_fpga_ack, + .irq_mask = frv_fpga_mask, + .irq_mask_ack = frv_fpga_mask_ack, + .irq_unmask = frv_fpga_unmask, +}; + +/* + * FPGA PIC interrupt handler + */ +static irqreturn_t fpga_interrupt(int irq, void *_mask) +{ + uint16_t imr, mask = (unsigned long) _mask; + + imr = __get_IMR(); + mask = mask & ~imr & __get_IFR(); + + /* poll all the triggered IRQs */ + while (mask) { + int irq; + + asm("scan %1,gr0,%0" : "=r"(irq) : "r"(mask)); + irq = 31 - irq; + mask &= ~(1 << irq); + + generic_handle_irq(IRQ_BASE_FPGA + irq); + } + + return IRQ_HANDLED; +} + +/* + * define an interrupt action for each FPGA PIC output + * - use dev_id to indicate the FPGA PIC input to output mappings + */ +static struct irqaction fpga_irq[1] = { + [0] = { + .handler = fpga_interrupt, + .name = "fpga.0", + .dev_id = (void *) 0x0700UL, + } +}; + +/* + * initialise the motherboard FPGA's PIC + */ +void __init fpga_init(void) +{ + int irq; + + /* all PIC inputs are all set to be edge triggered */ + __set_IMR(0x0700); + __clr_IFR(0x0000); + + for (irq = IRQ_BASE_FPGA + 8; irq <= IRQ_BASE_FPGA + 10; irq++) + irq_set_chip_and_handler(irq, &frv_fpga_pic, handle_edge_irq); + + /* the FPGA drives external IRQ input #2 on the CPU PIC */ + setup_irq(IRQ_CPU_EXTERNAL2, &fpga_irq[0]); +} diff --git a/arch/frv/kernel/irq-mb93493.c b/arch/frv/kernel/irq-mb93493.c new file mode 100644 index 000000000..8ca5aa4ff --- /dev/null +++ b/arch/frv/kernel/irq-mb93493.c @@ -0,0 +1,147 @@ +/* irq-mb93493.c: MB93493 companion chip interrupt handler + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/bitops.h> + +#include <asm/io.h> +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/irc-regs.h> +#include <asm/mb93493-irqs.h> +#include <asm/mb93493-regs.h> + +#define IRQ_ROUTE_ONE(X) (X##_ROUTE << (X - IRQ_BASE_MB93493)) + +#define IRQ_ROUTING \ + (IRQ_ROUTE_ONE(IRQ_MB93493_VDC) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_VCC) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_OUT) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_I2C_0) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_I2C_1) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_USB) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_LOCAL_BUS) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_PCMCIA) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_GPIO) | \ + IRQ_ROUTE_ONE(IRQ_MB93493_AUDIO_IN)) + +/* + * daughter board PIC operations + * - there is no way to ACK interrupts in the MB93493 chip + */ +static void frv_mb93493_mask(struct irq_data *d) +{ + uint32_t iqsr; + volatile void *piqsr; + + if (IRQ_ROUTING & (1 << (d->irq - IRQ_BASE_MB93493))) + piqsr = __addr_MB93493_IQSR(1); + else + piqsr = __addr_MB93493_IQSR(0); + + iqsr = readl(piqsr); + iqsr &= ~(1 << (d->irq - IRQ_BASE_MB93493 + 16)); + writel(iqsr, piqsr); +} + +static void frv_mb93493_ack(struct irq_data *d) +{ +} + +static void frv_mb93493_unmask(struct irq_data *d) +{ + uint32_t iqsr; + volatile void *piqsr; + + if (IRQ_ROUTING & (1 << (d->irq - IRQ_BASE_MB93493))) + piqsr = __addr_MB93493_IQSR(1); + else + piqsr = __addr_MB93493_IQSR(0); + + iqsr = readl(piqsr); + iqsr |= 1 << (d->irq - IRQ_BASE_MB93493 + 16); + writel(iqsr, piqsr); +} + +static struct irq_chip frv_mb93493_pic = { + .name = "mb93093", + .irq_ack = frv_mb93493_ack, + .irq_mask = frv_mb93493_mask, + .irq_mask_ack = frv_mb93493_mask, + .irq_unmask = frv_mb93493_unmask, +}; + +/* + * MB93493 PIC interrupt handler + */ +static irqreturn_t mb93493_interrupt(int irq, void *_piqsr) +{ + volatile void *piqsr = _piqsr; + uint32_t iqsr; + + iqsr = readl(piqsr); + iqsr = iqsr & (iqsr >> 16) & 0xffff; + + /* poll all the triggered IRQs */ + while (iqsr) { + int irq; + + asm("scan %1,gr0,%0" : "=r"(irq) : "r"(iqsr)); + irq = 31 - irq; + iqsr &= ~(1 << irq); + + generic_handle_irq(IRQ_BASE_MB93493 + irq); + } + + return IRQ_HANDLED; +} + +/* + * define an interrupt action for each MB93493 PIC output + * - use dev_id to indicate the MB93493 PIC input to output mappings + */ +static struct irqaction mb93493_irq[2] = { + [0] = { + .handler = mb93493_interrupt, + .flags = IRQF_SHARED, + .name = "mb93493.0", + .dev_id = (void *) __addr_MB93493_IQSR(0), + }, + [1] = { + .handler = mb93493_interrupt, + .flags = IRQF_SHARED, + .name = "mb93493.1", + .dev_id = (void *) __addr_MB93493_IQSR(1), + } +}; + +/* + * initialise the motherboard MB93493's PIC + */ +void __init mb93493_init(void) +{ + int irq; + + for (irq = IRQ_BASE_MB93493 + 0; irq <= IRQ_BASE_MB93493 + 10; irq++) + irq_set_chip_and_handler(irq, &frv_mb93493_pic, + handle_edge_irq); + + /* the MB93493 drives external IRQ inputs on the CPU PIC */ + setup_irq(IRQ_CPU_MB93493_0, &mb93493_irq[0]); + setup_irq(IRQ_CPU_MB93493_1, &mb93493_irq[1]); +} diff --git a/arch/frv/kernel/irq.c b/arch/frv/kernel/irq.c new file mode 100644 index 000000000..2239346fa --- /dev/null +++ b/arch/frv/kernel/irq.c @@ -0,0 +1,159 @@ +/* irq.c: FRV IRQ handling + * + * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/random.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/irq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/module.h> +#include <linux/bitops.h> + +#include <linux/atomic.h> +#include <asm/io.h> +#include <asm/smp.h> +#include <asm/uaccess.h> +#include <asm/pgalloc.h> +#include <asm/delay.h> +#include <asm/irq.h> +#include <asm/irc-regs.h> +#include <asm/gdb-stub.h> + +#define set_IRR(N,A,B,C,D) __set_IRR(N, (A << 28) | (B << 24) | (C << 20) | (D << 16)) + +extern void __init fpga_init(void); +#ifdef CONFIG_FUJITSU_MB93493 +extern void __init mb93493_init(void); +#endif + +#define __reg16(ADDR) (*(volatile unsigned short *)(ADDR)) + +atomic_t irq_err_count; + +int arch_show_interrupts(struct seq_file *p, int prec) +{ + seq_printf(p, "%*s: ", prec, "ERR"); + seq_printf(p, "%10u\n", atomic_read(&irq_err_count)); + return 0; +} + +/* + * on-CPU PIC operations + */ +static void frv_cpupic_ack(struct irq_data *d) +{ + __clr_RC(d->irq); + __clr_IRL(); +} + +static void frv_cpupic_mask(struct irq_data *d) +{ + __set_MASK(d->irq); +} + +static void frv_cpupic_mask_ack(struct irq_data *d) +{ + __set_MASK(d->irq); + __clr_RC(d->irq); + __clr_IRL(); +} + +static void frv_cpupic_unmask(struct irq_data *d) +{ + __clr_MASK(d->irq); +} + +static struct irq_chip frv_cpu_pic = { + .name = "cpu", + .irq_ack = frv_cpupic_ack, + .irq_mask = frv_cpupic_mask, + .irq_mask_ack = frv_cpupic_mask_ack, + .irq_unmask = frv_cpupic_unmask, +}; + +/* + * handles all normal device IRQs + * - registers are referred to by the __frame variable (GR28) + * - IRQ distribution is complicated in this arch because of the many PICs, the + * way they work and the way they cascade + */ +asmlinkage void do_IRQ(void) +{ + irq_enter(); + generic_handle_irq(__get_IRL()); + irq_exit(); +} + +/* + * handles all NMIs when not co-opted by the debugger + * - registers are referred to by the __frame variable (GR28) + */ +asmlinkage void do_NMI(void) +{ +} + +/* + * initialise the interrupt system + */ +void __init init_IRQ(void) +{ + int level; + + for (level = 1; level <= 14; level++) + irq_set_chip_and_handler(level, &frv_cpu_pic, + handle_level_irq); + + irq_set_handler(IRQ_CPU_TIMER0, handle_edge_irq); + + /* set the trigger levels for internal interrupt sources + * - timers all falling-edge + * - ERR0 is rising-edge + * - all others are high-level + */ + __set_IITMR(0, 0x003f0000); /* DMA0-3, TIMER0-2 */ + __set_IITMR(1, 0x20000000); /* ERR0-1, UART0-1, DMA4-7 */ + + /* route internal interrupts */ + set_IRR(4, IRQ_DMA3_LEVEL, IRQ_DMA2_LEVEL, IRQ_DMA1_LEVEL, + IRQ_DMA0_LEVEL); + set_IRR(5, 0, IRQ_TIMER2_LEVEL, IRQ_TIMER1_LEVEL, IRQ_TIMER0_LEVEL); + set_IRR(6, IRQ_GDBSTUB_LEVEL, IRQ_GDBSTUB_LEVEL, + IRQ_UART1_LEVEL, IRQ_UART0_LEVEL); + set_IRR(7, IRQ_DMA7_LEVEL, IRQ_DMA6_LEVEL, IRQ_DMA5_LEVEL, + IRQ_DMA4_LEVEL); + + /* route external interrupts */ + set_IRR(2, IRQ_XIRQ7_LEVEL, IRQ_XIRQ6_LEVEL, IRQ_XIRQ5_LEVEL, + IRQ_XIRQ4_LEVEL); + set_IRR(3, IRQ_XIRQ3_LEVEL, IRQ_XIRQ2_LEVEL, IRQ_XIRQ1_LEVEL, + IRQ_XIRQ0_LEVEL); + +#if defined(CONFIG_MB93091_VDK) + __set_TM1(0x55550000); /* XIRQ7-0 all active low */ +#elif defined(CONFIG_MB93093_PDK) + __set_TM1(0x15550000); /* XIRQ7 active high, 6-0 all active low */ +#else +#error dont know external IRQ trigger levels for this setup +#endif + + fpga_init(); +#ifdef CONFIG_FUJITSU_MB93493 + mb93493_init(); +#endif +} diff --git a/arch/frv/kernel/local.h b/arch/frv/kernel/local.h new file mode 100644 index 000000000..76606d13b --- /dev/null +++ b/arch/frv/kernel/local.h @@ -0,0 +1,59 @@ +/* local.h: local definitions + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#ifndef _FRV_LOCAL_H +#define _FRV_LOCAL_H + +#include <asm/sections.h> + +#ifndef __ASSEMBLY__ + +/* dma.c */ +extern unsigned long frv_dma_inprogress; + +extern void frv_dma_pause_all(void); +extern void frv_dma_resume_all(void); + +/* sleep.S */ +extern asmlinkage void frv_cpu_suspend(unsigned long); +extern asmlinkage void frv_cpu_core_sleep(void); + +/* setup.c */ +extern unsigned long __nongprelbss pdm_suspend_mode; +extern void determine_clocks(int verbose); +extern int __nongprelbss clock_p0_current; +extern int __nongprelbss clock_cm_current; +extern int __nongprelbss clock_cmode_current; + +#ifdef CONFIG_PM +extern int __nongprelbss clock_cmodes_permitted; +extern unsigned long __nongprelbss clock_bits_settable; +#define CLOCK_BIT_CM 0x0000000f +#define CLOCK_BIT_CM_H 0x00000001 /* CLKC.CM can be set to 0 */ +#define CLOCK_BIT_CM_M 0x00000002 /* CLKC.CM can be set to 1 */ +#define CLOCK_BIT_CM_L 0x00000004 /* CLKC.CM can be set to 2 */ +#define CLOCK_BIT_P0 0x00000010 /* CLKC.P0 can be changed */ +#define CLOCK_BIT_CMODE 0x00000020 /* CLKC.CMODE can be changed */ + +extern void (*__power_switch_wake_setup)(void); +extern int (*__power_switch_wake_check)(void); +extern void (*__power_switch_wake_cleanup)(void); +#endif + +/* time.c */ +extern void time_divisor_init(void); + +/* cmode.S */ +extern asmlinkage void frv_change_cmode(int); + + +#endif /* __ASSEMBLY__ */ +#endif /* _FRV_LOCAL_H */ diff --git a/arch/frv/kernel/local64.h b/arch/frv/kernel/local64.h new file mode 100644 index 000000000..36c93b5cc --- /dev/null +++ b/arch/frv/kernel/local64.h @@ -0,0 +1 @@ +#include <asm-generic/local64.h> diff --git a/arch/frv/kernel/module.c b/arch/frv/kernel/module.c new file mode 100644 index 000000000..9d9835f1f --- /dev/null +++ b/arch/frv/kernel/module.c @@ -0,0 +1,27 @@ +/* module.c: FRV specific module loading bits + * + * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/i386/kernel/module.c, Copyright (C) 2001 Rusty Russell. + * + * 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. + */ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt...) +#endif + +/* TODO: At least one of apply_relocate or apply_relocate_add must be + * implemented in order to get working module support. + */ diff --git a/arch/frv/kernel/pm-mb93093.c b/arch/frv/kernel/pm-mb93093.c new file mode 100644 index 000000000..eaa7b582e --- /dev/null +++ b/arch/frv/kernel/pm-mb93093.c @@ -0,0 +1,65 @@ +/* + * FR-V MB93093 Power Management Routines + * + * Copyright (c) 2004 Red Hat, Inc. + * + * Written by: msalter@redhat.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + */ + +#include <linux/init.h> +#include <linux/pm.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/sysctl.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +#include <asm/mb86943a.h> + +#include "local.h" + +static unsigned long imask; +/* + * Setup interrupt masks, etc to enable wakeup by power switch + */ +static void mb93093_power_switch_setup(void) +{ + /* mask all but FPGA interrupt sources. */ + imask = *(volatile unsigned long *)0xfeff9820; + *(volatile unsigned long *)0xfeff9820 = ~(1 << (IRQ_XIRQ2_LEVEL + 16)) & 0xfffe0000; +} + +/* + * Cleanup interrupt masks, etc after wakeup by power switch + */ +static void mb93093_power_switch_cleanup(void) +{ + *(volatile unsigned long *)0xfeff9820 = imask; +} + +/* + * Return non-zero if wakeup irq was caused by power switch + */ +static int mb93093_power_switch_check(void) +{ + return 1; +} + +/* + * Initialize power interface + */ +static int __init mb93093_pm_init(void) +{ + __power_switch_wake_setup = mb93093_power_switch_setup; + __power_switch_wake_check = mb93093_power_switch_check; + __power_switch_wake_cleanup = mb93093_power_switch_cleanup; + return 0; +} + +__initcall(mb93093_pm_init); + diff --git a/arch/frv/kernel/pm.c b/arch/frv/kernel/pm.c new file mode 100644 index 000000000..ac767d94a --- /dev/null +++ b/arch/frv/kernel/pm.c @@ -0,0 +1,352 @@ +/* + * FR-V Power Management Routines + * + * Copyright (c) 2004 Red Hat, Inc. + * + * Based on SA1100 version: + * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/sysctl.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +#include <asm/mb86943a.h> + +#include "local.h" + +/* + * Debug macros + */ +#define DEBUG + +int pm_do_suspend(void) +{ + local_irq_disable(); + + __set_LEDS(0xb1); + + /* go zzz */ + frv_cpu_suspend(pdm_suspend_mode); + + __set_LEDS(0xb2); + + local_irq_enable(); + + return 0; +} + +static unsigned long __irq_mask; + +/* + * Setup interrupt masks, etc to enable wakeup by power switch + */ +static void __default_power_switch_setup(void) +{ + /* default is to mask all interrupt sources. */ + __irq_mask = *(unsigned long *)0xfeff9820; + *(unsigned long *)0xfeff9820 = 0xfffe0000; +} + +/* + * Cleanup interrupt masks, etc after wakeup by power switch + */ +static void __default_power_switch_cleanup(void) +{ + *(unsigned long *)0xfeff9820 = __irq_mask; +} + +/* + * Return non-zero if wakeup irq was caused by power switch + */ +static int __default_power_switch_check(void) +{ + return 1; +} + +void (*__power_switch_wake_setup)(void) = __default_power_switch_setup; +int (*__power_switch_wake_check)(void) = __default_power_switch_check; +void (*__power_switch_wake_cleanup)(void) = __default_power_switch_cleanup; + +int pm_do_bus_sleep(void) +{ + local_irq_disable(); + + /* + * Here is where we need some platform-dependent setup + * of the interrupt state so that appropriate wakeup + * sources are allowed and all others are masked. + */ + __power_switch_wake_setup(); + + __set_LEDS(0xa1); + + /* go zzz + * + * This is in a loop in case power switch shares an irq with other + * devices. The wake_check() tells us if we need to finish waking + * or go back to sleep. + */ + do { + frv_cpu_suspend(HSR0_PDM_BUS_SLEEP); + } while (__power_switch_wake_check && !__power_switch_wake_check()); + + __set_LEDS(0xa2); + + /* + * Here is where we need some platform-dependent restore + * of the interrupt state prior to being called. + */ + __power_switch_wake_cleanup(); + + local_irq_enable(); + + return 0; +} + +unsigned long sleep_phys_sp(void *sp) +{ + return virt_to_phys(sp); +} + +#ifdef CONFIG_SYSCTL +/* + * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 + * when all the PM interfaces exist nicely. + */ +#define CTL_PM_SUSPEND 1 +#define CTL_PM_CMODE 2 +#define CTL_PM_P0 4 +#define CTL_PM_CM 5 + +static int user_atoi(char __user *ubuf, size_t len) +{ + char buf[16]; + unsigned long ret; + + if (len > 15) + return -EINVAL; + + if (copy_from_user(buf, ubuf, len)) + return -EFAULT; + + buf[len] = 0; + ret = simple_strtoul(buf, NULL, 0); + if (ret > INT_MAX) + return -ERANGE; + return ret; +} + +/* + * Send us to sleep. + */ +static int sysctl_pm_do_suspend(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *fpos) +{ + int mode; + + if (*lenp <= 0) + return -EIO; + + mode = user_atoi(buffer, *lenp); + switch (mode) { + case 1: + return pm_do_suspend(); + + case 5: + return pm_do_bus_sleep(); + + default: + return -EINVAL; + } +} + +static int try_set_cmode(int new_cmode) +{ + if (new_cmode > 15) + return -EINVAL; + if (!(clock_cmodes_permitted & (1<<new_cmode))) + return -EINVAL; + + /* now change cmode */ + local_irq_disable(); + frv_dma_pause_all(); + + frv_change_cmode(new_cmode); + + determine_clocks(0); + time_divisor_init(); + +#ifdef DEBUG + determine_clocks(1); +#endif + frv_dma_resume_all(); + local_irq_enable(); + + return 0; +} + + +static int cmode_procctl(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *fpos) +{ + int new_cmode; + + if (!write) + return proc_dointvec(ctl, write, buffer, lenp, fpos); + + new_cmode = user_atoi(buffer, *lenp); + + return try_set_cmode(new_cmode)?:*lenp; +} + +static int try_set_p0(int new_p0) +{ + unsigned long flags, clkc; + + if (new_p0 < 0 || new_p0 > 1) + return -EINVAL; + + local_irq_save(flags); + __set_PSR(flags & ~PSR_ET); + + frv_dma_pause_all(); + + clkc = __get_CLKC(); + if (new_p0) + clkc |= CLKC_P0; + else + clkc &= ~CLKC_P0; + __set_CLKC(clkc); + + determine_clocks(0); + time_divisor_init(); + +#ifdef DEBUG + determine_clocks(1); +#endif + frv_dma_resume_all(); + local_irq_restore(flags); + return 0; +} + +static int try_set_cm(int new_cm) +{ + unsigned long flags, clkc; + + if (new_cm < 0 || new_cm > 1) + return -EINVAL; + + local_irq_save(flags); + __set_PSR(flags & ~PSR_ET); + + frv_dma_pause_all(); + + clkc = __get_CLKC(); + clkc &= ~CLKC_CM; + clkc |= new_cm; + __set_CLKC(clkc); + + determine_clocks(0); + time_divisor_init(); + +#if 1 //def DEBUG + determine_clocks(1); +#endif + + frv_dma_resume_all(); + local_irq_restore(flags); + return 0; +} + +static int p0_procctl(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *fpos) +{ + int new_p0; + + if (!write) + return proc_dointvec(ctl, write, buffer, lenp, fpos); + + new_p0 = user_atoi(buffer, *lenp); + + return try_set_p0(new_p0)?:*lenp; +} + +static int cm_procctl(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, loff_t *fpos) +{ + int new_cm; + + if (!write) + return proc_dointvec(ctl, write, buffer, lenp, fpos); + + new_cm = user_atoi(buffer, *lenp); + + return try_set_cm(new_cm)?:*lenp; +} + +static struct ctl_table pm_table[] = +{ + { + .procname = "suspend", + .data = NULL, + .maxlen = 0, + .mode = 0200, + .proc_handler = sysctl_pm_do_suspend, + }, + { + .procname = "cmode", + .data = &clock_cmode_current, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = cmode_procctl, + }, + { + .procname = "p0", + .data = &clock_p0_current, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = p0_procctl, + }, + { + .procname = "cm", + .data = &clock_cm_current, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = cm_procctl, + }, + { } +}; + +static struct ctl_table pm_dir_table[] = +{ + { + .procname = "pm", + .mode = 0555, + .child = pm_table, + }, + { } +}; + +/* + * Initialize power interface + */ +static int __init pm_init(void) +{ + register_sysctl_table(pm_dir_table); + return 0; +} + +__initcall(pm_init); + +#endif diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c new file mode 100644 index 000000000..5d40aeb77 --- /dev/null +++ b/arch/frv/kernel/process.c @@ -0,0 +1,281 @@ +/* process.c: FRV specific parts of process handling + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/process.c + * + * 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. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/elf.h> +#include <linux/reboot.h> +#include <linux/interrupt.h> +#include <linux/pagemap.h> +#include <linux/rcupdate.h> + +#include <asm/asm-offsets.h> +#include <asm/uaccess.h> +#include <asm/setup.h> +#include <asm/pgtable.h> +#include <asm/tlb.h> +#include <asm/gdb-stub.h> +#include <asm/mb-regs.h> + +#include "local.h" + +asmlinkage void ret_from_fork(void); +asmlinkage void ret_from_kernel_thread(void); + +#include <asm/pgalloc.h> + +void (*pm_power_off)(void); +EXPORT_SYMBOL(pm_power_off); + +static void core_sleep_idle(void) +{ +#ifdef LED_DEBUG_SLEEP + /* Show that we're sleeping... */ + __set_LEDS(0x55aa); +#endif + frv_cpu_core_sleep(); +#ifdef LED_DEBUG_SLEEP + /* ... and that we woke up */ + __set_LEDS(0); +#endif + mb(); +} + +void arch_cpu_idle(void) +{ + if (!frv_dma_inprogress) + core_sleep_idle(); + else + local_irq_enable(); +} + +void machine_restart(char * __unused) +{ + unsigned long reset_addr; +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif + + if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551) + reset_addr = 0xfefff500; + else + reset_addr = 0xfeff0500; + + /* Software reset. */ + asm volatile(" dcef @(gr0,gr0),1 ! membar !" + " sti %1,@(%0,0) !" + " nop ! nop ! nop ! nop ! nop ! " + " nop ! nop ! nop ! nop ! nop ! " + " nop ! nop ! nop ! nop ! nop ! " + " nop ! nop ! nop ! nop ! nop ! " + : : "r" (reset_addr), "r" (1) ); + + for (;;) + ; +} + +void machine_halt(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif + + for (;;); +} + +void machine_power_off(void) +{ +#ifdef CONFIG_GDBSTUB + gdbstub_exit(0); +#endif + + for (;;); +} + +void flush_thread(void) +{ + /* nothing */ +} + +inline unsigned long user_stack(const struct pt_regs *regs) +{ + while (regs->next_frame) + regs = regs->next_frame; + return user_mode(regs) ? regs->sp : 0; +} + +/* + * set up the kernel stack and exception frames for a new process + */ +int copy_thread(unsigned long clone_flags, + unsigned long usp, unsigned long arg, + struct task_struct *p) +{ + struct pt_regs *childregs; + + childregs = (struct pt_regs *) + (task_stack_page(p) + THREAD_SIZE - FRV_FRAME0_SIZE); + + /* set up the userspace frame (the only place that the USP is stored) */ + *childregs = *current_pt_regs(); + + p->thread.frame = childregs; + p->thread.curr = p; + p->thread.sp = (unsigned long) childregs; + p->thread.fp = 0; + p->thread.lr = 0; + p->thread.frame0 = childregs; + + if (unlikely(p->flags & PF_KTHREAD)) { + childregs->gr9 = usp; /* function */ + childregs->gr8 = arg; + p->thread.pc = (unsigned long) ret_from_kernel_thread; + save_user_regs(p->thread.user); + return 0; + } + if (usp) + childregs->sp = usp; + childregs->next_frame = NULL; + + p->thread.pc = (unsigned long) ret_from_fork; + + /* the new TLS pointer is passed in as arg #5 to sys_clone() */ + if (clone_flags & CLONE_SETTLS) + childregs->gr29 = childregs->gr12; + + save_user_regs(p->thread.user); + + return 0; +} /* end copy_thread() */ + +unsigned long get_wchan(struct task_struct *p) +{ + struct pt_regs *regs0; + unsigned long fp, pc; + unsigned long stack_limit; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + stack_limit = (unsigned long) (p + 1); + fp = p->thread.fp; + regs0 = p->thread.frame0; + + do { + if (fp < stack_limit || fp >= (unsigned long) regs0 || fp & 3) + return 0; + + pc = ((unsigned long *) fp)[2]; + + /* FIXME: This depends on the order of these functions. */ + if (!in_sched_functions(pc)) + return pc; + + fp = *(unsigned long *) fp; + } while (count++ < 16); + + return 0; +} + +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + /* Check whether the thread is blocked in resume() */ + if (in_sched_functions(tsk->thread.pc)) + return ((unsigned long *)tsk->thread.fp)[2]; + else + return tsk->thread.pc; +} + +int elf_check_arch(const struct elf32_hdr *hdr) +{ + unsigned long hsr0 = __get_HSR(0); + unsigned long psr = __get_PSR(); + + if (hdr->e_machine != EM_FRV) + return 0; + + switch (hdr->e_flags & EF_FRV_GPR_MASK) { + case EF_FRV_GPR64: + if ((hsr0 & HSR0_GRN) == HSR0_GRN_32) + return 0; + case EF_FRV_GPR32: + case 0: + break; + default: + return 0; + } + + switch (hdr->e_flags & EF_FRV_FPR_MASK) { + case EF_FRV_FPR64: + if ((hsr0 & HSR0_FRN) == HSR0_FRN_32) + return 0; + case EF_FRV_FPR32: + case EF_FRV_FPR_NONE: + case 0: + break; + default: + return 0; + } + + if ((hdr->e_flags & EF_FRV_MULADD) == EF_FRV_MULADD) + if (PSR_IMPLE(psr) != PSR_IMPLE_FR405 && + PSR_IMPLE(psr) != PSR_IMPLE_FR451) + return 0; + + switch (hdr->e_flags & EF_FRV_CPU_MASK) { + case EF_FRV_CPU_GENERIC: + break; + case EF_FRV_CPU_FR300: + case EF_FRV_CPU_SIMPLE: + case EF_FRV_CPU_TOMCAT: + default: + return 0; + case EF_FRV_CPU_FR400: + if (PSR_IMPLE(psr) != PSR_IMPLE_FR401 && + PSR_IMPLE(psr) != PSR_IMPLE_FR405 && + PSR_IMPLE(psr) != PSR_IMPLE_FR451 && + PSR_IMPLE(psr) != PSR_IMPLE_FR551) + return 0; + break; + case EF_FRV_CPU_FR450: + if (PSR_IMPLE(psr) != PSR_IMPLE_FR451) + return 0; + break; + case EF_FRV_CPU_FR500: + if (PSR_IMPLE(psr) != PSR_IMPLE_FR501) + return 0; + break; + case EF_FRV_CPU_FR550: + if (PSR_IMPLE(psr) != PSR_IMPLE_FR551) + return 0; + break; + } + + return 1; +} + +int dump_fpu(struct pt_regs *regs, elf_fpregset_t *fpregs) +{ + memcpy(fpregs, + ¤t->thread.user->f, + sizeof(current->thread.user->f)); + return 1; +} diff --git a/arch/frv/kernel/ptrace.c b/arch/frv/kernel/ptrace.c new file mode 100644 index 000000000..3987ff88d --- /dev/null +++ b/arch/frv/kernel/ptrace.c @@ -0,0 +1,377 @@ +/* ptrace.c: FRV specific parts of process tracing + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/ptrace.c + * + * 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. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/security.h> +#include <linux/signal.h> +#include <linux/regset.h> +#include <linux/elf.h> +#include <linux/tracehook.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/processor.h> +#include <asm/unistd.h> + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* + * retrieve the contents of FRV userspace general registers + */ +static int genregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct user_int_regs *iregs = &target->thread.user->i; + int ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + iregs, 0, sizeof(*iregs)); + if (ret < 0) + return ret; + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(*iregs), -1); +} + +/* + * update the contents of the FRV userspace general registers + */ +static int genregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct user_int_regs *iregs = &target->thread.user->i; + unsigned int offs_gr0, offs_gr1; + int ret; + + /* not allowed to set PSR or __status */ + if (pos < offsetof(struct user_int_regs, psr) + sizeof(long) && + pos + count > offsetof(struct user_int_regs, psr)) + return -EIO; + + if (pos < offsetof(struct user_int_regs, __status) + sizeof(long) && + pos + count > offsetof(struct user_int_regs, __status)) + return -EIO; + + /* set the control regs */ + offs_gr0 = offsetof(struct user_int_regs, gr[0]); + offs_gr1 = offsetof(struct user_int_regs, gr[1]); + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + iregs, 0, offs_gr0); + if (ret < 0) + return ret; + + /* skip GR0/TBR */ + ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + offs_gr0, offs_gr1); + if (ret < 0) + return ret; + + /* set the general regs */ + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + &iregs->gr[1], offs_gr1, sizeof(*iregs)); + if (ret < 0) + return ret; + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + sizeof(*iregs), -1); +} + +/* + * retrieve the contents of FRV userspace FP/Media registers + */ +static int fpmregs_get(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + void *kbuf, void __user *ubuf) +{ + const struct user_fpmedia_regs *fpregs = &target->thread.user->f; + int ret; + + ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, + fpregs, 0, sizeof(*fpregs)); + if (ret < 0) + return ret; + + return user_regset_copyout_zero(&pos, &count, &kbuf, &ubuf, + sizeof(*fpregs), -1); +} + +/* + * update the contents of the FRV userspace FP/Media registers + */ +static int fpmregs_set(struct task_struct *target, + const struct user_regset *regset, + unsigned int pos, unsigned int count, + const void *kbuf, const void __user *ubuf) +{ + struct user_fpmedia_regs *fpregs = &target->thread.user->f; + int ret; + + ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, + fpregs, 0, sizeof(*fpregs)); + if (ret < 0) + return ret; + + return user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, + sizeof(*fpregs), -1); +} + +/* + * determine if the FP/Media registers have actually been used + */ +static int fpmregs_active(struct task_struct *target, + const struct user_regset *regset) +{ + return tsk_used_math(target) ? regset->n : 0; +} + +/* + * Define the register sets available on the FRV under Linux + */ +enum frv_regset { + REGSET_GENERAL, + REGSET_FPMEDIA, +}; + +static const struct user_regset frv_regsets[] = { + /* + * General register format is: + * PSR, ISR, CCR, CCCR, LR, LCR, PC, (STATUS), SYSCALLNO, ORIG_G8 + * GNER0-1, IACC0, TBR, GR1-63 + */ + [REGSET_GENERAL] = { + .core_note_type = NT_PRSTATUS, + .n = ELF_NGREG, + .size = sizeof(long), + .align = sizeof(long), + .get = genregs_get, + .set = genregs_set, + }, + /* + * FPU/Media register format is: + * FR0-63, FNER0-1, MSR0-1, ACC0-7, ACCG0-8, FSR + */ + [REGSET_FPMEDIA] = { + .core_note_type = NT_PRFPREG, + .n = sizeof(struct user_fpmedia_regs) / sizeof(long), + .size = sizeof(long), + .align = sizeof(long), + .get = fpmregs_get, + .set = fpmregs_set, + .active = fpmregs_active, + }, +}; + +static const struct user_regset_view user_frv_native_view = { + .name = "frv", + .e_machine = EM_FRV, + .regsets = frv_regsets, + .n = ARRAY_SIZE(frv_regsets), +}; + +const struct user_regset_view *task_user_regset_view(struct task_struct *task) +{ + return &user_frv_native_view; +} + +/* + * Get contents of register REGNO in task TASK. + */ +static inline long get_reg(struct task_struct *task, int regno) +{ + struct user_context *user = task->thread.user; + + if (regno < 0 || regno >= PT__END) + return 0; + + return ((unsigned long *) user)[regno]; +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + struct user_context *user = task->thread.user; + + if (regno < 0 || regno >= PT__END) + return -EIO; + + switch (regno) { + case PT_GR(0): + return 0; + case PT_PSR: + case PT__STATUS: + return -EIO; + default: + ((unsigned long *) user)[regno] = data; + return 0; + } +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Control h/w single stepping + */ +void user_enable_single_step(struct task_struct *child) +{ + child->thread.frame0->__status |= REG__STATUS_STEP; +} + +void user_disable_single_step(struct task_struct *child) +{ + child->thread.frame0->__status &= ~REG__STATUS_STEP; +} + +void ptrace_disable(struct task_struct *child) +{ + user_disable_single_step(child); +} + +long arch_ptrace(struct task_struct *child, long request, + unsigned long addr, unsigned long data) +{ + unsigned long tmp; + int ret; + int regno = addr >> 2; + unsigned long __user *datap = (unsigned long __user *) data; + + switch (request) { + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + tmp = 0; + ret = -EIO; + if (addr & 3) + break; + + ret = 0; + switch (regno) { + case 0 ... PT__END - 1: + tmp = get_reg(child, regno); + break; + + case PT__END + 0: + tmp = child->mm->end_code - child->mm->start_code; + break; + + case PT__END + 1: + tmp = child->mm->end_data - child->mm->start_data; + break; + + case PT__END + 2: + tmp = child->mm->start_stack - child->mm->start_brk; + break; + + case PT__END + 3: + tmp = child->mm->start_code; + break; + + case PT__END + 4: + tmp = child->mm->start_stack; + break; + + default: + ret = -EIO; + break; + } + + if (ret == 0) + ret = put_user(tmp, datap); + break; + } + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if (addr & 3) + break; + + switch (regno) { + case 0 ... PT__END - 1: + ret = put_reg(child, regno, data); + break; + } + break; + + case PTRACE_GETREGS: /* Get all integer regs from the child. */ + return copy_regset_to_user(child, &user_frv_native_view, + REGSET_GENERAL, + 0, sizeof(child->thread.user->i), + datap); + + case PTRACE_SETREGS: /* Set all integer regs in the child. */ + return copy_regset_from_user(child, &user_frv_native_view, + REGSET_GENERAL, + 0, sizeof(child->thread.user->i), + datap); + + case PTRACE_GETFPREGS: /* Get the child FP/Media state. */ + return copy_regset_to_user(child, &user_frv_native_view, + REGSET_FPMEDIA, + 0, sizeof(child->thread.user->f), + datap); + + case PTRACE_SETFPREGS: /* Set the child FP/Media state. */ + return copy_regset_from_user(child, &user_frv_native_view, + REGSET_FPMEDIA, + 0, sizeof(child->thread.user->f), + datap); + + default: + ret = ptrace_request(child, request, addr, data); + break; + } + return ret; +} + +/* + * handle tracing of system call entry + * - return the revised system call number or ULONG_MAX to cause ENOSYS + */ +asmlinkage unsigned long syscall_trace_entry(void) +{ + __frame->__status |= REG__STATUS_SYSC_ENTRY; + if (tracehook_report_syscall_entry(__frame)) { + /* tracing decided this syscall should not happen, so + * We'll return a bogus call number to get an ENOSYS + * error, but leave the original number in + * __frame->syscallno + */ + return ULONG_MAX; + } + + return __frame->syscallno; +} + +/* + * handle tracing of system call exit + */ +asmlinkage void syscall_trace_exit(void) +{ + __frame->__status |= REG__STATUS_SYSC_EXIT; + tracehook_report_syscall_exit(__frame, 0); +} diff --git a/arch/frv/kernel/setup.c b/arch/frv/kernel/setup.c new file mode 100644 index 000000000..9f4a9a607 --- /dev/null +++ b/arch/frv/kernel/setup.c @@ -0,0 +1,1178 @@ +/* setup.c: FRV specific setup + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/setup.c + * + * 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. + */ + +#include <generated/utsrelease.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/genhd.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/major.h> +#include <linux/bootmem.h> +#include <linux/highmem.h> +#include <linux/seq_file.h> +#include <linux/serial.h> +#include <linux/serial_core.h> +#include <linux/serial_reg.h> +#include <linux/serial_8250.h> + +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/sections.h> +#include <asm/pgalloc.h> +#include <asm/busctl-regs.h> +#include <asm/serial-regs.h> +#include <asm/timer-regs.h> +#include <asm/irc-regs.h> +#include <asm/spr-regs.h> +#include <asm/mb-regs.h> +#include <asm/mb93493-regs.h> +#include <asm/gdb-stub.h> +#include <asm/io.h> + +#ifdef CONFIG_BLK_DEV_INITRD +#include <asm/pgtable.h> +#endif + +#include "local.h" + +#ifdef CONFIG_MB93090_MB00 +static void __init mb93090_display(void); +#endif +#ifdef CONFIG_MMU +static void __init setup_linux_memory(void); +#else +static void __init setup_uclinux_memory(void); +#endif + +#ifdef CONFIG_MB93090_MB00 +static char __initdata mb93090_banner[] = "FJ/RH FR-V Linux"; +static char __initdata mb93090_version[] = UTS_RELEASE; + +int __nongprelbss mb93090_mb00_detected; +#endif + +const char __frv_unknown_system[] = "unknown"; +const char __frv_mb93091_cb10[] = "mb93091-cb10"; +const char __frv_mb93091_cb11[] = "mb93091-cb11"; +const char __frv_mb93091_cb30[] = "mb93091-cb30"; +const char __frv_mb93091_cb41[] = "mb93091-cb41"; +const char __frv_mb93091_cb60[] = "mb93091-cb60"; +const char __frv_mb93091_cb70[] = "mb93091-cb70"; +const char __frv_mb93091_cb451[] = "mb93091-cb451"; +const char __frv_mb93090_mb00[] = "mb93090-mb00"; + +const char __frv_mb93493[] = "mb93493"; + +const char __frv_mb93093[] = "mb93093"; + +static const char *__nongprelbss cpu_series; +static const char *__nongprelbss cpu_core; +static const char *__nongprelbss cpu_silicon; +static const char *__nongprelbss cpu_mmu; +static const char *__nongprelbss cpu_system; +static const char *__nongprelbss cpu_board1; +static const char *__nongprelbss cpu_board2; + +static unsigned long __nongprelbss cpu_psr_all; +static unsigned long __nongprelbss cpu_hsr0_all; + +unsigned long __nongprelbss pdm_suspend_mode; + +unsigned long __nongprelbss rom_length; +unsigned long __nongprelbss memory_start; +unsigned long __nongprelbss memory_end; + +unsigned long __nongprelbss dma_coherent_mem_start; +unsigned long __nongprelbss dma_coherent_mem_end; + +unsigned long __initdata __sdram_old_base; +unsigned long __initdata num_mappedpages; + +char __initdata command_line[COMMAND_LINE_SIZE]; +char __initdata redboot_command_line[COMMAND_LINE_SIZE]; + +#ifdef CONFIG_PM +#define __pminit +#define __pminitdata +#define __pminitconst +#else +#define __pminit __init +#define __pminitdata __initdata +#define __pminitconst __initconst +#endif + +struct clock_cmode { + uint8_t xbus, sdram, corebus, core, dsu; +}; + +#define _frac(N,D) ((N)<<4 | (D)) +#define _x0_16 _frac(1,6) +#define _x0_25 _frac(1,4) +#define _x0_33 _frac(1,3) +#define _x0_375 _frac(3,8) +#define _x0_5 _frac(1,2) +#define _x0_66 _frac(2,3) +#define _x0_75 _frac(3,4) +#define _x1 _frac(1,1) +#define _x1_5 _frac(3,2) +#define _x2 _frac(2,1) +#define _x3 _frac(3,1) +#define _x4 _frac(4,1) +#define _x4_5 _frac(9,2) +#define _x6 _frac(6,1) +#define _x8 _frac(8,1) +#define _x9 _frac(9,1) + +int __nongprelbss clock_p0_current; +int __nongprelbss clock_cm_current; +int __nongprelbss clock_cmode_current; +#ifdef CONFIG_PM +int __nongprelbss clock_cmodes_permitted; +unsigned long __nongprelbss clock_bits_settable; +#endif + +static struct clock_cmode __pminitdata undef_clock_cmode = { _x1, _x1, _x1, _x1, _x1 }; + +static struct clock_cmode __pminitdata clock_cmodes_fr401_fr403[16] = { + [4] = { _x1, _x1, _x2, _x2, _x0_25 }, + [5] = { _x1, _x2, _x4, _x4, _x0_5 }, + [8] = { _x1, _x1, _x1, _x2, _x0_25 }, + [9] = { _x1, _x2, _x2, _x4, _x0_5 }, + [11] = { _x1, _x4, _x4, _x8, _x1 }, + [12] = { _x1, _x1, _x2, _x4, _x0_5 }, + [13] = { _x1, _x2, _x4, _x8, _x1 }, +}; + +static struct clock_cmode __pminitdata clock_cmodes_fr405[16] = { + [0] = { _x1, _x1, _x1, _x1, _x0_5 }, + [1] = { _x1, _x1, _x1, _x3, _x0_25 }, + [2] = { _x1, _x1, _x2, _x6, _x0_5 }, + [3] = { _x1, _x2, _x2, _x6, _x0_5 }, + [4] = { _x1, _x1, _x2, _x2, _x0_16 }, + [8] = { _x1, _x1, _x1, _x2, _x0_16 }, + [9] = { _x1, _x2, _x2, _x4, _x0_33 }, + [12] = { _x1, _x1, _x2, _x4, _x0_33 }, + [14] = { _x1, _x3, _x3, _x9, _x0_75 }, + [15] = { _x1, _x1_5, _x1_5, _x4_5, _x0_375 }, + +#define CLOCK_CMODES_PERMITTED_FR405 0xd31f +}; + +static struct clock_cmode __pminitdata clock_cmodes_fr555[16] = { + [0] = { _x1, _x2, _x2, _x4, _x0_33 }, + [1] = { _x1, _x3, _x3, _x6, _x0_5 }, + [2] = { _x1, _x2, _x4, _x8, _x0_66 }, + [3] = { _x1, _x1_5, _x3, _x6, _x0_5 }, + [4] = { _x1, _x3, _x3, _x9, _x0_75 }, + [5] = { _x1, _x2, _x2, _x6, _x0_5 }, + [6] = { _x1, _x1_5, _x1_5, _x4_5, _x0_375 }, +}; + +static const struct clock_cmode __pminitconst *clock_cmodes; +static int __pminitdata clock_doubled; + +static struct uart_port __pminitdata __frv_uart0 = { + .uartclk = 0, + .membase = (char *) UART0_BASE, + .irq = IRQ_CPU_UART0, + .regshift = 3, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, +}; + +static struct uart_port __pminitdata __frv_uart1 = { + .uartclk = 0, + .membase = (char *) UART1_BASE, + .irq = IRQ_CPU_UART1, + .regshift = 3, + .iotype = UPIO_MEM, + .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, +}; + +#if 0 +static void __init printk_xampr(unsigned long ampr, unsigned long amlr, char i_d, int n) +{ + unsigned long phys, virt, cxn, size; + +#ifdef CONFIG_MMU + virt = amlr & 0xffffc000; + cxn = amlr & 0x3fff; +#else + virt = ampr & 0xffffc000; + cxn = 0; +#endif + phys = ampr & xAMPRx_PPFN; + size = 1 << (((ampr & xAMPRx_SS) >> 4) + 17); + + printk("%cAMPR%d: va %08lx-%08lx [pa %08lx] %c%c%c%c [cxn:%04lx]\n", + i_d, n, + virt, virt + size - 1, + phys, + ampr & xAMPRx_S ? 'S' : '-', + ampr & xAMPRx_C ? 'C' : '-', + ampr & DAMPRx_WP ? 'W' : '-', + ampr & xAMPRx_V ? 'V' : '-', + cxn + ); +} +#endif + +/*****************************************************************************/ +/* + * dump the memory map + */ +static void __init dump_memory_map(void) +{ + +#if 0 + /* dump the protection map */ + printk_xampr(__get_IAMPR(0), __get_IAMLR(0), 'I', 0); + printk_xampr(__get_IAMPR(1), __get_IAMLR(1), 'I', 1); + printk_xampr(__get_IAMPR(2), __get_IAMLR(2), 'I', 2); + printk_xampr(__get_IAMPR(3), __get_IAMLR(3), 'I', 3); + printk_xampr(__get_IAMPR(4), __get_IAMLR(4), 'I', 4); + printk_xampr(__get_IAMPR(5), __get_IAMLR(5), 'I', 5); + printk_xampr(__get_IAMPR(6), __get_IAMLR(6), 'I', 6); + printk_xampr(__get_IAMPR(7), __get_IAMLR(7), 'I', 7); + printk_xampr(__get_IAMPR(8), __get_IAMLR(8), 'I', 8); + printk_xampr(__get_IAMPR(9), __get_IAMLR(9), 'i', 9); + printk_xampr(__get_IAMPR(10), __get_IAMLR(10), 'I', 10); + printk_xampr(__get_IAMPR(11), __get_IAMLR(11), 'I', 11); + printk_xampr(__get_IAMPR(12), __get_IAMLR(12), 'I', 12); + printk_xampr(__get_IAMPR(13), __get_IAMLR(13), 'I', 13); + printk_xampr(__get_IAMPR(14), __get_IAMLR(14), 'I', 14); + printk_xampr(__get_IAMPR(15), __get_IAMLR(15), 'I', 15); + + printk_xampr(__get_DAMPR(0), __get_DAMLR(0), 'D', 0); + printk_xampr(__get_DAMPR(1), __get_DAMLR(1), 'D', 1); + printk_xampr(__get_DAMPR(2), __get_DAMLR(2), 'D', 2); + printk_xampr(__get_DAMPR(3), __get_DAMLR(3), 'D', 3); + printk_xampr(__get_DAMPR(4), __get_DAMLR(4), 'D', 4); + printk_xampr(__get_DAMPR(5), __get_DAMLR(5), 'D', 5); + printk_xampr(__get_DAMPR(6), __get_DAMLR(6), 'D', 6); + printk_xampr(__get_DAMPR(7), __get_DAMLR(7), 'D', 7); + printk_xampr(__get_DAMPR(8), __get_DAMLR(8), 'D', 8); + printk_xampr(__get_DAMPR(9), __get_DAMLR(9), 'D', 9); + printk_xampr(__get_DAMPR(10), __get_DAMLR(10), 'D', 10); + printk_xampr(__get_DAMPR(11), __get_DAMLR(11), 'D', 11); + printk_xampr(__get_DAMPR(12), __get_DAMLR(12), 'D', 12); + printk_xampr(__get_DAMPR(13), __get_DAMLR(13), 'D', 13); + printk_xampr(__get_DAMPR(14), __get_DAMLR(14), 'D', 14); + printk_xampr(__get_DAMPR(15), __get_DAMLR(15), 'D', 15); +#endif + +#if 0 + /* dump the bus controller registers */ + printk("LGCR: %08lx\n", __get_LGCR()); + printk("Master: %08lx-%08lx CR=%08lx\n", + __get_LEMBR(), __get_LEMBR() + __get_LEMAM(), + __get_LMAICR()); + + int loop; + for (loop = 1; loop <= 7; loop++) { + unsigned long lcr = __get_LCR(loop), lsbr = __get_LSBR(loop); + printk("CS#%d: %08lx-%08lx %c%c%c%c%c%c%c%c%c\n", + loop, + lsbr, lsbr + __get_LSAM(loop), + lcr & 0x80000000 ? 'r' : '-', + lcr & 0x40000000 ? 'w' : '-', + lcr & 0x08000000 ? 'b' : '-', + lcr & 0x04000000 ? 'B' : '-', + lcr & 0x02000000 ? 'C' : '-', + lcr & 0x01000000 ? 'D' : '-', + lcr & 0x00800000 ? 'W' : '-', + lcr & 0x00400000 ? 'R' : '-', + (lcr & 0x00030000) == 0x00000000 ? '4' : + (lcr & 0x00030000) == 0x00010000 ? '2' : + (lcr & 0x00030000) == 0x00020000 ? '1' : + '-' + ); + } +#endif + +#if 0 + printk("\n"); +#endif +} /* end dump_memory_map() */ + +/*****************************************************************************/ +/* + * attempt to detect a VDK motherboard and DAV daughter board on an MB93091 system + */ +#ifdef CONFIG_MB93091_VDK +static void __init detect_mb93091(void) +{ +#ifdef CONFIG_MB93090_MB00 + /* Detect CB70 without motherboard */ + if (!(cpu_system == __frv_mb93091_cb70 && ((*(unsigned short *)0xffc00030) & 0x100))) { + cpu_board1 = __frv_mb93090_mb00; + mb93090_mb00_detected = 1; + } +#endif + +#ifdef CONFIG_FUJITSU_MB93493 + cpu_board2 = __frv_mb93493; +#endif + +} /* end detect_mb93091() */ +#endif + +/*****************************************************************************/ +/* + * determine the CPU type and set appropriate parameters + * + * Family Series CPU Core Silicon Imple Vers + * ---------------------------------------------------------- + * FR-V --+-> FR400 --+-> FR401 --+-> MB93401 02 00 [1] + * | | | + * | | +-> MB93401/A 02 01 + * | | | + * | | +-> MB93403 02 02 + * | | + * | +-> FR405 ----> MB93405 04 00 + * | + * +-> FR450 ----> FR451 ----> MB93451 05 00 + * | + * +-> FR500 ----> FR501 --+-> MB93501 01 01 [2] + * | | + * | +-> MB93501/A 01 02 + * | + * +-> FR550 --+-> FR551 ----> MB93555 03 01 + * + * [1] The MB93401 is an obsolete CPU replaced by the MB93401A + * [2] The MB93501 is an obsolete CPU replaced by the MB93501A + * + * Imple is PSR(Processor Status Register)[31:28]. + * Vers is PSR(Processor Status Register)[27:24]. + * + * A "Silicon" consists of CPU core and some on-chip peripherals. + */ +static void __init determine_cpu(void) +{ + unsigned long hsr0 = __get_HSR(0); + unsigned long psr = __get_PSR(); + + /* work out what selectable services the CPU supports */ + __set_PSR(psr | PSR_EM | PSR_EF | PSR_CM | PSR_NEM); + cpu_psr_all = __get_PSR(); + __set_PSR(psr); + + __set_HSR(0, hsr0 | HSR0_GRLE | HSR0_GRHE | HSR0_FRLE | HSR0_FRHE); + cpu_hsr0_all = __get_HSR(0); + __set_HSR(0, hsr0); + + /* derive other service specs from the CPU type */ + cpu_series = "unknown"; + cpu_core = "unknown"; + cpu_silicon = "unknown"; + cpu_mmu = "Prot"; + cpu_system = __frv_unknown_system; + clock_cmodes = NULL; + clock_doubled = 0; +#ifdef CONFIG_PM + clock_bits_settable = CLOCK_BIT_CM_H | CLOCK_BIT_CM_M | CLOCK_BIT_P0; +#endif + + switch (PSR_IMPLE(psr)) { + case PSR_IMPLE_FR401: + cpu_series = "fr400"; + cpu_core = "fr401"; + pdm_suspend_mode = HSR0_PDM_PLL_RUN; + + switch (PSR_VERSION(psr)) { + case PSR_VERSION_FR401_MB93401: + cpu_silicon = "mb93401"; + cpu_system = __frv_mb93091_cb10; + clock_cmodes = clock_cmodes_fr401_fr403; + clock_doubled = 1; + break; + case PSR_VERSION_FR401_MB93401A: + cpu_silicon = "mb93401/A"; + cpu_system = __frv_mb93091_cb11; + clock_cmodes = clock_cmodes_fr401_fr403; + break; + case PSR_VERSION_FR401_MB93403: + cpu_silicon = "mb93403"; +#ifndef CONFIG_MB93093_PDK + cpu_system = __frv_mb93091_cb30; +#else + cpu_system = __frv_mb93093; +#endif + clock_cmodes = clock_cmodes_fr401_fr403; + break; + default: + break; + } + break; + + case PSR_IMPLE_FR405: + cpu_series = "fr400"; + cpu_core = "fr405"; + pdm_suspend_mode = HSR0_PDM_PLL_STOP; + + switch (PSR_VERSION(psr)) { + case PSR_VERSION_FR405_MB93405: + cpu_silicon = "mb93405"; + cpu_system = __frv_mb93091_cb60; + clock_cmodes = clock_cmodes_fr405; +#ifdef CONFIG_PM + clock_bits_settable |= CLOCK_BIT_CMODE; + clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405; +#endif + + /* the FPGA on the CB70 has extra registers + * - it has 0x0046 in the VDK_ID FPGA register at 0x1a0, which is + * how we tell the difference between it and a CB60 + */ + if (*(volatile unsigned short *) 0xffc001a0 == 0x0046) + cpu_system = __frv_mb93091_cb70; + break; + default: + break; + } + break; + + case PSR_IMPLE_FR451: + cpu_series = "fr450"; + cpu_core = "fr451"; + pdm_suspend_mode = HSR0_PDM_PLL_STOP; +#ifdef CONFIG_PM + clock_bits_settable |= CLOCK_BIT_CMODE; + clock_cmodes_permitted = CLOCK_CMODES_PERMITTED_FR405; +#endif + switch (PSR_VERSION(psr)) { + case PSR_VERSION_FR451_MB93451: + cpu_silicon = "mb93451"; + cpu_mmu = "Prot, SAT, xSAT, DAT"; + cpu_system = __frv_mb93091_cb451; + clock_cmodes = clock_cmodes_fr405; + break; + default: + break; + } + break; + + case PSR_IMPLE_FR501: + cpu_series = "fr500"; + cpu_core = "fr501"; + pdm_suspend_mode = HSR0_PDM_PLL_STOP; + + switch (PSR_VERSION(psr)) { + case PSR_VERSION_FR501_MB93501: cpu_silicon = "mb93501"; break; + case PSR_VERSION_FR501_MB93501A: cpu_silicon = "mb93501/A"; break; + default: + break; + } + break; + + case PSR_IMPLE_FR551: + cpu_series = "fr550"; + cpu_core = "fr551"; + pdm_suspend_mode = HSR0_PDM_PLL_RUN; + + switch (PSR_VERSION(psr)) { + case PSR_VERSION_FR551_MB93555: + cpu_silicon = "mb93555"; + cpu_mmu = "Prot, SAT"; + cpu_system = __frv_mb93091_cb41; + clock_cmodes = clock_cmodes_fr555; + clock_doubled = 1; + break; + default: + break; + } + break; + + default: + break; + } + + printk("- Series:%s CPU:%s Silicon:%s\n", + cpu_series, cpu_core, cpu_silicon); + +#ifdef CONFIG_MB93091_VDK + detect_mb93091(); +#endif + +#if defined(CONFIG_MB93093_PDK) && defined(CONFIG_FUJITSU_MB93493) + cpu_board2 = __frv_mb93493; +#endif + +} /* end determine_cpu() */ + +/*****************************************************************************/ +/* + * calculate the bus clock speed + */ +void __pminit determine_clocks(int verbose) +{ + const struct clock_cmode *mode, *tmode; + unsigned long clkc, psr, quot; + + clkc = __get_CLKC(); + psr = __get_PSR(); + + clock_p0_current = !!(clkc & CLKC_P0); + clock_cm_current = clkc & CLKC_CM; + clock_cmode_current = (clkc & CLKC_CMODE) >> CLKC_CMODE_s; + + if (verbose) + printk("psr=%08lx hsr0=%08lx clkc=%08lx\n", psr, __get_HSR(0), clkc); + + /* the CB70 has some alternative ways of setting the clock speed through switches accessed + * through the FPGA. */ + if (cpu_system == __frv_mb93091_cb70) { + unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL; + + if (clkswr & 0x1000) + __clkin_clock_speed_HZ = 60000000UL; + else + __clkin_clock_speed_HZ = + ((clkswr >> 8) & 0xf) * 10000000 + + ((clkswr >> 4) & 0xf) * 1000000 + + ((clkswr ) & 0xf) * 100000; + } + /* the FR451 is currently fixed at 24MHz */ + else if (cpu_system == __frv_mb93091_cb451) { + //__clkin_clock_speed_HZ = 24000000UL; // CB451-FPGA + unsigned short clkswr = *(volatile unsigned short *) 0xffc00104UL & 0x1fffUL; + + if (clkswr & 0x1000) + __clkin_clock_speed_HZ = 60000000UL; + else + __clkin_clock_speed_HZ = + ((clkswr >> 8) & 0xf) * 10000000 + + ((clkswr >> 4) & 0xf) * 1000000 + + ((clkswr ) & 0xf) * 100000; + } + /* otherwise determine the clockspeed from VDK or other registers */ + else { + __clkin_clock_speed_HZ = __get_CLKIN(); + } + + /* look up the appropriate clock relationships table entry */ + mode = &undef_clock_cmode; + if (clock_cmodes) { + tmode = &clock_cmodes[(clkc & CLKC_CMODE) >> CLKC_CMODE_s]; + if (tmode->xbus) + mode = tmode; + } + +#define CLOCK(SRC,RATIO) ((SRC) * (((RATIO) >> 4) & 0x0f) / ((RATIO) & 0x0f)) + + if (clock_doubled) + __clkin_clock_speed_HZ <<= 1; + + __ext_bus_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->xbus); + __sdram_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->sdram); + __dsu_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->dsu); + + switch (clkc & CLKC_CM) { + case 0: /* High */ + __core_bus_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->corebus); + __core_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->core); + break; + case 1: /* Medium */ + __core_bus_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->sdram); + __core_clock_speed_HZ = CLOCK(__clkin_clock_speed_HZ, mode->sdram); + break; + case 2: /* Low; not supported */ + case 3: /* UNDEF */ + printk("Unsupported CLKC CM %ld\n", clkc & CLKC_CM); + panic("Bye"); + } + + __res_bus_clock_speed_HZ = __ext_bus_clock_speed_HZ; + if (clkc & CLKC_P0) + __res_bus_clock_speed_HZ >>= 1; + + if (verbose) { + printk("CLKIN: %lu.%3.3luMHz\n", + __clkin_clock_speed_HZ / 1000000, + (__clkin_clock_speed_HZ / 1000) % 1000); + + printk("CLKS:" + " ext=%luMHz res=%luMHz sdram=%luMHz cbus=%luMHz core=%luMHz dsu=%luMHz\n", + __ext_bus_clock_speed_HZ / 1000000, + __res_bus_clock_speed_HZ / 1000000, + __sdram_clock_speed_HZ / 1000000, + __core_bus_clock_speed_HZ / 1000000, + __core_clock_speed_HZ / 1000000, + __dsu_clock_speed_HZ / 1000000 + ); + } + + /* calculate the number of __delay() loop iterations per sec (2 insn loop) */ + __delay_loops_MHz = __core_clock_speed_HZ / (1000000 * 2); + + /* set the serial prescaler */ + __serial_clock_speed_HZ = __res_bus_clock_speed_HZ; + quot = 1; + while (__serial_clock_speed_HZ / quot / 16 / 65536 > 3000) + quot += 1; + + /* double the divisor if P0 is clear, so that if/when P0 is set, it's still achievable + * - we have to be careful - dividing too much can mean we can't get 115200 baud + */ + if (__serial_clock_speed_HZ > 32000000 && !(clkc & CLKC_P0)) + quot <<= 1; + + __serial_clock_speed_HZ /= quot; + __frv_uart0.uartclk = __serial_clock_speed_HZ; + __frv_uart1.uartclk = __serial_clock_speed_HZ; + + if (verbose) + printk(" uart=%luMHz\n", __serial_clock_speed_HZ / 1000000 * quot); + + while (!(__get_UART0_LSR() & UART_LSR_TEMT)) + continue; + + while (!(__get_UART1_LSR() & UART_LSR_TEMT)) + continue; + + __set_UCPVR(quot); + __set_UCPSR(0); +} /* end determine_clocks() */ + +/*****************************************************************************/ +/* + * reserve some DMA consistent memory + */ +#ifdef CONFIG_RESERVE_DMA_COHERENT +static void __init reserve_dma_coherent(void) +{ + unsigned long ampr; + + /* find the first non-kernel memory tile and steal it */ +#define __steal_AMPR(r) \ + if (__get_DAMPR(r) & xAMPRx_V) { \ + ampr = __get_DAMPR(r); \ + __set_DAMPR(r, ampr | xAMPRx_S | xAMPRx_C); \ + __set_IAMPR(r, 0); \ + goto found; \ + } + + __steal_AMPR(1); + __steal_AMPR(2); + __steal_AMPR(3); + __steal_AMPR(4); + __steal_AMPR(5); + __steal_AMPR(6); + + if (PSR_IMPLE(__get_PSR()) == PSR_IMPLE_FR551) { + __steal_AMPR(7); + __steal_AMPR(8); + __steal_AMPR(9); + __steal_AMPR(10); + __steal_AMPR(11); + __steal_AMPR(12); + __steal_AMPR(13); + __steal_AMPR(14); + } + + /* unable to grant any DMA consistent memory */ + printk("No DMA consistent memory reserved\n"); + return; + + found: + dma_coherent_mem_start = ampr & xAMPRx_PPFN; + ampr &= xAMPRx_SS; + ampr >>= 4; + ampr = 1 << (ampr - 3 + 20); + dma_coherent_mem_end = dma_coherent_mem_start + ampr; + + printk("DMA consistent memory reserved %lx-%lx\n", + dma_coherent_mem_start, dma_coherent_mem_end); + +} /* end reserve_dma_coherent() */ +#endif + +/*****************************************************************************/ +/* + * calibrate the delay loop + */ +void calibrate_delay(void) +{ + loops_per_jiffy = __delay_loops_MHz * (1000000 / HZ); + + printk("Calibrating delay loop... %lu.%02lu BogoMIPS\n", + loops_per_jiffy / (500000 / HZ), + (loops_per_jiffy / (5000 / HZ)) % 100); + +} /* end calibrate_delay() */ + +/*****************************************************************************/ +/* + * look through the command line for some things we need to know immediately + */ +static void __init parse_cmdline_early(char *cmdline) +{ + if (!cmdline) + return; + + while (*cmdline) { + if (*cmdline == ' ') + cmdline++; + + /* "mem=XXX[kKmM]" sets SDRAM size to <mem>, overriding the value we worked + * out from the SDRAM controller mask register + */ + if (!strncmp(cmdline, "mem=", 4)) { + unsigned long long mem_size; + + mem_size = memparse(cmdline + 4, &cmdline); + memory_end = memory_start + mem_size; + } + + while (*cmdline && *cmdline != ' ') + cmdline++; + } + +} /* end parse_cmdline_early() */ + +/*****************************************************************************/ +/* + * + */ +void __init setup_arch(char **cmdline_p) +{ +#ifdef CONFIG_MMU + printk("Linux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n"); +#else + printk("uClinux FR-V port done by Red Hat Inc <dhowells@redhat.com>\n"); +#endif + + memcpy(boot_command_line, redboot_command_line, COMMAND_LINE_SIZE); + + determine_cpu(); + determine_clocks(1); + + /* For printk-directly-beats-on-serial-hardware hack */ + console_set_baud(115200); +#ifdef CONFIG_GDBSTUB + gdbstub_set_baud(115200); +#endif + +#ifdef CONFIG_RESERVE_DMA_COHERENT + reserve_dma_coherent(); +#endif + dump_memory_map(); + +#ifdef CONFIG_MB93090_MB00 + if (mb93090_mb00_detected) + mb93090_display(); +#endif + + /* register those serial ports that are available */ +#ifdef CONFIG_FRV_ONCPU_SERIAL +#ifndef CONFIG_GDBSTUB_UART0 + __reg(UART0_BASE + UART_IER * 8) = 0; + early_serial_setup(&__frv_uart0); +#endif +#ifndef CONFIG_GDBSTUB_UART1 + __reg(UART1_BASE + UART_IER * 8) = 0; + early_serial_setup(&__frv_uart1); +#endif +#endif + + /* deal with the command line - RedBoot may have passed one to the kernel */ + memcpy(command_line, boot_command_line, sizeof(command_line)); + *cmdline_p = &command_line[0]; + parse_cmdline_early(command_line); + + /* set up the memory description + * - by now the stack is part of the init task */ + printk("Memory %08lx-%08lx\n", memory_start, memory_end); + + BUG_ON(memory_start == memory_end); + + init_mm.start_code = (unsigned long) _stext; + init_mm.end_code = (unsigned long) _etext; + init_mm.end_data = (unsigned long) _edata; +#if 0 /* DAVIDM - don't set brk just incase someone decides to use it */ + init_mm.brk = (unsigned long) &_end; +#else + init_mm.brk = (unsigned long) 0; +#endif + +#ifdef DEBUG + printk("KERNEL -> TEXT=0x%p-0x%p DATA=0x%p-0x%p BSS=0x%p-0x%p\n", + _stext, _etext, _sdata, _edata, __bss_start, __bss_stop); +#endif + +#ifdef CONFIG_VT +#if defined(CONFIG_VGA_CONSOLE) + conswitchp = &vga_con; +#elif defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif +#endif + +#ifdef CONFIG_MMU + setup_linux_memory(); +#else + setup_uclinux_memory(); +#endif + + /* get kmalloc into gear */ + paging_init(); + + /* init DMA */ + frv_dma_init(); +#ifdef DEBUG + printk("Done setup_arch\n"); +#endif + + /* start the decrement timer running */ +// asm volatile("movgs %0,timerd" :: "r"(10000000)); +// __set_HSR(0, __get_HSR(0) | HSR0_ETMD); + +} /* end setup_arch() */ + +#if 0 +/*****************************************************************************/ +/* + * + */ +static int setup_arch_serial(void) +{ + /* register those serial ports that are available */ +#ifndef CONFIG_GDBSTUB_UART0 + early_serial_setup(&__frv_uart0); +#endif +#ifndef CONFIG_GDBSTUB_UART1 + early_serial_setup(&__frv_uart1); +#endif + + return 0; +} /* end setup_arch_serial() */ + +late_initcall(setup_arch_serial); +#endif + +/*****************************************************************************/ +/* + * set up the memory map for normal MMU linux + */ +#ifdef CONFIG_MMU +static void __init setup_linux_memory(void) +{ + unsigned long bootmap_size, low_top_pfn, kstart, kend, high_mem; + unsigned long physpages; + + kstart = (unsigned long) &__kernel_image_start - PAGE_OFFSET; + kend = (unsigned long) &__kernel_image_end - PAGE_OFFSET; + + kstart = kstart & PAGE_MASK; + kend = (kend + PAGE_SIZE - 1) & PAGE_MASK; + + /* give all the memory to the bootmap allocator, tell it to put the + * boot mem_map immediately following the kernel image + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), + kend >> PAGE_SHIFT, /* map addr */ + memory_start >> PAGE_SHIFT, /* start of RAM */ + memory_end >> PAGE_SHIFT /* end of RAM */ + ); + + /* pass the memory that the kernel can immediately use over to the bootmem allocator */ + max_mapnr = physpages = (memory_end - memory_start) >> PAGE_SHIFT; + low_top_pfn = (KERNEL_LOWMEM_END - KERNEL_LOWMEM_START) >> PAGE_SHIFT; + high_mem = 0; + + if (physpages > low_top_pfn) { +#ifdef CONFIG_HIGHMEM + high_mem = physpages - low_top_pfn; +#else + max_mapnr = physpages = low_top_pfn; +#endif + } + else { + low_top_pfn = physpages; + } + + min_low_pfn = memory_start >> PAGE_SHIFT; + max_low_pfn = low_top_pfn; + max_pfn = memory_end >> PAGE_SHIFT; + + num_mappedpages = low_top_pfn; + + printk(KERN_NOTICE "%ldMB LOWMEM available.\n", low_top_pfn >> (20 - PAGE_SHIFT)); + + free_bootmem(memory_start, low_top_pfn << PAGE_SHIFT); + +#ifdef CONFIG_HIGHMEM + if (high_mem) + printk(KERN_NOTICE "%ldMB HIGHMEM available.\n", high_mem >> (20 - PAGE_SHIFT)); +#endif + + /* take back the memory occupied by the kernel image and the bootmem alloc map */ + reserve_bootmem(kstart, kend - kstart + bootmap_size, + BOOTMEM_DEFAULT); + + /* reserve the memory occupied by the initial ramdisk */ +#ifdef CONFIG_BLK_DEV_INITRD + if (LOADER_TYPE && INITRD_START) { + if (INITRD_START + INITRD_SIZE <= (low_top_pfn << PAGE_SHIFT)) { + reserve_bootmem(INITRD_START, INITRD_SIZE, + BOOTMEM_DEFAULT); + initrd_start = INITRD_START + PAGE_OFFSET; + initrd_end = initrd_start + INITRD_SIZE; + } + else { + printk(KERN_ERR + "initrd extends beyond end of memory (0x%08lx > 0x%08lx)\n" + "disabling initrd\n", + INITRD_START + INITRD_SIZE, + low_top_pfn << PAGE_SHIFT); + initrd_start = 0; + } + } +#endif + +} /* end setup_linux_memory() */ +#endif + +/*****************************************************************************/ +/* + * set up the memory map for uClinux + */ +#ifndef CONFIG_MMU +static void __init setup_uclinux_memory(void) +{ +#ifdef CONFIG_PROTECT_KERNEL + unsigned long dampr; +#endif + unsigned long kend; + int bootmap_size; + + kend = (unsigned long) &__kernel_image_end; + kend = (kend + PAGE_SIZE - 1) & PAGE_MASK; + + /* give all the memory to the bootmap allocator, tell it to put the + * boot mem_map immediately following the kernel image + */ + bootmap_size = init_bootmem_node(NODE_DATA(0), + kend >> PAGE_SHIFT, /* map addr */ + memory_start >> PAGE_SHIFT, /* start of RAM */ + memory_end >> PAGE_SHIFT /* end of RAM */ + ); + + /* free all the usable memory */ + free_bootmem(memory_start, memory_end - memory_start); + + high_memory = (void *) (memory_end & PAGE_MASK); + max_mapnr = ((unsigned long) high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + + min_low_pfn = memory_start >> PAGE_SHIFT; + max_low_pfn = memory_end >> PAGE_SHIFT; + max_pfn = max_low_pfn; + + /* now take back the bits the core kernel is occupying */ +#ifndef CONFIG_PROTECT_KERNEL + reserve_bootmem(kend, bootmap_size, BOOTMEM_DEFAULT); + reserve_bootmem((unsigned long) &__kernel_image_start, + kend - (unsigned long) &__kernel_image_start, + BOOTMEM_DEFAULT); + +#else + dampr = __get_DAMPR(0); + dampr &= xAMPRx_SS; + dampr = (dampr >> 4) + 17; + dampr = 1 << dampr; + + reserve_bootmem(__get_DAMPR(0) & xAMPRx_PPFN, dampr, BOOTMEM_DEFAULT); +#endif + + /* reserve some memory to do uncached DMA through if requested */ +#ifdef CONFIG_RESERVE_DMA_COHERENT + if (dma_coherent_mem_start) + reserve_bootmem(dma_coherent_mem_start, + dma_coherent_mem_end - dma_coherent_mem_start, + BOOTMEM_DEFAULT); +#endif + +} /* end setup_uclinux_memory() */ +#endif + +/*****************************************************************************/ +/* + * get CPU information for use by procfs + */ +static int show_cpuinfo(struct seq_file *m, void *v) +{ + const char *gr, *fr, *fm, *fp, *cm, *nem, *ble; +#ifdef CONFIG_PM + const char *sep; +#endif + + gr = cpu_hsr0_all & HSR0_GRHE ? "gr0-63" : "gr0-31"; + fr = cpu_hsr0_all & HSR0_FRHE ? "fr0-63" : "fr0-31"; + fm = cpu_psr_all & PSR_EM ? ", Media" : ""; + fp = cpu_psr_all & PSR_EF ? ", FPU" : ""; + cm = cpu_psr_all & PSR_CM ? ", CCCR" : ""; + nem = cpu_psr_all & PSR_NEM ? ", NE" : ""; + ble = cpu_psr_all & PSR_BE ? "BE" : "LE"; + + seq_printf(m, + "CPU-Series:\t%s\n" + "CPU-Core:\t%s, %s, %s%s%s\n" + "CPU:\t\t%s\n" + "MMU:\t\t%s\n" + "FP-Media:\t%s%s%s\n" + "System:\t\t%s", + cpu_series, + cpu_core, gr, ble, cm, nem, + cpu_silicon, + cpu_mmu, + fr, fm, fp, + cpu_system); + + if (cpu_board1) + seq_printf(m, ", %s", cpu_board1); + + if (cpu_board2) + seq_printf(m, ", %s", cpu_board2); + + seq_printf(m, "\n"); + +#ifdef CONFIG_PM + seq_printf(m, "PM-Controls:"); + sep = "\t"; + + if (clock_bits_settable & CLOCK_BIT_CMODE) { + seq_printf(m, "%scmode=0x%04hx", sep, clock_cmodes_permitted); + sep = ", "; + } + + if (clock_bits_settable & CLOCK_BIT_CM) { + seq_printf(m, "%scm=0x%lx", sep, clock_bits_settable & CLOCK_BIT_CM); + sep = ", "; + } + + if (clock_bits_settable & CLOCK_BIT_P0) { + seq_printf(m, "%sp0=0x3", sep); + sep = ", "; + } + + seq_printf(m, "%ssuspend=0x22\n", sep); +#endif + + seq_printf(m, + "PM-Status:\tcmode=%d, cm=%d, p0=%d\n", + clock_cmode_current, clock_cm_current, clock_p0_current); + +#define print_clk(TAG, VAR) \ + seq_printf(m, "Clock-" TAG ":\t%lu.%2.2lu MHz\n", VAR / 1000000, (VAR / 10000) % 100) + + print_clk("In", __clkin_clock_speed_HZ); + print_clk("Core", __core_clock_speed_HZ); + print_clk("SDRAM", __sdram_clock_speed_HZ); + print_clk("CBus", __core_bus_clock_speed_HZ); + print_clk("Res", __res_bus_clock_speed_HZ); + print_clk("Ext", __ext_bus_clock_speed_HZ); + print_clk("DSU", __dsu_clock_speed_HZ); + + seq_printf(m, + "BogoMips:\t%lu.%02lu\n", + (loops_per_jiffy * HZ) / 500000, ((loops_per_jiffy * HZ) / 5000) % 100); + + return 0; +} /* end show_cpuinfo() */ + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? (void *) 0x12345678 : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +const struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ + *year = *mon = *day = *hour = *min = *sec = 0; +} + +/*****************************************************************************/ +/* + * + */ +#ifdef CONFIG_MB93090_MB00 +static void __init mb93090_sendlcdcmd(uint32_t cmd) +{ + unsigned long base = __addr_LCD(); + int loop; + + /* request reading of the busy flag */ + __set_LCD(base, LCD_CMD_READ_BUSY); + __set_LCD(base, LCD_CMD_READ_BUSY & ~LCD_E); + + /* wait for the busy flag to become clear */ + for (loop = 10000; loop > 0; loop--) + if (!(__get_LCD(base) & 0x80)) + break; + + /* send the command */ + __set_LCD(base, cmd); + __set_LCD(base, cmd & ~LCD_E); + +} /* end mb93090_sendlcdcmd() */ + +/*****************************************************************************/ +/* + * write to the MB93090 LEDs and LCD + */ +static void __init mb93090_display(void) +{ + const char *p; + + __set_LEDS(0); + + /* set up the LCD */ + mb93090_sendlcdcmd(LCD_CMD_CLEAR); + mb93090_sendlcdcmd(LCD_CMD_FUNCSET(1,1,0)); + mb93090_sendlcdcmd(LCD_CMD_ON(0,0)); + mb93090_sendlcdcmd(LCD_CMD_HOME); + + mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(0)); + for (p = mb93090_banner; *p; p++) + mb93090_sendlcdcmd(LCD_DATA_WRITE(*p)); + + mb93090_sendlcdcmd(LCD_CMD_SET_DD_ADDR(64)); + for (p = mb93090_version; *p; p++) + mb93090_sendlcdcmd(LCD_DATA_WRITE(*p)); + +} /* end mb93090_display() */ + +#endif // CONFIG_MB93090_MB00 diff --git a/arch/frv/kernel/signal.c b/arch/frv/kernel/signal.c new file mode 100644 index 000000000..82d5e914d --- /dev/null +++ b/arch/frv/kernel/signal.c @@ -0,0 +1,426 @@ +/* signal.c: FRV specific bits of signal handling + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/signal.c + * + * 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. + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/personality.h> +#include <linux/tracehook.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/cacheflush.h> + +#define DEBUG_SIG 0 + +struct fdpic_func_descriptor { + unsigned long text; + unsigned long GOT; +}; + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe +{ + __sigrestore_t pretcode; + int sig; + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + uint32_t retcode[2]; +}; + +struct rt_sigframe +{ + __sigrestore_t pretcode; + int sig; + struct siginfo __user *pinfo; + void __user *puc; + struct siginfo info; + struct ucontext uc; + uint32_t retcode[2]; +}; + +static int restore_sigcontext(struct sigcontext __user *sc, int *_gr8) +{ + struct user_context *user = current->thread.user; + unsigned long tbr, psr; + + /* Always make any pending restarted system calls return -EINTR */ + current->restart_block.fn = do_no_restart_syscall; + + tbr = user->i.tbr; + psr = user->i.psr; + if (copy_from_user(user, &sc->sc_context, sizeof(sc->sc_context))) + goto badframe; + user->i.tbr = tbr; + user->i.psr = psr; + + restore_user_regs(user); + + user->i.syscallno = -1; /* disable syscall checks */ + + *_gr8 = user->i.gr[8]; + return 0; + + badframe: + return 1; +} + +asmlinkage int sys_sigreturn(void) +{ + struct sigframe __user *frame = (struct sigframe __user *) __frame->sp; + sigset_t set; + int gr8; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.sc_oldmask)) + goto badframe; + + if (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, sizeof(frame->extramask))) + goto badframe; + + set_current_blocked(&set); + + if (restore_sigcontext(&frame->sc, &gr8)) + goto badframe; + return gr8; + + badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(void) +{ + struct rt_sigframe __user *frame = (struct rt_sigframe __user *) __frame->sp; + sigset_t set; + int gr8; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + set_current_blocked(&set); + + if (restore_sigcontext(&frame->uc.uc_mcontext, &gr8)) + goto badframe; + + if (restore_altstack(&frame->uc.uc_stack)) + goto badframe; + + return gr8; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame + */ +static int setup_sigcontext(struct sigcontext __user *sc, unsigned long mask) +{ + save_user_regs(current->thread.user); + + if (copy_to_user(&sc->sc_context, current->thread.user, sizeof(sc->sc_context)) != 0) + goto badframe; + + /* non-iBCS2 extensions.. */ + if (__put_user(mask, &sc->sc_oldmask) < 0) + goto badframe; + + return 0; + + badframe: + return 1; +} + +/*****************************************************************************/ +/* + * Determine which stack to use.. + */ +static inline void __user *get_sigframe(struct ksignal *ksig, + size_t frame_size) +{ + unsigned long sp = sigsp(__frame->sp, ksig); + + return (void __user *) ((sp - frame_size) & ~7UL); + +} /* end get_sigframe() */ + +/*****************************************************************************/ +/* + * + */ +static int setup_frame(struct ksignal *ksig, sigset_t *set) +{ + struct sigframe __user *frame; + int sig = ksig->sig; + + frame = get_sigframe(ksig, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + return -EFAULT; + + if (__put_user(sig, &frame->sig) < 0) + return -EFAULT; + + if (setup_sigcontext(&frame->sc, set->sig[0])) + return -EFAULT; + + if (_NSIG_WORDS > 1) { + if (__copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask))) + return -EFAULT; + } + + /* Set up to return from userspace. If provided, use a stub + * already in userspace. */ + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode) < 0) + return -EFAULT; + } + else { + /* Set up the following code on the stack: + * setlos #__NR_sigreturn,gr7 + * tira gr0,0 + */ + if (__put_user((__sigrestore_t)frame->retcode, &frame->pretcode) || + __put_user(0x8efc0000|__NR_sigreturn, &frame->retcode[0]) || + __put_user(0xc0700000, &frame->retcode[1])) + return -EFAULT; + + flush_icache_range((unsigned long) frame->retcode, + (unsigned long) (frame->retcode + 2)); + } + + /* Set up registers for the signal handler */ + if (current->personality & FDPIC_FUNCPTRS) { + struct fdpic_func_descriptor __user *funcptr = + (struct fdpic_func_descriptor __user *) ksig->ka.sa.sa_handler; + struct fdpic_func_descriptor desc; + if (copy_from_user(&desc, funcptr, sizeof(desc))) + return -EFAULT; + __frame->pc = desc.text; + __frame->gr15 = desc.GOT; + } else { + __frame->pc = (unsigned long) ksig->ka.sa.sa_handler; + __frame->gr15 = 0; + } + + __frame->sp = (unsigned long) frame; + __frame->lr = (unsigned long) &frame->retcode; + __frame->gr8 = sig; + +#if DEBUG_SIG + printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, __frame->pc, + frame->pretcode); +#endif + + return 0; +} /* end setup_frame() */ + +/*****************************************************************************/ +/* + * + */ +static int setup_rt_frame(struct ksignal *ksig, sigset_t *set) +{ + struct rt_sigframe __user *frame; + int sig = ksig->sig; + + frame = get_sigframe(ksig, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + return -EFAULT; + + if (__put_user(sig, &frame->sig) || + __put_user(&frame->info, &frame->pinfo) || + __put_user(&frame->uc, &frame->puc)) + return -EFAULT; + + if (copy_siginfo_to_user(&frame->info, &ksig->info)) + return -EFAULT; + + /* Create the ucontext. */ + if (__put_user(0, &frame->uc.uc_flags) || + __put_user(NULL, &frame->uc.uc_link) || + __save_altstack(&frame->uc.uc_stack, __frame->sp)) + return -EFAULT; + + if (setup_sigcontext(&frame->uc.uc_mcontext, set->sig[0])) + return -EFAULT; + + if (__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set))) + return -EFAULT; + + /* Set up to return from userspace. If provided, use a stub + * already in userspace. */ + if (ksig->ka.sa.sa_flags & SA_RESTORER) { + if (__put_user(ksig->ka.sa.sa_restorer, &frame->pretcode)) + return -EFAULT; + } + else { + /* Set up the following code on the stack: + * setlos #__NR_sigreturn,gr7 + * tira gr0,0 + */ + if (__put_user((__sigrestore_t)frame->retcode, &frame->pretcode) || + __put_user(0x8efc0000|__NR_rt_sigreturn, &frame->retcode[0]) || + __put_user(0xc0700000, &frame->retcode[1])) + return -EFAULT; + + flush_icache_range((unsigned long) frame->retcode, + (unsigned long) (frame->retcode + 2)); + } + + /* Set up registers for signal handler */ + if (current->personality & FDPIC_FUNCPTRS) { + struct fdpic_func_descriptor __user *funcptr = + (struct fdpic_func_descriptor __user *) ksig->ka.sa.sa_handler; + struct fdpic_func_descriptor desc; + if (copy_from_user(&desc, funcptr, sizeof(desc))) + return -EFAULT; + __frame->pc = desc.text; + __frame->gr15 = desc.GOT; + } else { + __frame->pc = (unsigned long) ksig->ka.sa.sa_handler; + __frame->gr15 = 0; + } + + __frame->sp = (unsigned long) frame; + __frame->lr = (unsigned long) &frame->retcode; + __frame->gr8 = sig; + __frame->gr9 = (unsigned long) &frame->info; + +#if DEBUG_SIG + printk("SIG deliver %d (%s:%d): sp=%p pc=%lx ra=%p\n", + sig, current->comm, current->pid, frame, __frame->pc, + frame->pretcode); +#endif + return 0; + +} /* end setup_rt_frame() */ + +/*****************************************************************************/ +/* + * OK, we're invoking a handler + */ +static void handle_signal(struct ksignal *ksig) +{ + sigset_t *oldset = sigmask_to_save(); + int ret; + + /* Are we from a system call? */ + if (__frame->syscallno != -1) { + /* If so, check system call restarting.. */ + switch (__frame->gr8) { + case -ERESTART_RESTARTBLOCK: + case -ERESTARTNOHAND: + __frame->gr8 = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ksig->ka.sa.sa_flags & SA_RESTART)) { + __frame->gr8 = -EINTR; + break; + } + + /* fallthrough */ + case -ERESTARTNOINTR: + __frame->gr8 = __frame->orig_gr8; + __frame->pc -= 4; + } + __frame->syscallno = -1; + } + + /* Set up the stack frame */ + if (ksig->ka.sa.sa_flags & SA_SIGINFO) + ret = setup_rt_frame(ksig, oldset); + else + ret = setup_frame(ksig, oldset); + + signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLESTEP)); +} /* end handle_signal() */ + +/*****************************************************************************/ +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +static void do_signal(void) +{ + struct ksignal ksig; + + if (get_signal(&ksig)) { + handle_signal(&ksig); + return; + } + + /* Did we come from a system call? */ + if (__frame->syscallno != -1) { + /* Restart the system call - no handlers present */ + switch (__frame->gr8) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + __frame->gr8 = __frame->orig_gr8; + __frame->pc -= 4; + break; + + case -ERESTART_RESTARTBLOCK: + __frame->gr7 = __NR_restart_syscall; + __frame->pc -= 4; + break; + } + __frame->syscallno = -1; + } + + /* if there's no signal to deliver, we just put the saved sigmask + * back */ + restore_saved_sigmask(); +} /* end do_signal() */ + +/*****************************************************************************/ +/* + * notification of userspace execution resumption + * - triggered by the TIF_WORK_MASK flags + */ +asmlinkage void do_notify_resume(__u32 thread_info_flags) +{ + /* pending single-step? */ + if (thread_info_flags & _TIF_SINGLESTEP) + clear_thread_flag(TIF_SINGLESTEP); + + /* deal with pending signal delivery */ + if (thread_info_flags & _TIF_SIGPENDING) + do_signal(); + + /* deal with notification on about to resume userspace execution */ + if (thread_info_flags & _TIF_NOTIFY_RESUME) { + clear_thread_flag(TIF_NOTIFY_RESUME); + tracehook_notify_resume(__frame); + } + +} /* end do_notify_resume() */ diff --git a/arch/frv/kernel/sleep.S b/arch/frv/kernel/sleep.S new file mode 100644 index 000000000..f67bf73cd --- /dev/null +++ b/arch/frv/kernel/sleep.S @@ -0,0 +1,373 @@ +/* sleep.S: power saving mode entry + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Woodhouse (dwmw2@infradead.org) + * + * 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. + * + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/page.h> +#include <asm/ptrace.h> +#include <asm/errno.h> +#include <asm/cache.h> +#include <asm/spr-regs.h> + +#define __addr_MASK 0xfeff9820 /* interrupt controller mask */ + +#define __addr_FR55X_DRCN 0xfeff0218 /* Address of DRCN register */ +#define FR55X_DSTS_OFFSET -4 /* Offset from DRCN to DSTS */ +#define FR55X_SDRAMC_DSTS_SSI 0x00000002 /* indicates that the SDRAM is in self-refresh mode */ + +#define __addr_FR4XX_DRCN 0xfe000430 /* Address of DRCN register */ +#define FR4XX_DSTS_OFFSET -8 /* Offset from DRCN to DSTS */ +#define FR4XX_SDRAMC_DSTS_SSI 0x00000001 /* indicates that the SDRAM is in self-refresh mode */ + +#define SDRAMC_DRCN_SR 0x00000001 /* transition SDRAM into self-refresh mode */ + + .section .bss + .balign 8 + .globl __sleep_save_area +__sleep_save_area: + .space 16 + + + .text + .balign 4 + +.macro li v r + sethi.p %hi(\v),\r + setlo %lo(\v),\r +.endm + +#ifdef CONFIG_PM +############################################################################### +# +# CPU suspension routine +# - void frv_cpu_suspend(unsigned long pdm_mode) +# +############################################################################### + .globl frv_cpu_suspend + .type frv_cpu_suspend,@function +frv_cpu_suspend: + + #---------------------------------------------------- + # save hsr0, psr, isr, and lr for resume code + #---------------------------------------------------- + li __sleep_save_area,gr11 + + movsg hsr0,gr4 + movsg psr,gr5 + movsg isr,gr6 + movsg lr,gr7 + stdi gr4,@(gr11,#0) + stdi gr6,@(gr11,#8) + + # store the return address from sleep in GR14, and its complement in GR13 as a check + li __ramboot_resume,gr14 +#ifdef CONFIG_MMU + # Resume via RAMBOOT# will turn MMU off, so bootloader needs a physical address. + sethi.p %hi(__page_offset),gr13 + setlo %lo(__page_offset),gr13 + sub gr14,gr13,gr14 +#endif + not gr14,gr13 + + #---------------------------------------------------- + # preload and lock into icache that code which may have to run + # when dram is in self-refresh state. + #---------------------------------------------------- + movsg hsr0, gr3 + li HSR0_ICE,gr4 + or gr3,gr4,gr3 + movgs gr3,hsr0 + or gr3,gr8,gr7 // add the sleep bits for later + + li #__icache_lock_start,gr3 + li #__icache_lock_end,gr4 +1: icpl gr3,gr0,#1 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,1b + + # disable exceptions + movsg psr,gr8 + andi.p gr8,#~PSR_PIL,gr8 + andi gr8,~PSR_ET,gr8 + movgs gr8,psr + ori gr8,#PSR_ET,gr8 + + srli gr8,#28,gr4 + subicc gr4,#3,gr0,icc0 + beq icc0,#0,1f + # FR4xx + li __addr_FR4XX_DRCN,gr4 + li FR4XX_SDRAMC_DSTS_SSI,gr5 + li FR4XX_DSTS_OFFSET,gr6 + bra __icache_lock_start +1: + # FR5xx + li __addr_FR55X_DRCN,gr4 + li FR55X_SDRAMC_DSTS_SSI,gr5 + li FR55X_DSTS_OFFSET,gr6 + bra __icache_lock_start + + .size frv_cpu_suspend, .-frv_cpu_suspend + +# +# the final part of the sleep sequence... +# - we want it to be be cacheline aligned so we can lock it into the icache easily +# On entry: gr7 holds desired hsr0 sleep value +# gr8 holds desired psr sleep value +# + .balign L1_CACHE_BYTES + .type __icache_lock_start,@function +__icache_lock_start: + + #---------------------------------------------------- + # put SDRAM in self-refresh mode + #---------------------------------------------------- + + # Flush all data in the cache using the DCEF instruction. + dcef @(gr0,gr0),#1 + + # Stop DMAC transfer + + # Execute dummy load from SDRAM + ldi @(gr11,#0),gr11 + + # put the SDRAM into self-refresh mode + ld @(gr4,gr0),gr11 + ori gr11,#SDRAMC_DRCN_SR,gr11 + st gr11,@(gr4,gr0) + membar + + # wait for SDRAM to reach self-refresh mode +1: ld @(gr4,gr6),gr11 + andcc gr11,gr5,gr11,icc0 + beq icc0,#0,1b + + # Set the GPIO register so that the IRQ[3:0] pins become valid, as required. + # Set the clock mode (CLKC register) as required. + # - At this time, also set the CLKC register P0 bit. + + # Set the HSR0 register PDM field. + movgs gr7,hsr0 + + # Execute NOP 32 times. + .rept 32 + nop + .endr + +#if 0 // Fujitsu recommend to skip this and will update docs. + # Release the interrupt mask setting of the MASK register of the + # interrupt controller if necessary. + sti gr10,@(gr9,#0) + membar +#endif + + # Set the PSR register ET bit to 1 to enable interrupts. + movgs gr8,psr + + ################################################### + # this is only reached if waking up via interrupt + ################################################### + + # Execute NOP 32 times. + .rept 32 + nop + .endr + + #---------------------------------------------------- + # wake SDRAM from self-refresh mode + #---------------------------------------------------- + ld @(gr4,gr0),gr11 + andi gr11,#~SDRAMC_DRCN_SR,gr11 + st gr11,@(gr4,gr0) + membar +2: + ld @(gr4,gr6),gr11 // Wait for it to come back... + andcc gr11,gr5,gr0,icc0 + bne icc0,0,2b + + # wait for the SDRAM to stabilise + li 0x0100000,gr3 +3: subicc gr3,#1,gr3,icc0 + bne icc0,#0,3b + + # now that DRAM is back, this is the end of the code which gets + # locked in icache. +__icache_lock_end: + .size __icache_lock_start, .-__icache_lock_start + + # Fall-through to the RAMBOOT# wakeup path + +############################################################################### +# +# resume from suspend re-entry point reached via RAMBOOT# and bootloader +# +############################################################################### +__ramboot_resume: + + #---------------------------------------------------- + # restore hsr0, psr, isr, and leave saved lr in gr7 + #---------------------------------------------------- + li __sleep_save_area,gr11 +#ifdef CONFIG_MMU + movsg hsr0,gr4 + sethi.p %hi(HSR0_EXMMU),gr3 + setlo %lo(HSR0_EXMMU),gr3 + andcc gr3,gr4,gr0,icc0 + bne icc0,#0,2f + + # need to use physical address + sethi.p %hi(__page_offset),gr3 + setlo %lo(__page_offset),gr3 + sub gr11,gr3,gr11 + + # flush all tlb entries + setlos #64,gr4 + setlos.p #PAGE_SIZE,gr5 + setlos #0,gr6 +1: + tlbpr gr6,gr0,#6,#0 + subicc.p gr4,#1,gr4,icc0 + add gr6,gr5,gr6 + bne icc0,#2,1b + + # need a temporary mapping for the current physical address we are + # using between time MMU is enabled and jump to virtual address is + # made. + sethi.p %hi(0x00000000),gr4 + setlo %lo(0x00000000),gr4 ; physical address + setlos #xAMPRx_L|xAMPRx_M|xAMPRx_SS_256Mb|xAMPRx_S_KERNEL|xAMPRx_V,gr5 + or gr4,gr5,gr5 + + movsg cxnr,gr13 + or gr4,gr13,gr4 + + movgs gr4,iamlr1 ; mapped from real address 0 + movgs gr5,iampr1 ; cached kernel memory at 0x00000000 +2: +#endif + + lddi @(gr11,#0),gr4 ; hsr0, psr + lddi @(gr11,#8),gr6 ; isr, lr + movgs gr4,hsr0 + bar + +#ifdef CONFIG_MMU + sethi.p %hi(1f),gr11 + setlo %lo(1f),gr11 + jmpl @(gr11,gr0) +1: + movgs gr0,iampr1 ; get rid of temporary mapping +#endif + movgs gr5,psr + movgs gr6,isr + + #---------------------------------------------------- + # unlock the icache which was locked before going to sleep + #---------------------------------------------------- + li __icache_lock_start,gr3 + li __icache_lock_end,gr4 +1: icul gr3 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,1b + + #---------------------------------------------------- + # back to business as usual + #---------------------------------------------------- + jmpl @(gr7,gr0) ; + +#endif /* CONFIG_PM */ + +############################################################################### +# +# CPU core sleep mode routine +# +############################################################################### + .globl frv_cpu_core_sleep + .type frv_cpu_core_sleep,@function +frv_cpu_core_sleep: + + # Preload into icache. + li #__core_sleep_icache_lock_start,gr3 + li #__core_sleep_icache_lock_end,gr4 + +1: icpl gr3,gr0,#1 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,1b + + bra __core_sleep_icache_lock_start + + .balign L1_CACHE_BYTES +__core_sleep_icache_lock_start: + + # (1) Set the PSR register ET bit to 0 to disable interrupts. + movsg psr,gr8 + andi.p gr8,#~(PSR_PIL),gr8 + andi gr8,#~(PSR_ET),gr4 + movgs gr4,psr + +#if 0 // Fujitsu recommend to skip this and will update docs. + # (2) Set '1' to all bits in the MASK register of the interrupt + # controller and mask interrupts. + sethi.p %hi(__addr_MASK),gr9 + setlo %lo(__addr_MASK),gr9 + sethi.p %hi(0xffff0000),gr4 + setlo %lo(0xffff0000),gr4 + ldi @(gr9,#0),gr10 + sti gr4,@(gr9,#0) +#endif + # (3) Flush all data in the cache using the DCEF instruction. + dcef @(gr0,gr0),#1 + + # (4) Execute the memory barrier instruction + membar + + # (5) Set the GPIO register so that the IRQ[3:0] pins become valid, as required. + # (6) Set the clock mode (CLKC register) as required. + # - At this time, also set the CLKC register P0 bit. + # (7) Set the HSR0 register PDM field to 001 . + movsg hsr0,gr4 + ori gr4,HSR0_PDM_CORE_SLEEP,gr4 + movgs gr4,hsr0 + + # (8) Execute NOP 32 times. + .rept 32 + nop + .endr + +#if 0 // Fujitsu recommend to skip this and will update docs. + # (9) Release the interrupt mask setting of the MASK register of the + # interrupt controller if necessary. + sti gr10,@(gr9,#0) + membar +#endif + + # (10) Set the PSR register ET bit to 1 to enable interrupts. + movgs gr8,psr + +__core_sleep_icache_lock_end: + + # Unlock from icache + li __core_sleep_icache_lock_start,gr3 + li __core_sleep_icache_lock_end,gr4 +1: icul gr3 + addi gr3,#L1_CACHE_BYTES,gr3 + cmp gr4,gr3,icc0 + bhi icc0,#0,1b + + bralr + + .size frv_cpu_core_sleep, .-frv_cpu_core_sleep diff --git a/arch/frv/kernel/switch_to.S b/arch/frv/kernel/switch_to.S new file mode 100644 index 000000000..b06668670 --- /dev/null +++ b/arch/frv/kernel/switch_to.S @@ -0,0 +1,489 @@ +############################################################################### +# +# switch_to.S: context switch operation +# +# Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. +# Written by David Howells (dhowells@redhat.com) +# +# 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. +# +############################################################################### + +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/processor.h> +#include <asm/registers.h> +#include <asm/spr-regs.h> + +.macro LEDS val + setlos #~\val,gr27 + st gr27,@(gr30,gr0) + membar + dcf @(gr30,gr0) +.endm + + .section .sdata + .balign 8 + + # address of frame 0 (userspace) on current kernel stack + .globl __kernel_frame0_ptr +__kernel_frame0_ptr: + .long init_thread_union + THREAD_SIZE - FRV_FRAME0_SIZE + + # address of current task + .globl __kernel_current_task +__kernel_current_task: + .long init_task + + .section .text + .balign 4 + +############################################################################### +# +# struct task_struct *__switch_to(struct thread_struct *prev_thread, +# struct thread_struct *next_thread, +# struct task_struct *prev) +# +############################################################################### + .globl __switch_to +__switch_to: + # save outgoing process's context + sethi.p %hi(__switch_back),gr13 + setlo %lo(__switch_back),gr13 + movsg lr,gr12 + + stdi gr28,@(gr8,#__THREAD_FRAME) + sti sp ,@(gr8,#__THREAD_SP) + sti fp ,@(gr8,#__THREAD_FP) + stdi gr12,@(gr8,#__THREAD_LR) + stdi gr16,@(gr8,#__THREAD_GR(16)) + stdi gr18,@(gr8,#__THREAD_GR(18)) + stdi gr20,@(gr8,#__THREAD_GR(20)) + stdi gr22,@(gr8,#__THREAD_GR(22)) + stdi gr24,@(gr8,#__THREAD_GR(24)) + stdi.p gr26,@(gr8,#__THREAD_GR(26)) + + or gr8,gr8,gr22 + ldi.p @(gr8,#__THREAD_USER),gr8 + call save_user_regs + or gr22,gr22,gr8 + + # retrieve the new context + sethi.p %hi(__kernel_frame0_ptr),gr6 + setlo %lo(__kernel_frame0_ptr),gr6 + movsg psr,gr4 + + lddi.p @(gr9,#__THREAD_FRAME),gr10 + or gr10,gr10,gr27 ; save prev for the return value + + ldi @(gr11,#4),gr19 ; get new_current->thread_info + + lddi @(gr9,#__THREAD_SP),gr12 + ldi @(gr9,#__THREAD_LR),gr14 + ldi @(gr9,#__THREAD_PC),gr18 + ldi.p @(gr9,#__THREAD_FRAME0),gr7 + + # actually switch kernel contexts with ordinary exceptions disabled + andi gr4,#~PSR_ET,gr5 + movgs gr5,psr + + or.p gr10,gr0,gr28 ; set __frame + or gr11,gr0,gr29 ; set __current + or.p gr12,gr0,sp + or gr13,gr0,fp + or gr19,gr0,gr15 ; set __current_thread_info + + sti gr7,@(gr6,#0) ; set __kernel_frame0_ptr + sti gr29,@(gr6,#4) ; set __kernel_current_task + + movgs gr14,lr + bar + + # jump to __switch_back or ret_from_fork as appropriate + # - move prev to GR8 + movgs gr4,psr + jmpl.p @(gr18,gr0) + or gr27,gr27,gr8 + +############################################################################### +# +# restore incoming process's context +# - on entry: +# - SP, FP, LR, GR15, GR28 and GR29 will have been set up appropriately +# - GR8 will point to the outgoing task_struct +# - GR9 will point to the incoming thread_struct +# +############################################################################### +__switch_back: + lddi @(gr9,#__THREAD_GR(16)),gr16 + lddi @(gr9,#__THREAD_GR(18)),gr18 + lddi @(gr9,#__THREAD_GR(20)),gr20 + lddi @(gr9,#__THREAD_GR(22)),gr22 + lddi @(gr9,#__THREAD_GR(24)),gr24 + lddi @(gr9,#__THREAD_GR(26)),gr26 + + # fall through into restore_user_regs() + ldi.p @(gr9,#__THREAD_USER),gr8 + or gr8,gr8,gr9 + +############################################################################### +# +# restore extra general regs and FP/Media regs +# - void *restore_user_regs(const struct user_context *target, void *retval) +# - on entry: +# - GR8 will point to the user context to swap in +# - GR9 will contain the value to be returned in GR8 (prev task on context switch) +# +############################################################################### + .globl restore_user_regs +restore_user_regs: + movsg hsr0,gr6 + ori gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6 + movgs gr6,hsr0 + movsg hsr0,gr6 + + movsg psr,gr7 + ori gr7,#PSR_EF|PSR_EM,gr7 + movgs gr7,psr + movsg psr,gr7 + srli gr7,#24,gr7 + bar + + lddi @(gr8,#__FPMEDIA_MSR(0)),gr4 + + movgs gr4,msr0 + movgs gr5,msr1 + + lddfi @(gr8,#__FPMEDIA_ACC(0)),fr16 + lddfi @(gr8,#__FPMEDIA_ACC(2)),fr18 + ldbfi @(gr8,#__FPMEDIA_ACCG(0)),fr20 + ldbfi @(gr8,#__FPMEDIA_ACCG(1)),fr21 + ldbfi @(gr8,#__FPMEDIA_ACCG(2)),fr22 + ldbfi @(gr8,#__FPMEDIA_ACCG(3)),fr23 + + mwtacc fr16,acc0 + mwtacc fr17,acc1 + mwtacc fr18,acc2 + mwtacc fr19,acc3 + mwtaccg fr20,accg0 + mwtaccg fr21,accg1 + mwtaccg fr22,accg2 + mwtaccg fr23,accg3 + + # some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs + subicc.p gr7,#0x50,gr0,icc0 + subicc gr7,#0x31,gr0,icc1 + beq icc0,#0,__restore_acc_fr451 + beq icc1,#0,__restore_acc_fr555 +__restore_acc_cont: + + # some CPU's have GR32-GR63 + setlos #HSR0_FRHE,gr4 + andcc gr6,gr4,gr0,icc0 + beq icc0,#1,__restore_skip_gr32_gr63 + + lddi @(gr8,#__INT_GR(32)),gr32 + lddi @(gr8,#__INT_GR(34)),gr34 + lddi @(gr8,#__INT_GR(36)),gr36 + lddi @(gr8,#__INT_GR(38)),gr38 + lddi @(gr8,#__INT_GR(40)),gr40 + lddi @(gr8,#__INT_GR(42)),gr42 + lddi @(gr8,#__INT_GR(44)),gr44 + lddi @(gr8,#__INT_GR(46)),gr46 + lddi @(gr8,#__INT_GR(48)),gr48 + lddi @(gr8,#__INT_GR(50)),gr50 + lddi @(gr8,#__INT_GR(52)),gr52 + lddi @(gr8,#__INT_GR(54)),gr54 + lddi @(gr8,#__INT_GR(56)),gr56 + lddi @(gr8,#__INT_GR(58)),gr58 + lddi @(gr8,#__INT_GR(60)),gr60 + lddi @(gr8,#__INT_GR(62)),gr62 +__restore_skip_gr32_gr63: + + # all CPU's have FR0-FR31 + lddfi @(gr8,#__FPMEDIA_FR( 0)),fr0 + lddfi @(gr8,#__FPMEDIA_FR( 2)),fr2 + lddfi @(gr8,#__FPMEDIA_FR( 4)),fr4 + lddfi @(gr8,#__FPMEDIA_FR( 6)),fr6 + lddfi @(gr8,#__FPMEDIA_FR( 8)),fr8 + lddfi @(gr8,#__FPMEDIA_FR(10)),fr10 + lddfi @(gr8,#__FPMEDIA_FR(12)),fr12 + lddfi @(gr8,#__FPMEDIA_FR(14)),fr14 + lddfi @(gr8,#__FPMEDIA_FR(16)),fr16 + lddfi @(gr8,#__FPMEDIA_FR(18)),fr18 + lddfi @(gr8,#__FPMEDIA_FR(20)),fr20 + lddfi @(gr8,#__FPMEDIA_FR(22)),fr22 + lddfi @(gr8,#__FPMEDIA_FR(24)),fr24 + lddfi @(gr8,#__FPMEDIA_FR(26)),fr26 + lddfi @(gr8,#__FPMEDIA_FR(28)),fr28 + lddfi.p @(gr8,#__FPMEDIA_FR(30)),fr30 + + # some CPU's have FR32-FR63 + setlos #HSR0_FRHE,gr4 + andcc gr6,gr4,gr0,icc0 + beq icc0,#1,__restore_skip_fr32_fr63 + + lddfi @(gr8,#__FPMEDIA_FR(32)),fr32 + lddfi @(gr8,#__FPMEDIA_FR(34)),fr34 + lddfi @(gr8,#__FPMEDIA_FR(36)),fr36 + lddfi @(gr8,#__FPMEDIA_FR(38)),fr38 + lddfi @(gr8,#__FPMEDIA_FR(40)),fr40 + lddfi @(gr8,#__FPMEDIA_FR(42)),fr42 + lddfi @(gr8,#__FPMEDIA_FR(44)),fr44 + lddfi @(gr8,#__FPMEDIA_FR(46)),fr46 + lddfi @(gr8,#__FPMEDIA_FR(48)),fr48 + lddfi @(gr8,#__FPMEDIA_FR(50)),fr50 + lddfi @(gr8,#__FPMEDIA_FR(52)),fr52 + lddfi @(gr8,#__FPMEDIA_FR(54)),fr54 + lddfi @(gr8,#__FPMEDIA_FR(56)),fr56 + lddfi @(gr8,#__FPMEDIA_FR(58)),fr58 + lddfi @(gr8,#__FPMEDIA_FR(60)),fr60 + lddfi @(gr8,#__FPMEDIA_FR(62)),fr62 +__restore_skip_fr32_fr63: + + lddi @(gr8,#__FPMEDIA_FNER(0)),gr4 + movsg fner0,gr4 + movsg fner1,gr5 + or.p gr9,gr9,gr8 + bralr + + # the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...) +__restore_acc_fr451: + lddfi @(gr8,#__FPMEDIA_ACC(4)),fr16 + lddfi @(gr8,#__FPMEDIA_ACC(6)),fr18 + ldbfi @(gr8,#__FPMEDIA_ACCG(4)),fr20 + ldbfi @(gr8,#__FPMEDIA_ACCG(5)),fr21 + ldbfi @(gr8,#__FPMEDIA_ACCG(6)),fr22 + ldbfi @(gr8,#__FPMEDIA_ACCG(7)),fr23 + + mwtacc fr16,acc8 + mwtacc fr17,acc9 + mwtacc fr18,acc10 + mwtacc fr19,acc11 + mwtaccg fr20,accg8 + mwtaccg fr21,accg9 + mwtaccg fr22,accg10 + mwtaccg fr23,accg11 + bra __restore_acc_cont + + # the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg +__restore_acc_fr555: + lddfi @(gr8,#__FPMEDIA_ACC(4)),fr16 + lddfi @(gr8,#__FPMEDIA_ACC(6)),fr18 + ldbfi @(gr8,#__FPMEDIA_ACCG(4)),fr20 + ldbfi @(gr8,#__FPMEDIA_ACCG(5)),fr21 + ldbfi @(gr8,#__FPMEDIA_ACCG(6)),fr22 + ldbfi @(gr8,#__FPMEDIA_ACCG(7)),fr23 + + mnop.p + mwtacc fr16,acc4 + mnop.p + mwtacc fr17,acc5 + mnop.p + mwtacc fr18,acc6 + mnop.p + mwtacc fr19,acc7 + mnop.p + mwtaccg fr20,accg4 + mnop.p + mwtaccg fr21,accg5 + mnop.p + mwtaccg fr22,accg6 + mnop.p + mwtaccg fr23,accg7 + + ldi @(gr8,#__FPMEDIA_FSR(0)),gr4 + movgs gr4,fsr0 + + bra __restore_acc_cont + + +############################################################################### +# +# save extra general regs and FP/Media regs +# - void save_user_regs(struct user_context *target) +# +############################################################################### + .globl save_user_regs +save_user_regs: + movsg hsr0,gr6 + ori gr6,#HSR0_GRHE|HSR0_FRLE|HSR0_FRHE,gr6 + movgs gr6,hsr0 + movsg hsr0,gr6 + + movsg psr,gr7 + ori gr7,#PSR_EF|PSR_EM,gr7 + movgs gr7,psr + movsg psr,gr7 + srli gr7,#24,gr7 + bar + + movsg fner0,gr4 + movsg fner1,gr5 + stdi.p gr4,@(gr8,#__FPMEDIA_FNER(0)) + + # some CPU's have GR32-GR63 + setlos #HSR0_GRHE,gr4 + andcc gr6,gr4,gr0,icc0 + beq icc0,#1,__save_skip_gr32_gr63 + + stdi gr32,@(gr8,#__INT_GR(32)) + stdi gr34,@(gr8,#__INT_GR(34)) + stdi gr36,@(gr8,#__INT_GR(36)) + stdi gr38,@(gr8,#__INT_GR(38)) + stdi gr40,@(gr8,#__INT_GR(40)) + stdi gr42,@(gr8,#__INT_GR(42)) + stdi gr44,@(gr8,#__INT_GR(44)) + stdi gr46,@(gr8,#__INT_GR(46)) + stdi gr48,@(gr8,#__INT_GR(48)) + stdi gr50,@(gr8,#__INT_GR(50)) + stdi gr52,@(gr8,#__INT_GR(52)) + stdi gr54,@(gr8,#__INT_GR(54)) + stdi gr56,@(gr8,#__INT_GR(56)) + stdi gr58,@(gr8,#__INT_GR(58)) + stdi gr60,@(gr8,#__INT_GR(60)) + stdi gr62,@(gr8,#__INT_GR(62)) +__save_skip_gr32_gr63: + + # all CPU's have FR0-FR31 + stdfi fr0 ,@(gr8,#__FPMEDIA_FR( 0)) + stdfi fr2 ,@(gr8,#__FPMEDIA_FR( 2)) + stdfi fr4 ,@(gr8,#__FPMEDIA_FR( 4)) + stdfi fr6 ,@(gr8,#__FPMEDIA_FR( 6)) + stdfi fr8 ,@(gr8,#__FPMEDIA_FR( 8)) + stdfi fr10,@(gr8,#__FPMEDIA_FR(10)) + stdfi fr12,@(gr8,#__FPMEDIA_FR(12)) + stdfi fr14,@(gr8,#__FPMEDIA_FR(14)) + stdfi fr16,@(gr8,#__FPMEDIA_FR(16)) + stdfi fr18,@(gr8,#__FPMEDIA_FR(18)) + stdfi fr20,@(gr8,#__FPMEDIA_FR(20)) + stdfi fr22,@(gr8,#__FPMEDIA_FR(22)) + stdfi fr24,@(gr8,#__FPMEDIA_FR(24)) + stdfi fr26,@(gr8,#__FPMEDIA_FR(26)) + stdfi fr28,@(gr8,#__FPMEDIA_FR(28)) + stdfi.p fr30,@(gr8,#__FPMEDIA_FR(30)) + + # some CPU's have FR32-FR63 + setlos #HSR0_FRHE,gr4 + andcc gr6,gr4,gr0,icc0 + beq icc0,#1,__save_skip_fr32_fr63 + + stdfi fr32,@(gr8,#__FPMEDIA_FR(32)) + stdfi fr34,@(gr8,#__FPMEDIA_FR(34)) + stdfi fr36,@(gr8,#__FPMEDIA_FR(36)) + stdfi fr38,@(gr8,#__FPMEDIA_FR(38)) + stdfi fr40,@(gr8,#__FPMEDIA_FR(40)) + stdfi fr42,@(gr8,#__FPMEDIA_FR(42)) + stdfi fr44,@(gr8,#__FPMEDIA_FR(44)) + stdfi fr46,@(gr8,#__FPMEDIA_FR(46)) + stdfi fr48,@(gr8,#__FPMEDIA_FR(48)) + stdfi fr50,@(gr8,#__FPMEDIA_FR(50)) + stdfi fr52,@(gr8,#__FPMEDIA_FR(52)) + stdfi fr54,@(gr8,#__FPMEDIA_FR(54)) + stdfi fr56,@(gr8,#__FPMEDIA_FR(56)) + stdfi fr58,@(gr8,#__FPMEDIA_FR(58)) + stdfi fr60,@(gr8,#__FPMEDIA_FR(60)) + stdfi fr62,@(gr8,#__FPMEDIA_FR(62)) +__save_skip_fr32_fr63: + + mrdacc acc0 ,fr4 + mrdacc acc1 ,fr5 + + stdfi.p fr4 ,@(gr8,#__FPMEDIA_ACC(0)) + + mrdacc acc2 ,fr6 + mrdacc acc3 ,fr7 + + stdfi.p fr6 ,@(gr8,#__FPMEDIA_ACC(2)) + + mrdaccg accg0,fr4 + stbfi.p fr4 ,@(gr8,#__FPMEDIA_ACCG(0)) + + mrdaccg accg1,fr5 + stbfi.p fr5 ,@(gr8,#__FPMEDIA_ACCG(1)) + + mrdaccg accg2,fr6 + stbfi.p fr6 ,@(gr8,#__FPMEDIA_ACCG(2)) + + mrdaccg accg3,fr7 + stbfi fr7 ,@(gr8,#__FPMEDIA_ACCG(3)) + + movsg msr0 ,gr4 + movsg msr1 ,gr5 + + stdi gr4 ,@(gr8,#__FPMEDIA_MSR(0)) + + # some CPUs have extra ACCx and ACCGx regs and maybe FSRx regs + subicc.p gr7,#0x50,gr0,icc0 + subicc gr7,#0x31,gr0,icc1 + beq icc0,#0,__save_acc_fr451 + beq icc1,#0,__save_acc_fr555 +__save_acc_cont: + + lddfi @(gr8,#__FPMEDIA_FR(4)),fr4 + lddfi.p @(gr8,#__FPMEDIA_FR(6)),fr6 + bralr + + # the FR451 also has ACC8-11/ACCG8-11 regs (but not 4-7...) +__save_acc_fr451: + mrdacc acc8 ,fr4 + mrdacc acc9 ,fr5 + + stdfi.p fr4 ,@(gr8,#__FPMEDIA_ACC(4)) + + mrdacc acc10,fr6 + mrdacc acc11,fr7 + + stdfi.p fr6 ,@(gr8,#__FPMEDIA_ACC(6)) + + mrdaccg accg8,fr4 + stbfi.p fr4 ,@(gr8,#__FPMEDIA_ACCG(4)) + + mrdaccg accg9,fr5 + stbfi.p fr5 ,@(gr8,#__FPMEDIA_ACCG(5)) + + mrdaccg accg10,fr6 + stbfi.p fr6 ,@(gr8,#__FPMEDIA_ACCG(6)) + + mrdaccg accg11,fr7 + stbfi fr7 ,@(gr8,#__FPMEDIA_ACCG(7)) + bra __save_acc_cont + + # the FR555 also has ACC4-7/ACCG4-7 regs and an FSR0 reg +__save_acc_fr555: + mnop.p + mrdacc acc4 ,fr4 + mnop.p + mrdacc acc5 ,fr5 + + stdfi fr4 ,@(gr8,#__FPMEDIA_ACC(4)) + + mnop.p + mrdacc acc6 ,fr6 + mnop.p + mrdacc acc7 ,fr7 + + stdfi fr6 ,@(gr8,#__FPMEDIA_ACC(6)) + + mnop.p + mrdaccg accg4,fr4 + stbfi fr4 ,@(gr8,#__FPMEDIA_ACCG(4)) + + mnop.p + mrdaccg accg5,fr5 + stbfi fr5 ,@(gr8,#__FPMEDIA_ACCG(5)) + + mnop.p + mrdaccg accg6,fr6 + stbfi fr6 ,@(gr8,#__FPMEDIA_ACCG(6)) + + mnop.p + mrdaccg accg7,fr7 + stbfi fr7 ,@(gr8,#__FPMEDIA_ACCG(7)) + + movsg fsr0 ,gr4 + sti gr4 ,@(gr8,#__FPMEDIA_FSR(0)) + bra __save_acc_cont diff --git a/arch/frv/kernel/sys_frv.c b/arch/frv/kernel/sys_frv.c new file mode 100644 index 000000000..9c4980825 --- /dev/null +++ b/arch/frv/kernel/sys_frv.c @@ -0,0 +1,44 @@ +/* sys_frv.c: FRV arch-specific syscall wrappers + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/sys_m68k.c + * + * 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. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/fs.h> +#include <linux/smp.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/syscalls.h> +#include <linux/ipc.h> + +#include <asm/setup.h> +#include <asm/uaccess.h> + +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + /* As with sparc32, make sure the shift for mmap2 is constant + (12), no matter what PAGE_SIZE we have.... */ + + /* But unlike sparc32, don't just silently break if we're + trying to map something we can't */ + if (pgoff & ((1 << (PAGE_SHIFT - 12)) - 1)) + return -EINVAL; + + return sys_mmap_pgoff(addr, len, prot, flags, fd, + pgoff >> (PAGE_SHIFT - 12)); +} diff --git a/arch/frv/kernel/sysctl.c b/arch/frv/kernel/sysctl.c new file mode 100644 index 000000000..f4dfae2c7 --- /dev/null +++ b/arch/frv/kernel/sysctl.c @@ -0,0 +1,221 @@ +/* sysctl.c: implementation of /proc/sys files relating to FRV specifically + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <asm/uaccess.h> + +static const char frv_cache_wback[] = "wback"; +static const char frv_cache_wthru[] = "wthru"; + +static void frv_change_dcache_mode(unsigned long newmode) +{ + unsigned long flags, hsr0; + + local_irq_save(flags); + + hsr0 = __get_HSR(0); + hsr0 &= ~HSR0_DCE; + __set_HSR(0, hsr0); + + asm volatile(" dcef @(gr0,gr0),#1 \n" + " membar \n" + : : : "memory" + ); + + hsr0 = (hsr0 & ~HSR0_CBM) | newmode; + __set_HSR(0, hsr0); + hsr0 |= HSR0_DCE; + __set_HSR(0, hsr0); + + local_irq_restore(flags); + + //printk("HSR0 now %08lx\n", hsr0); +} + +/*****************************************************************************/ +/* + * handle requests to dynamically switch the write caching mode delivered by /proc + */ +static int procctl_frv_cachemode(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + unsigned long hsr0; + char buff[8]; + int len; + + len = *lenp; + + if (write) { + /* potential state change */ + if (len <= 1 || len > sizeof(buff) - 1) + return -EINVAL; + + if (copy_from_user(buff, buffer, len) != 0) + return -EFAULT; + + if (buff[len - 1] == '\n') + buff[len - 1] = '\0'; + else + buff[len] = '\0'; + + if (strcmp(buff, frv_cache_wback) == 0) { + /* switch dcache into write-back mode */ + frv_change_dcache_mode(HSR0_CBM_COPY_BACK); + return 0; + } + + if (strcmp(buff, frv_cache_wthru) == 0) { + /* switch dcache into write-through mode */ + frv_change_dcache_mode(HSR0_CBM_WRITE_THRU); + return 0; + } + + return -EINVAL; + } + + /* read the state */ + if (*ppos > 0) { + *lenp = 0; + return 0; + } + + hsr0 = __get_HSR(0); + switch (hsr0 & HSR0_CBM) { + case HSR0_CBM_WRITE_THRU: + memcpy(buff, frv_cache_wthru, sizeof(frv_cache_wthru) - 1); + buff[sizeof(frv_cache_wthru) - 1] = '\n'; + len = sizeof(frv_cache_wthru); + break; + default: + memcpy(buff, frv_cache_wback, sizeof(frv_cache_wback) - 1); + buff[sizeof(frv_cache_wback) - 1] = '\n'; + len = sizeof(frv_cache_wback); + break; + } + + if (len > *lenp) + len = *lenp; + + if (copy_to_user(buffer, buff, len) != 0) + return -EFAULT; + + *lenp = len; + *ppos = len; + return 0; + +} /* end procctl_frv_cachemode() */ + +/*****************************************************************************/ +/* + * permit the mm_struct the nominated process is using have its MMU context ID pinned + */ +#ifdef CONFIG_MMU +static int procctl_frv_pin_cxnr(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + pid_t pid; + char buff[16], *p; + int len; + + len = *lenp; + + if (write) { + /* potential state change */ + if (len <= 1 || len > sizeof(buff) - 1) + return -EINVAL; + + if (copy_from_user(buff, buffer, len) != 0) + return -EFAULT; + + if (buff[len - 1] == '\n') + buff[len - 1] = '\0'; + else + buff[len] = '\0'; + + pid = simple_strtoul(buff, &p, 10); + if (*p) + return -EINVAL; + + return cxn_pin_by_pid(pid); + } + + /* read the currently pinned CXN */ + if (*ppos > 0) { + *lenp = 0; + return 0; + } + + len = snprintf(buff, sizeof(buff), "%d\n", cxn_pinned); + if (len > *lenp) + len = *lenp; + + if (copy_to_user(buffer, buff, len) != 0) + return -EFAULT; + + *lenp = len; + *ppos = len; + return 0; + +} /* end procctl_frv_pin_cxnr() */ +#endif + +/* + * FR-V specific sysctls + */ +static struct ctl_table frv_table[] = +{ + { + .procname = "cache-mode", + .data = NULL, + .maxlen = 0, + .mode = 0644, + .proc_handler = procctl_frv_cachemode, + }, +#ifdef CONFIG_MMU + { + .procname = "pin-cxnr", + .data = NULL, + .maxlen = 0, + .mode = 0644, + .proc_handler = procctl_frv_pin_cxnr + }, +#endif + {} +}; + +/* + * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 + * when all the PM interfaces exist nicely. + */ +static struct ctl_table frv_dir_table[] = +{ + { + .procname = "frv", + .mode = 0555, + .child = frv_table + }, + {} +}; + +/* + * Initialize power interface + */ +static int __init frv_sysctl_init(void) +{ + register_sysctl_table(frv_dir_table); + return 0; +} + +__initcall(frv_sysctl_init); diff --git a/arch/frv/kernel/time.c b/arch/frv/kernel/time.c new file mode 100644 index 000000000..332e00bf9 --- /dev/null +++ b/arch/frv/kernel/time.c @@ -0,0 +1,122 @@ +/* time.c: FRV arch-specific time handling + * + * Copyright (C) 2003-5 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * - Derived from arch/m68k/kernel/time.c + * + * 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. + */ + +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/profile.h> +#include <linux/irq.h> +#include <linux/mm.h> + +#include <asm/io.h> +#include <asm/timer-regs.h> +#include <asm/mb-regs.h> +#include <asm/mb86943a.h> + +#include <linux/timex.h> + +#define TICK_SIZE (tick_nsec / 1000) + +unsigned long __nongprelbss __clkin_clock_speed_HZ; +unsigned long __nongprelbss __ext_bus_clock_speed_HZ; +unsigned long __nongprelbss __res_bus_clock_speed_HZ; +unsigned long __nongprelbss __sdram_clock_speed_HZ; +unsigned long __nongprelbss __core_bus_clock_speed_HZ; +unsigned long __nongprelbss __core_clock_speed_HZ; +unsigned long __nongprelbss __dsu_clock_speed_HZ; +unsigned long __nongprelbss __serial_clock_speed_HZ; +unsigned long __delay_loops_MHz; + +static irqreturn_t timer_interrupt(int irq, void *dummy); + +static struct irqaction timer_irq = { + .handler = timer_interrupt, + .name = "timer", +}; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "xtime_update()" routine every clocktick + */ +static irqreturn_t timer_interrupt(int irq, void *dummy) +{ + profile_tick(CPU_PROFILING); + + xtime_update(1); + +#ifdef CONFIG_HEARTBEAT + static unsigned short n; + n++; + __set_LEDS(n); +#endif /* CONFIG_HEARTBEAT */ + + update_process_times(user_mode(get_irq_regs())); + + return IRQ_HANDLED; +} + +void time_divisor_init(void) +{ + unsigned short base, pre, prediv; + + /* set the scheduling timer going */ + pre = 1; + prediv = 4; + base = __res_bus_clock_speed_HZ / pre / HZ / (1 << prediv); + + __set_TPRV(pre); + __set_TxCKSL_DATA(0, prediv); + __set_TCTR(TCTR_SC_CTR0 | TCTR_RL_RW_LH8 | TCTR_MODE_2); + __set_TCSR_DATA(0, base & 0xff); + __set_TCSR_DATA(0, base >> 8); +} + + +void read_persistent_clock(struct timespec *ts) +{ + unsigned int year, mon, day, hour, min, sec; + + extern void arch_gettod(int *year, int *mon, int *day, int *hour, int *min, int *sec); + + /* FIX by dqg : Set to zero for platforms that don't have tod */ + /* without this time is undefined and can overflow time_t, causing */ + /* very strange errors */ + year = 1980; + mon = day = 1; + hour = min = sec = 0; + arch_gettod (&year, &mon, &day, &hour, &min, &sec); + + if ((year += 1900) < 1970) + year += 100; + ts->tv_sec = mktime(year, mon, day, hour, min, sec); + ts->tv_nsec = 0; +} + +void time_init(void) +{ + /* install scheduling interrupt handler */ + setup_irq(IRQ_CPU_TIMER0, &timer_irq); + + time_divisor_init(); +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return jiffies_64 * (1000000000 / HZ); +} diff --git a/arch/frv/kernel/traps.c b/arch/frv/kernel/traps.c new file mode 100644 index 000000000..a6d105d61 --- /dev/null +++ b/arch/frv/kernel/traps.c @@ -0,0 +1,646 @@ +/* traps.c: high-level exception handler for FR-V + * + * Copyright (C) 2003 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/user.h> +#include <linux/string.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <asm/asm-offsets.h> +#include <asm/setup.h> +#include <asm/fpu.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/siginfo.h> +#include <asm/unaligned.h> + +void show_backtrace(struct pt_regs *, unsigned long); + +extern asmlinkage void __break_hijack_kernel_event(void); + +/*****************************************************************************/ +/* + * instruction access error + */ +asmlinkage void insn_access_error(unsigned long esfr1, unsigned long epcr0, unsigned long esr0) +{ + siginfo_t info; + + die_if_kernel("-- Insn Access Error --\n" + "EPCR0 : %08lx\n" + "ESR0 : %08lx\n", + epcr0, esr0); + + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + info.si_errno = 0; + info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); + + force_sig_info(info.si_signo, &info, current); +} /* end insn_access_error() */ + +/*****************************************************************************/ +/* + * handler for: + * - illegal instruction + * - privileged instruction + * - unsupported trap + * - debug exceptions + */ +asmlinkage void illegal_instruction(unsigned long esfr1, unsigned long epcr0, unsigned long esr0) +{ + siginfo_t info; + + die_if_kernel("-- Illegal Instruction --\n" + "EPCR0 : %08lx\n" + "ESR0 : %08lx\n" + "ESFR1 : %08lx\n", + epcr0, esr0, esfr1); + + info.si_errno = 0; + info.si_addr = (void __user *) ((epcr0 & EPCR0_V) ? (epcr0 & EPCR0_PC) : __frame->pc); + + switch (__frame->tbr & TBR_TT) { + case TBR_TT_ILLEGAL_INSTR: + info.si_signo = SIGILL; + info.si_code = ILL_ILLOPC; + break; + case TBR_TT_PRIV_INSTR: + info.si_signo = SIGILL; + info.si_code = ILL_PRVOPC; + break; + case TBR_TT_TRAP2 ... TBR_TT_TRAP126: + info.si_signo = SIGILL; + info.si_code = ILL_ILLTRP; + break; + /* GDB uses "tira gr0, #1" as a breakpoint instruction. */ + case TBR_TT_TRAP1: + case TBR_TT_BREAK: + info.si_signo = SIGTRAP; + info.si_code = + (__frame->__status & REG__STATUS_STEPPED) ? TRAP_TRACE : TRAP_BRKPT; + break; + } + + force_sig_info(info.si_signo, &info, current); +} /* end illegal_instruction() */ + +/*****************************************************************************/ +/* + * handle atomic operations with errors + * - arguments in gr8, gr9, gr10 + * - original memory value placed in gr5 + * - replacement memory value placed in gr9 + */ +asmlinkage void atomic_operation(unsigned long esfr1, unsigned long epcr0, + unsigned long esr0) +{ + static DEFINE_SPINLOCK(atomic_op_lock); + unsigned long x, y, z; + unsigned long __user *p; + mm_segment_t oldfs; + siginfo_t info; + int ret; + + y = 0; + z = 0; + + oldfs = get_fs(); + if (!user_mode(__frame)) + set_fs(KERNEL_DS); + + switch (__frame->tbr & TBR_TT) { + /* TIRA gr0,#120 + * u32 __atomic_user_cmpxchg32(u32 *ptr, u32 test, u32 new) + */ + case TBR_TT_ATOMIC_CMPXCHG32: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + y = __frame->gr10; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + if (z != x) + goto done; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + if (z != x) + goto done2; + + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#121 + * u32 __atomic_kernel_xchg32(void *v, u32 new) + */ + case TBR_TT_ATOMIC_XCHG32: + p = (unsigned long __user *) __frame->gr8; + y = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#122 + * ulong __atomic_kernel_XOR_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_XOR: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x ^ z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#123 + * ulong __atomic_kernel_OR_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_OR: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x ^ z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#124 + * ulong __atomic_kernel_AND_return(ulong i, ulong *v) + */ + case TBR_TT_ATOMIC_AND: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = x & z; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#125 + * int __atomic_user_sub_return(atomic_t *v, int i) + */ + case TBR_TT_ATOMIC_SUB: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = z - x; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + /* TIRA gr0,#126 + * int __atomic_user_add_return(atomic_t *v, int i) + */ + case TBR_TT_ATOMIC_ADD: + p = (unsigned long __user *) __frame->gr8; + x = __frame->gr9; + + for (;;) { + ret = get_user(z, p); + if (ret < 0) + goto error; + + spin_lock_irq(&atomic_op_lock); + + if (__get_user(z, p) == 0) { + y = z + x; + if (__put_user(y, p) == 0) + goto done2; + goto error2; + } + + spin_unlock_irq(&atomic_op_lock); + } + + default: + BUG(); + } + +done2: + spin_unlock_irq(&atomic_op_lock); +done: + if (!user_mode(__frame)) + set_fs(oldfs); + __frame->gr5 = z; + __frame->gr9 = y; + return; + +error2: + spin_unlock_irq(&atomic_op_lock); +error: + if (!user_mode(__frame)) + set_fs(oldfs); + __frame->pc -= 4; + + die_if_kernel("-- Atomic Op Error --\n"); + + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + info.si_errno = 0; + info.si_addr = (void __user *) __frame->pc; + + force_sig_info(info.si_signo, &info, current); +} + +/*****************************************************************************/ +/* + * + */ +asmlinkage void media_exception(unsigned long msr0, unsigned long msr1) +{ + siginfo_t info; + + die_if_kernel("-- Media Exception --\n" + "MSR0 : %08lx\n" + "MSR1 : %08lx\n", + msr0, msr1); + + info.si_signo = SIGFPE; + info.si_code = FPE_MDAOVF; + info.si_errno = 0; + info.si_addr = (void __user *) __frame->pc; + + force_sig_info(info.si_signo, &info, current); +} /* end media_exception() */ + +/*****************************************************************************/ +/* + * instruction or data access exception + */ +asmlinkage void memory_access_exception(unsigned long esr0, + unsigned long ear0, + unsigned long epcr0) +{ + siginfo_t info; + +#ifdef CONFIG_MMU + unsigned long fixup; + + fixup = search_exception_table(__frame->pc); + if (fixup) { + __frame->pc = fixup; + return; + } +#endif + + die_if_kernel("-- Memory Access Exception --\n" + "ESR0 : %08lx\n" + "EAR0 : %08lx\n" + "EPCR0 : %08lx\n", + esr0, ear0, epcr0); + + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + info.si_errno = 0; + info.si_addr = NULL; + + if ((esr0 & (ESRx_VALID | ESR0_EAV)) == (ESRx_VALID | ESR0_EAV)) + info.si_addr = (void __user *) ear0; + + force_sig_info(info.si_signo, &info, current); + +} /* end memory_access_exception() */ + +/*****************************************************************************/ +/* + * data access error + * - double-word data load from CPU control area (0xFExxxxxx) + * - read performed on inactive or self-refreshing SDRAM + * - error notification from slave device + * - misaligned address + * - access to out of bounds memory region + * - user mode accessing privileged memory region + * - write to R/O memory region + */ +asmlinkage void data_access_error(unsigned long esfr1, unsigned long esr15, unsigned long ear15) +{ + siginfo_t info; + + die_if_kernel("-- Data Access Error --\n" + "ESR15 : %08lx\n" + "EAR15 : %08lx\n", + esr15, ear15); + + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; + info.si_errno = 0; + info.si_addr = (void __user *) + (((esr15 & (ESRx_VALID|ESR15_EAV)) == (ESRx_VALID|ESR15_EAV)) ? ear15 : 0); + + force_sig_info(info.si_signo, &info, current); +} /* end data_access_error() */ + +/*****************************************************************************/ +/* + * data store error - should only happen if accessing inactive or self-refreshing SDRAM + */ +asmlinkage void data_store_error(unsigned long esfr1, unsigned long esr15) +{ + die_if_kernel("-- Data Store Error --\n" + "ESR15 : %08lx\n", + esr15); + BUG(); +} /* end data_store_error() */ + +/*****************************************************************************/ +/* + * + */ +asmlinkage void division_exception(unsigned long esfr1, unsigned long esr0, unsigned long isr) +{ + siginfo_t info; + + die_if_kernel("-- Division Exception --\n" + "ESR0 : %08lx\n" + "ISR : %08lx\n", + esr0, isr); + + info.si_signo = SIGFPE; + info.si_code = FPE_INTDIV; + info.si_errno = 0; + info.si_addr = (void __user *) __frame->pc; + + force_sig_info(info.si_signo, &info, current); +} /* end division_exception() */ + +/*****************************************************************************/ +/* + * + */ +asmlinkage void compound_exception(unsigned long esfr1, + unsigned long esr0, unsigned long esr14, unsigned long esr15, + unsigned long msr0, unsigned long msr1) +{ + die_if_kernel("-- Compound Exception --\n" + "ESR0 : %08lx\n" + "ESR15 : %08lx\n" + "ESR15 : %08lx\n" + "MSR0 : %08lx\n" + "MSR1 : %08lx\n", + esr0, esr14, esr15, msr0, msr1); + BUG(); +} /* end compound_exception() */ + +void show_stack(struct task_struct *task, unsigned long *sp) +{ +} + +void show_trace_task(struct task_struct *tsk) +{ + printk("CONTEXT: stack=0x%lx frame=0x%p LR=0x%lx RET=0x%lx\n", + tsk->thread.sp, tsk->thread.frame, tsk->thread.lr, tsk->thread.sched_lr); +} + +static const char *regnames[] = { + "PSR ", "ISR ", "CCR ", "CCCR", + "LR ", "LCR ", "PC ", "_stt", + "sys ", "GR8*", "GNE0", "GNE1", + "IACH", "IACL", + "TBR ", "SP ", "FP ", "GR3 ", + "GR4 ", "GR5 ", "GR6 ", "GR7 ", + "GR8 ", "GR9 ", "GR10", "GR11", + "GR12", "GR13", "GR14", "GR15", + "GR16", "GR17", "GR18", "GR19", + "GR20", "GR21", "GR22", "GR23", + "GR24", "GR25", "GR26", "GR27", + "EFRM", "CURR", "GR30", "BFRM" +}; + +void show_regs(struct pt_regs *regs) +{ + unsigned long *reg; + int loop; + + printk("\n"); + show_regs_print_info(KERN_DEFAULT); + + printk("Frame: @%08lx [%s]\n", + (unsigned long) regs, + regs->psr & PSR_S ? "kernel" : "user"); + + reg = (unsigned long *) regs; + for (loop = 0; loop < NR_PT_REGS; loop++) { + printk("%s %08lx", regnames[loop + 0], reg[loop + 0]); + + if (loop == NR_PT_REGS - 1 || loop % 5 == 4) + printk("\n"); + else + printk(" | "); + } +} + +void die_if_kernel(const char *str, ...) +{ + char buffer[256]; + va_list va; + + if (user_mode(__frame)) + return; + + va_start(va, str); + vsnprintf(buffer, sizeof(buffer), str, va); + va_end(va); + + console_verbose(); + printk("\n===================================\n"); + printk("%s\n", buffer); + show_backtrace(__frame, 0); + + __break_hijack_kernel_event(); + do_exit(SIGSEGV); +} + +/*****************************************************************************/ +/* + * dump the contents of an exception frame + */ +static void show_backtrace_regs(struct pt_regs *frame) +{ + unsigned long *reg; + int loop; + + /* print the registers for this frame */ + printk("<-- %s Frame: @%p -->\n", + frame->psr & PSR_S ? "Kernel Mode" : "User Mode", + frame); + + reg = (unsigned long *) frame; + for (loop = 0; loop < NR_PT_REGS; loop++) { + printk("%s %08lx", regnames[loop + 0], reg[loop + 0]); + + if (loop == NR_PT_REGS - 1 || loop % 5 == 4) + printk("\n"); + else + printk(" | "); + } + + printk("--------\n"); +} /* end show_backtrace_regs() */ + +/*****************************************************************************/ +/* + * generate a backtrace of the kernel stack + */ +void show_backtrace(struct pt_regs *frame, unsigned long sp) +{ + struct pt_regs *frame0; + unsigned long tos = 0, stop = 0, base; + int format; + + base = ((((unsigned long) frame) + 8191) & ~8191) - sizeof(struct user_context); + frame0 = (struct pt_regs *) base; + + if (sp) { + tos = sp; + stop = (unsigned long) frame; + } + + printk("\nProcess %s (pid: %d)\n\n", current->comm, current->pid); + + for (;;) { + /* dump stack segment between frames */ + //printk("%08lx -> %08lx\n", tos, stop); + format = 0; + while (tos < stop) { + if (format == 0) + printk(" %04lx :", tos & 0xffff); + + printk(" %08lx", *(unsigned long *) tos); + + tos += 4; + format++; + if (format == 8) { + printk("\n"); + format = 0; + } + } + + if (format > 0) + printk("\n"); + + /* dump frame 0 outside of the loop */ + if (frame == frame0) + break; + + tos = frame->sp; + if (((unsigned long) frame) + sizeof(*frame) != tos) { + printk("-- TOS %08lx does not follow frame %p --\n", + tos, frame); + break; + } + + show_backtrace_regs(frame); + + /* dump the stack between this frame and the next */ + stop = (unsigned long) frame->next_frame; + if (stop != base && + (stop < tos || + stop > base || + (stop < base && stop + sizeof(*frame) > base) || + stop & 3)) { + printk("-- next_frame %08lx is invalid (range %08lx-%08lx) --\n", + stop, tos, base); + break; + } + + /* move to next frame */ + frame = frame->next_frame; + } + + /* we can always dump frame 0, even if the rest of the stack is corrupt */ + show_backtrace_regs(frame0); + +} /* end show_backtrace() */ + +/*****************************************************************************/ +/* + * initialise traps + */ +void __init trap_init (void) +{ +} /* end trap_init() */ diff --git a/arch/frv/kernel/uaccess.c b/arch/frv/kernel/uaccess.c new file mode 100644 index 000000000..374f88d6c --- /dev/null +++ b/arch/frv/kernel/uaccess.c @@ -0,0 +1,100 @@ +/* uaccess.c: userspace access functions + * + * Copyright (C) 2004 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * 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. + */ + +#include <linux/mm.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +/*****************************************************************************/ +/* + * copy a null terminated string from userspace + */ +long strncpy_from_user(char *dst, const char __user *src, long count) +{ + unsigned long max; + char *p, ch; + long err = -EFAULT; + + BUG_ON(count < 0); + + p = dst; + +#ifndef CONFIG_MMU + if ((unsigned long) src < memory_start) + goto error; +#endif + + if ((unsigned long) src >= get_addr_limit()) + goto error; + + max = get_addr_limit() - (unsigned long) src; + if ((unsigned long) count > max) { + memset(dst + max, 0, count - max); + count = max; + } + + err = 0; + for (; count > 0; count--, p++, src++) { + __get_user_asm(err, ch, src, "ub", "=r"); + if (err < 0) + goto error; + if (!ch) + break; + *p = ch; + } + + err = p - dst; /* return length excluding NUL */ + + error: + if (count > 0) + memset(p, 0, count); /* clear remainder of buffer [security] */ + + return err; + +} /* end strncpy_from_user() */ + +EXPORT_SYMBOL(strncpy_from_user); + +/*****************************************************************************/ +/* + * Return the size of a string (including the ending 0) + * + * Return 0 on exception, a value greater than N if too long + */ +long strnlen_user(const char __user *src, long count) +{ + const char __user *p; + long err = 0; + char ch; + + BUG_ON(count < 0); + +#ifndef CONFIG_MMU + if ((unsigned long) src < memory_start) + return 0; +#endif + + if ((unsigned long) src >= get_addr_limit()) + return 0; + + for (p = src; count > 0; count--, p++) { + __get_user_asm(err, ch, p, "ub", "=r"); + if (err < 0) + return 0; + if (!ch) + break; + } + + return p - src + 1; /* return length including NUL */ + +} /* end strnlen_user() */ + +EXPORT_SYMBOL(strnlen_user); diff --git a/arch/frv/kernel/vmlinux.lds.S b/arch/frv/kernel/vmlinux.lds.S new file mode 100644 index 000000000..7e958d829 --- /dev/null +++ b/arch/frv/kernel/vmlinux.lds.S @@ -0,0 +1,132 @@ +/* ld script to make FRV Linux kernel + * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>; + */ +OUTPUT_FORMAT("elf32-frv", "elf32-frv", "elf32-frv") +OUTPUT_ARCH(frv) +ENTRY(_start) + +#include <asm-generic/vmlinux.lds.h> +#include <asm/processor.h> +#include <asm/page.h> +#include <asm/cache.h> +#include <asm/thread_info.h> + +jiffies = jiffies_64 + 4; + +__page_offset = CONFIG_PAGE_OFFSET; /* start of area covered by struct pages */ +__kernel_image_start = __page_offset; /* address at which kernel image resides */ + +SECTIONS +{ + . = __kernel_image_start; + + /* discardable initialisation code and data */ + . = ALIGN(PAGE_SIZE); /* Init code and data */ + __init_begin = .; + + _sinittext = .; + .init.text : { + HEAD_TEXT +#ifndef CONFIG_DEBUG_INFO + INIT_TEXT + EXIT_TEXT + EXIT_DATA + *(.exitcall.exit) +#endif + } + _einittext = .; + + INIT_DATA_SECTION(8) + PERCPU_SECTION(L1_CACHE_BYTES) + + . = ALIGN(PAGE_SIZE); + __init_end = .; + + .trap : { + /* trap table management - read entry-table.S before modifying */ + . = ALIGN(8192); + __trap_tables = .; + *(.trap.user) + *(.trap.kernel) + . = ALIGN(4096); + *(.trap.break) + } + + /* Text and read-only data */ + . = ALIGN(4); + _text = .; + _stext = .; + .text : { + *(.text..start) + *(.text..entry) + *(.text..break) + *(.text..tlbmiss) + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT +#ifdef CONFIG_DEBUG_INFO + INIT_TEXT + EXIT_TEXT + *(.exitcall.exit) +#endif + *(.fixup) + *(.gnu.warning) + *(.exitcall.exit) + } = 0x9090 + + _etext = .; /* End of text section */ + + RODATA + + .rodata : { + *(.trap.vector) + + /* this clause must not be modified - the ordering and adjacency are imperative */ + __trap_fixup_tables = .; + *(.trap.fixup.user .trap.fixup.kernel) + + } + + EXCEPTION_TABLE(8) + + _sdata = .; + .data : { /* Data */ + INIT_TASK_DATA(THREAD_SIZE) + CACHELINE_ALIGNED_DATA(L1_CACHE_BYTES) + DATA_DATA + *(.data.*) + EXIT_DATA + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + /* GP section */ + . = ALIGN(L1_CACHE_BYTES); + _gp = . + 2048; + PROVIDE (gp = _gp); + + .sdata : { *(.sdata .sdata.*) } + + /* BSS */ + . = ALIGN(L1_CACHE_BYTES); + __bss_start = .; + + .sbss : { *(.sbss .sbss.*) } + .bss : { *(.bss .bss.*) } + .bss..stack : { *(.bss) } + + __bss_stop = .; + _end = . ; + . = ALIGN(PAGE_SIZE); + __kernel_image_end = .; + + STABS_DEBUG + DWARF_DEBUG + + .comment 0 : { *(.comment) } + + DISCARDS +} + +__kernel_image_size_no_bss = __bss_start - __kernel_image_start; |