root/usr/src/uts/sun4u/io/panther_asm.S
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * Assembly code support for the Cheetah+ module
 */

#include "assym.h"

#include <sys/asm_linkage.h>
#include <sys/mmu.h>
#include <vm/hat_sfmmu.h>
#include <sys/machparam.h>
#include <sys/machcpuvar.h>
#include <sys/machthread.h>
#include <sys/machtrap.h>
#include <sys/privregs.h>
#include <sys/asm_linkage.h>
#include <sys/trap.h>
#include <sys/cheetahregs.h>
#include <sys/xc_impl.h>
#include <sys/intreg.h>
#include <sys/async.h>
#include <sys/clock.h>
#include <sys/cheetahasm.h>
#include <sys/cmpregs.h>

#ifdef TRAPTRACE
#include <sys/traptrace.h>
#endif /* TRAPTRACE */


        .global retire_l2_start
        .global retire_l2_end
        .global unretire_l2_start
        .global unretire_l2_end
        .global retire_l3_start
        .global retire_l3_end
        .global unretire_l3_start
        .global unretire_l3_end

/*
 * Panther version to reflush a line from both the L2 cache and L3
 * cache by the respective indexes. Flushes all ways of the line from
 * each cache.
 *
 * l2_index     Index into the L2$ of the line to be flushed. This
 *              register will not be modified by this routine.
 * l3_index     Index into the L3$ of the line to be flushed. This
 *              register will not be modified by this routine.
 * scr2         scratch register.
 * scr3         scratch register.
 *
 */
#define PN_ECACHE_REFLUSH_LINE(l2_index, l3_index, scr2, scr3)          \
        set     PN_L2_MAX_SET, scr2;                                    \
        set     PN_L2_SET_SIZE, scr3;                                   \
1:                                                                      \
        ldxa    [l2_index + scr2]ASI_L2_TAG, %g0;                       \
        cmp     scr2, %g0;                                              \
        bg,a    1b;                                                     \
          sub   scr2, scr3, scr2;                                       \
        mov     6, scr2;                                                \
6:                                                                      \
        cmp     scr2, %g0;                                              \
        bg,a    6b;                                                     \
          sub   scr2, 1, scr2;                                          \
        set     PN_L3_MAX_SET, scr2;                                    \
        set     PN_L3_SET_SIZE, scr3;                                   \
2:                                                                      \
        ldxa    [l3_index + scr2]ASI_EC_DIAG, %g0;                      \
        cmp     scr2, %g0;                                              \
        bg,a    2b;                                                     \
          sub   scr2, scr3, scr2;

/*
 * Panther version of ecache_flush_line. Flushes the line corresponding
 * to physaddr from both the L2 cache and the L3 cache.
 *
 * physaddr     Input: Physical address to flush.
 *              Output: Physical address to flush (preserved).
 * l2_idx_out   Input: scratch register.
 *              Output: Index into the L2$ of the line to be flushed.
 * l3_idx_out   Input: scratch register.
 *              Output: Index into the L3$ of the line to be flushed.
 * scr3         scratch register.
 * scr4         scratch register.
 *
 */
#define PN_ECACHE_FLUSH_LINE(physaddr, l2_idx_out, l3_idx_out, scr3, scr4)      \
        set     PN_L3_SET_SIZE, l2_idx_out;                                     \
        sub     l2_idx_out, 1, l2_idx_out;                                      \
        and     physaddr, l2_idx_out, l3_idx_out;                               \
        set     PN_L3_IDX_DISP_FLUSH, l2_idx_out;                               \
        or      l2_idx_out, l3_idx_out, l3_idx_out;                             \
        set     PN_L2_SET_SIZE, l2_idx_out;                                     \
        sub     l2_idx_out, 1, l2_idx_out;                                      \
        and     physaddr, l2_idx_out, l2_idx_out;                               \
        set     PN_L2_IDX_DISP_FLUSH, scr3;                                     \
        or      l2_idx_out, scr3, l2_idx_out;                                   \
        PN_ECACHE_REFLUSH_LINE(l2_idx_out, l3_idx_out, scr3, scr4)


        .align 4096
        ENTRY(retire_l2)
retire_l2_start:

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value
        clr     %o5     ! assume success
8:
        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %g2, %g3)
1:
        ! Check if line is invalid; if so, NA it.
        ldxa    [%o0]ASI_L2_TAG, %o3
        btst    0x7, %o3
        bnz     %xcc, 2f
         nop
        stxa    %o1, [%o0]ASI_L2_TAG
        membar #Sync    ! still on same cache line
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions, so we cross a cache boundary
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
2:
        ! It is OK to have STATE as NA (if so, nothing to do!)
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        be,a,pt %xcc, 9b
         mov    1, %o5  ! indicate was already NA
        ! Hmm.  Not INV, not NA.
        cmp     %o5, 0
        be,a,pt %xcc, 8b        ! Flush the cacheline again
         mov    2, %o5  ! indicate retry was done
        ! We already Flushed cacheline second time. Return -1
        clr     %o5
        ba      9b
         dec    %o5
retire_l2_end:
        SET_SIZE(retire_l2)

        ENTRY(unretire_l2)
unretire_l2_start:

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check that line is in NA state; if so, INV it.
        ldxa    [%o0]ASI_L2_TAG, %o3
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        bne,a,pt %xcc, 9f       ! Wasn't NA, so something is wrong
         dec    %o5     ! indicate not NA
        stxa    %g0, [%o0]ASI_L2_TAG
        membar #Sync
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
unretire_l2_end:
        SET_SIZE(unretire_l2)

        ENTRY(retire_l3)
retire_l3_start:

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        ! PN-ECACHE-FLUSH_LINE is 30 instructions
        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check if line is invalid; if so, NA it.
        ldxa    [%o0]ASI_EC_DIAG, %o3
        btst    0x7, %o3
        bnz     %xcc, 2f
         nop
        stxa    %o1, [%o0]ASI_EC_DIAG
        membar #Sync    ! still on same cache line
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions, so we cross a cache boundary
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
2:
        ! It is OK to have STATE as NA (if so, nothing to do!)
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        be,a,pt %xcc, 9b
         inc    %o5     ! indicate was already NA
        ! Hmm.  Not INV, not NA
        ba      9b
         dec    %o5
retire_l3_end:
        SET_SIZE(retire_l3)

        ENTRY(unretire_l3)
unretire_l3_start:

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check that line is in NA state; if so, INV it.
        ldxa    [%o0]ASI_EC_DIAG, %o3
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        bne,a,pt %xcc, 9f       ! Wasn't NA, so something is wrong
         dec    %o5     ! indicate not NA
        stxa    %g0, [%o0]ASI_EC_DIAG
        membar #Sync
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
unretire_l3_end:
        SET_SIZE(unretire_l3)

        .align 2048

        ENTRY(retire_l2_alternate)

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value
        clr     %o5     ! assume success
8:
        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %g2, %g3)
1:
        ! Check if line is invalid; if so, NA it.
        ldxa    [%o0]ASI_L2_TAG, %o3
        btst    0x7, %o3
        bnz     %xcc, 2f
         nop
        stxa    %o1, [%o0]ASI_L2_TAG
        membar #Sync    ! still on same cache line
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions, so we cross a cache boundary
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
2:
        ! It is OK to have STATE as NA (if so, nothing to do!)
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        be,a,pt %xcc, 9b
         mov    1, %o5  ! indicate was already NA
        ! Hmm.  Not INV, not NA.
        cmp     %o5, 0
        be,a,pt %xcc, 8b        ! Flush the cacheline again
         mov    2, %o5  ! indicate retry was done
        ! We already Flushed cacheline second time. Return -1
        clr     %o5
        ba      9b
         dec    %o5
        SET_SIZE(retire_l2_alternate)

        ENTRY(unretire_l2_alternate)

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check that line is in NA state; if so, INV it.
        ldxa    [%o0]ASI_L2_TAG, %o3
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        bne,a,pt %xcc, 9f       ! Wasn't NA, so something is wrong
         dec    %o5     ! indicate not NA
        stxa    %g0, [%o0]ASI_L2_TAG
        membar #Sync
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
        SET_SIZE(unretire_l2_alternate)

        ENTRY(retire_l3_alternate)

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        ! PN-ECACHE-FLUSH_LINE is 30 instructions
        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check if line is invalid; if so, NA it.
        ldxa    [%o0]ASI_EC_DIAG, %o3
        btst    0x7, %o3
        bnz     %xcc, 2f
         nop
        stxa    %o1, [%o0]ASI_EC_DIAG
        membar #Sync    ! still on same cache line
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions, so we cross a cache boundary
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
2:
        ! It is OK to have STATE as NA (if so, nothing to do!)
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        be,a,pt %xcc, 9b
         inc    %o5     ! indicate was already NA
        ! Hmm.  Not INV, not NA
        ba      9b
         dec    %o5
        SET_SIZE(retire_l3_alternate)

        ENTRY(unretire_l3_alternate)

        ! since we disable interrupts, we don't need to do kpreempt_disable()
        rdpr    %pstate, %o2
        andn    %o2, PSTATE_IE, %g1
        wrpr    %g0, %g1, %pstate               ! disable interrupts
        /*
         * Save current DCU state.  Turn off IPS
         */
        setx    DCU_IPS_MASK, %g2, %o3
        ldxa    [%g0]ASI_DCU, %g1       ! save DCU in %g1
        andn    %g1, %o3, %g4
        stxa    %g4, [%g0]ASI_DCU
        flush   %g0     /* flush required after changing the IC bit */
        PARK_SIBLING_CORE(%g1, %o3, %o4)        ! %g1 has DCU value

        PN_ECACHE_FLUSH_LINE(%o0, %o3, %o4, %o5, %g2)
1:
        clr     %o5     ! assume success
        ! Check that line is in NA state; if so, INV it.
        ldxa    [%o0]ASI_EC_DIAG, %o3
        and     %o3, 0x7, %o3
        cmp     %o3, 0x5
        bne,a,pt %xcc, 9f       ! Wasn't NA, so something is wrong
         dec    %o5     ! indicate not NA
        stxa    %g0, [%o0]ASI_EC_DIAG
        membar #Sync
        ! now delay 15 cycles so we don't have hazard when we return
        mov     16, %o1
1:
        brnz,pt %o1, 1b
         dec    %o1
9:
        ! UNPARK-SIBLING_CORE is 7 instructions
        UNPARK_SIBLING_CORE(%g1, %o3, %o4)      ! 7 instructions
        /*
         * Restore the DCU
         */
        stxa    %g1, [%g0]ASI_DCU
        flush   %g0
        wrpr    %g0, %o2, %pstate               !restore pstate
        retl
         mov    %o5, %o0
        SET_SIZE(unretire_l3_alternate)

        ENTRY(get_ecache_dtags_tl1)


        PARK_SIBLING_CORE(%g3, %g4, %g5)
        add     %g2, CH_CLO_DATA + CH_CHD_EC_DATA, %g2
        rd      %asi, %g4
        wr      %g0, ASI_N, %asi
        GET_ECACHE_DTAGS(%g1, %g2, %g5, %g6, %g7)
        wr      %g4, %asi
        UNPARK_SIBLING_CORE(%g3, %g4, %g5)      ! can use %g3 again

        retry
        SET_SIZE(get_ecache_dtags_tl1)

        ENTRY(get_l2_tag_tl1)

        /*
         * Now read the tag data
         */
        ldxa    [%g1]ASI_L2_TAG, %g4            ! save tag_data
        stx     %g4, [%g2]

        retry
        SET_SIZE(get_l2_tag_tl1)

        ENTRY(get_l3_tag_tl1)

        /*
         * Now read the tag data
         */
        ldxa    [%g1]ASI_EC_DIAG, %g4           ! save tag_data
        stx     %g4, [%g2]

        retry
        SET_SIZE(get_l3_tag_tl1)