root/arch/xtensa/include/asm/asmmacro.h
/*
 * include/asm-xtensa/asmmacro.h
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2005 Tensilica Inc.
 */

#ifndef _XTENSA_ASMMACRO_H
#define _XTENSA_ASMMACRO_H

#include <linux/export.h>
#include <asm/core.h>

/*
 * Some little helpers for loops. Use zero-overhead-loops
 * where applicable and if supported by the processor.
 *
 * __loopi ar, at, size, inc
 *         ar   register initialized with the start address
 *         at   scratch register used by macro
 *         size size immediate value
 *         inc  increment
 *
 * __loops ar, as, at, inc_log2[, mask_log2][, cond][, ncond]
 *         ar   register initialized with the start address
 *         as   register initialized with the size
 *         at   scratch register use by macro
 *         inc_log2     increment [in log2]
 *         mask_log2    mask [in log2]
 *         cond         true condition (used in loop'cond')
 *         ncond        false condition (used in b'ncond')
 *
 * __loop  as
 *         restart loop. 'as' register must not have been modified!
 *
 * __endla ar, as, incr
 *         ar   start address (modified)
 *         as   scratch register used by __loops/__loopi macros or
 *              end address used by __loopt macro
 *         inc  increment
 */

/*
 * loop for given size as immediate
 */

        .macro  __loopi ar, at, size, incr

#if XCHAL_HAVE_LOOPS
                movi    \at, ((\size + \incr - 1) / (\incr))
                loop    \at, 99f
#else
                addi    \at, \ar, \size
                98:
#endif

        .endm

/*
 * loop for given size in register
 */

        .macro  __loops ar, as, at, incr_log2, mask_log2, cond, ncond

#if XCHAL_HAVE_LOOPS
                .ifgt \incr_log2 - 1
                        addi    \at, \as, (1 << \incr_log2) - 1
                        .ifnc \mask_log2,
                                extui   \at, \at, \incr_log2, \mask_log2
                        .else
                                srli    \at, \at, \incr_log2
                        .endif
                .endif
                loop\cond       \at, 99f
#else
                .ifnc \mask_log2,
                        extui   \at, \as, \incr_log2, \mask_log2
                .else
                        .ifnc \ncond,
                                srli    \at, \as, \incr_log2
                        .endif
                .endif
                .ifnc \ncond,
                        b\ncond \at, 99f

                .endif
                .ifnc \mask_log2,
                        slli    \at, \at, \incr_log2
                        add     \at, \ar, \at
                .else
                        add     \at, \ar, \as
                .endif
#endif
                98:

        .endm

/*
 * loop from ar to as
 */

        .macro  __loopt ar, as, at, incr_log2

#if XCHAL_HAVE_LOOPS
                sub     \at, \as, \ar
                .ifgt   \incr_log2 - 1
                        addi    \at, \at, (1 << \incr_log2) - 1
                        srli    \at, \at, \incr_log2
                .endif
                loop    \at, 99f
#else
                98:
#endif

        .endm

/*
 * restart loop. registers must be unchanged
 */

        .macro  __loop  as

#if XCHAL_HAVE_LOOPS
                loop    \as, 99f
#else
                98:
#endif

        .endm

/*
 * end of loop with no increment of the address.
 */

        .macro  __endl  ar, as
#if !XCHAL_HAVE_LOOPS
                bltu    \ar, \as, 98b
#endif
                99:
        .endm

/*
 * end of loop with increment of the address.
 */

        .macro  __endla ar, as, incr
                addi    \ar, \ar, \incr
                __endl  \ar \as
        .endm

/* Load or store instructions that may cause exceptions use the EX macro. */

#define EX(handler)                             \
        .section __ex_table, "a";               \
        .word   97f, handler;                   \
        .previous                               \
97:


/*
 * Extract unaligned word that is split between two registers w0 and w1
 * into r regardless of machine endianness. SAR must be loaded with the
 * starting bit of the word (see __ssa8).
 */

        .macro __src_b  r, w0, w1
#ifdef __XTENSA_EB__
                src     \r, \w0, \w1
#else
                src     \r, \w1, \w0
#endif
        .endm

/*
 * Load 2 lowest address bits of r into SAR for __src_b to extract unaligned
 * word starting at r from two registers loaded from consecutive aligned
 * addresses covering r regardless of machine endianness.
 *
 *      r   0   1   2   3
 * LE SAR   0   8  16  24
 * BE SAR  32  24  16   8
 */

        .macro __ssa8   r
#ifdef __XTENSA_EB__
                ssa8b   \r
#else
                ssa8l   \r
#endif
        .endm

        .macro  do_nsau cnt, val, tmp, a
#if XCHAL_HAVE_NSA
        nsau    \cnt, \val
#else
        mov     \a, \val
        movi    \cnt, 0
        extui   \tmp, \a, 16, 16
        bnez    \tmp, 0f
        movi    \cnt, 16
        slli    \a, \a, 16
0:
        extui   \tmp, \a, 24, 8
        bnez    \tmp, 1f
        addi    \cnt, \cnt, 8
        slli    \a, \a, 8
1:
        movi    \tmp, __nsau_data
        extui   \a, \a, 24, 8
        add     \tmp, \tmp, \a
        l8ui    \tmp, \tmp, 0
        add     \cnt, \cnt, \tmp
#endif /* !XCHAL_HAVE_NSA */
        .endm

        .macro  do_abs dst, src, tmp
#if XCHAL_HAVE_ABS
        abs     \dst, \src
#else
        neg     \tmp, \src
        movgez  \tmp, \src, \src
        mov     \dst, \tmp
#endif
        .endm

#if defined(__XTENSA_WINDOWED_ABI__)

/* Assembly instructions for windowed kernel ABI. */
#define KABI_W
/* Assembly instructions for call0 kernel ABI (will be ignored). */
#define KABI_C0 #

#define XTENSA_FRAME_SIZE_RESERVE       16
#define XTENSA_SPILL_STACK_RESERVE      32

#define abi_entry(frame_size) \
        entry sp, (XTENSA_FRAME_SIZE_RESERVE + \
                   (((frame_size) + XTENSA_STACK_ALIGNMENT - 1) & \
                    -XTENSA_STACK_ALIGNMENT))
#define abi_entry_default abi_entry(0)

#define abi_ret(frame_size) retw
#define abi_ret_default retw

        /* direct call */
#define abi_call call4
        /* indirect call */
#define abi_callx callx4
        /* outgoing call argument registers */
#define abi_arg0 a6
#define abi_arg1 a7
#define abi_arg2 a8
#define abi_arg3 a9
#define abi_arg4 a10
#define abi_arg5 a11
        /* return value */
#define abi_rv a6
        /* registers preserved across call */
#define abi_saved0 a2
#define abi_saved1 a3

        /* none of the above */
#define abi_tmp0 a4
#define abi_tmp1 a5

#elif defined(__XTENSA_CALL0_ABI__)

/* Assembly instructions for windowed kernel ABI (will be ignored). */
#define KABI_W #
/* Assembly instructions for call0 kernel ABI. */
#define KABI_C0

#define XTENSA_SPILL_STACK_RESERVE      0

#define abi_entry(frame_size) __abi_entry (frame_size)

        .macro  __abi_entry frame_size
        .ifgt \frame_size
        addi sp, sp, -(((\frame_size) + XTENSA_STACK_ALIGNMENT - 1) & \
                       -XTENSA_STACK_ALIGNMENT)
        .endif
        .endm

#define abi_entry_default

#define abi_ret(frame_size) __abi_ret (frame_size)

        .macro  __abi_ret frame_size
        .ifgt \frame_size
        addi sp, sp, (((\frame_size) + XTENSA_STACK_ALIGNMENT - 1) & \
                      -XTENSA_STACK_ALIGNMENT)
        .endif
        ret
        .endm

#define abi_ret_default ret

        /* direct call */
#define abi_call call0
        /* indirect call */
#define abi_callx callx0
        /* outgoing call argument registers */
#define abi_arg0 a2
#define abi_arg1 a3
#define abi_arg2 a4
#define abi_arg3 a5
#define abi_arg4 a6
#define abi_arg5 a7
        /* return value */
#define abi_rv a2
        /* registers preserved across call */
#define abi_saved0 a12
#define abi_saved1 a13

        /* none of the above */
#define abi_tmp0 a8
#define abi_tmp1 a9

#else
#error Unsupported Xtensa ABI
#endif

#if defined(USER_SUPPORT_WINDOWED)
/* Assembly instructions for windowed user ABI. */
#define UABI_W
/* Assembly instructions for call0 user ABI (will be ignored). */
#define UABI_C0 #
#else
/* Assembly instructions for windowed user ABI (will be ignored). */
#define UABI_W #
/* Assembly instructions for call0 user ABI. */
#define UABI_C0
#endif

#define __XTENSA_HANDLER        .section ".exception.text", "ax"

#endif /* _XTENSA_ASMMACRO_H */