root/tools/arch/loongarch/include/asm/inst.h
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
 */
#ifndef _ASM_INST_H
#define _ASM_INST_H

#include <linux/bitops.h>

#define LOONGARCH_INSN_NOP              0x03400000

enum reg0i15_op {
        break_op        = 0x54,
};

enum reg0i26_op {
        b_op            = 0x14,
        bl_op           = 0x15,
};

enum reg1i21_op {
        beqz_op         = 0x10,
        bnez_op         = 0x11,
        bceqz_op        = 0x12, /* bits[9:8] = 0x00 */
        bcnez_op        = 0x12, /* bits[9:8] = 0x01 */
};

enum reg2_op {
        ertn_op         = 0x1920e,
};

enum reg2i12_op {
        addid_op        = 0x0b,
        andi_op         = 0x0d,
        ldd_op          = 0xa3,
        std_op          = 0xa7,
};

enum reg2i14_op {
        ldptrd_op       = 0x26,
        stptrd_op       = 0x27,
};

enum reg2i16_op {
        jirl_op         = 0x13,
        beq_op          = 0x16,
        bne_op          = 0x17,
        blt_op          = 0x18,
        bge_op          = 0x19,
        bltu_op         = 0x1a,
        bgeu_op         = 0x1b,
};

enum reg3_op {
        amswapw_op      = 0x70c0,
};

struct reg0i15_format {
        unsigned int immediate : 15;
        unsigned int opcode : 17;
};

struct reg0i26_format {
        unsigned int immediate_h : 10;
        unsigned int immediate_l : 16;
        unsigned int opcode : 6;
};

struct reg1i21_format {
        unsigned int immediate_h  : 5;
        unsigned int rj : 5;
        unsigned int immediate_l : 16;
        unsigned int opcode : 6;
};

struct reg2_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
        unsigned int opcode : 22;
};

struct reg2i12_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
        unsigned int immediate : 12;
        unsigned int opcode : 10;
};

struct reg2i14_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
        unsigned int immediate : 14;
        unsigned int opcode : 8;
};

struct reg2i16_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
        unsigned int immediate : 16;
        unsigned int opcode : 6;
};

struct reg3_format {
        unsigned int rd : 5;
        unsigned int rj : 5;
        unsigned int rk : 5;
        unsigned int opcode : 17;
};

union loongarch_instruction {
        unsigned int word;
        struct reg0i15_format   reg0i15_format;
        struct reg0i26_format   reg0i26_format;
        struct reg1i21_format   reg1i21_format;
        struct reg2_format      reg2_format;
        struct reg2i12_format   reg2i12_format;
        struct reg2i14_format   reg2i14_format;
        struct reg2i16_format   reg2i16_format;
        struct reg3_format      reg3_format;
};

#define LOONGARCH_INSN_SIZE     sizeof(union loongarch_instruction)

enum loongarch_gpr {
        LOONGARCH_GPR_ZERO = 0,
        LOONGARCH_GPR_RA = 1,
        LOONGARCH_GPR_TP = 2,
        LOONGARCH_GPR_SP = 3,
        LOONGARCH_GPR_A0 = 4,   /* Reused as V0 for return value */
        LOONGARCH_GPR_A1,       /* Reused as V1 for return value */
        LOONGARCH_GPR_A2,
        LOONGARCH_GPR_A3,
        LOONGARCH_GPR_A4,
        LOONGARCH_GPR_A5,
        LOONGARCH_GPR_A6,
        LOONGARCH_GPR_A7,
        LOONGARCH_GPR_T0 = 12,
        LOONGARCH_GPR_T1,
        LOONGARCH_GPR_T2,
        LOONGARCH_GPR_T3,
        LOONGARCH_GPR_T4,
        LOONGARCH_GPR_T5,
        LOONGARCH_GPR_T6,
        LOONGARCH_GPR_T7,
        LOONGARCH_GPR_T8,
        LOONGARCH_GPR_FP = 22,
        LOONGARCH_GPR_S0 = 23,
        LOONGARCH_GPR_S1,
        LOONGARCH_GPR_S2,
        LOONGARCH_GPR_S3,
        LOONGARCH_GPR_S4,
        LOONGARCH_GPR_S5,
        LOONGARCH_GPR_S6,
        LOONGARCH_GPR_S7,
        LOONGARCH_GPR_S8,
        LOONGARCH_GPR_MAX
};

#define DEF_EMIT_REG2I16_FORMAT(NAME, OP)                               \
static inline void emit_##NAME(union loongarch_instruction *insn,       \
                               enum loongarch_gpr rj,                   \
                               enum loongarch_gpr rd,                   \
                               int offset)                              \
{                                                                       \
        insn->reg2i16_format.opcode = OP;                               \
        insn->reg2i16_format.immediate = offset;                        \
        insn->reg2i16_format.rj = rj;                                   \
        insn->reg2i16_format.rd = rd;                                   \
}

DEF_EMIT_REG2I16_FORMAT(jirl, jirl_op)

#endif /* _ASM_INST_H */