/* ********************************************************************* * Broadcom Common Firmware Environment (CFE) * * CPU init module File: init_ram.S * * This module contains the vectors and lowest-level CPU startup * functions for CFE. * * This is very similar to "init_mips.S" but is used when * you want to locate CFE in DRAM, loading it like an * application program. * * Author: Mitch Lichtenberg (mpl@broadcom.com) * ********************************************************************* * * Copyright 2000,2001,2002,2003 * Broadcom Corporation. All rights reserved. * * This software is furnished under license and may be used and * copied only in accordance with the following terms and * conditions. Subject to these conditions, you may download, * copy, install, use, modify and distribute modified or unmodified * copies of this software in source and/or binary form. No title * or ownership is transferred hereby. * * 1) Any source code used, modified or distributed must reproduce * and retain this copyright notice and list of conditions * as they appear in the source file. * * 2) No right is granted to use any trade name, trademark, or * logo of Broadcom Corporation. The "Broadcom Corporation" * name may not be used to endorse or promote products derived * from this software without the prior written permission of * Broadcom Corporation. * * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. ********************************************************************* */ #include "sbmips.h" #include "exception.h" #include "bsp_config.h" #include "cpu_config.h" #include "cfe_devfuncs.h" /* ********************************************************************* * Check some stuff ********************************************************************* */ #if CFG_RELOC #error "RAM version is not compatible with relocation." #endif #if !(CFG_RUNFROMKSEG0) #error "RAM version should be run cached" #endif #if CFG_MULTI_CPUS #error "Multiple CPUs not compatible with RAM version" #endif /* ********************************************************************* * Macros ********************************************************************* */ #include "mipsmacros.h" /* ********************************************************************* * SETLEDS(a,b,c,d) * * Sets the on-board LED display (if present). * * Input parameters: * a,b,c,d - four ASCII characters (literal constants) * * Return value: * a0,k1,ra trashed ********************************************************************* */ #define SETLEDS(a,b,c,d) \ li a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ; \ jal board_setleds ; /* ********************************************************************* * Other constants ********************************************************************* */ /* * This is the size of the stack, rounded to KByte boundaries. */ #ifndef CFG_STACK_SIZE #error "CFG_STACK_SIZE not defined" #else #define STACK_SIZE ((CFG_STACK_SIZE+1023) & ~1023) #endif /* * Duplicates from cfe_iocb.h -- warning! */ #define CFE_CACHE_FLUSH_D 1 #define CFE_CACHE_INVAL_I 2 #define CFE_CACHE_INVAL_D 4 #define CFE_CACHE_INVAL_L2 8 #define CFE_CACHE_FLUSH_L2 16 #define CFE_CACHE_INVAL_RANGE 32 #define CFE_CACHE_FLUSH_RANGE 64 /* * To make life easier reading this code, define "KSEGBASE" * to either K0BASE or K1BASE depending on whether we're running * uncached. */ #define KSEGBASE K0BASE /* RAM version always cached */ /* ********************************************************************* * Names of registers used in this module ********************************************************************* */ .sdata #include "initdata.h" /* declare variables we use here */ #if CFG_MULTI_CPUS .globl cfe_spinlock cfe_spinlock: .word 0 #endif .extern _fdata .extern _edata .extern _etext /* ********************************************************************* * uninitialized data ********************************************************************* */ .bss .comm __junk,4 .text .set noreorder /* ********************************************************************* * CFE Entry Point (used by OS boot loaders and such) ********************************************************************* */ .set noreorder .globl vec_reset vec_reset: b cpu_reset nop vec_apientry: b cpu_apientry nop .word CFE_EPTSEAL .word CFE_EPTSEAL .set reorder /* ********************************************************************* * Segment Table. * * Addresses of data segments and of certain routines we're going * to call from KSEG1. These are here mostly for the embedded * PIC case, since we can't count on the 'la' instruction to * do the expected thing (the assembler expands it into a macro * for doing GP-relative stuff, and the code is NOT GP-relative. * So, we (relocatably) get the offset of this table and then * index within it. * * Pointer values in this segment will be relative to KSEG0 for * cached versions of CFE, so we need to OR in K1BASE in the * case of calling to a uncached address. * * The LOADREL macro handles most of the nastiness here. ********************************************************************* */ #include "segtable.h" .globl segment_table segment_table: _LONG_ _etext # [ 0] End of text (R_SEG_ETEXT) _LONG_ _fdata # [ 1] Beginning of data (R_SEG_FDATA) _LONG_ _edata # [ 2] End of data (R_SEG_EDATA) _LONG_ _end # [ 3] End of BSS (R_SEG_END) _LONG_ _ftext # [ 4] Beginning of text (R_SEG_FTEXT) _LONG_ _fbss # [ 5] Beginning of BSS (R_SEG_FBSS) _LONG_ _gp # [ 6] Global Pointer (R_SEG_GP) _LONG_ 0 # [ 7] Beginning of reloc entries _LONG_ 0 # [ 8] End of reloc entries _LONG_ cpu_apientry # [ 9] R_SEG_APIENTRY /* ********************************************************************* * Init Table. * * This is like segment_table except it contains pointers to * routines used during initialization. It serves both as a * table for doing PIC stuff and also to separate out * machine-specific init routines. * * The CALLINIT_xxx macros are used to call routines in this table. ********************************************************************* */ .globl init_table init_table: _LONG_ board_earlyinit # [ 0] R_INIT_EARLYINIT _LONG_ board_setleds # [ 1] R_INIT_SETLEDS _LONG_ board_draminfo # [ 2] R_INIT_DRAMINFO _LONG_ CPUCFG_CPUINIT # [ 3] R_INIT_CPUINIT _LONG_ CPUCFG_ALTCPU_START1 # [ 4] R_INIT_ALTCPU_START1 _LONG_ CPUCFG_ALTCPU_START2 # [ 5] R_INIT_ALTCPU_START2 _LONG_ CPUCFG_ALTCPU_RESET # [ 6] R_INIT_ALTCPU_RESET _LONG_ CPUCFG_CPURESTART # [ 7] R_INIT_CPURESTART _LONG_ CPUCFG_DRAMINIT # [ 8] R_INIT_DRAMINIT _LONG_ CPUCFG_CACHEOPS # [ 9] R_INIT_CACHEOPS _LONG_ CPUCFG_TLBHANDLER # [ 10] R_INIT_TLBHANDLER _LONG_ cfe_main # [ 11] R_INIT_CMDSTART _LONG_ cfe_command_restart # [ 12] R_INIT_CMDRESTART _LONG_ cfe_doxreq # [ 13] R_INIT_DOXREQ _LONG_ CPUCFG_TP1_SWITCH # [ 14] R_INIT_TP1_SWITCH _LONG_ bcmcore_null # [ 15] R_INIT_SIZERAM /* ********************************************************************* * CPU Startup Code ********************************************************************* */ cpu_reset: #------------------------------------------------------------------------------ /* * Do low-level board initialization. This is our first * chance to customize the startup sequence. */ CALLINIT_KSEG0(init_table,R_INIT_EARLYINIT) SETLEDS('H','E','L','O') #------------------------------------------------------------------------------ /* * DRAM is now running, and we're alive in cacheable memory * on cpu0 in K0SEG. Set up GP. */ LOADREL(a0,segment_table) LR gp,R_SEG_GP(a0) #------------------------------------------------------------------------------ /* * Zero BSS */ SETLEDS('Z','B','S','S') LOADREL(a0,segment_table) __ZeroBss: LR v0,R_SEG_FBSS(a0) LR v1,R_SEG_END(a0) 1: SR zero,0(v0) # Zero one cacheline at a time SR zero,(REGSIZE*1)(v0) SR zero,(REGSIZE*2)(v0) SR zero,(REGSIZE*3)(v0) add v0,REGSIZE*4 blt v0,v1,1b #------------------------------------------------------------------------------ li k0,256 # memory size in megabytes #ifdef __long64 mfc0 t0,C0_SR or t0,t0,M_SR_KX mtc0 t0,C0_SR #endif #------------------------------------------------------------------------------ /* * Remember total amount of memory. This is *still* in k0 * after all this time. Hopefully. */ __MemVars: SR k0,mem_totalsize SR zero,mem_datareloc move v0,zero LOADREL(a0,segment_table) # trashed by l2 cache flush LR v0,R_SEG_FTEXT(a0) # bottom = beginning of text LR v1,R_SEG_END(a0) SR v0,mem_bottomofmem SR v1,mem_heapstart add v1,(CFG_HEAP_SIZE*1024) # Otherwise add v1,STACK_SIZE SR v1,mem_topofmem SR zero,mem_textreloc LR t1,R_SEG_FTEXT(a0) LR t0,R_SEG_ETEXT(a0) sub t0,t0,t1 SR t0,mem_textsize SR t1,mem_textbase #------------------------------------------------------------------------------ #if CFG_MULTI_CPUS /* * Let secondary CPU(s) run their idle loops. Set the * mailbox register to our relocation factor so we can read * it out of the mailbox register and relocate GP properly. */ move a0,zero CALLINIT_KSEG0(init_table,R_INIT_ALTCPU_START2) #endif /* * Stash away some config register stuff */ mfc0 v0,C0_PRID SR v0,cpu_prid #------------------------------------------------------------------------------ /* * Set up the "C" stack and jump to the main routine. */ SETLEDS('M','A','I','N') LR sp,mem_heapstart ADD sp,((CFG_HEAP_SIZE*1024)+STACK_SIZE - 8) li a0,0 # call as "cfe_main(0,0)" li a1,0 CALLINIT_KSEG0(init_table,R_INIT_CMDSTART) # should not return /* * Terminate the simulator. */ crash_sim: li $2,1 li $4,0 syscall 0xCA b cpu_reset /* ********************************************************************* * CFE_WARMSTART * * Restart the command interpreter * * Input parameters: * A0 - command status * nothing (GP has already been set up for us) * * Return value: * nothing ********************************************************************* */ LEAF(cfe_warmstart) SR a0,0(sp) # store on old stack LOADREL(v0,init_table) LR v0,R_INIT_CPURESTART(v0) jal v0 # had better not trash GP or K1 LR a0,0(sp) LR sp,mem_heapstart ADD sp,((CFG_HEAP_SIZE*1024)+STACK_SIZE - 8) /* * If someone called the API to do a warm start, clear the * spin lock, since the call will never return. */ #if CFG_MULTI_CPUS SPIN_UNLOCK(cfe_spinlock,t0) #endif CALLINIT_KSEG0(init_table,R_INIT_CMDRESTART) # should not return END(cfe_warmstart) /* ********************************************************************* * CFE_FLUSHCACHE * * Perform certain cache operations * * Input parameters: * a0 - flags (CFE_CACHE_xxx flags, or zero for a default) * a1,a2 - start/end of range for "range invalidate" operations * (not used otherwise) * * Return value: * nothing ********************************************************************* */ LEAF(_cfe_flushcache) sub sp,32 SR ra,0(sp) SR a0,8(sp) SR s0,16(sp) SR v1,24(sp) CALLINIT_KSEG0(init_table,R_INIT_CACHEOPS) LR v1,24(sp) LR s0,16(sp) LR a0,8(sp) LR ra,0(sp) add sp,32 j ra END(_cfe_flushcache) /* ********************************************************************* * CFE_LAUNCH * * Start the user program. The program is passed a handle * that must be passed back when calling the firmware. * * Parameters passed to the called program are as follows: * * a0 - CFE handle * a1 - entry vector * a2 - reserved, will be 0 * a3 - entrypoint signature. * * Input parameters: * a0 - entry vector * * Return value: * does not return ********************************************************************* */ LEAF(cfe_launch) sub sp,8 SR a0,0(sp) /* * Mask all interrupts. */ mfc0 v0,C0_SR # Get current interrupt flag li v1,M_SR_IE # master interrupt control not v1 # disable interrupts and v0,v1 # SR now has IE=0 mtc0 v0,C0_SR # put back into CP0 /* * Flush the D-Cache, since the program we loaded is "data". * Invalidate the I-Cache, so that addresses in the program * region will miss and need to be filled from the data we * just flushed above. */ li a0,CFE_CACHE_FLUSH_D|CFE_CACHE_INVAL_I CALLINIT_KSEG0(init_table,R_INIT_CACHEOPS) /* * Set things up for launching the program. Pass the * handle in A0 - apps need to remember that and pass it * back. */ j RunProgram END(cfe_launch) /* * This is a nice place to set a breakpoint. */ LEAF(RunProgram) LOADREL(a2,segment_table) LR a2,R_SEG_APIENTRY(a2) # A2 = code entry move t0,a0 # move a1,zero # A1 = 0 move a0,gp # A0 = handle li a3,CFE_EPTSEAL # A3 = entrypoint signature LR t0,0(sp) # entry point j t0 # go for it. END(RunProgram) /* ********************************************************************* * CFE_LEDS * * Set the on-board LEDs. * * Input parameters: * a0 - LEDs * * Return value: * nothing ********************************************************************* */ LEAF(cfe_leds) j board_setleds # jump to BSP routine END(cfe_leds) /* ********************************************************************* * TLB Fill Exeption Handler ********************************************************************* */ cpu_tlbfill: move k0,ra # Save, we're about to trash LOADREL(k1,init_table) # Load offset of init table LR k1,R_INIT_TLBHANDLER(k1) # Get entry from table move ra,k0 # restore trashed ra j k1 # Dispatch to handler /* ********************************************************************* * XTLB Fill Exception Handler ********************************************************************* */ cpu_xtlbfill: j _exc_entry /* ********************************************************************* * Cache Error Exception Handler ********************************************************************* */ cpu_cacheerr: j _exc_entry /* ********************************************************************* * General Exception Handler ********************************************************************* */ cpu_exception: j _exc_entry /* ********************************************************************* * General Interrupt Handler ********************************************************************* */ cpu_interrupt: j _exc_entry /* ********************************************************************* * EJTAG Debug Exception Handler ********************************************************************* */ cpu_ejtag: j cpu_reset /* ********************************************************************* * cpu_apientry(handle,iocb) * * API entry point for external apps. * * Input parameters: * a0 - firmware handle (used to determine the location of * our relocated data) * a1 - pointer to IOCB to execute * * Return value: * v0 - return code, 0 if ok ********************************************************************* */ #define _regidx(x) ((x)*8) #define CAE_SRSAVE _regidx(0) #define CAE_GPSAVE _regidx(1) #define CAE_RASAVE _regidx(2) #define CAE_S0SAVE _regidx(3) #define CAE_S1SAVE _regidx(4) #define CAE_S2SAVE _regidx(5) #define CAE_S3SAVE _regidx(6) #define CAE_S4SAVE _regidx(7) #define CAE_S5SAVE _regidx(8) #define CAE_S6SAVE _regidx(9) #define CAE_S7SAVE _regidx(10) #define CAE_STKSIZE _regidx(11) LEAF(cpu_apientry) sub sp,CAE_STKSIZE # Make room for our stuff mfc0 v0,C0_SR # Get current interrupt flag SR v0,CAE_SRSAVE(sp) # save on stack li t0,M_SR_IE # master interrupt control not t0 # disable interrupts and v0,t0 # SR now has IE=0 mtc0 v0,C0_SR # put back into CP0 SR gp,CAE_GPSAVE(sp) # save GP SR ra,CAE_RASAVE(sp) # and old RA SR s0,CAE_S0SAVE(sp) SR s1,CAE_S1SAVE(sp) SR s2,CAE_S2SAVE(sp) SR s3,CAE_S3SAVE(sp) SR s4,CAE_S4SAVE(sp) SR s5,CAE_S5SAVE(sp) SR s6,CAE_S6SAVE(sp) SR s7,CAE_S7SAVE(sp) move gp,a0 # set up new GP move a0,a1 # A0 points at IOCB #if CFG_MULTI_CPUS SPIN_LOCK(cfe_spinlock,t0,t1) #endif CALLINIT_KSEG0(init_table,R_INIT_DOXREQ) # should not return #if CFG_MULTI_CPUS SPIN_UNLOCK(cfe_spinlock,t0) #endif # # Restore the saved registers. # LR s7,CAE_S7SAVE(sp) LR s6,CAE_S6SAVE(sp) LR s5,CAE_S5SAVE(sp) LR s4,CAE_S4SAVE(sp) LR s3,CAE_S3SAVE(sp) LR s2,CAE_S2SAVE(sp) LR s1,CAE_S1SAVE(sp) LR s0,CAE_S0SAVE(sp) LR ra,CAE_RASAVE(sp) # unwind the stack LR gp,CAE_GPSAVE(sp) LR t0,CAE_SRSAVE(sp) # old interrupt mask add sp,CAE_STKSIZE # restore old stack pointer mtc0 t0,C0_SR # restore interrupts j ra nop END(cpu_apientry) /* ********************************************************************* * CPU_KSEG0_SWITCH * * Hack the return address so we will come back in KSEG0 * * Input parameters: * nothing * * Return value: * nothing ********************************************************************* */ LEAF(cpu_kseg0_switch) and ra,(K0SIZE-1) or ra,K0BASE jr ra END(cpu_kseg0_switch) /* ********************************************************************* * _GETSTATUS() * * Read the STATUS register into v0 * * Input parameters: * nothing * * Return value: * v0 - Status register ********************************************************************* */ LEAF(_getstatus) mfc0 v0,C0_SR j ra END(_getstatus) /* ********************************************************************* * _SETSTATUS() * * Set the STATUS register to the value in a0 * * Input parameters: * nothing * * Return value: * v0 - Status register ********************************************************************* */ LEAF(_setstatus) mtc0 a0,C0_SR j ra END(_setstatus) /* ********************************************************************* * _GETCAUSE() * * Read the CAUSE register into v0 * * Input parameters: * nothing * * Return value: * v0 - Cause register ********************************************************************* */ LEAF(_getcause) mfc0 v0,C0_CAUSE j ra END(_getcause) /* ********************************************************************* * _GETTICKS() * * Read the COUNT register into v0 * * Input parameters: * nothing * * Return value: * v0 - count register ********************************************************************* */ LEAF(_getticks) mfc0 v0,C0_COUNT j ra END(_getticks) /* ********************************************************************* * _SETALARM(ticks) * * Set the C0_Compare register from a0 * * Input parameters: * a0 - compare register * * Return value: * none ********************************************************************* */ LEAF(_setalarm) mtc0 a0,C0_COMPARE j ra END(_setalarm) /* ********************************************************************* * _SETCONTEXT() * * Set the CONTEXT register. * * Input parameters: * a0 - context * * Return value: * nothing ********************************************************************* */ LEAF(_setcontext) mtc0 a0,C0_CTEXT j ra END(_setcontext) /* ********************************************************************* * _GETSEGTBL() * * Return the address of the segment table. We use this * to display the startup messages. * * You can't just address the table from C because it lives * in the text segment. * * Input parameters: * nothing * * Return value: * address of table ********************************************************************* */ LEAF(_getsegtbl) move t0,ra LOADREL(v0,segment_table) move ra,t0 j ra END(_getsegtbl) /* ********************************************************************* * _wbflush() * * Flush the write buffer. This is probably not necessary * on SiByte CPUs, but we have it for completeness. * * Input parameters: * nothing * * Return value: * nothing ********************************************************************* */ LEAF(_wbflush) sync /* drain the buffers */ la t0,__junk /* do an uncached read to force it out */ or t0,K1BASE lw zero,0(t0) j ra END(_wbflush) /* ********************************************************************* * End ********************************************************************* */