/* ********************************************************************* * Broadcom Common Firmware Environment (CFE) * * CPU init module File: init_mips.S * * This module contains the vectors and lowest-level CPU startup * functions for CFE. * * 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" #ifdef _CFE_ #include "cfe_devfuncs.h" #else #define cfe_command_restart 0 #endif /* BCM63XX specific change. */ #include "bcm_hwdefs.h" /* ********************************************************************* * Macros ********************************************************************* */ #include "mipsmacros.h" /* ********************************************************************* * SETLEDS(a,b,c,d) * SETLEDS1(a,b,c,d) * * Sets the on-board LED display (if present). Two variants * of this routine are provided. If you're running KSEG1, * call the SETLEDS1 variant, else call SETLEDS. * * 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)) ; \ CALLINIT_KSEG0(init_table,R_INIT_SETLEDS) #define SETLEDS1(a,b,c,d) \ li a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ; \ CALLINIT_KSEG1(init_table,R_INIT_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 #ifdef __MIPSEB #define TEXTSECTION 0x2e746578 # ".tex", big-endian #else #define TEXTSECTION 0x7865742e # ".tex", little-endian #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. */ #if CFG_RUNFROMKSEG0 #define KSEGBASE K0BASE #else #define KSEGBASE K1BASE #endif /* ********************************************************************* * Names of registers used in this module ********************************************************************* */ #define RELOCOFFSET s8 /* $30 (fp) */ #define TEXTOFFSET t9 /* $25 (t9) */ #define MEMTOP t8 /* $24 (t8) */ #define TEXTBASE s7 /* $23 (s7) */ #undef BOOT_OFFSET #define BOOT_OFFSET s6 /* $22 (s6) */ .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 /* ********************************************************************* * Exception Vectors ********************************************************************* */ .text .set noreorder /* * Declare the actual vectors. This expands to code that * must be at the very beginning of the text segment. */ DECLARE_VECTOR(0x0000,vec_reset,cpu_reset) .set reorder /* ********************************************************************* * Some offsets depend on our current configuration ********************************************************************* */ #define RUNTIME_RELOC_START 0 #define RUNTIME_RELOC_STOP 0 /* ********************************************************************* * 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" #include "cfe.h" .org 0x570 .byte 'c','f','e','-','v',CFE_VER_MAJOR,CFE_VER_MINOR,CFE_VER_BUILD,BCM63XX_MAJOR,BCM63XX_MINOR # CFE version info for applications .org 0x580 # move past exception vectors /* * BCM963XX NVRAM Data Storage */ .globl nvram_data_storage nvram_data_storage: .word NVRAM_DATA_ID .space 0x400 .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_ 0 # [ 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_ cfe_size_ram # [ 15] R_INIT_SIZERAM /* ********************************************************************* * CPU Startup Code ********************************************************************* */ cpu_reset: /* * Start with GP as zero. Nobody should touch * this or set it to any other value until we're ready * to use it. This is used to tell when we should start * using relocated references in the init table, * so beware! (see CALLINIT_RELOC in mipsmacros.h) */ move gp,zero # start with no GP. .set noreorder bal 1f nop 1: nop .set reorder li BOOT_OFFSET, 0x1fff0000 and BOOT_OFFSET, ra #------------------------------------------------------------------------------ /* * Do low-level board initialization. This is our first * chance to customize the startup sequence. */ move a0, BOOT_OFFSET CALLINIT_KSEG1(init_table,R_INIT_EARLYINIT) SETLEDS1('H','E','L','O') CALLINIT_KSEG1(init_table,R_INIT_CPUINIT) #------------------------------------------------------------------------------ /* * Now, switch from KSEG1 to KSEG0 */ #if CFG_RUNFROMKSEG0 bal cpu_kseg0_switch #endif #------------------------------------------------------------------------------ /* * Now running on cpu0 in K0SEG. */ #if CFG_CMT /* * Check if the thread switch is required. If we are already * running on thread 1 this function will do nothing and just return * If we are running on thread 0 this function will take thread 1 * out of reset and put thread 0 to sleep waiting for singnal from * thread 1. */ CALLINIT_KSEG0(init_table,R_INIT_TP1_SWITCH) #endif #if CFG_INIT_DRAM SETLEDS('D','R','A','M') CALLINIT_KSEG0(init_table,R_INIT_DRAMINFO) move a0,v0 # pass these params CALLINIT_KSEG0(init_table,R_INIT_DRAMINIT) CALLINIT_KSEG0(init_table,R_INIT_SIZERAM) move k0,v0 # Save in k0 for now #else li k0,(CFG_DRAM_SIZE * 1024) #endif #------------------------------------------------------------------------------ #if CFG_BOOTRAM b have_ram # No RAM is ok if using emulator RAM #endif bne k0,zero,have_ram SETLEDS('R','A','M','X') # die here if no ram die1: b die1 have_ram: /* * If this is the 64-bit version, turn on the KX bit * to allow 64-bit accesses. */ #ifdef __long64 mfc0 t0,C0_SR or t0,t0,M_SR_KX mtc0 t0,C0_SR #endif #------------------------------------------------------------------------------ /* * K0 contains the RAM size (and therefore the top of RAM * offset). Start there, and subtract the amount of memory * we expect to use. If we have more than 256MB of * physical memory, work backwards from the 256MB * boundary. */ __CalcMemTop: li MEMTOP,256 # 256MB boundary bgt k0,MEMTOP,1f # use 256MB if k0 is greater move MEMTOP,k0 # otherwise keep top 1: sll MEMTOP,20 # make into byte amount li RELOCOFFSET,0 # not relocating, no offset li TEXTOFFSET,0 /* * 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) add gp,RELOCOFFSET #------------------------------------------------------------------------------ /* * Zero BSS */ SETLEDS('Z','B','S','S') LOADREL(a0,segment_table) __ZeroBss: LR v0,R_SEG_FBSS(a0) LR v1,R_SEG_END(a0) ADD v0,RELOCOFFSET # Relocate to actual data segment ADD v1,RELOCOFFSET 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 #------------------------------------------------------------------------------ /* * Copy code */ SETLEDS('C','O','D','E') LOADREL(a0,segment_table) __CopyCode: LR t1,R_SEG_FTEXT(a0) # destination address move TEXTBASE,t1 LR t2,R_SEG_FTEXT(a0) # Source address FIXUP (t2); LR t3,R_SEG_ETEXT(a0) FIXUP (t3); 1: LR t4,0(t2) # read one cache line LR t5,(REGSIZE*1)(t2) LR t6,(REGSIZE*2)(t2) LR t7,(REGSIZE*3)(t2) SR t4,0(t1) # write one cache line SR t5,(REGSIZE*1)(t1) SR t6,(REGSIZE*2)(t1) SR t7,(REGSIZE*3)(t1) add t1,REGSIZE*4 add t2,REGSIZE*4 bltu t2,t3,1b #------------------------------------------------------------------------------ /* * Copy initialized data */ #if (CFG_BOOTRAM == 0) SETLEDS('D','A','T','A') LOADREL(a0,segment_table) __CopyData: LR t1,R_SEG_FDATA(a0) FIXUP (t1); li t0,15 add t1,t0 not t0 and t1,t0 # t1 = _etext rounded up to 16-byte boundary LR t2,R_SEG_FDATA(a0) LR t3,R_SEG_EDATA(a0) ADD t2,RELOCOFFSET # Relocate to actual data segment ADD t3,RELOCOFFSET 1: LR t4,0(t1) # read one cache line LR t5,(REGSIZE*1)(t1) LR t6,(REGSIZE*2)(t1) LR t7,(REGSIZE*3)(t1) SR t4,0(t2) # write one cache line SR t5,(REGSIZE*1)(t2) SR t6,(REGSIZE*2)(t2) SR t7,(REGSIZE*3)(t2) add t1,(REGSIZE*4) add t2,(REGSIZE*4) bltu t2,t3,1b #endif #------------------------------------------------------------------------------ /* * Flush the cache, then switch to relocated code * We need to flush the cache since we just moved the code and * it may still live in our L1 DCache. We also need to * flush L2, since there are some rare times we run * uncached from DRAM, like when we start/stop a CPU. * * In the case of running completely uncached, don't flush the * cache. It should not have any dirty lines in it, but you * never know... */ __GoRelo: #if CFG_RUNFROMKSEG0 SETLEDS('L','1','2','F') li a0,CFE_CACHE_FLUSH_D | CFE_CACHE_FLUSH_L2 CALLINIT_KSEG0(init_table,R_INIT_CACHEOPS) li a0,CFE_CACHE_INVAL_I CALLINIT_KSEG0(init_table,R_INIT_CACHEOPS) #endif /* CFG_RUNFROMKSEG0 */ la t0,gorelo # Now jump to an address code was compiled for j t0 # and go there gorelo: nop li BOOT_OFFSET, 0 # no longer running at offset /* * Remember total amount of memory. This is *still* in k0 * after all this time. Hopefully. */ __MemVars: SR k0,mem_totalsize SR RELOCOFFSET,mem_datareloc move v0,zero LOADREL(a0,segment_table) # trashed by l2 cache flush LR v0,R_SEG_FDATA(a0) ADD v0,RELOCOFFSET LR v1,R_SEG_END(a0) ADD v1,RELOCOFFSET SR v0,mem_bottomofmem SR v1,mem_heapstart add v1,(CFG_HEAP_SIZE*1024) # Otherwise add v1,STACK_SIZE SR v1,mem_topofmem SR TEXTOFFSET,mem_textreloc /* At this point it's safe to use the CALLINIT_RELOC macro */ LR t1,R_SEG_FTEXT(a0) FIXUP (t1); LR t0,R_SEG_ETEXT(a0) FIXUP (t0); sub t0,t0,t1 SR t0,mem_textsize add t1,TEXTOFFSET SR t1,mem_textbase #------------------------------------------------------------------------------ /* * 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_RELOC(init_table,R_INIT_CMDSTART) # should not return /* ********************************************************************* * 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) /* * This function runs in RAM so BOOT_OFFSET is 0. It is called from * C which could have modified the BOOT_OFFSET register, s6. */ li BOOT_OFFSET, 0 /* * 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_RELOC(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) LR t0,0(sp) # entry point j t0 # go for it. END(RunProgram) /* ********************************************************************* * 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) /* ********************************************************************* * End ********************************************************************* */