Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

23
arch/frv/kernel/Makefile Normal file
View file

@ -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

View file

@ -0,0 +1,108 @@
/*
* 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_EXEC_DOMAIN, thread_info, exec_domain);
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);
OFFSET(TI_RESTART_BLOCK, thread_info, restart_block);
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();
}

792
arch/frv/kernel/break.S Normal file
View file

@ -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

189
arch/frv/kernel/cmode.S Normal file
View file

@ -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

View file

@ -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

463
arch/frv/kernel/dma.c Normal file
View file

@ -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);

View file

@ -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

1519
arch/frv/kernel/entry.S Normal file

File diff suppressed because it is too large Load diff

113
arch/frv/kernel/frv_ksyms.c Normal file
View file

@ -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);

242
arch/frv/kernel/futex.c Normal file
View file

@ -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() */

215
arch/frv/kernel/gdb-io.c Normal file
View file

@ -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() */

55
arch/frv/kernel/gdb-io.h Normal file
View file

@ -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 */

2149
arch/frv/kernel/gdb-stub.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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)

638
arch/frv/kernel/head.S Normal file
View file

@ -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

50
arch/frv/kernel/head.inc Normal file
View file

@ -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

View file

@ -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]);
}

View file

@ -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]);
}

View file

@ -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]);
}

159
arch/frv/kernel/irq.c Normal file
View file

@ -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
}

59
arch/frv/kernel/local.h Normal file
View file

@ -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 */

View file

@ -0,0 +1 @@
#include <asm-generic/local64.h>

27
arch/frv/kernel/module.c Normal file
View file

@ -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.
*/

View file

@ -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);

352
arch/frv/kernel/pm.c Normal file
View file

@ -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

281
arch/frv/kernel/process.c Normal file
View file

@ -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,
&current->thread.user->f,
sizeof(current->thread.user->f));
return 1;
}

377
arch/frv/kernel/ptrace.c Normal file
View file

@ -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);
}

1178
arch/frv/kernel/setup.c Normal file

File diff suppressed because it is too large Load diff

442
arch/frv/kernel/signal.c Normal file
View file

@ -0,0 +1,442 @@
/* 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_thread_info()->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 rsig, sig = ksig->sig;
set_fs(USER_DS);
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
rsig = sig;
if (sig < 32 &&
__current_thread_info->exec_domain &&
__current_thread_info->exec_domain->signal_invmap)
rsig = __current_thread_info->exec_domain->signal_invmap[sig];
if (__put_user(rsig, &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 rsig, sig = ksig->sig;
set_fs(USER_DS);
frame = get_sigframe(ksig, sizeof(*frame));
if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
return -EFAULT;
rsig = sig;
if (sig < 32 &&
__current_thread_info->exec_domain &&
__current_thread_info->exec_domain->signal_invmap)
rsig = __current_thread_info->exec_domain->signal_invmap[sig];
if (__put_user(rsig, &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() */

373
arch/frv/kernel/sleep.S Normal file
View file

@ -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

489
arch/frv/kernel/switch_to.S Normal file
View file

@ -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

44
arch/frv/kernel/sys_frv.c Normal file
View file

@ -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));
}

221
arch/frv/kernel/sysctl.c Normal file
View file

@ -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);

122
arch/frv/kernel/time.c Normal file
View file

@ -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);
}

646
arch/frv/kernel/traps.c Normal file
View file

@ -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() */

100
arch/frv/kernel/uaccess.c Normal file
View file

@ -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);

View file

@ -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;