1 /* *********************************************************************
2 * Broadcom Common Firmware Environment (CFE)
4 * CPU init module File: sb1250_altcpu.S
6 * Secondary core startup routines for CFE
8 * Author: Mitch Lichtenberg (mpl@broadcom.com)
10 *********************************************************************
12 * Copyright 2000,2001,2002,2003
13 * Broadcom Corporation. All rights reserved.
15 * This software is furnished under license and may be used and
16 * copied only in accordance with the following terms and
17 * conditions. Subject to these conditions, you may download,
18 * copy, install, use, modify and distribute modified or unmodified
19 * copies of this software in source and/or binary form. No title
20 * or ownership is transferred hereby.
22 * 1) Any source code used, modified or distributed must reproduce
23 * and retain this copyright notice and list of conditions
24 * as they appear in the source file.
26 * 2) No right is granted to use any trade name, trademark, or
27 * logo of Broadcom Corporation. The "Broadcom Corporation"
28 * name may not be used to endorse or promote products derived
29 * from this software without the prior written permission of
30 * Broadcom Corporation.
32 * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
33 * IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
34 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
35 * PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
36 * SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
37 * PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
38 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
39 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
40 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
41 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
42 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
43 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
44 * THE POSSIBILITY OF SUCH DAMAGE.
45 ********************************************************************* */
48 #include "exception.h"
50 #include "bsp_config.h"
53 #include "cfe_devfuncs.h"
55 #define CFE_EPTSEAL 0x43464531
58 #include "sb1250_defs.h"
59 #include "sb1250_regs.h"
60 #include "sb1250_scd.h"
62 #include "cpu_config.h"
64 /* *********************************************************************
66 ********************************************************************* */
68 #include "mipsmacros.h"
70 #define SETLEDS1(a,b,c,d) \
71 li a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ; \
72 CALLINIT_KSEG1(altcpu_table,R_ALT_SETLEDS)
73 #define SETLEDS(a,b,c,d) \
74 li a0,(((a)<<24)|((b)<<16)|((c)<<8)|(d)) ; \
75 CALLINIT_KSEG0(altcpu_table,R_ALT_SETLEDS)
78 /* *********************************************************************
80 ********************************************************************* */
85 * Initial start addresses for secondary CPUs
88 .globl cpu_startvectors
90 _VECT_ 0 # cpu #0 (not used)
96 * Initial values for SP, GP, and A1 (user argument)
100 _VECT_ 0 # cpu #0 (not used)
106 _VECT_ 0 # cpu #0 (not used)
112 _VECT_ 0 # cpu #0 (not used)
118 .extern mem_datareloc
121 /* *********************************************************************
124 * This table contains pointers to routines in other modules.
125 * we do things this way so we can stay position-independent and
126 * also avoid problems with the limitations of relative branching.
127 ********************************************************************* */
133 #define R_ALT_CPUINIT _TBLIDX(0)
134 #define R_ALT_L1CINIT _TBLIDX(1)
135 #define R_ALT_SETLEDS _TBLIDX(2)
136 #define R_ALT_GP _TBLIDX(3)
137 #define R_ALT_APIENTRY _TBLIDX(4)
140 _LONG_ sb1_cpu_init # [ 0] R_ALT_CPUINIT
141 _LONG_ sb1250_l1cache_init # [ 1] R_ALT_L1CINIT
142 _LONG_ board_setleds # [ 2] R_ALT_SETLEDS
143 _LONG_ _gp # [ 3] R_ALT_GP
144 _LONG_ cpu_apientry # [ 4] R_ALT_APIENTRY
147 /* *********************************************************************
148 * ALTCPU_KSEG1_SWITCH
150 * Hack the return address so we will come back in KSEG1 (uncached)
157 ********************************************************************* */
159 LEAF(altcpu_kseg1_switch)
165 END(altcpu_kseg1_switch)
167 /* *********************************************************************
168 * ALTCPU_KSEG0_SWITCH
170 * Hack the return address so we will come back in KSEG0
177 ********************************************************************* */
179 LEAF(altcpu_kseg0_switch)
185 END(altcpu_kseg0_switch)
187 /* *********************************************************************
188 * SB1250_ALTCPU_START1
190 * Start secondary processor(s). These processors will start
191 * running the code at ALTCPU_RESET (see below). We wait here
192 * for the secondary processor(s) to finish their cache
193 * initialization and then return.
195 * This routine is normally run from KSEG1
202 ********************************************************************* */
204 LEAF(sb1250_altcpu_start1)
207 * Don't do this if we have only one CPU. This way we can
208 * support running the multiprocessor version of CFE
209 * with only one core.
212 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
213 ld t0,(t0) # Get system revision
214 dsrl t0,S_SYS_PART # Shift part # to low bits
215 dsrl t0,8 # isolate CPU part of number
216 and t0,0x0F # T0 = number of CPUs
217 bgt t0,1,1f # Keep going if more than one CPU
218 j ra # Go back home, nothing to do
222 * Clear out our mailbox registers (both CPUs)
225 la a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CLR_CPU))
226 dli t0,-1 # clear all 64 bits
228 la a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CLR_CPU))
232 la a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
234 dli t1,M_SYS_CPU_RESET_1 # Reset mask
235 not t1 # clear this bit
236 and t0,t1 # New value to write
237 sd t0,0(a0) # CPU1 is now running
240 * Wait for the other CPU to ring our doorbell
244 1: la a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CPU));
245 ld t0,(a0) # Read mailbox
246 beq t0,zero,1b # Loop till the bit is set
249 * Clear the mailbox to dismiss the pending interrupts
252 la a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_CLR_CPU))
253 dli t0,-1 # clear all 64 bits
257 * Okay, it's safe to return
262 END(sb1250_altcpu_start1)
264 /* *********************************************************************
265 * SB1250_ALTCPU_START2
267 * Finish startup of secondary processor(s) - we pass the relocation
268 * offset to the other CPUs here, and the CPUs relocate their
269 * data segments and go to the idle loop.
271 * This routine is normally run from KSEG0
274 * a0 - data relocation offset (0=none)
278 ********************************************************************* */
280 LEAF(sb1250_altcpu_start2)
283 * Don't do this if we have only one CPU.
286 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
287 ld t0,(t0) # Get system revision
288 dsrl t0,S_SYS_PART # Shift part # to low bits
289 dsrl t0,8 # isolate CPU part of number
290 and t0,0x0F # T0 = number of CPUs
291 bgt t0,1,1f # Keep going if more than one CPU
292 j ra # Go back home, nothing to do
296 * Let secondary CPU(s) run their idle loops. Set the
297 * mailbox register to our relocation factor so we can read
298 * it out of the mailbox register and relocate GP properly.
301 la t1,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_SET_CPU))
302 or t0,a0,1 # hack - make sure reloc is nonzero
303 sd t0,0(t1) # Write to mailbox register
307 END(sb1250_altcpu_start2)
309 /* *********************************************************************
312 * Kill a secondary CPU, causing it to return to the idle
313 * loop. We do this by switching to uncached mode,
314 * asserting RESET on the other CPU, and then re-run
315 * ALTCPU_START again.
322 ********************************************************************* */
324 LEAF(sb1250_altcpu_kill)
327 * Don't do this if we have only one CPU.
330 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
331 ld t0,(t0) # Get system revision
332 dsrl t0,S_SYS_PART # Shift part # to low bits
333 dsrl t0,8 # isolate CPU part of number
334 and t0,0x0F # T0 = number of CPUs
335 bgt t0,1,1f # Keep going if more than one CPU
336 j ra # Go back home, nothing to do
340 * More than one CPU, go ahead...
343 move t7,ra # save RA, we'll make calls
345 #ifdef _SB1250_PASS1_WORKAROUNDS_
347 # Not sure what we need to do here wrt cacheability of
348 # the genbus space, if anything. Some portion of CPU1's
349 # istream will come from L1, but the data should all be from
350 # DRAM. These references will be cacheable noncoherent,
351 # should we worry if cpu0 is coherent shared at this time?
357 # XXX For now, this only works on dual-CPU machines.
358 # XXX some work needs to be done to handle more than 2 cores.
361 move t0,zero # zero the start address
362 la t1,cpu_startvectors
363 SR t0,REGSIZE(t1) # Reset address of CPU1
366 # Flush the D cache to ensure that the write above made it
370 jal sb1250_l1cache_flush_d # uses t0, t2, t3
373 # Switch to KSEG1 to quiesce our cache activity.
376 bal altcpu_kseg1_switch # switch to uncached mode
379 # Force CPU1 into reset
382 li a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
384 dli t1,M_SYS_CPU_RESET_1 # Reset mask
385 or t0,t1 # New value to write
386 sd t0,0(a0) # CPU1 is now in reset
389 # Not sure how long we're supposed to wait.
400 bal sb1250_altcpu_start1
403 # It's safe to be cached again.
406 bal altcpu_kseg0_switch
409 # At this point, CPU1 is waiting for us to indicate that it's
410 # okay to use memory again. Ring its doorbell.
413 la a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_SET_CPU))
419 # CPU1 is back in our control.
428 END(sb1250_altcpu_kill)
431 /* *********************************************************************
434 * Start address for secondary CPU(s) - do the initialization of
435 * the local CPU and then notify CPU0 that we're done.
437 * This routine is called in KSEG1.
440 * t0 - CPU identifier
444 ********************************************************************* */
447 LEAF(sb1250_altcpu_reset)
450 mfc0 t0,C0_PRID # get CPU PRID register
451 and t0,t0,0xe000000 # determine cpu number
452 beq t0,zero,iscpu0 # go if on CPU0
455 * Note: we should never get to the CPU1 code if we're
456 * with only one CPU. Theoretically, nobody got past the
457 * check in altcpu_start. But, just in case, if we
458 * get here and we're on CPU1, and we supposedly only
459 * have one CPU, reset CPU1.
462 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
463 ld t0,(t0) # Get system revision
464 dsrl t0,S_SYS_PART # Shift part # to low bits
465 dsrl t0,8 # isolate CPU part of number
466 and t0,0x0F # T0 = number of CPUs
467 beq t0,1,iscpu0 # If only one CPU, kill off CPU1
471 * Initialize CPU registers.
474 CALLINIT_KSEG1(altcpu_table,R_ALT_CPUINIT)
476 #ifdef _SB1250_PASS1_WORKAROUNDS_
478 SETCCAMODE(v0,K_CFG_K0COH_CACHEABLE) /* cacheable NONCOHERENT */
481 SETLEDS1('C','P','U','1')
484 * Initialize the L1 cache
488 CALLINIT_KSEG1(altcpu_table,R_ALT_L1CINIT)
493 * Notify the SCD that we're done initializing. Do this by
494 * ringing CPU0's doorbell.
497 la a0,PHYS_TO_K1(A_IMR_REGISTER(0,R_IMR_MAILBOX_SET_CPU));
499 mfc0 t0,C0_PRID # get processor number
500 srl t0,t0,25 # shift CPU bits into low
501 and t0,t0,7 # keep only low 3 bits
502 li t1,1 # make a bit mask depending on CPU
503 sll t1,t1,t0 # calculate t1 = 1 shl cpu number
504 sd t1,0(a0) # set corresponding bit in mailbox
508 * Go to the idle loop
511 b altcpu_idle # go to idle loop
514 * We get here if we were running on CPU0. Make things
515 * pretty for the reset of CPU initialization.
522 * If we are on CPU0, then force CPU1 into reset. This is needed
523 * for the case where the firmware has crashed and we need to get
524 * control of the system again.
527 li a0,PHYS_TO_K1(A_SCD_SYSTEM_CFG)
529 dli t1,M_SYS_CPU_RESET_1 # Reset mask
530 or t0,t1 # New value to write
531 sd t0,0(a0) # CPU1 is now in reset
533 j ra # return (we were on CPU 0)
535 END(sb1250_altcpu_reset)
538 /* *********************************************************************
539 * ALTCPU_CMD_START(cpu,addr)
541 * Start an alternate CPU.
544 * a0 - cpu number (must be 1 for the SB1250)
545 * a1 - pointer to start parameters (four 64-bit values)
546 * array[0] = start address (PC)
547 * array[1] = start stack pointer (SP)
548 * array[2] = start global pointer (GP)
549 * array[3] = start user argument (A1)
553 * else -1 if request could not be handled
554 ********************************************************************* */
556 #define R_CPUSTART_PCVAL 0
557 #define R_CPUSTART_SPVAL 8
558 #define R_CPUSTART_GPVAL 16
559 #define R_CPUSTART_A1VAL 24
561 LEAF(altcpu_cmd_start)
563 li v0,-1 /* assume failure */
564 bne a0,1,1f /* go if not CPU 1 */
567 * Return an error if running in uniprocessor mode.
570 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
571 ld t0,(t0) # Get system revision
572 dsrl t0,S_SYS_PART # Shift part # to low bits
573 dsrl t0,8 # isolate CPU part of number
574 and t0,0x0F # T0 = number of CPUs
575 beq t0,1,1f # If only one CPU, error.
578 * Multiprocessor mode, start the other CPU
581 move t0,a0 /* get CPU number */
582 sll t0,BPWSIZE /* multiply by 8/4 for 64/32-bit offset */
584 la t1,cpu_start_gpvals
585 add t1,t0 /* copy the GP value */
586 ld t2,R_CPUSTART_GPVAL(a1)
589 la t1,cpu_start_spvals
590 add t1,t0 /* copy the SP value */
591 ld t2,R_CPUSTART_SPVAL(a1)
595 add t1,t0 /* copy the A1 value */
596 ld t2,R_CPUSTART_A1VAL(a1)
599 la t1,cpu_startvectors
600 add t1,t0 /* copy the PC value */
601 ld t2,R_CPUSTART_PCVAL(a1)
604 move v0,zero /* success */
608 END(altcpu_cmd_start)
610 /* *********************************************************************
611 * ALTCPU_CMD_STOP(cpu)
613 * Stop the specified CPU.
615 * We don't really support this at the moment.
621 * v0 - 0 if ok, else error code
622 ********************************************************************* */
624 LEAF(altcpu_cmd_stop)
626 li v0,-1 /* assume failure */
627 bne a0,1,1f /* go if not CPU 1 */
630 * Return an error if running in uniprocessor mode.
633 la t0,PHYS_TO_K1(A_SCD_SYSTEM_REVISION)
634 ld t0,(t0) # Get system revision
635 dsrl t0,S_SYS_PART # Shift part # to low bits
636 dsrl t0,8 # isolate CPU part of number
637 and t0,0x0F # T0 = number of CPUs
638 beq t0,1,1f # If only one CPU, error.
641 * Multiprocessor mode, stop the other CPU
644 b sb1250_altcpu_kill /* kill the CPU */
650 /* *********************************************************************
653 * Loop forever waiting for someone to tell us where to go.
660 ********************************************************************* */
665 * Switch to KSEG0 (cached)
668 bal altcpu_kseg0_switch
670 SETLEDS('c','p','u','1')
674 1: la a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CPU))
675 ld t0,(a0) # Read mailbox
676 beq t0,zero,1b # Loop till the bit is set
679 * Clear all the bits in the mailbox register to dismiss the
683 la a0,PHYS_TO_K1(A_IMR_REGISTER(1,R_IMR_MAILBOX_CLR_CPU))
689 * We may need GP, especially in relocated version
691 * Yucky hack: The relocation factor was passed to us in
692 * the mailbox register, which is conveniently in t0 right now.
693 * (except the lower bit is set just in case the reloc was
694 * zero, so clear that first).
699 and t0,t1 # clear lower bit.
701 LOADREL(a0,altcpu_table)
703 ADD gp,t0 # relocate GP.
707 * Now that we can talk to memory again, get the "text relocation"
708 * and move the loop into DRAM.
712 LR t1,mem_textreloc # will get from GP-relative addr
713 LOADREL(t0,altcpu_gorel) # get addr of next instruction
714 add t0,t1 # add in relocation factor
721 * Get our processor number
724 mfc0 t0,C0_PRID # Get PRID (for processor id)
726 srl t0,t0,25 # shift CPU bits into low
727 and t0,t0,7 # keep only low 3 bits
728 sll t0,t0,BPWSIZE # mult by 8/4 for 64/32-bit offset
731 * Set up registers like we were launching a program.
734 LR a2,R_ALT_APIENTRY(a0) # A2 = firmware entry vector
735 move a0,gp # A0 = handle
736 li a3,CFE_EPTSEAL # A3 = entrypoint signature
739 #ifdef _SB1250_PASS1_WORKAROUNDS_
741 * Okay, it's safe now to be coherent.
742 * Flush the D cache to invalidate all the lines we have,
743 * then change the config register back.
745 * Danger! It's imperative that *no stores to memory* be done
746 * prior to this point, otherwise flushing the cache
747 * will race with core 0, which will also be flushing
748 * lines at this time.
751 jal sb1250_l1cache_flush_d
752 SETCCAMODE(v0,K_CFG_K0COH_COHERENT) /* cacheable coherent */
757 * Read the start address from the CPU restart table
758 * and jump to it. For an idle CPU, the address in the
759 * table below will be the zero, causing
760 * the CPU to loop forever. To start a secondary CPU,
761 * just write an address in cpu_startvectors[cpu_id]
763 * Warning: This kind of assumes that this code will
764 * live in cacheable space. If it doesn't, it will
765 * probably cause lots of unwanted bus traffic.
769 loop_forever: LR t1,cpu_startvectors(t0) # Load address of routine
770 beq t1,zero,loop_forever
771 LR a1,cpu_start_args(t0) # Load user argument (A1)
772 LR sp,cpu_start_spvals(t0) # Load stack pointer
773 LR t2,cpu_start_gpvals(t0) # Load global pointer
774 move gp,t2 # and put in real register
775 j t1 # jump to start address
778 /* *********************************************************************
780 ********************************************************************* */