root/sys/arch/powerpc/booke/e500_mpsubr.S
/*-
 * Copyright (c) 2011, 2012 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Matt Thomas of 3am Software Foundry.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY 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)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * r3 = fdt pointer (ignored)
 * r4 = 0
 * r5 = 0
 * r6 = EPAPR magic (0x45505150)
 * r7 = TLB1[0] entry size (64MB)
 * r8 = 0
 * r9 = 0
 */
        .p2align 5
ENTRY_NOPROFILE(e500_spinup_trampoline)

        lis     %r31, 0xdeadbeef@h
        ori     %r31, %r31, 0xdeadbeef@l
        mr      %r30, %r31
        mr      %r29, %r31
        mr      %r28, %r31
        mr      %r27, %r31
        mr      %r26, %r31
        mr      %r25, %r31
        mr      %r24, %r31
        mr      %r23, %r31
        mr      %r22, %r31
        mr      %r21, %r31
        mr      %r20, %r31
        mr      %r19, %r31
        mr      %r18, %r31
        mr      %r17, %r31
        mr      %r16, %r31
        mr      %r15, %r31
        mr      %r14, %r31
        mr      %r13, %r31
        mr      %r12, %r31
        mr      %r11, %r31
        mr      %r10, %r31
        mr      %r2, %r31

        /*
         * First thing we need to do is to set SPRG0 with our cpu_info
         * and get our initial stack pointer (this must be within the
         * bounds of the TLB1[0] entry U-boot setup for us).
         *
         * cpu_hatch will return a new SP to use.
         *
         * All the caller-saved register are ours to use.  So we will.
         */
        lis     %r20, _C_LABEL(cpu_hatch_data)@h
        ori     %r20, %r20, _C_LABEL(cpu_hatch_data)@l
        sync

        /*
         * Ensure that the TLB entry we are using is memory coherent.
         */
        lis     %r0, (MASX_TLBSEL_MAKE(1)|MAS0_ESEL_MAKE(0))@h
        mtspr   SPR_MAS0, %r0                   /* setup MAS0 */
        lis     %r3, (MAS1_V|MAS1_IPROT)@h      /* V | IPROT */
        ori     %r3, %r3, MASX_TSIZE_64MB       /* and 64MB */
        mtspr   SPR_MAS1, %r3                   /* save MAS1 */
        li      %r3, MAS2_M                     /* set M bit */
        mtspr   SPR_MAS2, %r3                   /* save MAS2 */
        li      %r3, MAS3_SX|MAS3_SR|MAS3_SW    /* set kernel RWX */
        mtspr   SPR_MAS3, %r3                   /* save MAS3 */
        tlbwe                                   /* update entry */
        isync                                   /* flush i-stream */
        sync                                    /* sync memory. */

        li      %r0, 0
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */
        sync
#if 0
        dcbf    0, %r20
#endif

        lwz     %r1, HATCH_SP(%r20)             /* get hatch SP */
        lwz     %r21, HATCH_CI(%r20)            /* get cpu_info */
        mtsprg0 %r21                            /* save cpu_info */
        lwz     %r13, CI_CURLWP(%r21)           /* load r13 with curlwp */
        mtsprg2 %r13                            /* save it in sprg2 */
        addi    %r0,%r21,CI_SAVELIFO            /* get SAVE area start */
        mtsprg3 %r0                             /* save it in sprg3 */

        /*
         * Now to synchronize timebase values.  First to make sure HID0 is
         * set correctly, except with the timebase disabled.
         */
        lwz     %r22, HATCH_HID0(%r20)          /* get HID0 */
        li      %r28, HID0_TBEN                 /* HID0_TBEN */
        andc    %r0,%r22,%r28                   /* clear TBEN from HID0 */
        mtspr   SPR_HID0, %r0                   /* set HID0 (timebase off) */
        isync
        lwz     %r24, HATCH_TBL(%r20)           /* get lower timebase value */
        lwz     %r23, HATCH_TBU(%r20)           /* get upper timebase value */

        /*
         * Figure out how much we are adjusting the timebase
         */
        mftbl   %r4                             /* get lower timebase */
        subfc   %r0, %r4, %r24                  /* subtract from new value */
        stw     %r0, HATCH_TBL(%r20)            /* save it */
        mftbu   %r3                             /* get upper timebase */
        subfe   %r0, %r3, %r23                  /* subtract from new value */
        stw     %r0, HATCH_TBU(%r20)            /* save it */

        /*
         * Since we've disabled timebase, we can set the timebase registers
         * without fear of them changing.  Have to do this after we read the
         * previous values.
         */
        mttbu   %r23                            /* set upper timebase */
        mttbl   %r24                            /* set lower timebase */

        /*
         * Now we loop until the boot cpu tells us to enable timebase
         */
1:      lwz     %r0, HATCH_RUNNING(%r20)        /* is it time? */
        cmplwi  %r0, 0
        beq+    1b

        mtspr   SPR_HID0, %r22                  /* start timebase running */
        isync

        li      %r0, 2
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */
        sync

        /*
         * We have to setup the IVOR SPRs since the ones u-boot setup
         * don't work for us.
         */
        bl      _C_LABEL(exception_init)        /* setup IVORs */

        li      %r0, 3
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * U-boot has mapped the bottom 64MB in TLB1[0].  We are going to need
         * to change this entry and it's not safe to do so while running out
         * of it.  So we copy TLB1[0] to TLB1[1] but set it for AS1.  We then
         * switch to AS1 and reload TLB1[0] with its correct value, and then we
         * switch back to AS0.  After that, we can load the rest of the TLB1
         * entries.
         */

        /*
         * Fetch TLB1[0]
         */
        lis     %r16, (MASX_TLBSEL_MAKE(1)|MAS0_ESEL_MAKE(0))@h
        mtspr   SPR_MAS0, %r16
        tlbre

        li      %r0, 4
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Copy TLB1[0] to TLB[1] and set it to use AS1
         */
        mfspr   %r3, SPR_MAS0
        addis   %r3, %r3, MAS0_ESEL@h           /* advance to next TLB entry */
        mtspr   SPR_MAS0, %r3                   /* place into SPR */
        mfspr   %r4, SPR_MAS1
        ori     %r4, %r4, MAS1_TS@l             /* Make it use AS1 */
        mtspr   SPR_MAS1, %r4
        tlbwe                                   /* write the TLB entry */

        li      %r0, 5
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Let's find out what TLB1 entry we are supposed to use.
         */
        lwz     %r3, HATCH_TLBIDX(%r20)
        bl      _C_LABEL(e500_tlb1_fetch)
        lmw     %r28, 0(%r3)                    /* load the saved TLB1 entry */

        li      %r0, 6
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Now to switch to running in AS1
         */
        mfmsr   %r3
        ori     %r4,%r3,(PSL_DS|PSL_IS)@l
        mtsrr1  %r4

        bl      1f
1:      mflr    %r11
        addi    %r4,%r11,.Las1start-1b
        addi    %r5,%r11,.Las1end-1b
        mtsrr0  %r4
        li      %r0, 7
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */
        rfi                     /* switch to AS1, context synchronizing */

.Las1start:
        /*
         * We are now running in AS1, update TLB1[0]
         */
        li      %r0, 8
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Let's clear TBL1[0] and TBL1[1]
         */
        li      %r8, 0
        mtspr   SPR_MAS1, %r8
        mtspr   SPR_MAS2, %r8
        mtspr   SPR_MAS3, %r8
        mtspr   SPR_MAS7, %r8

        lis     %r8, (MASX_TLBSEL_MAKE(1)|MAS0_ESEL_MAKE(0))@h
        mtspr   SPR_MAS0, %r8
        tlbwe

        lis     %r8, (MASX_TLBSEL_MAKE(1)|MAS0_ESEL_MAKE(1))@h
        mtspr   SPR_MAS0, %r8
        tlbwe

        /*
         * Now load the new TLB data into the MAS registers.
         */
        mtspr   SPR_MAS0, %r28                  /* place into SPRs */
        mtspr   SPR_MAS1, %r29
        mtspr   SPR_MAS2, %r30
        mtspr   SPR_MAS3, %r31
        tlbwe

        mtsrr0  %r5
        mtsrr1  %r3

        li      %r0, 9
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        rfi                     /* switch back to AS0, context synchronizing */

.Las1end:
        li      %r0, 10
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * We now have our TLB1[0] in place.  Now we need to load the rest of
         * TLB1 with our entries.  After this is done, we should have access
         * to everything.
         */
        bl      _C_LABEL(e500_tlb1_sync)

        li      %r0, 11
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Now we can use our stack...
         */
        lwz     %r3, CI_CURPCB(%r21)
        lwz     %r1, PCB_SP(%r3)

        li      %r0, 12
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Tell spinup code we are done with the hatch stack.
         */
        li      %r0, 0
        stw     %r0, HATCH_SP(%r20)

        li      %r0, 13
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * We've gotten the low level stuff done.
         * Now to do more advanced stuff.
         */
        mr      %r3, %r21
        bl      _C_LABEL(e500_cpu_hatch)        

        li      %r0, 14
        stw     %r0, HATCH_RUNNING(%r20)        /* progress */

        /*
         * Now wait to become runnable
         */
        bl      _C_LABEL(cpu_hatch)

        wrteei  1                               /* allow interrupts */
        bl      _C_LABEL(spl0)                  /* unblock interrupts */

        b       _C_LABEL(idle_loop)